From b5be1bbfec69f05876e7a0c9bf5706864b3fab7d Mon Sep 17 00:00:00 2001 From: TianyangZhang Date: Wed, 13 May 2026 10:03:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(fac=5Fcal):=20=E4=BC=98=E5=8C=96=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E6=95=B0=E6=8D=AE=E6=80=BB=E8=A7=88=E5=8F=8A=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=95=B0=E6=8D=AE=E6=A0=87=E5=87=86=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. hfnf_index: 数据总量改为从接口实时获取,四个工厂按比例自动分配 2. cement_data_template: 去除表格标题中的"表一""表二"前缀 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/views/fac_cal/cement_data_template.vue | 4 +- src/views/fac_cal/hfnf_index.vue | 899 +++++++++++---------- 2 files changed, 486 insertions(+), 417 deletions(-) diff --git a/src/views/fac_cal/cement_data_template.vue b/src/views/fac_cal/cement_data_template.vue index ab7764b4..0bd2f1f7 100644 --- a/src/views/fac_cal/cement_data_template.vue +++ b/src/views/fac_cal/cement_data_template.vue @@ -7,7 +7,7 @@
-

表一 通用硅酸盐水泥生产工艺数据模板

+

通用硅酸盐水泥生产工艺数据模板

@@ -19,7 +19,7 @@
-

表二 元数据元素内容和格式

+

元数据元素内容和格式

diff --git a/src/views/fac_cal/hfnf_index.vue b/src/views/fac_cal/hfnf_index.vue index 57448129..fdb4b5e4 100644 --- a/src/views/fac_cal/hfnf_index.vue +++ b/src/views/fac_cal/hfnf_index.vue @@ -1,131 +1,171 @@ @@ -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() })