From 3fc5f85600e51d82a353e0f02164866ced8f49aa Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期四, 02 四月 2026 15:37:33 +0800
Subject: [PATCH] Merge branch 'dev_银川_中盛建材' of http://114.132.189.42:9002/r/product-inventory-management into dev_银川_中盛建材
---
src/views/reportAnalysis/productionStatistics/index.vue | 437 +++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 380 insertions(+), 57 deletions(-)
diff --git a/src/views/reportAnalysis/productionStatistics/index.vue b/src/views/reportAnalysis/productionStatistics/index.vue
index 34a7b8d..47822ce 100644
--- a/src/views/reportAnalysis/productionStatistics/index.vue
+++ b/src/views/reportAnalysis/productionStatistics/index.vue
@@ -61,7 +61,8 @@
:class="{ active: blockSelectedMaterial === name }"
@click="selectBlockMaterial(name)">{{ name }}</span>
</div>
- <div class="material-info-card">
+ <div v-if="blockSelectedMaterial !== '鍏ㄩ儴'"
+ class="material-info-card">
<div class="material-icon">
<svg width="24"
height="24"
@@ -168,7 +169,8 @@
:class="{ active: boardSelectedMaterial === name }"
@click="selectBoardMaterial(name)">{{ name }}</span>
</div>
- <div class="material-info-card">
+ <div v-if="boardSelectedMaterial !== '鍏ㄩ儴'"
+ class="material-info-card">
<div class="material-icon">
<svg width="24"
height="24"
@@ -341,19 +343,26 @@
yearlyConsumption: "--",
});
+ /** xMode: time=妯酱涓烘椂闂达紱material=閫夈�屽叏閮ㄣ�嶆椂妯酱涓哄悇鐗╂枡鍚嶇О */
const blockCostChartSeries = ref({
+ xMode: "time",
categories: [],
input: [],
output: [],
});
const boardCostChartSeries = ref({
+ xMode: "time",
categories: [],
input: [],
output: [],
});
const productionChartSeries = ref({
categories: [],
+ /** single锛氬崟绾匡紱dual锛氬叏閮ㄦ椂鐮屽潡+鏉挎潗鍙岀嚎 */
+ mode: "single",
values: [],
+ blockValues: [],
+ plateValues: [],
});
// 鍥哄簾澶勭悊閲忔姌绾垮浘锛�/home/productionStatistics/solidWaste锛�
@@ -391,9 +400,12 @@
let productionChartInstance = null;
let customerTrendChartInstance = null;
let salesRankingChartInstance = null;
+ let blockCostChartResizeObserver = null;
+ let boardCostChartResizeObserver = null;
// 鐢熶骇鍗曡�楀浘琛ㄩ厤缃紙鐮屽潡锛屾帴鍙f暟鎹級
const blockCostChartOption = computed(() => {
+ const xMode = blockCostChartSeries.value.xMode || "time";
const periods = blockCostChartSeries.value.categories || [];
const inputData = blockCostChartSeries.value.input || [];
const outputData = blockCostChartSeries.value.output || [];
@@ -437,8 +449,8 @@
grid: {
left: "1%",
right: "1%",
- bottom: "1%",
top: "18%",
+ bottom: xMode === "material" ? "0%" : "1%",
containLabel: true,
},
xAxis: {
@@ -449,7 +461,12 @@
axisLabel: {
color: "#B8C8E0",
fontSize: getResponsiveValue(11),
- margin: getResponsiveValue(10),
+ margin:
+ xMode === "material"
+ ? getResponsiveValue(6)
+ : getResponsiveValue(10),
+ rotate: xMode === "material" && periods.length > 5 ? 28 : 0,
+ interval: 0,
},
splitLine: { show: false },
},
@@ -469,6 +486,7 @@
// 鏉挎潗鍗曡�楀浘琛ㄩ厤缃紙鎺ュ彛鏁版嵁锛屼笌鐮屽潡缁撴瀯涓�鑷达級
const boardCostChartOption = computed(() => {
+ const xMode = boardCostChartSeries.value.xMode || "time";
const periods = boardCostChartSeries.value.categories || [];
const inputData = boardCostChartSeries.value.input || [];
const outputData = boardCostChartSeries.value.output || [];
@@ -512,8 +530,8 @@
grid: {
left: "1%",
right: "1%",
- bottom: "1%",
top: "18%",
+ bottom: xMode === "material" ? "0%" : "1%",
containLabel: true,
},
xAxis: {
@@ -524,7 +542,12 @@
axisLabel: {
color: "#B8C8E0",
fontSize: getResponsiveValue(11),
- margin: getResponsiveValue(10),
+ margin:
+ xMode === "material"
+ ? getResponsiveValue(6)
+ : getResponsiveValue(10),
+ rotate: xMode === "material" && periods.length > 5 ? 28 : 0,
+ interval: 0,
},
splitLine: { show: false },
},
@@ -545,26 +568,51 @@
// 鐗╂枡鐢熶骇閲忓垎鏋愶紙鎺ュ彛鏁版嵁锛�
const productionChartOption = computed(() => {
const periods = productionChartSeries.value.categories || [];
- const values = productionChartSeries.value.values || [];
- const lineColor = "#4A8BFF";
- const seriesName = "浜ч噺";
- const series = [
- {
- name: seriesName,
- data: values,
- type: "line",
- smooth: true,
- lineStyle: { width: getResponsiveValue(2), color: lineColor },
- itemStyle: { color: lineColor },
- areaStyle: {
- opacity: 0.3,
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: lineColor + "80" },
- { offset: 1, color: lineColor + "00" },
- ]),
- },
+ const mode = productionChartSeries.value.mode || "single";
+ const blockLineColor = "#4A8BFF";
+ const plateLineColor = "#52C9A0";
+
+ const buildAreaLine = (name, data, lineColor) => ({
+ name,
+ data,
+ type: "line",
+ smooth: true,
+ lineStyle: { width: getResponsiveValue(2), color: lineColor },
+ itemStyle: { color: lineColor },
+ areaStyle: {
+ opacity: 0.3,
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: lineColor + "80" },
+ { offset: 1, color: lineColor + "00" },
+ ]),
},
- ];
+ });
+
+ let series;
+ if (mode === "dual") {
+ series = [
+ buildAreaLine(
+ "鐮屽潡",
+ productionChartSeries.value.blockValues || [],
+ blockLineColor
+ ),
+ buildAreaLine(
+ "鏉挎潗",
+ productionChartSeries.value.plateValues || [],
+ plateLineColor
+ ),
+ ];
+ } else {
+ const cat = productionCategory.value;
+ const seriesName = cat === "鐮屽潡" ? "鐮屽潡" : "鏉挎潗";
+ series = [
+ buildAreaLine(
+ seriesName,
+ productionChartSeries.value.values || [],
+ blockLineColor
+ ),
+ ];
+ }
return {
backgroundColor: "transparent",
@@ -575,22 +623,12 @@
borderWidth: getResponsiveValue(1),
textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
},
- legend: {
- data: [seriesName],
- top: "10%",
- right: "1%",
- textStyle: {
- color: "#B8C8E0",
- fontSize: getResponsiveValue(9),
- },
- itemWidth: getResponsiveValue(10),
- itemHeight: getResponsiveValue(10),
- },
+ legend: { show: false },
grid: {
left: "1%",
right: "1%",
bottom: "1%",
- top: "28%",
+ top: "10%",
containLabel: true,
},
xAxis: {
@@ -857,6 +895,36 @@
鏉挎潗: "plateOutput",
};
+ /** 鍏ㄩ儴锛氭寜 dateStr 鍚堝苟鐮屽潡/鏉挎潗涓ゆ潯搴忓垪 */
+ /** 灏嗘煇鐗╂枡鏃堕棿搴忓垪 chartData 姹囨�讳负鎶曞叆/浜у嚭鍚堣锛堢敤浜庛�屽叏閮ㄣ�嶆í杞�=鐗╂枡瀵规瘮锛� */
+ const sumChartDataInputOutput = chartData => {
+ const arr = Array.isArray(chartData) ? chartData : [];
+ const input = arr.reduce((s, c) => s + (Number(c.inputSum) || 0), 0);
+ const output = arr.reduce((s, c) => s + (Number(c.outputSum) || 0), 0);
+ return { input, output };
+ };
+
+ const mergeBlockPlateProductionSeries = (blockList, plateList) => {
+ const blocks = Array.isArray(blockList) ? blockList : [];
+ const plates = Array.isArray(plateList) ? plateList : [];
+ const blockByDate = new Map(blocks.map(i => [i.dateStr, i]));
+ const plateByDate = new Map(plates.map(i => [i.dateStr, i]));
+ const dates = [
+ ...new Set([...blockByDate.keys(), ...plateByDate.keys()]),
+ ].sort();
+ return {
+ categories: dates,
+ blockValues: dates.map(d => {
+ const row = blockByDate.get(d);
+ return row ? Number(row.blockOutput) || 0 : 0;
+ }),
+ plateValues: dates.map(d => {
+ const row = plateByDate.get(d);
+ return row ? Number(row.plateOutput) || 0 : 0;
+ }),
+ };
+ };
+
const loadBlockCost = async () => {
const materialName = blockSelectedMaterial.value;
if (!materialName) {
@@ -865,13 +933,95 @@
monthlyConsumption: "--",
yearlyConsumption: "--",
};
- blockCostChartSeries.value = { categories: [], input: [], output: [] };
+ blockCostChartSeries.value = {
+ xMode: "time",
+ categories: [],
+ input: [],
+ output: [],
+ };
updateCharts();
return;
}
const dateType = mapTimeDimensionToDateType(blockTimeDimension.value);
try {
- const res = await getProductionStatisticsBlocks({ materialName, dateType });
+ if (materialName === "鍏ㄩ儴") {
+ const names = blockMaterialList.value.filter(n => n !== "鍏ㄩ儴");
+ if (!names.length) {
+ blockMaterialSummary.value = {
+ materialName: "鍏ㄩ儴",
+ monthlyConsumption: "--",
+ yearlyConsumption: "--",
+ };
+ blockCostChartSeries.value = {
+ xMode: "material",
+ categories: [],
+ input: [],
+ output: [],
+ };
+ updateCharts();
+ return;
+ }
+ const res = await getProductionStatisticsBlocks({ dateType });
+ const rows = Array.isArray(res.data) ? res.data : [];
+ const rowByName = new Map(
+ rows.filter(r => r && r.materialName).map(r => [r.materialName, r])
+ );
+ const useBulk = names.every(n => rowByName.has(n));
+
+ let categories;
+ let input;
+ let output;
+ if (useBulk) {
+ categories = [];
+ input = [];
+ output = [];
+ for (const name of names) {
+ const row = rowByName.get(name);
+ const chartData = Array.isArray(row.chartData) ? row.chartData : [];
+ const sums = sumChartDataInputOutput(chartData);
+ categories.push(name);
+ input.push(sums.input);
+ output.push(sums.output);
+ }
+ } else {
+ const parts = await Promise.all(
+ names.map(async name => {
+ const r = await getProductionStatisticsBlocks({
+ dateType,
+ materialName: name,
+ });
+ const rows2 = Array.isArray(r.data) ? r.data : [];
+ const row =
+ rows2.find(x => x.materialName === name) || rows2[0] || {};
+ const chartData = Array.isArray(row.chartData) ? row.chartData : [];
+ const sums = sumChartDataInputOutput(chartData);
+ return { name, ...sums };
+ })
+ );
+ categories = parts.map(p => p.name);
+ input = parts.map(p => p.input);
+ output = parts.map(p => p.output);
+ }
+
+ blockMaterialSummary.value = {
+ materialName: "鍏ㄩ儴",
+ monthlyConsumption: "--",
+ yearlyConsumption: "--",
+ };
+ blockCostChartSeries.value = {
+ xMode: "material",
+ categories,
+ input,
+ output,
+ };
+ updateCharts();
+ return;
+ }
+
+ const res = await getProductionStatisticsBlocks({
+ dateType,
+ materialName,
+ });
const rows = Array.isArray(res.data) ? res.data : [];
const row =
rows.find(r => r.materialName === materialName) || rows[0] || {};
@@ -882,6 +1032,7 @@
yearlyConsumption: row.yearlyConsumption ?? "--",
};
blockCostChartSeries.value = {
+ xMode: "time",
categories: chartData.map(c => c.date),
input: chartData.map(c => c.inputSum ?? 0),
output: chartData.map(c => c.outputSum ?? 0),
@@ -900,13 +1051,95 @@
monthlyConsumption: "--",
yearlyConsumption: "--",
};
- boardCostChartSeries.value = { categories: [], input: [], output: [] };
+ boardCostChartSeries.value = {
+ xMode: "time",
+ categories: [],
+ input: [],
+ output: [],
+ };
updateCharts();
return;
}
const dateType = mapTimeDimensionToDateType(boardTimeDimension.value);
try {
- const res = await getProductionStatisticsPlates({ materialName, dateType });
+ if (materialName === "鍏ㄩ儴") {
+ const names = boardMaterialList.value.filter(n => n !== "鍏ㄩ儴");
+ if (!names.length) {
+ boardMaterialSummary.value = {
+ materialName: "鍏ㄩ儴",
+ monthlyConsumption: "--",
+ yearlyConsumption: "--",
+ };
+ boardCostChartSeries.value = {
+ xMode: "material",
+ categories: [],
+ input: [],
+ output: [],
+ };
+ updateCharts();
+ return;
+ }
+ const res = await getProductionStatisticsPlates({ dateType });
+ const rows = Array.isArray(res.data) ? res.data : [];
+ const rowByName = new Map(
+ rows.filter(r => r && r.materialName).map(r => [r.materialName, r])
+ );
+ const useBulk = names.every(n => rowByName.has(n));
+
+ let categories;
+ let input;
+ let output;
+ if (useBulk) {
+ categories = [];
+ input = [];
+ output = [];
+ for (const name of names) {
+ const row = rowByName.get(name);
+ const chartData = Array.isArray(row.chartData) ? row.chartData : [];
+ const sums = sumChartDataInputOutput(chartData);
+ categories.push(name);
+ input.push(sums.input);
+ output.push(sums.output);
+ }
+ } else {
+ const parts = await Promise.all(
+ names.map(async name => {
+ const r = await getProductionStatisticsPlates({
+ dateType,
+ materialName: name,
+ });
+ const rows2 = Array.isArray(r.data) ? r.data : [];
+ const row =
+ rows2.find(x => x.materialName === name) || rows2[0] || {};
+ const chartData = Array.isArray(row.chartData) ? row.chartData : [];
+ const sums = sumChartDataInputOutput(chartData);
+ return { name, ...sums };
+ })
+ );
+ categories = parts.map(p => p.name);
+ input = parts.map(p => p.input);
+ output = parts.map(p => p.output);
+ }
+
+ boardMaterialSummary.value = {
+ materialName: "鍏ㄩ儴",
+ monthlyConsumption: "--",
+ yearlyConsumption: "--",
+ };
+ boardCostChartSeries.value = {
+ xMode: "material",
+ categories,
+ input,
+ output,
+ };
+ updateCharts();
+ return;
+ }
+
+ const res = await getProductionStatisticsPlates({
+ dateType,
+ materialName,
+ });
const rows = Array.isArray(res.data) ? res.data : [];
const row =
rows.find(r => r.materialName === materialName) || rows[0] || {};
@@ -917,6 +1150,7 @@
yearlyConsumption: row.yearlyConsumption ?? "--",
};
boardCostChartSeries.value = {
+ xMode: "time",
categories: chartData.map(c => c.date),
input: chartData.map(c => c.inputSum ?? 0),
output: chartData.map(c => c.outputSum ?? 0),
@@ -930,7 +1164,8 @@
const loadBlockMaterials = async () => {
try {
const res = await getProductionMaterials({ materialType: "1" });
- const list = Array.isArray(res.data) ? [...res.data] : [];
+ const raw = Array.isArray(res.data) ? [...res.data] : [];
+ const list = ["鍏ㄩ儴", ...raw];
blockMaterialList.value = list;
if (list.length) {
if (!list.includes(blockSelectedMaterial.value)) {
@@ -944,7 +1179,12 @@
monthlyConsumption: "--",
yearlyConsumption: "--",
};
- blockCostChartSeries.value = { categories: [], input: [], output: [] };
+ blockCostChartSeries.value = {
+ xMode: "time",
+ categories: [],
+ input: [],
+ output: [],
+ };
updateCharts();
}
} catch (e) {
@@ -955,7 +1195,8 @@
const loadBoardMaterials = async () => {
try {
const res = await getProductionMaterials({ materialType: "2" });
- const list = Array.isArray(res.data) ? [...res.data] : [];
+ const raw = Array.isArray(res.data) ? [...res.data] : [];
+ const list = ["鍏ㄩ儴", ...raw];
boardMaterialList.value = list;
if (list.length) {
if (!list.includes(boardSelectedMaterial.value)) {
@@ -969,7 +1210,12 @@
monthlyConsumption: "--",
yearlyConsumption: "--",
};
- boardCostChartSeries.value = { categories: [], input: [], output: [] };
+ boardCostChartSeries.value = {
+ xMode: "time",
+ categories: [],
+ input: [],
+ output: [],
+ };
updateCharts();
}
} catch (e) {
@@ -983,12 +1229,30 @@
const res = await getMaterialProductionAnalysis({ dateType });
const payload = res.data && typeof res.data === "object" ? res.data : {};
const cat = productionCategory.value;
- const list = Array.isArray(payload[cat]) ? payload[cat] : [];
- const field = productionOutputKey[cat] || "totalOutput";
- productionChartSeries.value = {
- categories: list.map(i => i.dateStr),
- values: list.map(i => Number(i[field]) || 0),
- };
+
+ if (cat === "鍏ㄩ儴") {
+ const merged = mergeBlockPlateProductionSeries(
+ payload["鐮屽潡"],
+ payload["鏉挎潗"]
+ );
+ productionChartSeries.value = {
+ categories: merged.categories,
+ mode: "dual",
+ values: [],
+ blockValues: merged.blockValues,
+ plateValues: merged.plateValues,
+ };
+ } else {
+ const list = Array.isArray(payload[cat]) ? payload[cat] : [];
+ const field = productionOutputKey[cat] || "totalOutput";
+ productionChartSeries.value = {
+ categories: list.map(i => i.dateStr),
+ mode: "single",
+ values: list.map(i => Number(i[field]) || 0),
+ blockValues: [],
+ plateValues: [],
+ };
+ }
updateCharts();
} catch (e) {
console.error(e);
@@ -1094,21 +1358,37 @@
updateCharts();
};
+ /** 鐮屽潡/鏉挎潗瀹瑰櫒楂樺害鍙樺寲鍚庯紙濡傞殣钘忔眹鎬诲崱锛夐渶 resize锛屽惁鍒欏簳閮ㄧ暀鐧� */
+ const resizeCostPanelCharts = () => {
+ nextTick(() => {
+ requestAnimationFrame(() => {
+ blockCostChartInstance?.resize();
+ boardCostChartInstance?.resize();
+ });
+ });
+ };
+
// 鏇存柊鍥捐〃
const updateCharts = () => {
- // 鏇存柊鐮屽潡鎴愭湰鍥捐〃
+ // 鏇存柊鐮屽潡鎴愭湰鍥捐〃锛坮eplaceMerge锛氬叏閮ㄢ啍鍗曠墿鏂欐椂妯酱鏃堕棿/鐗╂枡鍒囨崲锛�
if (blockCostChartInstance) {
- blockCostChartInstance.setOption(blockCostChartOption.value);
+ blockCostChartInstance.setOption(blockCostChartOption.value, {
+ replaceMerge: ["series", "xAxis"],
+ });
}
// 鏇存柊鏉挎潗鎴愭湰鍥捐〃
if (boardCostChartInstance) {
- boardCostChartInstance.setOption(boardCostChartOption.value);
+ boardCostChartInstance.setOption(boardCostChartOption.value, {
+ replaceMerge: ["series", "xAxis"],
+ });
}
- // 鏇存柊浜ч噺鍒嗘瀽鍥捐〃
+ // 鏇存柊浜ч噺鍒嗘瀽鍥捐〃锛坮eplaceMerge锛氬叏閮ㄢ啍鐮屽潡/鏉挎潗鍒囨崲鏃跺幓鎺夊浣欐姌绾匡紝閬垮厤鍚堝苟娈嬬暀锛�
if (productionChartInstance) {
- productionChartInstance.setOption(productionChartOption.value);
+ productionChartInstance.setOption(productionChartOption.value, {
+ replaceMerge: ["series"],
+ });
}
// 鏇存柊鏂板瀹㈡埛瓒嬪娍鍥捐〃
@@ -1120,6 +1400,8 @@
if (salesRankingChartInstance) {
salesRankingChartInstance.setOption(salesRankingChartOption.value);
}
+
+ resizeCostPanelCharts();
};
// 澶勭悊鏃堕棿缁村害閫夋嫨
@@ -1182,6 +1464,20 @@
// 绛夊緟DOM鏇存柊鍚庡垵濮嬪寲鍥捐〃骞舵媺鍙栨帴鍙f暟鎹�
nextTick(async () => {
initCharts();
+ if (typeof ResizeObserver !== "undefined") {
+ if (blockCostChart.value) {
+ blockCostChartResizeObserver = new ResizeObserver(() => {
+ blockCostChartInstance?.resize();
+ });
+ blockCostChartResizeObserver.observe(blockCostChart.value);
+ }
+ if (boardCostChart.value) {
+ boardCostChartResizeObserver = new ResizeObserver(() => {
+ boardCostChartInstance?.resize();
+ });
+ boardCostChartResizeObserver.observe(boardCostChart.value);
+ }
+ }
await Promise.all([
loadBlockMaterials(),
loadBoardMaterials(),
@@ -1190,6 +1486,7 @@
loadSolidWasteData(),
loadEnergyData(),
]);
+ resizeCostPanelCharts();
});
// 娣诲姞绐楀彛澶у皬鍙樺寲鐩戝惉
@@ -1203,6 +1500,11 @@
clearInterval(timeTicker);
timeTicker = null;
}
+
+ blockCostChartResizeObserver?.disconnect();
+ blockCostChartResizeObserver = null;
+ boardCostChartResizeObserver?.disconnect();
+ boardCostChartResizeObserver = null;
if (blockCostChartInstance) {
blockCostChartInstance.dispose();
@@ -1610,9 +1912,30 @@
margin-bottom: 0.2vh;
}
+ /* 鐮屽潡/鏉挎潗锛氬浘琛ㄥ崰婊� Tab 涓庢眹鎬诲崱涓嬫柟鐨勫墿浣欓珮搴︼紱闅愯棌姹囨�诲崱鏃惰嚜鍔ㄦ媺楂� */
+ .bi-panel-top-left,
+ .bi-panel-bottom-left {
+ min-height: 0;
+ }
+
+ .bi-panel-top-left .bi-panel-body,
+ .bi-panel-bottom-left .bi-panel-body {
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+ }
+
+ .bi-panel-top-left .chart-filter-tabs,
+ .bi-panel-bottom-left .chart-filter-tabs {
+ flex-shrink: 0;
+ }
+
.bi-panel-top-left .echart-fill,
.bi-panel-bottom-left .echart-fill {
- height: 24vh;
+ flex: 1;
+ min-height: 0;
+ height: auto;
+ width: 100%;
}
.bi-panel-bottom-right .echart-fill {
--
Gitblit v1.9.3