110 lines
3.5 KiB
Vue
110 lines
3.5 KiB
Vue
<template>
|
|
<div>
|
|
<div class="screen-grid">
|
|
<div class="screen-card" style="grid-column: span 6;">
|
|
<h3>工厂地区分布</h3>
|
|
<div ref="regionChart" style="height: 260px;"></div>
|
|
</div>
|
|
<div class="screen-card" style="grid-column: span 6;">
|
|
<h3>工厂材料分类分布</h3>
|
|
<div ref="categoryChart" style="height: 260px;"></div>
|
|
</div>
|
|
<div class="screen-card" style="grid-column: span 12;">
|
|
<h3>工厂列表</h3>
|
|
<div class="scroll-list">
|
|
<div class="scroll-inner">
|
|
<div v-for="item in factories" :key="item.id" class="scroll-item" @click="goDetail(item)">
|
|
<div>{{ item.factory_name }} · {{ formatRegion(item.province, item.city, '') }}</div>
|
|
<div style="font-size: 12px; color: #9fb3c8;">{{ item.website }}</div>
|
|
</div>
|
|
<div v-for="item in factories" :key="`copy-${item.id}`" class="scroll-item" @click="goDetail(item)">
|
|
<div>{{ item.factory_name }} · {{ formatRegion(item.province, item.city, '') }}</div>
|
|
<div style="font-size: 12px; color: #9fb3c8;">{{ item.website }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import * as echarts from 'echarts'
|
|
import { regionLabel, formatRegion } from '@/utils/region'
|
|
import { fetchFactoryStats } from '@/api/statistics'
|
|
|
|
const router = useRouter()
|
|
const factories = ref([])
|
|
const regionChart = ref(null)
|
|
const categoryChart = ref(null)
|
|
let charts = []
|
|
let timer = null
|
|
const onResize = () => charts.forEach((chart) => chart.resize())
|
|
|
|
const initCharts = () => {
|
|
charts = [
|
|
echarts.init(regionChart.value),
|
|
echarts.init(categoryChart.value)
|
|
]
|
|
}
|
|
|
|
const updateCharts = (data) => {
|
|
const regionData = (data.region_stats || []).map((item) => ({
|
|
name: `${regionLabel(item.province)}-${regionLabel(item.city)}`,
|
|
value: item.count
|
|
}))
|
|
charts[0].setOption({
|
|
tooltip: { trigger: 'item' },
|
|
series: [{ type: 'pie', radius: ['30%', '70%'], data: regionData }]
|
|
})
|
|
|
|
const factoryStats = data.factory_category_stats || []
|
|
const factoriesAxis = factoryStats.map((item) => item.factory_name)
|
|
const categories = [...new Set(factoryStats.flatMap((item) => item.categories.map((c) => c.material_category)))]
|
|
|
|
const series = categories.map((cat) => ({
|
|
name: cat,
|
|
type: 'bar',
|
|
stack: 'total',
|
|
data: factoryStats.map((item) => {
|
|
const found = item.categories.find((c) => c.material_category === cat)
|
|
return found ? found.count : 0
|
|
})
|
|
}))
|
|
|
|
charts[1].setOption({
|
|
tooltip: { trigger: 'axis' },
|
|
legend: { textStyle: { color: '#c9d7e6' } },
|
|
grid: { left: 40, right: 20, top: 30, bottom: 40 },
|
|
xAxis: { type: 'category', data: factoriesAxis, axisLabel: { color: '#9fb3c8', rotate: 20 } },
|
|
yAxis: { type: 'value', axisLabel: { color: '#9fb3c8' } },
|
|
series
|
|
})
|
|
}
|
|
|
|
const loadData = async () => {
|
|
const data = await fetchFactoryStats()
|
|
factories.value = data.factories_list || []
|
|
updateCharts(data)
|
|
}
|
|
|
|
const goDetail = (item) => {
|
|
router.push(`/factories/${item.id}`)
|
|
}
|
|
|
|
onMounted(async () => {
|
|
initCharts()
|
|
await loadData()
|
|
timer = setInterval(loadData, 10000)
|
|
window.addEventListener('resize', onResize)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
charts.forEach((chart) => chart.dispose())
|
|
clearInterval(timer)
|
|
window.removeEventListener('resize', onResize)
|
|
})
|
|
</script>
|