| | |
| | | </div> |
| | | <div class="bi-panel-body"> |
| | | <div class="chart-filter-tabs"> |
| | | <span v-for="area in salesAreas" |
| | | :key="area" |
| | | <span v-for="name in blockMaterialList" |
| | | :key="name" |
| | | class="cf-tab" |
| | | :class="{ active: blockSelectedArea === area }" |
| | | @click="handleBlockAreaChange(area)">{{ area }}</span> |
| | | :class="{ active: blockSelectedMaterial === name }" |
| | | @click="selectBlockMaterial(name)">{{ name }}</span> |
| | | </div> |
| | | <div class="material-info-card"> |
| | | <div class="material-icon"> |
| | |
| | | </svg> |
| | | </div> |
| | | <div class="material-details"> |
| | | <div class="material-name">{{ blockMaterialType }}AAA</div> |
| | | <div class="material-name">{{ blockMaterialSummary.materialName || "—" }}</div> |
| | | <div class="material-stats"> |
| | | <div class="stat-item"> |
| | | <span class="stat-label">月累计单耗</span> |
| | | <span class="stat-value">78/12</span> |
| | | <span class="stat-value">{{ blockMaterialSummary.monthlyConsumption }}</span> |
| | | <span class="stat-unit">吨</span> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <span class="stat-label">年累计单耗</span> |
| | | <span class="stat-value">78/12</span> |
| | | <span class="stat-value">{{ blockMaterialSummary.yearlyConsumption }}</span> |
| | | <span class="stat-unit">吨</span> |
| | | </div> |
| | | </div> |
| | |
| | | @click="handleProductionTimeDimensionChange('month')">月</span> |
| | | </div> |
| | | <div class="bi-panel-body"> |
| | | <div class="chart-filter-tabs"> |
| | | <span v-for="cat in productionCategories" |
| | | :key="cat" |
| | | class="cf-tab" |
| | | :class="{ active: productionCategory === cat }" |
| | | @click="selectProductionCategory(cat)">{{ cat }}</span> |
| | | </div> |
| | | <div class="chart-unit-row"> |
| | | <span>单位:件</span> |
| | | </div> |
| | |
| | | </div> |
| | | <div class="ring-box-left"> |
| | | <div class="left-label">粉煤灰</div> |
| | | <div class="left-value">月处理 <span style="font-weight: bold;font-size: 1.3vh;">7812</span> 吨 年处理 <span style="font-weight: bold;font-size: 1.3vh;">7812</span> 吨</div> |
| | | <div class="left-value">月处理 <span style="font-weight: bold;font-size: 1.3vh;">{{ middleRingStats.flyAshMonth }}</span> 吨 年处理 <span style="font-weight: bold;font-size: 1.3vh;">{{ middleRingStats.flyAshYear }}</span> 吨</div> |
| | | <div class="left-label" |
| | | style="margin-top: 2vh;">石膏</div> |
| | | <div class="left-value">月处理 <span style="font-weight: bold;font-size: 1.3vh;">7812</span> 吨 年处理 <span style="font-weight: bold;font-size: 1.3vh;">7812</span> 吨</div> |
| | | <div class="left-value">月处理 <span style="font-weight: bold;font-size: 1.3vh;">{{ middleRingStats.gypsumMonth }}</span> 吨 年处理 <span style="font-weight: bold;font-size: 1.3vh;">{{ middleRingStats.gypsumYear }}</span> 吨</div> |
| | | </div> |
| | | <div class="ring-box-topleft"> |
| | | <div class="topleft-label">项目产量</div> |
| | | </div> |
| | | <div class="ring-box-right"> |
| | | <div class="right-label">砌块产量</div> |
| | | <div class="right-value">月产量 <span style="font-weight: bold;font-size: 1.3vh;">7812</span> 吨 年产量 <span style="font-weight: bold;font-size: 1.3vh;">7812 |
| | | </span> 吨</div> |
| | | <div class="right-value">月产量 <span style="font-weight: bold;font-size: 1.3vh;">{{ middleRingStats.blockMonth }}</span> 吨 年产量 <span style="font-weight: bold;font-size: 1.3vh;">{{ middleRingStats.blockYear }}</span> 吨</div> |
| | | <div class="right-label" |
| | | style="margin-top: 2vh;">板材产量</div> |
| | | <div class="right-value">月产量 <span style="font-weight: bold;font-size: 1.3vh;">7812</span> 吨 年产量 <span style="font-weight: bold;font-size: 1.3vh;">7812</span> 吨</div> |
| | | <div class="right-value">月产量 <span style="font-weight: bold;font-size: 1.3vh;">{{ middleRingStats.plateMonth }}</span> 吨 年产量 <span style="font-weight: bold;font-size: 1.3vh;">{{ middleRingStats.plateYear }}</span> 吨</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </div> |
| | | <div class="bi-panel-body"> |
| | | <div class="chart-filter-tabs"> |
| | | <span v-for="area in salesAreas" |
| | | :key="area" |
| | | <span v-for="name in boardMaterialList" |
| | | :key="name" |
| | | class="cf-tab" |
| | | :class="{ active: blockSelectedArea === area }" |
| | | @click="handleBlockAreaChange(area)">{{ area }}</span> |
| | | :class="{ active: boardSelectedMaterial === name }" |
| | | @click="selectBoardMaterial(name)">{{ name }}</span> |
| | | </div> |
| | | <div class="material-info-card"> |
| | | <div class="material-icon"> |
| | |
| | | </svg> |
| | | </div> |
| | | <div class="material-details"> |
| | | <div class="material-name">{{ boardMaterialType }}AAA</div> |
| | | <div class="material-name">{{ boardMaterialSummary.materialName || "—" }}</div> |
| | | <div class="material-stats"> |
| | | <div class="stat-item"> |
| | | <span class="stat-label">月累计单耗</span> |
| | | <span class="stat-value">78/12</span> |
| | | <span class="stat-value">{{ boardMaterialSummary.monthlyConsumption }}</span> |
| | | <span class="stat-unit">吨</span> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <span class="stat-label">年累计单耗</span> |
| | | <span class="stat-value">78/12</span> |
| | | <span class="stat-value">{{ boardMaterialSummary.yearlyConsumption }}</span> |
| | | <span class="stat-unit">吨</span> |
| | | </div> |
| | | </div> |
| | |
| | | computed, |
| | | onMounted, |
| | | onBeforeUnmount, |
| | | watch, |
| | | nextTick, |
| | | } from "vue"; |
| | | import * as echarts from "echarts"; |
| | | import dayjs from "dayjs"; |
| | | import PanelHeader from "@/views/reportAnalysis/PSIDataAnalysis/components/PanelHeader.vue"; |
| | | import { |
| | | getMaterialProductionAnalysis, |
| | | getProductionMaterials, |
| | | getProductionStatisticsBlocks, |
| | | getProductionStatisticsPlates, |
| | | getProductionStatisticsMiddle, |
| | | getProductionStatisticsSolidWaste, |
| | | getProductionStatisticsEnergy, |
| | | } from "@/api/reportAnalysis/productionStatistics.js"; |
| | | |
| | | const screenRoot = ref(null); |
| | | const isFullscreen = ref(false); |
| | |
| | | |
| | | // 选择器数据 |
| | | const blockTimeDimension = ref("year"); |
| | | const blockMaterialType = ref("石灰"); |
| | | const boardTimeDimension = ref("year"); |
| | | const boardMaterialType = ref("石灰"); |
| | | const productionTimeDimension = ref("year"); |
| | | const customerTimeDimension = ref("year"); |
| | | const salesTimeDimension = ref("year"); |
| | | const selectedArea = ref("全部"); |
| | | |
| | | const salesAreas = [ |
| | | "全部", |
| | | "石灰", |
| | | "水泥", |
| | | "铝粉膏", |
| | | "脱模剂", |
| | | "防腐剂", |
| | | "氧化镁", |
| | | "冷拔丝", |
| | | ]; |
| | | const productionCategories = ["全部", "砌块", "板材"]; |
| | | const productionCategory = ref("全部"); |
| | | |
| | | // 中心环数据 |
| | | const projectProduction = ref(12345); |
| | | const solidWaste处理量 = ref(6789); |
| | | const blockProduction = ref(7812); |
| | | const boardProduction = ref(7812); |
| | | const blockMaterialList = ref([]); |
| | | const blockSelectedMaterial = ref(""); |
| | | const boardMaterialList = ref([]); |
| | | const boardSelectedMaterial = ref(""); |
| | | |
| | | const blockMaterialSummary = ref({ |
| | | materialName: "", |
| | | monthlyConsumption: "--", |
| | | yearlyConsumption: "--", |
| | | }); |
| | | const boardMaterialSummary = ref({ |
| | | materialName: "", |
| | | monthlyConsumption: "--", |
| | | yearlyConsumption: "--", |
| | | }); |
| | | |
| | | const blockCostChartSeries = ref({ |
| | | categories: [], |
| | | input: [], |
| | | output: [], |
| | | }); |
| | | const boardCostChartSeries = ref({ |
| | | categories: [], |
| | | input: [], |
| | | output: [], |
| | | }); |
| | | const productionChartSeries = ref({ |
| | | categories: [], |
| | | values: [], |
| | | }); |
| | | |
| | | // 固废处理量折线图(/home/productionStatistics/solidWaste) |
| | | const solidWasteChartSeries = ref({ |
| | | categories: [], |
| | | total: [], |
| | | flyAsh: [], |
| | | gypsum: [], |
| | | lime: [], |
| | | }); |
| | | |
| | | // 能耗统计(/home/productionStatistics/energy) |
| | | const energyChartSeries = ref({ |
| | | categories: [], |
| | | water: [], |
| | | electricity: [], |
| | | steam: [], |
| | | }); |
| | | |
| | | // 中心环:固废(粉煤灰/石膏)+ 项目产量(砌块/板材),接口 /home/productionStatistics/middle |
| | | const middleRingStats = ref({ |
| | | flyAshMonth: 0, |
| | | flyAshYear: 0, |
| | | gypsumMonth: 0, |
| | | gypsumYear: 0, |
| | | blockMonth: 0, |
| | | blockYear: 0, |
| | | plateMonth: 0, |
| | | plateYear: 0, |
| | | }); |
| | | |
| | | // 图表实例 |
| | | let blockCostChartInstance = null; |
| | |
| | | let customerTrendChartInstance = null; |
| | | let salesRankingChartInstance = null; |
| | | |
| | | // 生产单耗图表配置 |
| | | // 生产单耗图表配置(砌块,接口数据) |
| | | const blockCostChartOption = computed(() => { |
| | | const materials = ["消耗量"]; |
| | | const colors = ["#8A6BFF"]; |
| | | const year = 2024; |
| | | const periodType = blockTimeDimension.value; |
| | | |
| | | // 生成时间段 |
| | | let periods = []; |
| | | if (periodType === "year") { |
| | | // 年度数据:6个月 |
| | | for (let month = 9; month <= 12; month++) { |
| | | periods.push(`${month}/${year.toString().slice(2)}`); |
| | | } |
| | | for (let month = 1; month <= 3; month++) { |
| | | periods.push(`${month}/${(year + 1).toString().slice(2)}`); |
| | | } |
| | | } else { |
| | | // 月度数据:30天 |
| | | const month = 1; |
| | | for (let day = 1; day <= 30; day++) { |
| | | periods.push(`${month}/${day}`); |
| | | } |
| | | } |
| | | |
| | | // 为每种材料生成数据 |
| | | const series = materials.map((material, index) => { |
| | | const data = periods.map(() => { |
| | | return periodType === "year" |
| | | ? Math.floor(Math.random() * 50) + 150 |
| | | : Math.floor(Math.random() * 5) + 15; |
| | | }); |
| | | |
| | | return { |
| | | name: material, |
| | | data: data, |
| | | const periods = blockCostChartSeries.value.categories || []; |
| | | const inputData = blockCostChartSeries.value.input || []; |
| | | const outputData = blockCostChartSeries.value.output || []; |
| | | const legendNames = ["投入量", "产出量"]; |
| | | const colors = ["#6B9DFF", "#8A6BFF"]; |
| | | const series = [ |
| | | { |
| | | name: legendNames[0], |
| | | data: inputData, |
| | | type: "bar", |
| | | itemStyle: { color: colors[index] }, |
| | | }; |
| | | }); |
| | | itemStyle: { color: colors[0] }, |
| | | }, |
| | | { |
| | | name: legendNames[1], |
| | | data: outputData, |
| | | type: "bar", |
| | | itemStyle: { color: colors[1] }, |
| | | }, |
| | | ]; |
| | | |
| | | return { |
| | | backgroundColor: "transparent", |
| | |
| | | borderWidth: getResponsiveValue(1), |
| | | textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) }, |
| | | }, |
| | | // legend: { |
| | | // data: materials, |
| | | // top: "10%", |
| | | // right: "1%", |
| | | // textStyle: { |
| | | // color: "#B8C8E0", |
| | | // fontSize: getResponsiveValue(9), |
| | | // }, |
| | | // itemWidth: getResponsiveValue(10), |
| | | // itemHeight: getResponsiveValue(10), |
| | | // }, |
| | | legend: { |
| | | data: legendNames, |
| | | top: "2%", |
| | | right: "1%", |
| | | textStyle: { |
| | | color: "#B8C8E0", |
| | | fontSize: getResponsiveValue(9), |
| | | }, |
| | | itemWidth: getResponsiveValue(10), |
| | | itemHeight: getResponsiveValue(10), |
| | | }, |
| | | grid: { |
| | | left: "1%", |
| | | right: "1%", |
| | | bottom: "1%", |
| | | top: "8%", |
| | | top: "18%", |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | |
| | | }, |
| | | splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } }, |
| | | }, |
| | | series: series, |
| | | series, |
| | | }; |
| | | }); |
| | | |
| | | // 板材单耗图表配置 |
| | | // 板材单耗图表配置(接口数据,与砌块结构一致) |
| | | const boardCostChartOption = computed(() => { |
| | | const materials = ["消耗量"]; |
| | | const colors = [ |
| | | "#00A4ED", |
| | | "#34D8F7", |
| | | "#4A8BFF", |
| | | "#8A6BFF", |
| | | "#C8C447", |
| | | "#FF6B6B", |
| | | ]; |
| | | const year = 2024; |
| | | const periodType = boardTimeDimension.value; |
| | | |
| | | // 生成时间段 |
| | | let periods = []; |
| | | if (periodType === "year") { |
| | | // 年度数据:6个月 |
| | | for (let month = 9; month <= 12; month++) { |
| | | periods.push(`${month}/${year.toString().slice(2)}`); |
| | | } |
| | | for (let month = 1; month <= 3; month++) { |
| | | periods.push(`${month}/${(year + 1).toString().slice(2)}`); |
| | | } |
| | | } else { |
| | | // 月度数据:30天 |
| | | const month = 1; |
| | | for (let day = 1; day <= 30; day++) { |
| | | periods.push(`${month}/${day}`); |
| | | } |
| | | } |
| | | |
| | | // 为每种材料生成数据 |
| | | const series = materials.map((material, index) => { |
| | | const data = periods.map(() => { |
| | | return periodType === "year" |
| | | ? Math.floor(Math.random() * 50) + 150 |
| | | : Math.floor(Math.random() * 5) + 15; |
| | | }); |
| | | |
| | | return { |
| | | name: material, |
| | | data: data, |
| | | const periods = boardCostChartSeries.value.categories || []; |
| | | const inputData = boardCostChartSeries.value.input || []; |
| | | const outputData = boardCostChartSeries.value.output || []; |
| | | const legendNames = ["投入量", "产出量"]; |
| | | const colors = ["#00A4ED", "#34D8F7"]; |
| | | const series = [ |
| | | { |
| | | name: legendNames[0], |
| | | data: inputData, |
| | | type: "bar", |
| | | itemStyle: { color: colors[index] }, |
| | | }; |
| | | }); |
| | | |
| | | return { |
| | | backgroundColor: "transparent", |
| | | tooltip: { |
| | | trigger: "axis", |
| | | backgroundColor: "rgba(0,0,0,0.55)", |
| | | borderColor: "rgba(64,158,255,0.25)", |
| | | borderWidth: getResponsiveValue(1), |
| | | textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) }, |
| | | itemStyle: { color: colors[0] }, |
| | | }, |
| | | // legend: { |
| | | // data: materials, |
| | | // top: "10%", |
| | | // right: "1%", |
| | | // textStyle: { |
| | | // color: "#B8C8E0", |
| | | // fontSize: getResponsiveValue(9), |
| | | // }, |
| | | // itemWidth: getResponsiveValue(10), |
| | | // itemHeight: getResponsiveValue(10), |
| | | // }, |
| | | grid: { |
| | | left: "1%", |
| | | right: "1%", |
| | | bottom: "1%", |
| | | top: "8%", |
| | | containLabel: true, |
| | | { |
| | | name: legendNames[1], |
| | | data: outputData, |
| | | type: "bar", |
| | | itemStyle: { color: colors[1] }, |
| | | }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: periods, |
| | | axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } }, |
| | | axisTick: { show: false }, |
| | | axisLabel: { |
| | | color: "#B8C8E0", |
| | | fontSize: getResponsiveValue(11), |
| | | margin: getResponsiveValue(10), |
| | | }, |
| | | splitLine: { show: false }, |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } }, |
| | | axisLabel: { |
| | | color: "#B8C8E0", |
| | | fontSize: getResponsiveValue(11), |
| | | margin: getResponsiveValue(8), |
| | | }, |
| | | splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } }, |
| | | }, |
| | | series: series, |
| | | }; |
| | | }); |
| | | |
| | | // 产量分析图表配置 |
| | | const productionChartOption = computed(() => { |
| | | const salesAreas = ["全部", "砌块", "板材"]; |
| | | const colors = [ |
| | | "#00A4ED", |
| | | "#34D8F7", |
| | | "#4A8BFF", |
| | | "#8A6BFF", |
| | | "#C8C447", |
| | | "#FF6B6B", |
| | | ]; |
| | | const year = 2024; |
| | | const periodType = productionTimeDimension.value; |
| | | |
| | | // 生成时间段 |
| | | let periods = []; |
| | | if (periodType === "year") { |
| | | // 年度数据:6个月 |
| | | for (let month = 9; month <= 12; month++) { |
| | | periods.push(`${month}/${year.toString().slice(2)}`); |
| | | } |
| | | for (let month = 1; month <= 3; month++) { |
| | | periods.push(`${month}/${(year + 1).toString().slice(2)}`); |
| | | } |
| | | } else { |
| | | // 月度数据:30天 |
| | | const month = 1; |
| | | for (let day = 1; day <= 30; day++) { |
| | | periods.push(`${month}/${day}`); |
| | | } |
| | | } |
| | | |
| | | // 为每个销售区生成数据 |
| | | const series = salesAreas.map((area, index) => { |
| | | const data = periods.map(() => { |
| | | return periodType === "year" |
| | | ? Math.floor(Math.random() * 50) + 150 |
| | | : Math.floor(Math.random() * 5) + 15; |
| | | }); |
| | | |
| | | return { |
| | | name: area, |
| | | data: data, |
| | | type: "line", |
| | | smooth: true, |
| | | lineStyle: { width: getResponsiveValue(2), color: colors[index] }, |
| | | itemStyle: { color: colors[index] }, |
| | | areaStyle: { |
| | | opacity: 0.3, |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: colors[index] + "80" }, |
| | | { offset: 1, color: colors[index] + "00" }, |
| | | ]), |
| | | }, |
| | | }; |
| | | }); |
| | | |
| | | return { |
| | | backgroundColor: "transparent", |
| | |
| | | textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) }, |
| | | }, |
| | | legend: { |
| | | data: salesAreas, |
| | | data: legendNames, |
| | | top: "2%", |
| | | right: "1%", |
| | | textStyle: { |
| | | color: "#B8C8E0", |
| | | fontSize: getResponsiveValue(9), |
| | | }, |
| | | itemWidth: getResponsiveValue(10), |
| | | itemHeight: getResponsiveValue(10), |
| | | }, |
| | | grid: { |
| | | left: "1%", |
| | | right: "1%", |
| | | bottom: "1%", |
| | | top: "18%", |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: periods, |
| | | axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } }, |
| | | axisTick: { show: false }, |
| | | axisLabel: { |
| | | color: "#B8C8E0", |
| | | fontSize: getResponsiveValue(11), |
| | | margin: getResponsiveValue(10), |
| | | }, |
| | | splitLine: { show: false }, |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } }, |
| | | axisLabel: { |
| | | color: "#B8C8E0", |
| | | fontSize: getResponsiveValue(11), |
| | | margin: getResponsiveValue(8), |
| | | }, |
| | | splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } }, |
| | | }, |
| | | series, |
| | | }; |
| | | }); |
| | | |
| | | // 物料生产量分析(接口数据) |
| | | 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" }, |
| | | ]), |
| | | }, |
| | | }, |
| | | ]; |
| | | |
| | | return { |
| | | backgroundColor: "transparent", |
| | | tooltip: { |
| | | trigger: "axis", |
| | | backgroundColor: "rgba(0,0,0,0.55)", |
| | | borderColor: "rgba(64,158,255,0.25)", |
| | | borderWidth: getResponsiveValue(1), |
| | | textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) }, |
| | | }, |
| | | legend: { |
| | | data: [seriesName], |
| | | top: "10%", |
| | | right: "1%", |
| | | textStyle: { |
| | |
| | | }, |
| | | splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } }, |
| | | }, |
| | | series: series, |
| | | series, |
| | | }; |
| | | }); |
| | | |
| | | // 新增客户趋势图表配置 |
| | | // 固废处理量图表(接口 solidWaste) |
| | | const customerTrendChartOption = computed(() => { |
| | | const customerTypes = ["全部", "粉煤灰", "石膏", "石灰"]; |
| | | const legendNames = ["全部", "粉煤灰", "石膏", "石灰"]; |
| | | const colors = ["#00A4ED", "#4A8BFF", "#8A6BFF", "#C8C447"]; |
| | | const year = 2024; |
| | | const periodType = customerTimeDimension.value; |
| | | const periods = solidWasteChartSeries.value.categories || []; |
| | | const dataBySeries = [ |
| | | solidWasteChartSeries.value.total || [], |
| | | solidWasteChartSeries.value.flyAsh || [], |
| | | solidWasteChartSeries.value.gypsum || [], |
| | | solidWasteChartSeries.value.lime || [], |
| | | ]; |
| | | |
| | | // 生成时间段 |
| | | let periods = []; |
| | | if (periodType === "year") { |
| | | // 年度数据:6个月 |
| | | for (let month = 9; month <= 12; month++) { |
| | | periods.push(`${month}/${year.toString().slice(2)}`); |
| | | } |
| | | for (let month = 1; month <= 5; month++) { |
| | | periods.push(`${month}/${(year + 1).toString().slice(2)}`); |
| | | } |
| | | } else { |
| | | // 月度数据:30天 |
| | | const month = 1; |
| | | for (let day = 1; day <= 30; day++) { |
| | | periods.push(`${month}/${day}`); |
| | | } |
| | | } |
| | | |
| | | // 为每种客户类型生成数据 |
| | | const series = customerTypes.map((type, index) => { |
| | | const data = periods.map(() => { |
| | | return periodType === "year" |
| | | ? Math.floor(Math.random() * 10) + 5 |
| | | : Math.floor(Math.random() * 3) + 1; |
| | | }); |
| | | |
| | | return { |
| | | name: type, |
| | | data: data, |
| | | type: "line", |
| | | smooth: true, |
| | | lineStyle: { width: getResponsiveValue(2), color: colors[index] }, |
| | | itemStyle: { color: colors[index] }, |
| | | areaStyle: { |
| | | opacity: 0.3, |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: colors[index] + "80" }, |
| | | { offset: 1, color: colors[index] + "00" }, |
| | | ]), |
| | | }, |
| | | }; |
| | | }); |
| | | const series = legendNames.map((name, index) => ({ |
| | | name, |
| | | data: dataBySeries[index] || [], |
| | | type: "line", |
| | | smooth: true, |
| | | lineStyle: { width: getResponsiveValue(2), color: colors[index] }, |
| | | itemStyle: { color: colors[index] }, |
| | | areaStyle: { |
| | | opacity: 0.3, |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: colors[index] + "80" }, |
| | | { offset: 1, color: colors[index] + "00" }, |
| | | ]), |
| | | }, |
| | | })); |
| | | |
| | | return { |
| | | backgroundColor: "transparent", |
| | |
| | | textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) }, |
| | | }, |
| | | legend: { |
| | | data: customerTypes, |
| | | data: legendNames, |
| | | top: "10%", |
| | | right: "1%", |
| | | textStyle: { |
| | |
| | | }, |
| | | splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } }, |
| | | }, |
| | | series: series, |
| | | series, |
| | | }; |
| | | }); |
| | | |
| | | // 能耗统计图表配置 |
| | | // 能耗统计图表配置(接口 energy) |
| | | const salesRankingChartOption = computed(() => { |
| | | const energyTypes = ["水", "电", "蒸汽"]; |
| | | const colors = ["#00A4ED", "#AC43C2", "#F5BC4A"]; |
| | | const year = 2024; |
| | | const periodType = salesTimeDimension.value; |
| | | |
| | | // 生成时间段 |
| | | let periods = []; |
| | | if (periodType === "year") { |
| | | // 年度数据:6个月 |
| | | for (let month = 9; month <= 12; month++) { |
| | | periods.push(`${month}/${year.toString().slice(2)}`); |
| | | } |
| | | for (let month = 1; month <= 3; month++) { |
| | | periods.push(`${month}/${(year + 1).toString().slice(2)}`); |
| | | } |
| | | } else { |
| | | // 月度数据:7天 |
| | | const month = 1; |
| | | for (let day = 1; day <= 7; day++) { |
| | | periods.push(`${month}/${day}`); |
| | | } |
| | | } |
| | | |
| | | // 为每种能源类型生成数据 |
| | | const waterData = periods.map(() => { |
| | | return periodType === "year" |
| | | ? Math.floor(Math.random() * 300) + 400 |
| | | : Math.floor(Math.random() * 30) + 40; |
| | | }); |
| | | const steamData = periods.map(() => { |
| | | return periodType === "year" |
| | | ? Math.floor(Math.random() * 400) + 500 |
| | | : Math.floor(Math.random() * 40) + 50; |
| | | }); |
| | | const electricityData = periods.map(() => { |
| | | return periodType === "year" |
| | | ? Math.floor(Math.random() * 200) + 300 |
| | | : Math.floor(Math.random() * 20) + 30; |
| | | }); |
| | | const periods = energyChartSeries.value.categories || []; |
| | | const waterData = energyChartSeries.value.water || []; |
| | | const electricityData = energyChartSeries.value.electricity || []; |
| | | const steamData = energyChartSeries.value.steam || []; |
| | | |
| | | const series = [ |
| | | { |
| | |
| | | return Math.round((baseValue * window.innerWidth) / baseWidth.value); |
| | | }; |
| | | |
| | | const mapTimeDimensionToDateType = dim => (dim === "year" ? "2" : "1"); |
| | | |
| | | const productionOutputKey = { |
| | | 全部: "totalOutput", |
| | | 砌块: "blockOutput", |
| | | 板材: "plateOutput", |
| | | }; |
| | | |
| | | const loadBlockCost = async () => { |
| | | const materialName = blockSelectedMaterial.value; |
| | | if (!materialName) { |
| | | blockMaterialSummary.value = { |
| | | materialName: "", |
| | | monthlyConsumption: "--", |
| | | yearlyConsumption: "--", |
| | | }; |
| | | blockCostChartSeries.value = { categories: [], input: [], output: [] }; |
| | | updateCharts(); |
| | | return; |
| | | } |
| | | const dateType = mapTimeDimensionToDateType(blockTimeDimension.value); |
| | | try { |
| | | const res = await getProductionStatisticsBlocks({ materialName, dateType }); |
| | | const rows = Array.isArray(res.data) ? res.data : []; |
| | | const row = |
| | | rows.find(r => r.materialName === materialName) || rows[0] || {}; |
| | | const chartData = Array.isArray(row.chartData) ? row.chartData : []; |
| | | blockMaterialSummary.value = { |
| | | materialName: row.materialName || materialName, |
| | | monthlyConsumption: row.monthlyConsumption ?? "--", |
| | | yearlyConsumption: row.yearlyConsumption ?? "--", |
| | | }; |
| | | blockCostChartSeries.value = { |
| | | categories: chartData.map(c => c.date), |
| | | input: chartData.map(c => c.inputSum ?? 0), |
| | | output: chartData.map(c => c.outputSum ?? 0), |
| | | }; |
| | | updateCharts(); |
| | | } catch (e) { |
| | | console.error(e); |
| | | } |
| | | }; |
| | | |
| | | const loadBoardCost = async () => { |
| | | const materialName = boardSelectedMaterial.value; |
| | | if (!materialName) { |
| | | boardMaterialSummary.value = { |
| | | materialName: "", |
| | | monthlyConsumption: "--", |
| | | yearlyConsumption: "--", |
| | | }; |
| | | boardCostChartSeries.value = { categories: [], input: [], output: [] }; |
| | | updateCharts(); |
| | | return; |
| | | } |
| | | const dateType = mapTimeDimensionToDateType(boardTimeDimension.value); |
| | | try { |
| | | const res = await getProductionStatisticsPlates({ materialName, dateType }); |
| | | const rows = Array.isArray(res.data) ? res.data : []; |
| | | const row = |
| | | rows.find(r => r.materialName === materialName) || rows[0] || {}; |
| | | const chartData = Array.isArray(row.chartData) ? row.chartData : []; |
| | | boardMaterialSummary.value = { |
| | | materialName: row.materialName || materialName, |
| | | monthlyConsumption: row.monthlyConsumption ?? "--", |
| | | yearlyConsumption: row.yearlyConsumption ?? "--", |
| | | }; |
| | | boardCostChartSeries.value = { |
| | | categories: chartData.map(c => c.date), |
| | | input: chartData.map(c => c.inputSum ?? 0), |
| | | output: chartData.map(c => c.outputSum ?? 0), |
| | | }; |
| | | updateCharts(); |
| | | } catch (e) { |
| | | console.error(e); |
| | | } |
| | | }; |
| | | |
| | | const loadBlockMaterials = async () => { |
| | | try { |
| | | const res = await getProductionMaterials({ materialType: "1" }); |
| | | const list = Array.isArray(res.data) ? [...res.data] : []; |
| | | blockMaterialList.value = list; |
| | | if (list.length) { |
| | | if (!list.includes(blockSelectedMaterial.value)) { |
| | | blockSelectedMaterial.value = list[0]; |
| | | } |
| | | await loadBlockCost(); |
| | | } else { |
| | | blockSelectedMaterial.value = ""; |
| | | blockMaterialSummary.value = { |
| | | materialName: "", |
| | | monthlyConsumption: "--", |
| | | yearlyConsumption: "--", |
| | | }; |
| | | blockCostChartSeries.value = { categories: [], input: [], output: [] }; |
| | | updateCharts(); |
| | | } |
| | | } catch (e) { |
| | | console.error(e); |
| | | } |
| | | }; |
| | | |
| | | const loadBoardMaterials = async () => { |
| | | try { |
| | | const res = await getProductionMaterials({ materialType: "2" }); |
| | | const list = Array.isArray(res.data) ? [...res.data] : []; |
| | | boardMaterialList.value = list; |
| | | if (list.length) { |
| | | if (!list.includes(boardSelectedMaterial.value)) { |
| | | boardSelectedMaterial.value = list[0]; |
| | | } |
| | | await loadBoardCost(); |
| | | } else { |
| | | boardSelectedMaterial.value = ""; |
| | | boardMaterialSummary.value = { |
| | | materialName: "", |
| | | monthlyConsumption: "--", |
| | | yearlyConsumption: "--", |
| | | }; |
| | | boardCostChartSeries.value = { categories: [], input: [], output: [] }; |
| | | updateCharts(); |
| | | } |
| | | } catch (e) { |
| | | console.error(e); |
| | | } |
| | | }; |
| | | |
| | | const loadMaterialProduction = async () => { |
| | | const dateType = mapTimeDimensionToDateType(productionTimeDimension.value); |
| | | try { |
| | | 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), |
| | | }; |
| | | updateCharts(); |
| | | } catch (e) { |
| | | console.error(e); |
| | | } |
| | | }; |
| | | |
| | | const loadMiddleRingStats = async () => { |
| | | try { |
| | | const res = await getProductionStatisticsMiddle(); |
| | | const d = res.data && typeof res.data === "object" ? res.data : {}; |
| | | middleRingStats.value = { |
| | | flyAshMonth: Number(d.flyAshMonth) || 0, |
| | | flyAshYear: Number(d.flyAshYear) || 0, |
| | | gypsumMonth: Number(d.gypsumMonth) || 0, |
| | | gypsumYear: Number(d.gypsumYear) || 0, |
| | | blockMonth: Number(d.blockMonth) || 0, |
| | | blockYear: Number(d.blockYear) || 0, |
| | | plateMonth: Number(d.plateMonth) || 0, |
| | | plateYear: Number(d.plateYear) || 0, |
| | | }; |
| | | } catch (e) { |
| | | console.error(e); |
| | | } |
| | | }; |
| | | |
| | | const loadSolidWasteData = async () => { |
| | | const dateType = mapTimeDimensionToDateType(customerTimeDimension.value); |
| | | try { |
| | | const res = await getProductionStatisticsSolidWaste({ dateType }); |
| | | const list = Array.isArray(res.data) ? res.data : []; |
| | | solidWasteChartSeries.value = { |
| | | categories: list.map(i => i.dateStr), |
| | | total: list.map(i => Number(i.total) || 0), |
| | | flyAsh: list.map(i => Number(i.flyAsh) || 0), |
| | | gypsum: list.map(i => Number(i.gypsum) || 0), |
| | | lime: list.map(i => Number(i.lime) || 0), |
| | | }; |
| | | updateCharts(); |
| | | } catch (e) { |
| | | console.error(e); |
| | | } |
| | | }; |
| | | |
| | | const loadEnergyData = async () => { |
| | | const dateType = mapTimeDimensionToDateType(salesTimeDimension.value); |
| | | try { |
| | | const res = await getProductionStatisticsEnergy({ dateType }); |
| | | const list = Array.isArray(res.data) ? res.data : []; |
| | | energyChartSeries.value = { |
| | | categories: list.map(i => i.dateStr), |
| | | water: list.map(i => Number(i.water) || 0), |
| | | electricity: list.map(i => Number(i.electricity) || 0), |
| | | steam: list.map(i => Number(i.steam) || 0), |
| | | }; |
| | | updateCharts(); |
| | | } catch (e) { |
| | | console.error(e); |
| | | } |
| | | }; |
| | | |
| | | const selectBlockMaterial = name => { |
| | | blockSelectedMaterial.value = name; |
| | | loadBlockCost(); |
| | | }; |
| | | |
| | | const selectBoardMaterial = name => { |
| | | boardSelectedMaterial.value = name; |
| | | loadBoardCost(); |
| | | }; |
| | | |
| | | const selectProductionCategory = cat => { |
| | | productionCategory.value = cat; |
| | | loadMaterialProduction(); |
| | | }; |
| | | |
| | | // 初始化图表 |
| | | const initCharts = () => { |
| | | // 初始化砌块成本图表 |
| | |
| | | // 处理时间维度选择 |
| | | const handleBlockTimeDimensionChange = dimension => { |
| | | blockTimeDimension.value = dimension; |
| | | updateCharts(); |
| | | loadBlockCost(); |
| | | }; |
| | | |
| | | const handleBoardTimeDimensionChange = dimension => { |
| | | boardTimeDimension.value = dimension; |
| | | updateCharts(); |
| | | loadBoardCost(); |
| | | }; |
| | | |
| | | const handleProductionTimeDimensionChange = dimension => { |
| | | productionTimeDimension.value = dimension; |
| | | updateCharts(); |
| | | loadMaterialProduction(); |
| | | }; |
| | | |
| | | const handleCustomerTimeDimensionChange = dimension => { |
| | | customerTimeDimension.value = dimension; |
| | | updateCharts(); |
| | | loadSolidWasteData(); |
| | | }; |
| | | |
| | | const handleSalesTimeDimensionChange = dimension => { |
| | | salesTimeDimension.value = dimension; |
| | | updateCharts(); |
| | | }; |
| | | |
| | | // 处理材料类型选择 |
| | | const handleBlockMaterialTypeChange = type => { |
| | | blockMaterialType.value = type; |
| | | updateCharts(); |
| | | }; |
| | | |
| | | const handleBoardMaterialTypeChange = type => { |
| | | boardMaterialType.value = type; |
| | | updateCharts(); |
| | | }; |
| | | |
| | | // 处理销售区选择 |
| | | const handleAreaChange = area => { |
| | | selectedArea.value = area; |
| | | updateCharts(); |
| | | loadEnergyData(); |
| | | }; |
| | | |
| | | // 监听窗口大小变化 |
| | |
| | | }, 1000); |
| | | } |
| | | |
| | | // 等待DOM更新后初始化图表 |
| | | nextTick(() => { |
| | | // 等待DOM更新后初始化图表并拉取接口数据 |
| | | nextTick(async () => { |
| | | initCharts(); |
| | | await Promise.all([ |
| | | loadBlockMaterials(), |
| | | loadBoardMaterials(), |
| | | loadMaterialProduction(), |
| | | loadMiddleRingStats(), |
| | | loadSolidWasteData(), |
| | | loadEnergyData(), |
| | | ]); |
| | | }); |
| | | |
| | | // 添加窗口大小变化监听 |