+
+
-
-
-
-
各工厂数据量对比
-
+
+
水泥生产数据采集总览
+
覆盖 {{ factories.length }} 家工厂 · 实时数据监控
-
-
-
-
-
-
工厂数据轮播
-
-
-
-
#{{ activeFactory + 1 }}
-
{{ factories[activeFactory]?.name }}
-
{{ formatCompact(factories[activeFactory]?.count || 0) }}
-
{{ formatNumber(factories[activeFactory]?.count || 0) }} 条
-
-
- 占比 {{ (factories[activeFactory]?.percentage || 0).toFixed(1) }}%
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
工厂数据排行
-
-
-
{{ index + 1 }}
-
-
{{ formatCompact(item.count) }}
-
{{ item.percentage.toFixed(1) }}%
-
-
-
-
-
-
-
-
-
-
- 查看详情
- 进入测点数据明细 →
-
+
+
+ 数据总量
+ {{ formatNumber(totalCount) }}
+ 条
+
+
+
+
+
+
+
+ {{ formatNumber(item.count) }}
+ 条
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.name }}
+
+
+
+
+
+ {{ formatNumber(row.count) }}
+
+
+
+
+
+ {{ row.percentage.toFixed(1) }}%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 系统名称
+ 水泥生产数据人工智能采集融合系统
+
+
+
+
+
+
+
+ 接入工厂
+ {{ factories.length }} 家
+
+
+
+
+
+
+
+ 数据总量
+ {{ formatNumber(totalCount) }} 条
+
+
+
+
+
+
+
+ 平均数据量
+ {{ formatNumber(Math.round(totalCount / factories.length)) }} 条/厂
+
+
+
+
+
+
+
+ 最大采集量
+ {{ maxFactory.name }}({{ formatNumber(maxFactory.count) }})
+
+
+
+
+
+
+
+ 查看详情
+ 进入测点数据明细 →
+
+
+
+
+
+
@@ -138,11 +178,13 @@ import API from '@/api'
const router = useRouter()
const barChartRef = ref(null)
const pieChartRef = ref(null)
-let barChart = null, pieChart = null
-let clockTimer = null, carouselTimer = null, scrollTimer = null
+let barChart = null
+let pieChart = null
-const colors = ['#409eff', '#00e0a1', '#f5a623', '#ff6b6b']
+const colors = ['#409eff', '#67c23a', '#e6a23c', '#f56c6c']
+const bgColors = ['#ecf5ff', '#f0f9eb', '#fdf6ec', '#fef0f0']
+// 各工厂基础比例
const baseFactories = [
{ name: '合肥南方', ratio: 0.4382 },
{ name: '中联万吨', ratio: 0.3081 },
@@ -151,93 +193,87 @@ const baseFactories = [
]
const totalCount = ref(0)
-const activeFactory = ref(0)
-const clock = ref('')
-const scrollY = ref(0)
-const scrollWrap = ref(null)
const factories = computed(() => {
const total = totalCount.value
+ // 前3个按比例取整,最后一个用总量减去前3个保证总和精确
const counts = baseFactories.map(f => Math.round(total * f.ratio))
- counts[3] = total - counts[0] - counts[1] - counts[2]
+ const sumFirst3 = counts[0] + counts[1] + counts[2]
+ counts[3] = total - sumFirst3
+
return baseFactories.map((f, i) => ({
- name: f.name, count: counts[i], color: colors[i],
+ name: f.name,
+ count: counts[i],
+ color: colors[i],
+ bgColor: bgColors[i],
percentage: total > 0 ? (counts[i] / total) * 100 : 0,
}))
})
-const formattedTotal = computed(() => formatNumber(totalCount.value).split(''))
+const maxFactory = computed(() => {
+ if (factories.value.length === 0) return { name: '-', count: 0 }
+ return factories.value.reduce((a, b) => (a.count > b.count ? a : b))
+})
-// 模拟滚动数据
-const scrollData = ref([])
-const genScrollData = () => {
- const rows = []
- for (let i = 0; i < 30; i++) {
- const fi = Math.floor(Math.random() * 4)
- const h = String(Math.floor(Math.random() * 24)).padStart(2, '0')
- const m = String(Math.floor(Math.random() * 60)).padStart(2, '0')
- rows.push({
- time: `${h}:${m}`,
- factory: baseFactories[fi].name,
- color: colors[fi],
- count: Math.floor(Math.random() * 50000) + 10000,
- })
- }
- scrollData.value = rows
+const formatNumber = (num) => {
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
-const formatNumber = (n) => n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
-const formatCompact = (n) => {
- if (n >= 1e8) return (n / 1e8).toFixed(2) + '亿'
- if (n >= 1e4) return (n / 1e4).toFixed(0) + '万'
- return n.toString()
-}
-const goDetail = () => router.push('/hfnf_mplogx')
-
-const updateClock = () => {
- const d = new Date()
- clock.value = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}`
+const goDetail = () => {
+ router.push('/hfnf_mplogx')
}
const initBarChart = () => {
if (!barChartRef.value) return
barChart = echarts.init(barChartRef.value)
barChart.setOption({
- backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
- backgroundColor: 'rgba(10,22,40,0.95)', borderColor: 'rgba(64,158,255,0.3)',
- textStyle: { color: '#e0e6ed' },
- formatter: (p) => `${p[0].name}
数据量:
${formatNumber(p[0].value)} 条`,
+ formatter: (params) => {
+ const p = params[0]
+ return `${p.name}
数据量:
${formatNumber(p.value)} 条`
+ },
},
- grid: { left: 10, right: 15, top: 25, bottom: 20, containLabel: true },
+ grid: { left: 20, right: 30, top: 20, bottom: 30, containLabel: true },
xAxis: {
- type: 'category', data: factories.value.map(f => f.name),
- axisLabel: { color: '#5a7a9a', fontSize: 11 },
- axisLine: { lineStyle: { color: 'rgba(255,255,255,0.06)' } },
+ type: 'category',
+ data: factories.value.map((f) => f.name),
+ axisLabel: { color: '#666', fontSize: 13 },
+ axisLine: { lineStyle: { color: '#e4e7ed' } },
axisTick: { show: false },
},
yAxis: {
type: 'value',
- axisLabel: { color: '#3a5570', formatter: v => v >= 1e8 ? (v/1e8).toFixed(1)+'亿' : (v/1e4).toFixed(0)+'万' },
- splitLine: { lineStyle: { color: 'rgba(255,255,255,0.03)' } },
- axisLine: { show: false },
+ axisLabel: {
+ color: '#999',
+ formatter: (v) => (v >= 1e8 ? (v / 1e8).toFixed(1) + '亿' : (v / 1e4).toFixed(0) + '万'),
+ },
+ splitLine: { lineStyle: { color: '#f0f0f0' } },
},
- series: [{
- type: 'bar', barWidth: '50%',
- data: factories.value.map((f, i) => ({
- value: f.count,
- itemStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: colors[i] }, { offset: 1, color: colors[i] + '15' },
- ]),
- borderRadius: [4, 4, 0, 0],
- shadowColor: colors[i] + '30', shadowBlur: 10,
+ series: [
+ {
+ type: 'bar',
+ data: factories.value.map((f) => ({
+ value: f.count,
+ itemStyle: {
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: f.color },
+ { offset: 1, color: f.bgColor },
+ ]),
+ borderRadius: [6, 6, 0, 0],
+ },
+ })),
+ barWidth: '45%',
+ label: {
+ show: true,
+ position: 'top',
+ formatter: (p) => (p.value / 1e8).toFixed(2) + '亿',
+ color: '#333',
+ fontSize: 13,
+ fontWeight: 600,
},
- })),
- label: { show: true, position: 'top', formatter: p => formatCompact(p.value), color: '#8a9bb5', fontSize: 12, fontWeight: 600 },
- animationDuration: 1500, animationEasing: 'cubicOut',
- }],
+ },
+ ],
})
}
@@ -245,279 +281,312 @@ const initPieChart = () => {
if (!pieChartRef.value) return
pieChart = echarts.init(pieChartRef.value)
pieChart.setOption({
- backgroundColor: 'transparent',
tooltip: {
trigger: 'item',
- backgroundColor: 'rgba(10,22,40,0.95)', borderColor: 'rgba(64,158,255,0.3)',
- textStyle: { color: '#e0e6ed' },
+ formatter: (p) => `${p.name}
数据量:
${formatNumber(p.value)} 条
占比:
${p.percent}%`,
},
legend: {
- orient: 'vertical', right: 8, top: 'center',
- textStyle: { fontSize: 11, color: '#5a7a9a' },
- icon: 'circle', itemWidth: 8, itemHeight: 8,
+ orient: 'vertical',
+ right: 10,
+ top: 'center',
+ textStyle: { fontSize: 13, color: '#666' },
},
- series: [{
- type: 'pie', radius: ['44%', '72%'], center: ['38%', '50%'],
- itemStyle: { borderRadius: 5, borderColor: '#0a1628', borderWidth: 3 },
- label: { formatter: '{d}%', fontSize: 11, color: '#5a7a9a' },
- labelLine: { length: 8, length2: 6, lineStyle: { color: 'rgba(255,255,255,0.1)' } },
- emphasis: { itemStyle: { shadowBlur: 20, shadowColor: 'rgba(0,0,0,0.5)' } },
- data: factories.value.map(f => ({ value: f.count, name: f.name, itemStyle: { color: f.color } })),
- animationType: 'scale', animationEasing: 'elasticOut', animationDuration: 1500,
- }],
+ series: [
+ {
+ type: 'pie',
+ radius: ['42%', '70%'],
+ center: ['40%', '50%'],
+ avoidLabelOverlap: true,
+ itemStyle: { borderRadius: 6, borderColor: '#fff', borderWidth: 3 },
+ label: {
+ show: true,
+ formatter: '{b}\n{d}%',
+ fontSize: 12,
+ lineHeight: 18,
+ },
+ labelLine: { length: 15, length2: 10 },
+ emphasis: {
+ itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.2)' },
+ },
+ data: factories.value.map((f) => ({
+ value: f.count,
+ name: f.name,
+ itemStyle: { color: f.color },
+ })),
+ },
+ ],
})
}
-const handleResize = () => { barChart?.resize(); pieChart?.resize() }
+const handleResize = () => {
+ barChart?.resize()
+ pieChart?.resize()
+}
onMounted(() => {
- updateClock()
- clockTimer = setInterval(updateClock, 1000)
- genScrollData()
-
- // 轮播
- carouselTimer = setInterval(() => {
- activeFactory.value = (activeFactory.value + 1) % 4
- }, 4000)
-
- // 滚动
- let scrollPos = 0
- scrollTimer = setInterval(() => {
- scrollPos -= 1
- const rowH = 36, total = scrollData.value.length * rowH
- if (Math.abs(scrollPos) >= total / 2) scrollPos = 0
- scrollY.value = scrollPos
- }, 60)
-
API.hfnf.mplogx.list.req({ page: 1, page_size: 1 }).then(res => {
totalCount.value = res.count
- nextTick(() => { initBarChart(); initPieChart() })
+ nextTick(() => {
+ initBarChart()
+ initPieChart()
+ })
})
window.addEventListener('resize', handleResize)
})
onBeforeUnmount(() => {
- clearInterval(clockTimer)
- clearInterval(carouselTimer)
- clearInterval(scrollTimer)
window.removeEventListener('resize', handleResize)
- barChart?.dispose(); pieChart?.dispose()
+ barChart?.dispose()
+ pieChart?.dispose()
})