| | |
| | | <template> |
| | | <div class="dashboard-container"> |
| | | <div class="data-dashboard"> |
| | | |
| | | <!-- 顶鍿 颿 --> |
| | | <!-- <div class="dashboard-header"> |
| | | <!-- 顶鍿 颿 --> |
| | | <!-- <div class="dashboard-header"> |
| | | <div class="factory-name">ç产ç»è®¡çæ¿</div> |
| | | </div> --> |
| | | |
| | | <!-- çéåºå --> |
| | | <div class="filter-area"> |
| | | <div class="filter-section"> |
| | | <span class="filter-label">æ¶é´ç»´åº¦ï¼</span> |
| | | <el-radio-group v-model="dateType" @change="handleDateTypeChange" class="radio-group"> |
| | | <el-radio-button label="month">æåº¦</el-radio-button> |
| | | <el-radio-button label="year">年度</el-radio-button> |
| | | </el-radio-group> |
| | | <!-- çéåºå --> |
| | | <div class="filter-area"> |
| | | <div class="filter-section"> |
| | | <span class="filter-label">æ¶é´ç»´åº¦ï¼</span> |
| | | <el-radio-group v-model="dateType" |
| | | @change="handleDateTypeChange" |
| | | class="radio-group"> |
| | | <el-radio-button label="month">æåº¦</el-radio-button> |
| | | <el-radio-button label="year">年度</el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | <div class="filter-section"> |
| | | <span class="filter-label">产åç±»åï¼</span> |
| | | <el-radio-group v-model="productType" |
| | | @change="handleProductTypeChange" |
| | | class="radio-group"> |
| | | <el-radio-button label="block">ç å</el-radio-button> |
| | | <el-radio-button label="plate">æ¿æ</el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | </div> |
| | | <div class="filter-section"> |
| | | <span class="filter-label">产åç±»åï¼</span> |
| | | <el-radio-group v-model="productType" @change="handleProductTypeChange" class="radio-group"> |
| | | <el-radio-button label="block">ç å</el-radio-button> |
| | | <el-radio-button label="plate">æ¿æ</el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 主è¦å
容åºå --> |
| | | <div class="dashboard-content"> |
| | | <!-- 第ä¸è¡ --> |
| | | <div class="row row-1"> |
| | | <div class="panel-card card-1"> |
| | | <div class="panel-title">äº§éææ </div> |
| | | <div class="chart-container"> |
| | | <div ref="productionChart" style="width: 100%; height: 100%"></div> |
| | | <!-- 主è¦å
容åºå --> |
| | | <div class="dashboard-content"> |
| | | <!-- 第ä¸è¡ --> |
| | | <div class="row row-1"> |
| | | <div class="panel-card card-1"> |
| | | <div class="panel-title">äº§éææ </div> |
| | | <div class="chart-container"> |
| | | <div ref="productionChart" |
| | | style="width: 100%; height: 100%"></div> |
| | | </div> |
| | | </div> |
| | | <div class="panel-card card-2"> |
| | | <div class="panel-title">åºåºå¤çé</div> |
| | | <div class="chart-container"> |
| | | <div ref="solidWasteChart" |
| | | style="width: 100%; height: 100%"></div> |
| | | </div> |
| | | </div> |
| | | <div class="panel-card card-3"> |
| | | <div class="panel-title">综åç»è®¡</div> |
| | | <div class="stats-grid"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">æ»äº§è½</div> |
| | | <div class="stat-value production-color">{{ totalProduction }}</div> |
| | | <div class="stat-unit">ç«æ¹ç±³</div> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">æ»åºåºå¤ç</div> |
| | | <div class="stat-value waste-color">{{ totalSolidWaste }}</div> |
| | | <div class="stat-unit">å¨</div> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">å¹³ååè</div> |
| | | <div class="stat-value consumption-color">{{ averageUnitConsumption }}</div> |
| | | <div class="stat-unit">å¨/ç«æ¹ç±³</div> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">æ»è½è</div> |
| | | <div class="stat-value energy-color">{{ totalEnergy }}</div> |
| | | <div class="stat-unit">kWh</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="panel-card card-2"> |
| | | <div class="panel-title">åºåºå¤çé</div> |
| | | <div class="chart-container"> |
| | | <div ref="solidWasteChart" style="width: 100%; height: 100%"></div> |
| | | <!-- 第äºè¡ --> |
| | | <div class="row row-2"> |
| | | <div class="panel-card card-4"> |
| | | <div class="panel-title">çäº§ææ¬åè</div> |
| | | <div class="chart-container"> |
| | | <div ref="costChart" |
| | | style="width: 100%; height: 100%"></div> |
| | | </div> |
| | | </div> |
| | | <div class="panel-card card-5"> |
| | | <div class="panel-title">ç产è½èæ°æ®</div> |
| | | <div class="chart-container"> |
| | | <div ref="energyChart" |
| | | style="width: 100%; height: 100%"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="panel-card card-3"> |
| | | <div class="panel-title">综åç»è®¡</div> |
| | | <div class="stats-grid"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">æ»äº§è½</div> |
| | | <div class="stat-value">{{ totalProduction }}</div> |
| | | <div class="stat-unit">ç«æ¹ç±³</div> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">æ»åºåºå¤ç</div> |
| | | <div class="stat-value">{{ totalSolidWaste }}</div> |
| | | <div class="stat-unit">å¨</div> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">å¹³ååè</div> |
| | | <div class="stat-value">{{ averageUnitConsumption }}</div> |
| | | <div class="stat-unit">å¨/ç«æ¹ç±³</div> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">æ»è½è</div> |
| | | <div class="stat-value">{{ totalEnergy }}</div> |
| | | <div class="stat-unit">kWh</div> |
| | | <!-- 第ä¸è¡ --> |
| | | <div class="row row-3"> |
| | | <div class="panel-card card-6"> |
| | | <div class="panel-title">åèæ°æ®æç»</div> |
| | | <div class="table-container"> |
| | | <el-table :data="costTableData" |
| | | style="width: 100%"> |
| | | <el-table-column prop="material" |
| | | label="ç©æç±»å" |
| | | width="120" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getMaterialTypeType(scope.row.material)"> |
| | | {{ scope.row.material }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="unit" |
| | | label="åä½" |
| | | width="100" /> |
| | | <el-table-column prop="monthlyConsumption" |
| | | label="æåº¦ç´¯è®¡ç¨é" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.monthlyConsumption }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="monthlyProduction" |
| | | label="æåº¦ç´¯è®¡äº§é" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.monthlyProduction }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="monthlyUnitConsumption" |
| | | label="æåº¦ç´¯è®¡åè" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.monthlyUnitConsumption }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="yearlyConsumption" |
| | | label="年度累计ç¨é" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.yearlyConsumption }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="yearlyProduction" |
| | | label="年度累计产é" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.yearlyProduction }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="yearlyUnitConsumption" |
| | | label="年度累计åè" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.yearlyUnitConsumption }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 第äºè¡ --> |
| | | <div class="row row-2"> |
| | | <div class="panel-card card-4"> |
| | | <div class="panel-title">çäº§ææ¬åè</div> |
| | | <div class="chart-container"> |
| | | <div ref="costChart" style="width: 100%; height: 100%"></div> |
| | | </div> |
| | | </div> |
| | | <div class="panel-card card-5"> |
| | | <div class="panel-title">ç产è½èæ°æ®</div> |
| | | <div class="chart-container"> |
| | | <div ref="energyChart" style="width: 100%; height: 100%"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 第ä¸è¡ --> |
| | | <div class="row row-3"> |
| | | <div class="panel-card card-6"> |
| | | <div class="panel-title">åèæ°æ®æç»</div> |
| | | <div class="table-container"> |
| | | <el-table :data="costTableData" style="width: 100%"> |
| | | <el-table-column prop="material" label="ç©æç±»å" width="120" /> |
| | | <el-table-column prop="unit" label="åä½" width="100" /> |
| | | <el-table-column prop="monthlyConsumption" label="æåº¦ç´¯è®¡ç¨é" /> |
| | | <el-table-column prop="monthlyProduction" label="æåº¦ç´¯è®¡äº§é" /> |
| | | <el-table-column prop="monthlyUnitConsumption" label="æåº¦ç´¯è®¡åè" /> |
| | | <el-table-column prop="yearlyConsumption" label="年度累计ç¨é" /> |
| | | <el-table-column prop="yearlyProduction" label="年度累计产é" /> |
| | | <el-table-column prop="yearlyUnitConsumption" label="年度累计åè" /> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from 'vue' |
| | | import * as echarts from 'echarts' |
| | | import { |
| | | ref, |
| | | computed, |
| | | onMounted, |
| | | onBeforeUnmount, |
| | | nextTick, |
| | | watch, |
| | | } from "vue"; |
| | | import * as echarts from "echarts"; |
| | | |
| | | // ç鿡件 |
| | | const dateType = ref('month') // month æ year |
| | | const productType = ref('block') // block æ plate |
| | | // ç鿡件 |
| | | const dateType = ref("month"); // month æ year |
| | | const productType = ref("block"); // block æ plate |
| | | |
| | | // å¾è¡¨å¼ç¨ |
| | | const productionChart = ref(null) |
| | | const solidWasteChart = ref(null) |
| | | const costChart = ref(null) |
| | | const energyChart = ref(null) |
| | | // å¾è¡¨å¼ç¨ |
| | | const productionChart = ref(null); |
| | | const solidWasteChart = ref(null); |
| | | const costChart = ref(null); |
| | | const energyChart = ref(null); |
| | | |
| | | // å¾è¡¨å®ä¾ |
| | | let productionChartInstance = null |
| | | let solidWasteChartInstance = null |
| | | let costChartInstance = null |
| | | let energyChartInstance = null |
| | | // å¾è¡¨å®ä¾ |
| | | let productionChartInstance = null; |
| | | let solidWasteChartInstance = null; |
| | | let costChartInstance = null; |
| | | let energyChartInstance = null; |
| | | |
| | | // æ¨¡ææ°æ® |
| | | const productionData = ref({ |
| | | month: [ |
| | | { name: '1æ', block: 1200, plate: 800 }, |
| | | { name: '2æ', block: 1300, plate: 850 }, |
| | | { name: '3æ', block: 1100, plate: 750 }, |
| | | { name: '4æ', block: 1400, plate: 900 }, |
| | | { name: '5æ', block: 1500, plate: 950 }, |
| | | { name: '6æ', block: 1350, plate: 880 }, |
| | | { name: '7æ', block: 1450, plate: 920 }, |
| | | { name: '8æ', block: 1600, plate: 1000 }, |
| | | { name: '9æ', block: 1550, plate: 980 }, |
| | | { name: '10æ', block: 1700, plate: 1050 }, |
| | | { name: '11æ', block: 1650, plate: 1020 }, |
| | | { name: '12æ', block: 1800, plate: 1100 } |
| | | ], |
| | | year: [ |
| | | { name: '2023', block: 15000, plate: 9500 }, |
| | | { name: '2024', block: 16500, plate: 10200 }, |
| | | { name: '2025', block: 18000, plate: 11000 } |
| | | ] |
| | | }) |
| | | // æ¨¡ææ°æ® |
| | | const productionData = ref({ |
| | | month: [ |
| | | { name: "1æ", block: 1200, plate: 800 }, |
| | | { name: "2æ", block: 1300, plate: 850 }, |
| | | { name: "3æ", block: 1100, plate: 750 }, |
| | | { name: "4æ", block: 1400, plate: 900 }, |
| | | { name: "5æ", block: 1500, plate: 950 }, |
| | | { name: "6æ", block: 1350, plate: 880 }, |
| | | { name: "7æ", block: 1450, plate: 920 }, |
| | | { name: "8æ", block: 1600, plate: 1000 }, |
| | | { name: "9æ", block: 1550, plate: 980 }, |
| | | { name: "10æ", block: 1700, plate: 1050 }, |
| | | { name: "11æ", block: 1650, plate: 1020 }, |
| | | { name: "12æ", block: 1800, plate: 1100 }, |
| | | ], |
| | | year: [ |
| | | { name: "2023", block: 15000, plate: 9500 }, |
| | | { name: "2024", block: 16500, plate: 10200 }, |
| | | { name: "2025", block: 18000, plate: 11000 }, |
| | | ], |
| | | }); |
| | | |
| | | const solidWasteData = ref({ |
| | | month: [ |
| | | { name: '1æ', ç²ç
¤ç°: 200, ç³è: 150, ç³ç°: 100 }, |
| | | { name: '2æ', ç²ç
¤ç°: 220, ç³è: 160, ç³ç°: 110 }, |
| | | { name: '3æ', ç²ç
¤ç°: 190, ç³è: 140, ç³ç°: 95 }, |
| | | { name: '4æ', ç²ç
¤ç°: 230, ç³è: 170, ç³ç°: 115 }, |
| | | { name: '5æ', ç²ç
¤ç°: 240, ç³è: 180, ç³ç°: 120 }, |
| | | { name: '6æ', ç²ç
¤ç°: 225, ç³è: 165, ç³ç°: 112 } |
| | | ], |
| | | year: [ |
| | | { name: '2023', ç²ç
¤ç°: 2500, ç³è: 1800, ç³ç°: 1200 }, |
| | | { name: '2024', ç²ç
¤ç°: 2700, ç³è: 1950, ç³ç°: 1300 }, |
| | | { name: '2025', ç²ç
¤ç°: 2900, ç³è: 2100, ç³ç°: 1400 } |
| | | ] |
| | | }) |
| | | const solidWasteData = ref({ |
| | | month: [ |
| | | { name: "1æ", ç²ç
¤ç°: 200, ç³è: 150, ç³ç°: 100 }, |
| | | { name: "2æ", ç²ç
¤ç°: 220, ç³è: 160, ç³ç°: 110 }, |
| | | { name: "3æ", ç²ç
¤ç°: 190, ç³è: 140, ç³ç°: 95 }, |
| | | { name: "4æ", ç²ç
¤ç°: 230, ç³è: 170, ç³ç°: 115 }, |
| | | { name: "5æ", ç²ç
¤ç°: 240, ç³è: 180, ç³ç°: 120 }, |
| | | { name: "6æ", ç²ç
¤ç°: 225, ç³è: 165, ç³ç°: 112 }, |
| | | ], |
| | | year: [ |
| | | { name: "2023", ç²ç
¤ç°: 2500, ç³è: 1800, ç³ç°: 1200 }, |
| | | { name: "2024", ç²ç
¤ç°: 2700, ç³è: 1950, ç³ç°: 1300 }, |
| | | { name: "2025", ç²ç
¤ç°: 2900, ç³è: 2100, ç³ç°: 1400 }, |
| | | ], |
| | | }); |
| | | |
| | | const costData = ref({ |
| | | materials: ['æ°´æ³¥', 'éç²è', 'è±æ¨¡å', 'é²è
å', 'æ°¯åå', 'å·æä¸'], |
| | | month: { |
| | | consumption: [1200, 50, 80, 30, 40, 60], |
| | | production: [12000, 12000, 12000, 8000, 8000, 8000], |
| | | unitConsumption: [0.1, 0.0042, 0.0067, 0.0038, 0.005, 0.0075] |
| | | }, |
| | | year: { |
| | | consumption: [14000, 600, 950, 350, 480, 720], |
| | | production: [140000, 140000, 140000, 95000, 95000, 95000], |
| | | unitConsumption: [0.1, 0.0043, 0.0068, 0.0037, 0.0051, 0.0076] |
| | | } |
| | | }) |
| | | |
| | | const energyData = ref({ |
| | | month: [ |
| | | { name: '1æ', çµé: 12000, æ°´é: 8000, æ°é: 5000 }, |
| | | { name: '2æ', çµé: 13000, æ°´é: 8500, æ°é: 5500 }, |
| | | { name: '3æ', çµé: 11000, æ°´é: 7500, æ°é: 4800 }, |
| | | { name: '4æ', çµé: 14000, æ°´é: 9000, æ°é: 6000 }, |
| | | { name: '5æ', çµé: 15000, æ°´é: 9500, æ°é: 6500 }, |
| | | { name: '6æ', çµé: 13500, æ°´é: 8800, æ°é: 5800 } |
| | | ], |
| | | year: [ |
| | | { name: '2023', çµé: 140000, æ°´é: 95000, æ°é: 65000 }, |
| | | { name: '2024', çµé: 150000, æ°´é: 100000, æ°é: 70000 }, |
| | | { name: '2025', çµé: 160000, æ°´é: 105000, æ°é: 75000 } |
| | | ] |
| | | }) |
| | | |
| | | // 计ç®å±æ§ |
| | | const productionChartOption = computed(() => { |
| | | const data = productionData.value[dateType.value] |
| | | return { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | } |
| | | const costData = ref({ |
| | | materials: ["æ°´æ³¥", "éç²è", "è±æ¨¡å", "é²è
å", "æ°¯åå", "å·æä¸"], |
| | | month: { |
| | | consumption: [1200, 50, 80, 30, 40, 60], |
| | | production: [12000, 12000, 12000, 8000, 8000, 8000], |
| | | unitConsumption: [0.1, 0.0042, 0.0067, 0.0038, 0.005, 0.0075], |
| | | }, |
| | | legend: { |
| | | data: ['ç å', 'æ¿æ'], |
| | | textStyle: { |
| | | color: '#333' |
| | | } |
| | | year: { |
| | | consumption: [14000, 600, 950, 350, 480, 720], |
| | | production: [140000, 140000, 140000, 95000, 95000, 95000], |
| | | unitConsumption: [0.1, 0.0043, 0.0068, 0.0037, 0.0051, 0.0076], |
| | | }, |
| | | grid: { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: data.map(item => item.name), |
| | | axisLabel: { |
| | | color: '#333' |
| | | } |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: '产é (ç«æ¹ç±³)', |
| | | axisLabel: { |
| | | color: '#333' |
| | | } |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'ç å', |
| | | type: 'line', |
| | | data: data.map(item => item.block), |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3 |
| | | }); |
| | | |
| | | const energyData = ref({ |
| | | month: [ |
| | | { name: "1æ", çµé: 12000, æ°´é: 8000, æ°é: 5000 }, |
| | | { name: "2æ", çµé: 13000, æ°´é: 8500, æ°é: 5500 }, |
| | | { name: "3æ", çµé: 11000, æ°´é: 7500, æ°é: 4800 }, |
| | | { name: "4æ", çµé: 14000, æ°´é: 9000, æ°é: 6000 }, |
| | | { name: "5æ", çµé: 15000, æ°´é: 9500, æ°é: 6500 }, |
| | | { name: "6æ", çµé: 13500, æ°´é: 8800, æ°é: 5800 }, |
| | | ], |
| | | year: [ |
| | | { name: "2023", çµé: 140000, æ°´é: 95000, æ°é: 65000 }, |
| | | { name: "2024", çµé: 150000, æ°´é: 100000, æ°é: 70000 }, |
| | | { name: "2025", çµé: 160000, æ°´é: 105000, æ°é: 75000 }, |
| | | ], |
| | | }); |
| | | |
| | | // 计ç®å±æ§ |
| | | const productionChartOption = computed(() => { |
| | | const data = productionData.value[dateType.value]; |
| | | return { |
| | | tooltip: { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "shadow", |
| | | }, |
| | | itemStyle: { |
| | | color: '#409EFF' |
| | | } |
| | | }, |
| | | { |
| | | name: 'æ¿æ', |
| | | type: 'line', |
| | | data: data.map(item => item.plate), |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3 |
| | | legend: { |
| | | data: ["ç å", "æ¿æ"], |
| | | textStyle: { |
| | | color: "#333", |
| | | }, |
| | | itemStyle: { |
| | | color: '#67C23A' |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | }) |
| | | |
| | | const solidWasteChartOption = computed(() => { |
| | | const data = solidWasteData.value[dateType.value] |
| | | return { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | } |
| | | }, |
| | | legend: { |
| | | data: ['ç²ç
¤ç°', 'ç³è', 'ç³ç°'], |
| | | textStyle: { |
| | | color: '#333' |
| | | } |
| | | }, |
| | | grid: { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: data.map(item => item.name), |
| | | axisLabel: { |
| | | color: '#333' |
| | | } |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: 'å¤çé (å¨)', |
| | | axisLabel: { |
| | | color: '#333' |
| | | } |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'ç²ç
¤ç°', |
| | | type: 'bar', |
| | | data: data.map(item => item.ç²ç
¤ç°), |
| | | itemStyle: { |
| | | color: '#909399' |
| | | } |
| | | }, |
| | | { |
| | | name: 'ç³è', |
| | | type: 'bar', |
| | | data: data.map(item => item.ç³è), |
| | | itemStyle: { |
| | | color: '#E6A23C' |
| | | } |
| | | grid: { |
| | | left: "3%", |
| | | right: "4%", |
| | | bottom: "3%", |
| | | containLabel: true, |
| | | }, |
| | | { |
| | | name: 'ç³ç°', |
| | | type: 'bar', |
| | | data: data.map(item => item.ç³ç°), |
| | | itemStyle: { |
| | | color: '#F56C6C' |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | }) |
| | | |
| | | const costChartOption = computed(() => { |
| | | const data = costData.value |
| | | return { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | } |
| | | }, |
| | | legend: { |
| | | data: ['æåº¦åè', '年度åè'], |
| | | textStyle: { |
| | | color: '#333' |
| | | } |
| | | }, |
| | | grid: { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: data.materials, |
| | | axisLabel: { |
| | | color: '#333', |
| | | rotate: 45 |
| | | } |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: 'åè (å¨/ç«æ¹ç±³)', |
| | | axisLabel: { |
| | | color: '#333' |
| | | } |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'æåº¦åè', |
| | | type: 'bar', |
| | | data: data.month.unitConsumption, |
| | | itemStyle: { |
| | | color: '#409EFF' |
| | | } |
| | | }, |
| | | { |
| | | name: '年度åè', |
| | | type: 'bar', |
| | | data: data.year.unitConsumption, |
| | | itemStyle: { |
| | | color: '#67C23A' |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | }) |
| | | |
| | | const energyChartOption = computed(() => { |
| | | const data = energyData.value[dateType.value] |
| | | return { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | } |
| | | }, |
| | | legend: { |
| | | data: ['çµé', 'æ°´é', 'æ°é'], |
| | | textStyle: { |
| | | color: '#333' |
| | | } |
| | | }, |
| | | grid: { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: data.map(item => item.name), |
| | | axisLabel: { |
| | | color: '#333' |
| | | } |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: 'è½èé', |
| | | axisLabel: { |
| | | color: '#333' |
| | | } |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'çµé', |
| | | type: 'line', |
| | | data: data.map(item => item.çµé), |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3 |
| | | xAxis: { |
| | | type: "category", |
| | | data: data.map(item => item.name), |
| | | axisLabel: { |
| | | color: "#333", |
| | | }, |
| | | itemStyle: { |
| | | color: '#409EFF' |
| | | } |
| | | }, |
| | | { |
| | | name: 'æ°´é', |
| | | type: 'line', |
| | | data: data.map(item => item.æ°´é), |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3 |
| | | yAxis: { |
| | | type: "value", |
| | | name: "产é (ç«æ¹ç±³)", |
| | | axisLabel: { |
| | | color: "#333", |
| | | }, |
| | | itemStyle: { |
| | | color: '#67C23A' |
| | | } |
| | | }, |
| | | { |
| | | name: 'æ°é', |
| | | type: 'line', |
| | | data: data.map(item => item.æ°é), |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3 |
| | | series: [ |
| | | { |
| | | name: "ç å", |
| | | type: "line", |
| | | data: data.map(item => item.block), |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3, |
| | | }, |
| | | itemStyle: { |
| | | color: "#409EFF", |
| | | }, |
| | | }, |
| | | itemStyle: { |
| | | color: '#E6A23C' |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | }) |
| | | { |
| | | name: "æ¿æ", |
| | | type: "line", |
| | | data: data.map(item => item.plate), |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3, |
| | | }, |
| | | itemStyle: { |
| | | color: "#67C23A", |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | const costTableData = computed(() => { |
| | | const data = costData.value |
| | | const materials = data.materials |
| | | const monthData = data.month |
| | | const yearData = data.year |
| | | |
| | | return materials.map((material, index) => ({ |
| | | material, |
| | | unit: 'å¨/ç«æ¹ç±³', |
| | | monthlyConsumption: monthData.consumption[index], |
| | | monthlyProduction: monthData.production[index], |
| | | monthlyUnitConsumption: monthData.unitConsumption[index].toFixed(4), |
| | | yearlyConsumption: yearData.consumption[index], |
| | | yearlyProduction: yearData.production[index], |
| | | yearlyUnitConsumption: yearData.unitConsumption[index].toFixed(4) |
| | | })) |
| | | }) |
| | | const solidWasteChartOption = computed(() => { |
| | | const data = solidWasteData.value[dateType.value]; |
| | | return { |
| | | tooltip: { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "shadow", |
| | | }, |
| | | }, |
| | | legend: { |
| | | data: ["ç²ç
¤ç°", "ç³è", "ç³ç°"], |
| | | textStyle: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | grid: { |
| | | left: "3%", |
| | | right: "4%", |
| | | bottom: "3%", |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: data.map(item => item.name), |
| | | axisLabel: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | name: "å¤çé (å¨)", |
| | | axisLabel: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | series: [ |
| | | { |
| | | name: "ç²ç
¤ç°", |
| | | type: "bar", |
| | | data: data.map(item => item.ç²ç
¤ç°), |
| | | itemStyle: { |
| | | color: "#909399", |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç³è", |
| | | type: "bar", |
| | | data: data.map(item => item.ç³è), |
| | | itemStyle: { |
| | | color: "#E6A23C", |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç³ç°", |
| | | type: "bar", |
| | | data: data.map(item => item.ç³ç°), |
| | | itemStyle: { |
| | | color: "#F56C6C", |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | const totalProduction = computed(() => { |
| | | const data = productionData.value[dateType.value] |
| | | if (dateType.value === 'month') { |
| | | return data.reduce((sum, item) => sum + item[productType.value === 'block' ? 'block' : 'plate'], 0) |
| | | } else { |
| | | return data[data.length - 1][productType.value === 'block' ? 'block' : 'plate'] |
| | | } |
| | | }) |
| | | const costChartOption = computed(() => { |
| | | const data = costData.value; |
| | | return { |
| | | tooltip: { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "shadow", |
| | | }, |
| | | }, |
| | | legend: { |
| | | data: ["æåº¦åè", "年度åè"], |
| | | textStyle: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | grid: { |
| | | left: "3%", |
| | | right: "4%", |
| | | bottom: "3%", |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: data.materials, |
| | | axisLabel: { |
| | | color: "#333", |
| | | rotate: 45, |
| | | }, |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | name: "åè (å¨/ç«æ¹ç±³)", |
| | | axisLabel: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | series: [ |
| | | { |
| | | name: "æåº¦åè", |
| | | type: "bar", |
| | | data: data.month.unitConsumption, |
| | | itemStyle: { |
| | | color: "#409EFF", |
| | | }, |
| | | }, |
| | | { |
| | | name: "年度åè", |
| | | type: "bar", |
| | | data: data.year.unitConsumption, |
| | | itemStyle: { |
| | | color: "#67C23A", |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | const totalSolidWaste = computed(() => { |
| | | const data = solidWasteData.value[dateType.value] |
| | | if (dateType.value === 'month') { |
| | | return data.reduce((sum, item) => sum + item.ç²ç
¤ç° + item.ç³è + item.ç³ç°, 0) |
| | | } else { |
| | | const lastItem = data[data.length - 1] |
| | | return lastItem.ç²ç
¤ç° + lastItem.ç³è + lastItem.ç³ç° |
| | | } |
| | | }) |
| | | const energyChartOption = computed(() => { |
| | | const data = energyData.value[dateType.value]; |
| | | return { |
| | | tooltip: { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "shadow", |
| | | }, |
| | | }, |
| | | legend: { |
| | | data: ["çµé", "æ°´é", "æ°é"], |
| | | textStyle: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | grid: { |
| | | left: "3%", |
| | | right: "4%", |
| | | bottom: "3%", |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: data.map(item => item.name), |
| | | axisLabel: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | name: "è½èé", |
| | | axisLabel: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | series: [ |
| | | { |
| | | name: "çµé", |
| | | type: "line", |
| | | data: data.map(item => item.çµé), |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3, |
| | | }, |
| | | itemStyle: { |
| | | color: "#409EFF", |
| | | }, |
| | | }, |
| | | { |
| | | name: "æ°´é", |
| | | type: "line", |
| | | data: data.map(item => item.æ°´é), |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3, |
| | | }, |
| | | itemStyle: { |
| | | color: "#67C23A", |
| | | }, |
| | | }, |
| | | { |
| | | name: "æ°é", |
| | | type: "line", |
| | | data: data.map(item => item.æ°é), |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3, |
| | | }, |
| | | itemStyle: { |
| | | color: "#E6A23C", |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | const averageUnitConsumption = computed(() => { |
| | | const data = costData.value |
| | | const unitConsumption = dateType.value === 'month' ? data.month.unitConsumption : data.year.unitConsumption |
| | | const average = unitConsumption.reduce((sum, value) => sum + value, 0) / unitConsumption.length |
| | | return average.toFixed(4) |
| | | }) |
| | | const costTableData = computed(() => { |
| | | const data = costData.value; |
| | | const materials = data.materials; |
| | | const monthData = data.month; |
| | | const yearData = data.year; |
| | | |
| | | const totalEnergy = computed(() => { |
| | | const data = energyData.value[dateType.value] |
| | | if (dateType.value === 'month') { |
| | | return data.reduce((sum, item) => sum + item.çµé + item.æ°´é + item.æ°é, 0) |
| | | } else { |
| | | const lastItem = data[data.length - 1] |
| | | return lastItem.çµé + lastItem.æ°´é + lastItem.æ°é |
| | | } |
| | | }) |
| | | return materials.map((material, index) => ({ |
| | | material, |
| | | unit: "å¨/ç«æ¹ç±³", |
| | | monthlyConsumption: monthData.consumption[index], |
| | | monthlyProduction: monthData.production[index], |
| | | monthlyUnitConsumption: monthData.unitConsumption[index].toFixed(4), |
| | | yearlyConsumption: yearData.consumption[index], |
| | | yearlyProduction: yearData.production[index], |
| | | yearlyUnitConsumption: yearData.unitConsumption[index].toFixed(4), |
| | | })); |
| | | }); |
| | | |
| | | // äºä»¶å¤ç |
| | | const handleDateTypeChange = () => { |
| | | updateCharts() |
| | | } |
| | | const totalProduction = computed(() => { |
| | | const data = productionData.value[dateType.value]; |
| | | if (dateType.value === "month") { |
| | | return data.reduce( |
| | | (sum, item) => |
| | | sum + item[productType.value === "block" ? "block" : "plate"], |
| | | 0 |
| | | ); |
| | | } else { |
| | | return data[data.length - 1][ |
| | | productType.value === "block" ? "block" : "plate" |
| | | ]; |
| | | } |
| | | }); |
| | | |
| | | const handleProductTypeChange = () => { |
| | | updateCharts() |
| | | } |
| | | const totalSolidWaste = computed(() => { |
| | | const data = solidWasteData.value[dateType.value]; |
| | | if (dateType.value === "month") { |
| | | return data.reduce( |
| | | (sum, item) => sum + item.ç²ç
¤ç° + item.ç³è + item.ç³ç°, |
| | | 0 |
| | | ); |
| | | } else { |
| | | const lastItem = data[data.length - 1]; |
| | | return lastItem.ç²ç
¤ç° + lastItem.ç³è + lastItem.ç³ç°; |
| | | } |
| | | }); |
| | | |
| | | // åå§åå¾è¡¨ |
| | | const initCharts = () => { |
| | | if (productionChart.value) { |
| | | productionChartInstance = echarts.init(productionChart.value) |
| | | productionChartInstance.setOption(productionChartOption.value) |
| | | } |
| | | |
| | | if (solidWasteChart.value) { |
| | | solidWasteChartInstance = echarts.init(solidWasteChart.value) |
| | | solidWasteChartInstance.setOption(solidWasteChartOption.value) |
| | | } |
| | | |
| | | if (costChart.value) { |
| | | costChartInstance = echarts.init(costChart.value) |
| | | costChartInstance.setOption(costChartOption.value) |
| | | } |
| | | |
| | | if (energyChart.value) { |
| | | energyChartInstance = echarts.init(energyChart.value) |
| | | energyChartInstance.setOption(energyChartOption.value) |
| | | } |
| | | } |
| | | const averageUnitConsumption = computed(() => { |
| | | const data = costData.value; |
| | | const unitConsumption = |
| | | dateType.value === "month" |
| | | ? data.month.unitConsumption |
| | | : data.year.unitConsumption; |
| | | const average = |
| | | unitConsumption.reduce((sum, value) => sum + value, 0) / |
| | | unitConsumption.length; |
| | | return average.toFixed(4); |
| | | }); |
| | | |
| | | // æ´æ°å¾è¡¨ |
| | | const updateCharts = () => { |
| | | if (productionChartInstance) { |
| | | productionChartInstance.setOption(productionChartOption.value) |
| | | } |
| | | |
| | | if (solidWasteChartInstance) { |
| | | solidWasteChartInstance.setOption(solidWasteChartOption.value) |
| | | } |
| | | |
| | | if (costChartInstance) { |
| | | costChartInstance.setOption(costChartOption.value) |
| | | } |
| | | |
| | | if (energyChartInstance) { |
| | | energyChartInstance.setOption(energyChartOption.value) |
| | | } |
| | | } |
| | | const totalEnergy = computed(() => { |
| | | const data = energyData.value[dateType.value]; |
| | | if (dateType.value === "month") { |
| | | return data.reduce( |
| | | (sum, item) => sum + item.çµé + item.æ°´é + item.æ°é, |
| | | 0 |
| | | ); |
| | | } else { |
| | | const lastItem = data[data.length - 1]; |
| | | return lastItem.çµé + lastItem.æ°´é + lastItem.æ°é; |
| | | } |
| | | }); |
| | | |
| | | // è°æ´å¾è¡¨å¤§å° |
| | | const resizeCharts = () => { |
| | | productionChartInstance?.resize() |
| | | solidWasteChartInstance?.resize() |
| | | costChartInstance?.resize() |
| | | energyChartInstance?.resize() |
| | | } |
| | | // äºä»¶å¤ç |
| | | const handleDateTypeChange = () => { |
| | | updateCharts(); |
| | | }; |
| | | |
| | | // çªå£å¤§å°ååå¤ç |
| | | const handleResize = () => { |
| | | // å»¶è¿æ§è¡ï¼ç¡®ä¿DOMæ´æ°å®æ |
| | | setTimeout(() => { |
| | | resizeCharts() |
| | | }, 100) |
| | | } |
| | | const handleProductTypeChange = () => { |
| | | updateCharts(); |
| | | }; |
| | | |
| | | // çå½å¨æé©å |
| | | onMounted(() => { |
| | | // 使ç¨nextTickç¡®ä¿DOMå®å
¨æ¸²æåååå§å |
| | | nextTick(() => { |
| | | // åå§åå¾è¡¨ |
| | | initCharts() |
| | | }) |
| | | // åå§åå¾è¡¨ |
| | | const initCharts = () => { |
| | | if (productionChart.value) { |
| | | productionChartInstance = echarts.init(productionChart.value); |
| | | productionChartInstance.setOption(productionChartOption.value); |
| | | } |
| | | |
| | | window.addEventListener('resize', handleResize) |
| | | }) |
| | | if (solidWasteChart.value) { |
| | | solidWasteChartInstance = echarts.init(solidWasteChart.value); |
| | | solidWasteChartInstance.setOption(solidWasteChartOption.value); |
| | | } |
| | | |
| | | onBeforeUnmount(() => { |
| | | window.removeEventListener('resize', handleResize) |
| | | |
| | | // 鿝å¾è¡¨å®ä¾ |
| | | productionChartInstance?.dispose() |
| | | solidWasteChartInstance?.dispose() |
| | | costChartInstance?.dispose() |
| | | energyChartInstance?.dispose() |
| | | }) |
| | | if (costChart.value) { |
| | | costChartInstance = echarts.init(costChart.value); |
| | | costChartInstance.setOption(costChartOption.value); |
| | | } |
| | | |
| | | if (energyChart.value) { |
| | | energyChartInstance = echarts.init(energyChart.value); |
| | | energyChartInstance.setOption(energyChartOption.value); |
| | | } |
| | | }; |
| | | |
| | | // æ´æ°å¾è¡¨ |
| | | const updateCharts = () => { |
| | | if (productionChartInstance) { |
| | | productionChartInstance.setOption(productionChartOption.value); |
| | | } |
| | | |
| | | if (solidWasteChartInstance) { |
| | | solidWasteChartInstance.setOption(solidWasteChartOption.value); |
| | | } |
| | | |
| | | if (costChartInstance) { |
| | | costChartInstance.setOption(costChartOption.value); |
| | | } |
| | | |
| | | if (energyChartInstance) { |
| | | energyChartInstance.setOption(energyChartOption.value); |
| | | } |
| | | }; |
| | | |
| | | // è°æ´å¾è¡¨å¤§å° |
| | | const resizeCharts = () => { |
| | | productionChartInstance?.resize(); |
| | | solidWasteChartInstance?.resize(); |
| | | costChartInstance?.resize(); |
| | | energyChartInstance?.resize(); |
| | | }; |
| | | |
| | | // çªå£å¤§å°ååå¤ç |
| | | const handleResize = () => { |
| | | // å»¶è¿æ§è¡ï¼ç¡®ä¿DOMæ´æ°å®æ |
| | | setTimeout(() => { |
| | | resizeCharts(); |
| | | }, 100); |
| | | }; |
| | | |
| | | // è·åç©æç±»åæ ç¾ç±»å |
| | | const getMaterialTypeType = material => { |
| | | const typeMap = { |
| | | æ°´æ³¥: "primary", |
| | | éç²è: "success", |
| | | è±æ¨¡å: "warning", |
| | | é²è
å: "danger", |
| | | æ°¯åå: "info", |
| | | å·æä¸: "purple", |
| | | }; |
| | | return typeMap[material] || "info"; |
| | | }; |
| | | |
| | | // çå½å¨æé©å |
| | | onMounted(() => { |
| | | // 使ç¨nextTickç¡®ä¿DOMå®å
¨æ¸²æåååå§å |
| | | nextTick(() => { |
| | | // åå§åå¾è¡¨ |
| | | initCharts(); |
| | | }); |
| | | |
| | | window.addEventListener("resize", handleResize); |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | window.removeEventListener("resize", handleResize); |
| | | |
| | | // 鿝å¾è¡¨å®ä¾ |
| | | productionChartInstance?.dispose(); |
| | | solidWasteChartInstance?.dispose(); |
| | | costChartInstance?.dispose(); |
| | | energyChartInstance?.dispose(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | /* å¤é¨å®¹å¨ - å æ®æ´ä¸ªè§å£ */ |
| | | .dashboard-container { |
| | | position: relative; |
| | | width: 100%; |
| | | /* 页é¢å¨å¸¸è§å¸å±ä¸ï¼æé¡¶æ ï¼é»è®¤åå» 84pxï¼é¿å
å
容被è£å */ |
| | | min-height: calc(100vh - 84px); |
| | | background-color: #f5f7fa; |
| | | overflow: hidden; |
| | | } |
| | | /* å¤é¨å®¹å¨ - å æ®æ´ä¸ªè§å£ */ |
| | | .dashboard-container { |
| | | position: relative; |
| | | width: 100%; |
| | | /* 页é¢å¨å¸¸è§å¸å±ä¸ï¼æé¡¶æ ï¼é»è®¤åå» 84pxï¼é¿å
å
容被è£å */ |
| | | min-height: calc(100vh - 84px); |
| | | background-color: #f5f7fa; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* å
é¨å
容åºå - èªéåºå®½åº¦ */ |
| | | .data-dashboard { |
| | | position: relative; |
| | | width: 100%; |
| | | min-height: 100%; |
| | | background-color: #ffffff; |
| | | box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); |
| | | } |
| | | /* å
é¨å
容åºå - èªéåºå®½åº¦ */ |
| | | .data-dashboard { |
| | | position: relative; |
| | | width: 100%; |
| | | min-height: 100%; |
| | | background-color: #ffffff; |
| | | box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .dashboard-header { |
| | | position: relative; |
| | | z-index: 1; |
| | | height: 86px; |
| | | background-color: #ffffff; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | .dashboard-header { |
| | | position: relative; |
| | | z-index: 1; |
| | | height: 86px; |
| | | background-color: #ffffff; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .factory-name { |
| | | font-weight: 600; |
| | | font-size: 32px; |
| | | color: #303133; |
| | | } |
| | | .factory-name { |
| | | font-weight: 600; |
| | | font-size: 32px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .filter-area { |
| | | padding: 20px; |
| | | background-color: #ffffff; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | display: flex; |
| | | gap: 40px; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | } |
| | | .filter-area { |
| | | padding: 20px; |
| | | background-color: #ffffff; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | display: flex; |
| | | gap: 40px; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .filter-section { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | .filter-section { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .filter-label { |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | white-space: nowrap; |
| | | } |
| | | .filter-label { |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .radio-group { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | .radio-group { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | /* æé®æ ·å¼ */ |
| | | :deep(.el-radio-button__inner) { |
| | | border-radius: 4px; |
| | | padding: 8px 20px; |
| | | font-size: 14px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | /* æé®æ ·å¼ */ |
| | | :deep(.el-radio-button__inner) { |
| | | border-radius: 4px; |
| | | padding: 8px 20px; |
| | | font-size: 14px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | :deep(.el-radio-button__orig-radio:checked + .el-radio-button__inner) { |
| | | background-color: #409eff; |
| | | border-color: #409eff; |
| | | color: #ffffff; |
| | | box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3); |
| | | } |
| | | :deep(.el-radio-button__orig-radio:checked + .el-radio-button__inner) { |
| | | background-color: #409eff; |
| | | border-color: #409eff; |
| | | color: #ffffff; |
| | | box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3); |
| | | } |
| | | |
| | | :deep(.el-radio-button__inner:hover) { |
| | | color: #409eff; |
| | | border-color: #c6e2ff; |
| | | } |
| | | :deep(.el-radio-button__inner:hover) { |
| | | color: #409eff; |
| | | border-color: #c6e2ff; |
| | | } |
| | | |
| | | :deep(.el-radio-button:first-child .el-radio-button__inner) { |
| | | border-radius: 4px 0 0 4px; |
| | | } |
| | | :deep(.el-radio-button:first-child .el-radio-button__inner) { |
| | | border-radius: 4px 0 0 4px; |
| | | } |
| | | |
| | | :deep(.el-radio-button:last-child .el-radio-button__inner) { |
| | | border-radius: 0 4px 4px 0; |
| | | } |
| | | :deep(.el-radio-button:last-child .el-radio-button__inner) { |
| | | border-radius: 0 4px 4px 0; |
| | | } |
| | | |
| | | .dashboard-content { |
| | | position: relative; |
| | | z-index: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | padding: 20px; |
| | | min-height: 800px; |
| | | overflow: hidden; |
| | | } |
| | | .dashboard-content { |
| | | position: relative; |
| | | z-index: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | padding: 20px; |
| | | min-height: 800px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* è¡å¸å± */ |
| | | .row { |
| | | display: flex; |
| | | gap: 20px; |
| | | align-items: stretch; |
| | | } |
| | | /* è¡å¸å± */ |
| | | .row { |
| | | display: flex; |
| | | gap: 20px; |
| | | align-items: stretch; |
| | | } |
| | | |
| | | /* 第ä¸è¡ï¼3个å¡ç */ |
| | | .row-1 { |
| | | height: 300px; |
| | | } |
| | | /* 第ä¸è¡ï¼3个å¡ç */ |
| | | .row-1 { |
| | | height: 300px; |
| | | } |
| | | |
| | | /* 第äºè¡ï¼2个å¡ç */ |
| | | .row-2 { |
| | | height: 300px; |
| | | } |
| | | /* 第äºè¡ï¼2个å¡ç */ |
| | | .row-2 { |
| | | height: 300px; |
| | | } |
| | | |
| | | /* 第ä¸è¡ï¼1个å¡ç */ |
| | | .row-3 { |
| | | min-height: 250px; |
| | | } |
| | | /* 第ä¸è¡ï¼1个å¡ç */ |
| | | .row-3 { |
| | | min-height: 250px; |
| | | } |
| | | |
| | | /* å¡çæ ·å¼ */ |
| | | .panel-card { |
| | | background-color: #ffffff; |
| | | border-radius: 8px; |
| | | border: 1px solid #e4e7ed; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | /* å¡çæ ·å¼ */ |
| | | .panel-card { |
| | | background-color: #ffffff; |
| | | border-radius: 8px; |
| | | border: 1px solid #e4e7ed; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .panel-card:hover { |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); |
| | | transform: translateY(-2px); |
| | | } |
| | | .panel-card:hover { |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); |
| | | transform: translateY(-2px); |
| | | } |
| | | |
| | | /* å¡çå¸å± */ |
| | | .card-1 { |
| | | flex: 1; |
| | | } |
| | | /* å¡çå¸å± */ |
| | | .card-1 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-2 { |
| | | flex: 1; |
| | | } |
| | | .card-2 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-3 { |
| | | flex: 0.8; |
| | | } |
| | | .card-3 { |
| | | flex: 0.8; |
| | | } |
| | | |
| | | .card-4 { |
| | | flex: 1.2; |
| | | } |
| | | .card-4 { |
| | | flex: 1.2; |
| | | } |
| | | |
| | | .card-5 { |
| | | flex: 0.8; |
| | | } |
| | | .card-5 { |
| | | flex: 0.8; |
| | | } |
| | | |
| | | .card-6 { |
| | | flex: 1; |
| | | } |
| | | .card-6 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .panel-card { |
| | | background-color: #ffffff; |
| | | border-radius: 8px; |
| | | border: 1px solid #e4e7ed; |
| | | overflow: hidden; |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | .panel-card { |
| | | background-color: #ffffff; |
| | | border-radius: 8px; |
| | | border: 1px solid #e4e7ed; |
| | | overflow: hidden; |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .panel-title { |
| | | padding: 15px 20px; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | background-color: #fafafa; |
| | | } |
| | | .panel-title { |
| | | padding: 15px 20px; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | background-color: #fafafa; |
| | | } |
| | | |
| | | .chart-container { |
| | | flex: 1; |
| | | padding: 20px; |
| | | } |
| | | .card-1 .panel-title { |
| | | border-left: 4px solid #409eff; |
| | | } |
| | | |
| | | .table-container { |
| | | flex: 1; |
| | | padding: 20px; |
| | | overflow: auto; |
| | | } |
| | | .card-2 .panel-title { |
| | | border-left: 4px solid #f56c6c; |
| | | } |
| | | |
| | | .stats-grid { |
| | | flex: 1; |
| | | padding: 15px; |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | grid-template-rows: repeat(2, 1fr); |
| | | gap: 15px; |
| | | } |
| | | .card-3 .panel-title { |
| | | border-left: 4px solid #e6a23c; |
| | | } |
| | | |
| | | .stat-item { |
| | | background-color: #fafafa; |
| | | border-radius: 8px; |
| | | padding: 15px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 1px solid #e4e7ed; |
| | | min-height: 80px; |
| | | } |
| | | .card-4 .panel-title { |
| | | border-left: 4px solid #409eff; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | margin-bottom: 8px; |
| | | } |
| | | .card-5 .panel-title { |
| | | border-left: 4px solid #67c23a; |
| | | } |
| | | |
| | | .stat-value { |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 3px; |
| | | } |
| | | .card-6 .panel-title { |
| | | border-left: 4px solid #e6a23c; |
| | | } |
| | | |
| | | .stat-unit { |
| | | font-size: 11px; |
| | | color: #909399; |
| | | } |
| | | .chart-container { |
| | | flex: 1; |
| | | padding: 20px; |
| | | } |
| | | |
| | | /* è¡¨æ ¼æ ·å¼ */ |
| | | :deep(.el-table) { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | } |
| | | .table-container { |
| | | flex: 1; |
| | | padding: 20px; |
| | | overflow: auto; |
| | | } |
| | | |
| | | :deep(.el-table th) { |
| | | background-color: #fafafa; |
| | | font-weight: 500; |
| | | } |
| | | .stats-grid { |
| | | flex: 1; |
| | | padding: 15px; |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | grid-template-rows: repeat(2, 1fr); |
| | | gap: 15px; |
| | | } |
| | | |
| | | :deep(.el-table tr:hover > td) { |
| | | background-color: #ecf5ff; |
| | | } |
| | | .stat-item { |
| | | background-color: #fafafa; |
| | | border-radius: 8px; |
| | | padding: 15px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 1px solid #e4e7ed; |
| | | min-height: 80px; |
| | | } |
| | | |
| | | /* æé®æ ·å¼ */ |
| | | :deep(.el-radio-button__inner) { |
| | | border-radius: 4px; |
| | | } |
| | | .stat-label { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | :deep(.el-radio-button__orig-radio:checked + .el-radio-button__inner) { |
| | | background-color: #409eff; |
| | | border-color: #409eff; |
| | | color: #ffffff; |
| | | } |
| | | .stat-value { |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 3px; |
| | | } |
| | | |
| | | .production-color { |
| | | color: #409eff; |
| | | text-shadow: 0 2px 4px rgba(64, 158, 255, 0.3); |
| | | } |
| | | |
| | | .waste-color { |
| | | color: #f56c6c; |
| | | text-shadow: 0 2px 4px rgba(245, 108, 108, 0.3); |
| | | } |
| | | |
| | | .consumption-color { |
| | | color: #e6a23c; |
| | | text-shadow: 0 2px 4px rgba(230, 162, 60, 0.3); |
| | | } |
| | | |
| | | .energy-color { |
| | | color: #67c23a; |
| | | text-shadow: 0 2px 4px rgba(103, 194, 58, 0.3); |
| | | } |
| | | |
| | | .stat-unit { |
| | | font-size: 11px; |
| | | color: #909399; |
| | | } |
| | | |
| | | /* è¡¨æ ¼æ ·å¼ */ |
| | | :deep(.el-table) { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | :deep(.el-table th) { |
| | | background-color: #fafafa; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | :deep(.el-table tr:hover > td) { |
| | | background-color: #ecf5ff; |
| | | } |
| | | |
| | | .data-value { |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | } |
| | | |
| | | /* æé®æ ·å¼ */ |
| | | :deep(.el-radio-button__inner) { |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | :deep(.el-radio-button__orig-radio:checked + .el-radio-button__inner) { |
| | | background-color: #409eff; |
| | | border-color: #409eff; |
| | | color: #ffffff; |
| | | } |
| | | </style> |
| | |
| | | <!-- <div class="dashboard-header"> |
| | | <div class="factory-name">éå®ç»è®¡çæ¿</div> |
| | | </div> --> |
| | | |
| | | <!-- ç鿡件 --> |
| | | <div class="filter-area"> |
| | | <div class="filter-section"> |
| | | <span class="filter-label">æ¶é´èå´ï¼</span> |
| | | <el-date-picker |
| | | v-model="dateRange" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | @change="handleDateChange" |
| | | style="width: 240px;" |
| | | /> |
| | | <el-date-picker v-model="dateRange" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | @change="handleDateChange" |
| | | style="width: 240px;" /> |
| | | </div> |
| | | <div class="filter-section"> |
| | | <span class="filter-label">产åç±»åï¼</span> |
| | | <el-select v-model="productType" placeholder="è¯·éæ©äº§åç±»å" @change="handleFilterChange" style="width: 160px;"> |
| | | <el-option label="å
¨é¨" value="" /> |
| | | <el-option label="ç å" value="block" /> |
| | | <el-option label="æ¿æ" value="board" /> |
| | | <el-option label="åæ" value="profile" /> |
| | | <el-select v-model="productType" |
| | | placeholder="è¯·éæ©äº§åç±»å" |
| | | @change="handleFilterChange" |
| | | style="width: 160px;"> |
| | | <el-option label="å
¨é¨" |
| | | value="" /> |
| | | <el-option label="ç å" |
| | | value="block" /> |
| | | <el-option label="æ¿æ" |
| | | value="board" /> |
| | | <el-option label="åæ" |
| | | value="profile" /> |
| | | </el-select> |
| | | </div> |
| | | <div class="filter-section"> |
| | | <span class="filter-label">éå®åºåï¼</span> |
| | | <el-select v-model="salesArea" placeholder="è¯·éæ©éå®åºå" @change="handleFilterChange" style="width: 160px;"> |
| | | <el-option label="å
¨é¨" value="" /> |
| | | <el-option label="åä¸" value="east" /> |
| | | <el-option label="åå" value="north" /> |
| | | <el-option label="åå" value="south" /> |
| | | <el-option label="西å" value="southwest" /> |
| | | <el-option label="西å" value="northwest" /> |
| | | <el-select v-model="salesArea" |
| | | placeholder="è¯·éæ©éå®åºå" |
| | | @change="handleFilterChange" |
| | | style="width: 160px;"> |
| | | <el-option label="å
¨é¨" |
| | | value="" /> |
| | | <el-option label="åä¸" |
| | | value="east" /> |
| | | <el-option label="åå" |
| | | value="north" /> |
| | | <el-option label="åå" |
| | | value="south" /> |
| | | <el-option label="西å" |
| | | value="southwest" /> |
| | | <el-option label="西å" |
| | | value="northwest" /> |
| | | </el-select> |
| | | </div> |
| | | <div class="filter-section"> |
| | | <span class="filter-label">ç»è®¡ç»´åº¦ï¼</span> |
| | | <el-select v-model="statDimension" placeholder="è¯·éæ©ç»è®¡ç»´åº¦" @change="handleFilterChange" style="width: 160px;"> |
| | | <el-option label="æåº¦" value="month" /> |
| | | <el-option label="年度" value="year" /> |
| | | <el-select v-model="statDimension" |
| | | placeholder="è¯·éæ©ç»è®¡ç»´åº¦" |
| | | @change="handleFilterChange" |
| | | style="width: 160px;"> |
| | | <el-option label="æåº¦" |
| | | value="month" /> |
| | | <el-option label="年度" |
| | | value="year" /> |
| | | </el-select> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="dashboard-content"> |
| | | <!-- æ ¸å¿ææ å¡ç --> |
| | | <div class="row row-1"> |
| | |
| | | <div class="panel-title">å计éé</div> |
| | | <div class="stats-grid"> |
| | | <div class="stat-item"> |
| | | <div class="stat-value">{{ totalSalesVolume }}</div> |
| | | <div class="stat-value sales-volume-color">{{ totalSalesVolume }}</div> |
| | | <div class="stat-unit">ç«æ¹ç±³</div> |
| | | <div class="stat-change">{{ salesVolumeChange }}%</div> |
| | | </div> |
| | |
| | | <div class="panel-title">éå®éé¢</div> |
| | | <div class="stats-grid"> |
| | | <div class="stat-item"> |
| | | <div class="stat-value">{{ totalSalesAmount }}</div> |
| | | <div class="stat-value sales-amount-color">{{ totalSalesAmount }}</div> |
| | | <div class="stat-unit">ä¸å
</div> |
| | | <div class="stat-change">{{ salesAmountChange }}%</div> |
| | | </div> |
| | |
| | | <div class="panel-title">æ°å¢å®¢æ·</div> |
| | | <div class="stats-grid"> |
| | | <div class="stat-item"> |
| | | <div class="stat-value">{{ newCustomerCount }}</div> |
| | | <div class="stat-value new-customer-color">{{ newCustomerCount }}</div> |
| | | <div class="stat-unit">个</div> |
| | | <div class="stat-change">{{ customerCountChange }}%</div> |
| | | </div> |
| | |
| | | <div class="panel-title">å计客æ·</div> |
| | | <div class="stats-grid"> |
| | | <div class="stat-item"> |
| | | <div class="stat-value">{{ totalCustomerCount }}</div> |
| | | <div class="stat-value total-customer-color">{{ totalCustomerCount }}</div> |
| | | <div class="stat-unit">个</div> |
| | | <div class="stat-change">{{ totalCustomerChange }}%</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- ééåéå®éé¢è¶å¿ --> |
| | | <div class="row row-2"> |
| | | <div class="panel-card card-5"> |
| | | <div class="panel-title">ééè¶å¿</div> |
| | | <div class="chart-container"> |
| | | <div ref="salesVolumeChart" style="width: 100%; height: 100%;"></div> |
| | | <div ref="salesVolumeChart" |
| | | style="width: 100%; height: 100%;"></div> |
| | | </div> |
| | | </div> |
| | | <div class="panel-card card-6"> |
| | | <div class="panel-title">éå®éé¢è¶å¿</div> |
| | | <div class="chart-container"> |
| | | <div ref="salesAmountChart" style="width: 100%; height: 100%;"></div> |
| | | <div ref="salesAmountChart" |
| | | style="width: 100%; height: 100%;"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- ç´¯è®¡æ°æ®è¶å¿ --> |
| | | <div class="row row-3"> |
| | | <!-- <div class="row row-3"> |
| | | <div class="panel-card card-10"> |
| | | <div class="panel-title">累计ééè¶å¿</div> |
| | | <div class="chart-container"> |
| | | <div ref="cumulativeSalesVolumeChart" style="width: 100%; height: 100%;"></div> |
| | | <div ref="cumulativeSalesVolumeChart" |
| | | style="width: 100%; height: 100%;"></div> |
| | | </div> |
| | | </div> |
| | | <div class="panel-card card-11"> |
| | | <div class="panel-title">累计éå®éé¢è¶å¿</div> |
| | | <div class="chart-container"> |
| | | <div ref="cumulativeSalesAmountChart" style="width: 100%; height: 100%;"></div> |
| | | <div ref="cumulativeSalesAmountChart" |
| | | style="width: 100%; height: 100%;"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | </div> --> |
| | | <!-- å¾è¡¨åºååè¡¨æ ¼ --> |
| | | <div class="row row-4"> |
| | | <!-- 左边ï¼è¯¦ç»æ°æ®è¡¨æ ¼ --> |
| | | <div class="panel-card card-9" style="flex: 2;"> |
| | | <div class="panel-card card-9" |
| | | style="flex: 2;"> |
| | | <div class="panel-title">éå®ç»è®¡è¯¦ç»æ°æ®</div> |
| | | <div class="table-container"> |
| | | <el-table :data="tableData" style="width: 100%"> |
| | | <el-table-column prop="productType" label="产åç±»å" width="120" /> |
| | | <el-table-column prop="salesArea" label="éå®åºå" width="120" /> |
| | | <el-table-column prop="period" label="ç»è®¡å¨æ" width="120" /> |
| | | <el-table-column prop="salesVolume" label="éé(ç«æ¹ç±³)" /> |
| | | <el-table-column prop="salesAmount" label="éå®éé¢(ä¸å
)" /> |
| | | <el-table-column prop="newCustomers" label="æ°å¢å®¢æ·(个)" width="150" /> |
| | | <el-table-column prop="totalCustomers" label="å计客æ·(个)" width="150" /> |
| | | <el-table :data="tableData" |
| | | style="width: 100%"> |
| | | <el-table-column prop="productType" |
| | | label="产åç±»å" |
| | | width="120" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getProductTypeType(scope.row.productType)"> |
| | | {{ scope.row.productType }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="salesArea" |
| | | label="éå®åºå" |
| | | width="120" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getSalesAreaType(scope.row.salesArea)"> |
| | | {{ scope.row.salesArea }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="period" |
| | | label="ç»è®¡å¨æ" |
| | | width="120" /> |
| | | <el-table-column prop="salesVolume" |
| | | label="éé(ç«æ¹ç±³)" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.salesVolume }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="salesAmount" |
| | | label="éå®éé¢(ä¸å
)" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.salesAmount }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="newCustomers" |
| | | label="æ°å¢å®¢æ·(个)" |
| | | width="150" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.newCustomers }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="totalCustomers" |
| | | label="å计客æ·(个)" |
| | | width="150" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.totalCustomers }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- å³è¾¹ï¼äº§åç±»ååå¸åéå®åºååå¸ --> |
| | | <div class="chart-column" style="flex: 1; display: flex; flex-direction: column; gap: 20px;"> |
| | | <div class="panel-card card-7" style="flex: 1;"> |
| | | <div class="chart-column" |
| | | style="flex: 1; display: flex; flex-direction: column; gap: 20px;"> |
| | | <div class="panel-card card-7" |
| | | style="flex: 1;"> |
| | | <div class="panel-title">产åç±»ååå¸</div> |
| | | <div class="chart-container"> |
| | | <div ref="productTypeChart" style="width: 100%; height: 100%;"></div> |
| | | <div ref="productTypeChart" |
| | | style="width: 100%; height: 100%;"></div> |
| | | </div> |
| | | </div> |
| | | <div class="panel-card card-8" style="flex: 1;"> |
| | | <div class="panel-card card-8" |
| | | style="flex: 1;"> |
| | | <div class="panel-title">éå®åºååå¸</div> |
| | | <div class="chart-container"> |
| | | <div ref="salesAreaChart" style="width: 100%; height: 100%;"></div> |
| | | <div ref="salesAreaChart" |
| | | style="width: 100%; height: 100%;"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | import * as echarts from 'echarts'; |
| | | import dayjs from 'dayjs'; |
| | | import { |
| | | ref, |
| | | computed, |
| | | onMounted, |
| | | onBeforeUnmount, |
| | | watch, |
| | | nextTick, |
| | | } from "vue"; |
| | | import { useRouter } from "vue-router"; |
| | | import * as echarts from "echarts"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const router = useRouter(); |
| | | const router = useRouter(); |
| | | |
| | | // ç鿡件 |
| | | const dateRange = ref([]); |
| | | const productType = ref(''); |
| | | const salesArea = ref(''); |
| | | const statDimension = ref('month'); |
| | | // ç鿡件 |
| | | const dateRange = ref([]); |
| | | const productType = ref(""); |
| | | const salesArea = ref(""); |
| | | const statDimension = ref("month"); |
| | | |
| | | // å¾è¡¨å¼ç¨ |
| | | const salesVolumeChart = ref(null); |
| | | const salesAmountChart = ref(null); |
| | | const productTypeChart = ref(null); |
| | | const salesAreaChart = ref(null); |
| | | const cumulativeSalesVolumeChart = ref(null); |
| | | const cumulativeSalesAmountChart = ref(null); |
| | | // å¾è¡¨å¼ç¨ |
| | | const salesVolumeChart = ref(null); |
| | | const salesAmountChart = ref(null); |
| | | const productTypeChart = ref(null); |
| | | const salesAreaChart = ref(null); |
| | | const cumulativeSalesVolumeChart = ref(null); |
| | | const cumulativeSalesAmountChart = ref(null); |
| | | |
| | | // å¾è¡¨å®ä¾ |
| | | let salesVolumeChartInstance = null; |
| | | let salesAmountChartInstance = null; |
| | | let productTypeChartInstance = null; |
| | | let salesAreaChartInstance = null; |
| | | let cumulativeSalesVolumeChartInstance = null; |
| | | let cumulativeSalesAmountChartInstance = null; |
| | | // å¾è¡¨å®ä¾ |
| | | let salesVolumeChartInstance = null; |
| | | let salesAmountChartInstance = null; |
| | | let productTypeChartInstance = null; |
| | | let salesAreaChartInstance = null; |
| | | let cumulativeSalesVolumeChartInstance = null; |
| | | let cumulativeSalesAmountChartInstance = null; |
| | | |
| | | // æ¨¡ææ°æ® |
| | | const mockData = [ |
| | | // 2026å¹´1ææ°æ® |
| | | { productType: 'ç å', salesArea: 'åä¸', period: '2026-01', salesVolume: 1200, salesAmount: 180, newCustomers: 5, totalCustomers: 120 }, |
| | | { productType: 'ç å', salesArea: 'åå', period: '2026-01', salesVolume: 800, salesAmount: 120, newCustomers: 3, totalCustomers: 80 }, |
| | | { productType: 'ç å', salesArea: 'åå', period: '2026-01', salesVolume: 600, salesAmount: 90, newCustomers: 2, totalCustomers: 60 }, |
| | | { productType: 'æ¿æ', salesArea: 'åä¸', period: '2026-01', salesVolume: 900, salesAmount: 270, newCustomers: 4, totalCustomers: 100 }, |
| | | { productType: 'æ¿æ', salesArea: 'åå', period: '2026-01', salesVolume: 500, salesAmount: 150, newCustomers: 2, totalCustomers: 70 }, |
| | | { productType: 'åæ', salesArea: 'åä¸', period: '2026-01', salesVolume: 400, salesAmount: 200, newCustomers: 3, totalCustomers: 50 }, |
| | | // 2026å¹´2ææ°æ® |
| | | { productType: 'ç å', salesArea: 'åä¸', period: '2026-02', salesVolume: 1300, salesAmount: 195, newCustomers: 4, totalCustomers: 124 }, |
| | | { productType: 'ç å', salesArea: 'åå', period: '2026-02', salesVolume: 850, salesAmount: 127.5, newCustomers: 2, totalCustomers: 82 }, |
| | | { productType: 'ç å', salesArea: 'åå', period: '2026-02', salesVolume: 650, salesAmount: 97.5, newCustomers: 1, totalCustomers: 61 }, |
| | | { productType: 'æ¿æ', salesArea: 'åä¸', period: '2026-02', salesVolume: 950, salesAmount: 285, newCustomers: 3, totalCustomers: 103 }, |
| | | { productType: 'æ¿æ', salesArea: 'åå', period: '2026-02', salesVolume: 550, salesAmount: 165, newCustomers: 1, totalCustomers: 71 }, |
| | | { productType: 'åæ', salesArea: 'åä¸', period: '2026-02', salesVolume: 450, salesAmount: 225, newCustomers: 2, totalCustomers: 52 }, |
| | | // 2026å¹´3ææ°æ® |
| | | { productType: 'ç å', salesArea: 'åä¸', period: '2026-03', salesVolume: 1400, salesAmount: 210, newCustomers: 6, totalCustomers: 130 }, |
| | | { productType: 'ç å', salesArea: 'åå', period: '2026-03', salesVolume: 900, salesAmount: 135, newCustomers: 3, totalCustomers: 85 }, |
| | | { productType: 'ç å', salesArea: 'åå', period: '2026-03', salesVolume: 700, salesAmount: 105, newCustomers: 2, totalCustomers: 63 }, |
| | | { productType: 'æ¿æ', salesArea: 'åä¸', period: '2026-03', salesVolume: 1000, salesAmount: 300, newCustomers: 5, totalCustomers: 108 }, |
| | | { productType: 'æ¿æ', salesArea: 'åå', period: '2026-03', salesVolume: 600, salesAmount: 180, newCustomers: 2, totalCustomers: 73 }, |
| | | { productType: 'åæ', salesArea: 'åä¸', period: '2026-03', salesVolume: 500, salesAmount: 250, newCustomers: 3, totalCustomers: 55 }, |
| | | // 西åå西åå°åºæ°æ® |
| | | { productType: 'ç å', salesArea: '西å', period: '2026-03', salesVolume: 500, salesAmount: 75, newCustomers: 2, totalCustomers: 40 }, |
| | | { productType: 'æ¿æ', salesArea: '西å', period: '2026-03', salesVolume: 300, salesAmount: 90, newCustomers: 1, totalCustomers: 30 }, |
| | | { productType: 'ç å', salesArea: '西å', period: '2026-03', salesVolume: 400, salesAmount: 60, newCustomers: 1, totalCustomers: 35 }, |
| | | { productType: 'æ¿æ', salesArea: '西å', period: '2026-03', salesVolume: 200, salesAmount: 60, newCustomers: 1, totalCustomers: 25 }, |
| | | ]; |
| | | // æ¨¡ææ°æ® |
| | | const mockData = [ |
| | | // 2026å¹´1ææ°æ® |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "åä¸", |
| | | period: "2026-01", |
| | | salesVolume: 1200, |
| | | salesAmount: 180, |
| | | newCustomers: 5, |
| | | totalCustomers: 120, |
| | | }, |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "åå", |
| | | period: "2026-01", |
| | | salesVolume: 800, |
| | | salesAmount: 120, |
| | | newCustomers: 3, |
| | | totalCustomers: 80, |
| | | }, |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "åå", |
| | | period: "2026-01", |
| | | salesVolume: 600, |
| | | salesAmount: 90, |
| | | newCustomers: 2, |
| | | totalCustomers: 60, |
| | | }, |
| | | { |
| | | productType: "æ¿æ", |
| | | salesArea: "åä¸", |
| | | period: "2026-01", |
| | | salesVolume: 900, |
| | | salesAmount: 270, |
| | | newCustomers: 4, |
| | | totalCustomers: 100, |
| | | }, |
| | | { |
| | | productType: "æ¿æ", |
| | | salesArea: "åå", |
| | | period: "2026-01", |
| | | salesVolume: 500, |
| | | salesAmount: 150, |
| | | newCustomers: 2, |
| | | totalCustomers: 70, |
| | | }, |
| | | { |
| | | productType: "åæ", |
| | | salesArea: "åä¸", |
| | | period: "2026-01", |
| | | salesVolume: 400, |
| | | salesAmount: 200, |
| | | newCustomers: 3, |
| | | totalCustomers: 50, |
| | | }, |
| | | // 2026å¹´2ææ°æ® |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "åä¸", |
| | | period: "2026-02", |
| | | salesVolume: 1300, |
| | | salesAmount: 195, |
| | | newCustomers: 4, |
| | | totalCustomers: 124, |
| | | }, |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "åå", |
| | | period: "2026-02", |
| | | salesVolume: 850, |
| | | salesAmount: 127.5, |
| | | newCustomers: 2, |
| | | totalCustomers: 82, |
| | | }, |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "åå", |
| | | period: "2026-02", |
| | | salesVolume: 650, |
| | | salesAmount: 97.5, |
| | | newCustomers: 1, |
| | | totalCustomers: 61, |
| | | }, |
| | | { |
| | | productType: "æ¿æ", |
| | | salesArea: "åä¸", |
| | | period: "2026-02", |
| | | salesVolume: 950, |
| | | salesAmount: 285, |
| | | newCustomers: 3, |
| | | totalCustomers: 103, |
| | | }, |
| | | { |
| | | productType: "æ¿æ", |
| | | salesArea: "åå", |
| | | period: "2026-02", |
| | | salesVolume: 550, |
| | | salesAmount: 165, |
| | | newCustomers: 1, |
| | | totalCustomers: 71, |
| | | }, |
| | | { |
| | | productType: "åæ", |
| | | salesArea: "åä¸", |
| | | period: "2026-02", |
| | | salesVolume: 450, |
| | | salesAmount: 225, |
| | | newCustomers: 2, |
| | | totalCustomers: 52, |
| | | }, |
| | | // 2026å¹´3ææ°æ® |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "åä¸", |
| | | period: "2026-03", |
| | | salesVolume: 1400, |
| | | salesAmount: 210, |
| | | newCustomers: 6, |
| | | totalCustomers: 130, |
| | | }, |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "åå", |
| | | period: "2026-03", |
| | | salesVolume: 900, |
| | | salesAmount: 135, |
| | | newCustomers: 3, |
| | | totalCustomers: 85, |
| | | }, |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "åå", |
| | | period: "2026-03", |
| | | salesVolume: 700, |
| | | salesAmount: 105, |
| | | newCustomers: 2, |
| | | totalCustomers: 63, |
| | | }, |
| | | { |
| | | productType: "æ¿æ", |
| | | salesArea: "åä¸", |
| | | period: "2026-03", |
| | | salesVolume: 1000, |
| | | salesAmount: 300, |
| | | newCustomers: 5, |
| | | totalCustomers: 108, |
| | | }, |
| | | { |
| | | productType: "æ¿æ", |
| | | salesArea: "åå", |
| | | period: "2026-03", |
| | | salesVolume: 600, |
| | | salesAmount: 180, |
| | | newCustomers: 2, |
| | | totalCustomers: 73, |
| | | }, |
| | | { |
| | | productType: "åæ", |
| | | salesArea: "åä¸", |
| | | period: "2026-03", |
| | | salesVolume: 500, |
| | | salesAmount: 250, |
| | | newCustomers: 3, |
| | | totalCustomers: 55, |
| | | }, |
| | | // 西åå西åå°åºæ°æ® |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "西å", |
| | | period: "2026-03", |
| | | salesVolume: 500, |
| | | salesAmount: 75, |
| | | newCustomers: 2, |
| | | totalCustomers: 40, |
| | | }, |
| | | { |
| | | productType: "æ¿æ", |
| | | salesArea: "西å", |
| | | period: "2026-03", |
| | | salesVolume: 300, |
| | | salesAmount: 90, |
| | | newCustomers: 1, |
| | | totalCustomers: 30, |
| | | }, |
| | | { |
| | | productType: "ç å", |
| | | salesArea: "西å", |
| | | period: "2026-03", |
| | | salesVolume: 400, |
| | | salesAmount: 60, |
| | | newCustomers: 1, |
| | | totalCustomers: 35, |
| | | }, |
| | | { |
| | | productType: "æ¿æ", |
| | | salesArea: "西å", |
| | | period: "2026-03", |
| | | salesVolume: 200, |
| | | salesAmount: 60, |
| | | newCustomers: 1, |
| | | totalCustomers: 25, |
| | | }, |
| | | ]; |
| | | |
| | | // 计ç®å±æ§ |
| | | const filteredData = computed(() => { |
| | | let result = [...mockData]; |
| | | |
| | | // 计ç®å±æ§ |
| | | const filteredData = computed(() => { |
| | | let result = [...mockData]; |
| | | |
| | | // æäº§åç±»åçé |
| | | if (productType.value) { |
| | | result = result.filter(item => { |
| | | const typeMap = { block: 'ç å', board: 'æ¿æ', profile: 'åæ' }; |
| | | return item.productType === typeMap[productType.value]; |
| | | }); |
| | | } |
| | | |
| | | // æéå®åºåçé |
| | | if (salesArea.value) { |
| | | result = result.filter(item => { |
| | | const areaMap = { east: 'åä¸', north: 'åå', south: 'åå', southwest: '西å', northwest: '西å' }; |
| | | return item.salesArea === areaMap[salesArea.value]; |
| | | }); |
| | | } |
| | | |
| | | // ææ¶é´èå´çé |
| | | if (dateRange.value && dateRange.value.length === 2) { |
| | | const startDate = dayjs(dateRange.value[0]); |
| | | const endDate = dayjs(dateRange.value[1]); |
| | | |
| | | result = result.filter(item => { |
| | | const itemDate = dayjs(item.period); |
| | | return itemDate.isAfter(startDate.subtract(1, 'day')) && itemDate.isBefore(endDate.add(1, 'day')); |
| | | }); |
| | | } |
| | | |
| | | return result; |
| | | }); |
| | | |
| | | // æ ¸å¿ææ è®¡ç® |
| | | const totalSalesVolume = computed(() => { |
| | | return filteredData.value.reduce((sum, item) => sum + item.salesVolume, 0); |
| | | }); |
| | | |
| | | const totalSalesAmount = computed(() => { |
| | | return filteredData.value.reduce((sum, item) => sum + item.salesAmount, 0).toFixed(2); |
| | | }); |
| | | |
| | | const newCustomerCount = computed(() => { |
| | | return filteredData.value.reduce((sum, item) => sum + item.newCustomers, 0); |
| | | }); |
| | | |
| | | const totalCustomerCount = computed(() => { |
| | | // è®¡ç®æ¯ä¸ªåºåå产åç±»åçæå¤§å®¢æ·æ° |
| | | const customerMap = {}; |
| | | filteredData.value.forEach(item => { |
| | | const key = `${item.productType}-${item.salesArea}`; |
| | | if (!customerMap[key] || item.totalCustomers > customerMap[key]) { |
| | | customerMap[key] = item.totalCustomers; |
| | | // æäº§åç±»åçé |
| | | if (productType.value) { |
| | | result = result.filter(item => { |
| | | const typeMap = { block: "ç å", board: "æ¿æ", profile: "åæ" }; |
| | | return item.productType === typeMap[productType.value]; |
| | | }); |
| | | } |
| | | |
| | | // æéå®åºåçé |
| | | if (salesArea.value) { |
| | | result = result.filter(item => { |
| | | const areaMap = { |
| | | east: "åä¸", |
| | | north: "åå", |
| | | south: "åå", |
| | | southwest: "西å", |
| | | northwest: "西å", |
| | | }; |
| | | return item.salesArea === areaMap[salesArea.value]; |
| | | }); |
| | | } |
| | | |
| | | // ææ¶é´èå´çé |
| | | if (dateRange.value && dateRange.value.length === 2) { |
| | | const startDate = dayjs(dateRange.value[0]); |
| | | const endDate = dayjs(dateRange.value[1]); |
| | | |
| | | result = result.filter(item => { |
| | | const itemDate = dayjs(item.period); |
| | | return ( |
| | | itemDate.isAfter(startDate.subtract(1, "day")) && |
| | | itemDate.isBefore(endDate.add(1, "day")) |
| | | ); |
| | | }); |
| | | } |
| | | |
| | | return result; |
| | | }); |
| | | return Object.values(customerMap).reduce((sum, count) => sum + count, 0); |
| | | }); |
| | | |
| | | // ååç计ç®ï¼æ¨¡æï¼ |
| | | const salesVolumeChange = ref('+5.2'); |
| | | const salesAmountChange = ref('+7.8'); |
| | | const customerCountChange = ref('+3.5'); |
| | | const totalCustomerChange = ref('+2.1'); |
| | | // æ ¸å¿ææ è®¡ç® |
| | | const totalSalesVolume = computed(() => { |
| | | return filteredData.value.reduce((sum, item) => sum + item.salesVolume, 0); |
| | | }); |
| | | |
| | | // è¡¨æ ¼æ°æ® |
| | | const tableData = computed(() => { |
| | | return filteredData.value.map(item => { |
| | | // 计ç®ç´¯è®¡å¼ï¼æ¨¡æï¼ |
| | | const cumulativeSalesVolume = item.salesVolume * 1.5; |
| | | const cumulativeSalesAmount = item.salesAmount * 1.5; |
| | | const cumulativeNewCustomers = item.newCustomers * 2; |
| | | |
| | | const totalSalesAmount = computed(() => { |
| | | return filteredData.value |
| | | .reduce((sum, item) => sum + item.salesAmount, 0) |
| | | .toFixed(2); |
| | | }); |
| | | |
| | | const newCustomerCount = computed(() => { |
| | | return filteredData.value.reduce((sum, item) => sum + item.newCustomers, 0); |
| | | }); |
| | | |
| | | const totalCustomerCount = computed(() => { |
| | | // è®¡ç®æ¯ä¸ªåºåå产åç±»åçæå¤§å®¢æ·æ° |
| | | const customerMap = {}; |
| | | filteredData.value.forEach(item => { |
| | | const key = `${item.productType}-${item.salesArea}`; |
| | | if (!customerMap[key] || item.totalCustomers > customerMap[key]) { |
| | | customerMap[key] = item.totalCustomers; |
| | | } |
| | | }); |
| | | return Object.values(customerMap).reduce((sum, count) => sum + count, 0); |
| | | }); |
| | | |
| | | // ååç计ç®ï¼æ¨¡æï¼ |
| | | const salesVolumeChange = ref("+5.2"); |
| | | const salesAmountChange = ref("+7.8"); |
| | | const customerCountChange = ref("+3.5"); |
| | | const totalCustomerChange = ref("+2.1"); |
| | | |
| | | // è¡¨æ ¼æ°æ® |
| | | const tableData = computed(() => { |
| | | return filteredData.value.map(item => { |
| | | // 计ç®ç´¯è®¡å¼ï¼æ¨¡æï¼ |
| | | const cumulativeSalesVolume = item.salesVolume * 1.5; |
| | | const cumulativeSalesAmount = item.salesAmount * 1.5; |
| | | const cumulativeNewCustomers = item.newCustomers * 2; |
| | | |
| | | return { |
| | | ...item, |
| | | cumulativeSalesVolume, |
| | | cumulativeSalesAmount, |
| | | cumulativeNewCustomers, |
| | | }; |
| | | }); |
| | | }); |
| | | |
| | | // ééè¶å¿å¾è¡¨é
ç½® |
| | | const salesVolumeChartOption = computed(() => { |
| | | // æå¨æåç» |
| | | const periodMap = {}; |
| | | filteredData.value.forEach(item => { |
| | | if (!periodMap[item.period]) { |
| | | periodMap[item.period] = 0; |
| | | } |
| | | periodMap[item.period] += item.salesVolume; |
| | | }); |
| | | |
| | | const periods = Object.keys(periodMap).sort(); |
| | | const values = periods.map(period => periodMap[period]); |
| | | |
| | | return { |
| | | ...item, |
| | | cumulativeSalesVolume, |
| | | cumulativeSalesAmount, |
| | | cumulativeNewCustomers |
| | | tooltip: { |
| | | trigger: "axis", |
| | | formatter: "{b}: {c} ç«æ¹ç±³", |
| | | }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: periods, |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | name: "ééï¼ç«æ¹ç±³ï¼", |
| | | }, |
| | | series: [ |
| | | { |
| | | data: values, |
| | | type: "line", |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3, |
| | | }, |
| | | itemStyle: { |
| | | color: "#409EFF", |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | }); |
| | | |
| | | // ééè¶å¿å¾è¡¨é
ç½® |
| | | const salesVolumeChartOption = computed(() => { |
| | | // æå¨æåç» |
| | | const periodMap = {}; |
| | | filteredData.value.forEach(item => { |
| | | if (!periodMap[item.period]) { |
| | | periodMap[item.period] = 0; |
| | | } |
| | | periodMap[item.period] += item.salesVolume; |
| | | }); |
| | | |
| | | const periods = Object.keys(periodMap).sort(); |
| | | const values = periods.map(period => periodMap[period]); |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | formatter: '{b}: {c} ç«æ¹ç±³' |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: periods |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: 'ééï¼ç«æ¹ç±³ï¼' |
| | | }, |
| | | series: [{ |
| | | data: values, |
| | | type: 'line', |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 3 |
| | | // éå®éé¢è¶å¿å¾è¡¨é
ç½® |
| | | const salesAmountChartOption = computed(() => { |
| | | // æå¨æåç» |
| | | const periodMap = {}; |
| | | filteredData.value.forEach(item => { |
| | | if (!periodMap[item.period]) { |
| | | periodMap[item.period] = 0; |
| | | } |
| | | periodMap[item.period] += item.salesAmount; |
| | | }); |
| | | |
| | | const periods = Object.keys(periodMap).sort(); |
| | | const values = periods.map(period => periodMap[period]); |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: "axis", |
| | | formatter: "{b}: {c} ä¸å
", |
| | | }, |
| | | itemStyle: { |
| | | color: '#409EFF' |
| | | } |
| | | }] |
| | | }; |
| | | }); |
| | | |
| | | // éå®éé¢è¶å¿å¾è¡¨é
ç½® |
| | | const salesAmountChartOption = computed(() => { |
| | | // æå¨æåç» |
| | | const periodMap = {}; |
| | | filteredData.value.forEach(item => { |
| | | if (!periodMap[item.period]) { |
| | | periodMap[item.period] = 0; |
| | | } |
| | | periodMap[item.period] += item.salesAmount; |
| | | }); |
| | | |
| | | const periods = Object.keys(periodMap).sort(); |
| | | const values = periods.map(period => periodMap[period]); |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | formatter: '{b}: {c} ä¸å
' |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: periods |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: 'éå®éé¢ï¼ä¸å
ï¼' |
| | | }, |
| | | series: [{ |
| | | data: values, |
| | | type: 'bar', |
| | | itemStyle: { |
| | | color: '#67C23A' |
| | | } |
| | | }] |
| | | }; |
| | | }); |
| | | |
| | | // 产åç±»ååå¸å¾è¡¨é
ç½® |
| | | const productTypeChartOption = computed(() => { |
| | | // æäº§åç±»ååç» |
| | | const typeMap = {}; |
| | | filteredData.value.forEach(item => { |
| | | if (!typeMap[item.productType]) { |
| | | typeMap[item.productType] = 0; |
| | | } |
| | | typeMap[item.productType] += item.salesVolume; |
| | | }); |
| | | |
| | | const types = Object.keys(typeMap); |
| | | const values = types.map(type => typeMap[type]); |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: 'item', |
| | | formatter: '{b}: {c} ç«æ¹ç±³ ({d}%)' |
| | | }, |
| | | series: [{ |
| | | type: 'pie', |
| | | radius: '60%', |
| | | data: types.map((type, index) => ({ |
| | | name: type, |
| | | value: values[index] |
| | | })), |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0.5)' |
| | | } |
| | | } |
| | | }] |
| | | }; |
| | | }); |
| | | |
| | | // éå®åºååå¸å¾è¡¨é
ç½® |
| | | const salesAreaChartOption = computed(() => { |
| | | // æéå®åºååç» |
| | | const areaMap = {}; |
| | | filteredData.value.forEach(item => { |
| | | if (!areaMap[item.salesArea]) { |
| | | areaMap[item.salesArea] = 0; |
| | | } |
| | | areaMap[item.salesArea] += item.salesVolume; |
| | | }); |
| | | |
| | | const areas = Object.keys(areaMap); |
| | | const values = areas.map(area => areaMap[area]); |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: 'item', |
| | | formatter: '{b}: {c} ç«æ¹ç±³ ({d}%)' |
| | | }, |
| | | series: [{ |
| | | type: 'pie', |
| | | radius: '60%', |
| | | data: areas.map((area, index) => ({ |
| | | name: area, |
| | | value: values[index] |
| | | })), |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0.5)' |
| | | } |
| | | } |
| | | }] |
| | | }; |
| | | }); |
| | | |
| | | // 累计ééè¶å¿å¾è¡¨é
ç½® |
| | | const cumulativeSalesVolumeChartOption = computed(() => { |
| | | // æå¨æåç» |
| | | const periodMap = {}; |
| | | let cumulativeValue = 0; |
| | | |
| | | // æå¨ææåº |
| | | const sortedData = [...filteredData.value].sort((a, b) => a.period.localeCompare(b.period)); |
| | | |
| | | sortedData.forEach(item => { |
| | | cumulativeValue += item.salesVolume; |
| | | periodMap[item.period] = cumulativeValue; |
| | | }); |
| | | |
| | | const periods = Object.keys(periodMap).sort(); |
| | | const values = periods.map(period => periodMap[period]); |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | formatter: '{b}: {c} ç«æ¹ç±³' |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: periods |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: '累计ééï¼ç«æ¹ç±³ï¼' |
| | | }, |
| | | series: [{ |
| | | data: values, |
| | | type: 'line', |
| | | smooth: true, |
| | | areaStyle: { |
| | | opacity: 0.3 |
| | | xAxis: { |
| | | type: "category", |
| | | data: periods, |
| | | }, |
| | | itemStyle: { |
| | | color: '#E6A23C' |
| | | yAxis: { |
| | | type: "value", |
| | | name: "éå®éé¢ï¼ä¸å
ï¼", |
| | | }, |
| | | lineStyle: { |
| | | width: 3 |
| | | } |
| | | }] |
| | | }; |
| | | }); |
| | | |
| | | // 累计éå®éé¢è¶å¿å¾è¡¨é
ç½® |
| | | const cumulativeSalesAmountChartOption = computed(() => { |
| | | // æå¨æåç» |
| | | const periodMap = {}; |
| | | let cumulativeValue = 0; |
| | | |
| | | // æå¨ææåº |
| | | const sortedData = [...filteredData.value].sort((a, b) => a.period.localeCompare(b.period)); |
| | | |
| | | sortedData.forEach(item => { |
| | | cumulativeValue += item.salesAmount; |
| | | periodMap[item.period] = cumulativeValue; |
| | | series: [ |
| | | { |
| | | data: values, |
| | | type: "bar", |
| | | itemStyle: { |
| | | color: "#67C23A", |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | const periods = Object.keys(periodMap).sort(); |
| | | const values = periods.map(period => periodMap[period]); |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | formatter: '{b}: {c} ä¸å
' |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: periods |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: '累计éå®éé¢ï¼ä¸å
ï¼' |
| | | }, |
| | | series: [{ |
| | | data: values, |
| | | type: 'bar', |
| | | itemStyle: { |
| | | color: '#F56C6C' |
| | | |
| | | // 产åç±»ååå¸å¾è¡¨é
ç½® |
| | | const productTypeChartOption = computed(() => { |
| | | // æäº§åç±»ååç» |
| | | const typeMap = {}; |
| | | filteredData.value.forEach(item => { |
| | | if (!typeMap[item.productType]) { |
| | | typeMap[item.productType] = 0; |
| | | } |
| | | }] |
| | | }; |
| | | }); |
| | | typeMap[item.productType] += item.salesVolume; |
| | | }); |
| | | |
| | | // æ¹æ³ |
| | | const goBack = () => { |
| | | router.back(); |
| | | }; |
| | | const types = Object.keys(typeMap); |
| | | const values = types.map(type => typeMap[type]); |
| | | |
| | | const handleDateChange = () => { |
| | | // å¤çæ¥æåå |
| | | updateCharts(); |
| | | }; |
| | | |
| | | const handleFilterChange = () => { |
| | | // å¤çç鿡件åå |
| | | updateCharts(); |
| | | }; |
| | | |
| | | // åå§åå¾è¡¨ |
| | | const initCharts = () => { |
| | | // åå§åééè¶å¿å¾è¡¨ |
| | | if (salesVolumeChart.value && !salesVolumeChartInstance) { |
| | | salesVolumeChartInstance = echarts.init(salesVolumeChart.value); |
| | | } |
| | | |
| | | // åå§åéå®éé¢è¶å¿å¾è¡¨ |
| | | if (salesAmountChart.value && !salesAmountChartInstance) { |
| | | salesAmountChartInstance = echarts.init(salesAmountChart.value); |
| | | } |
| | | |
| | | // åå§å产åç±»ååå¸å¾è¡¨ |
| | | if (productTypeChart.value && !productTypeChartInstance) { |
| | | productTypeChartInstance = echarts.init(productTypeChart.value); |
| | | } |
| | | |
| | | // åå§åéå®åºååå¸å¾è¡¨ |
| | | if (salesAreaChart.value && !salesAreaChartInstance) { |
| | | salesAreaChartInstance = echarts.init(salesAreaChart.value); |
| | | } |
| | | |
| | | // åå§å累计ééè¶å¿å¾è¡¨ |
| | | if (cumulativeSalesVolumeChart.value && !cumulativeSalesVolumeChartInstance) { |
| | | cumulativeSalesVolumeChartInstance = echarts.init(cumulativeSalesVolumeChart.value); |
| | | } |
| | | |
| | | // åå§å累计éå®éé¢è¶å¿å¾è¡¨ |
| | | if (cumulativeSalesAmountChart.value && !cumulativeSalesAmountChartInstance) { |
| | | cumulativeSalesAmountChartInstance = echarts.init(cumulativeSalesAmountChart.value); |
| | | } |
| | | |
| | | updateCharts(); |
| | | }; |
| | | |
| | | // æ´æ°å¾è¡¨ |
| | | const updateCharts = () => { |
| | | // æ´æ°ééè¶å¿å¾è¡¨ |
| | | if (salesVolumeChartInstance) { |
| | | salesVolumeChartInstance.setOption(salesVolumeChartOption.value); |
| | | } |
| | | |
| | | // æ´æ°éå®éé¢è¶å¿å¾è¡¨ |
| | | if (salesAmountChartInstance) { |
| | | salesAmountChartInstance.setOption(salesAmountChartOption.value); |
| | | } |
| | | |
| | | // æ´æ°äº§åç±»ååå¸å¾è¡¨ |
| | | if (productTypeChartInstance) { |
| | | productTypeChartInstance.setOption(productTypeChartOption.value); |
| | | } |
| | | |
| | | // æ´æ°éå®åºååå¸å¾è¡¨ |
| | | if (salesAreaChartInstance) { |
| | | salesAreaChartInstance.setOption(salesAreaChartOption.value); |
| | | } |
| | | |
| | | // æ´æ°ç´¯è®¡ééè¶å¿å¾è¡¨ |
| | | if (cumulativeSalesVolumeChartInstance) { |
| | | cumulativeSalesVolumeChartInstance.setOption(cumulativeSalesVolumeChartOption.value); |
| | | } |
| | | |
| | | // æ´æ°ç´¯è®¡éå®éé¢è¶å¿å¾è¡¨ |
| | | if (cumulativeSalesAmountChartInstance) { |
| | | cumulativeSalesAmountChartInstance.setOption(cumulativeSalesAmountChartOption.value); |
| | | } |
| | | }; |
| | | |
| | | // çå¬çªå£å¤§å°åå |
| | | const handleResize = () => { |
| | | if (salesVolumeChartInstance) { |
| | | salesVolumeChartInstance.resize(); |
| | | } |
| | | if (salesAmountChartInstance) { |
| | | salesAmountChartInstance.resize(); |
| | | } |
| | | if (productTypeChartInstance) { |
| | | productTypeChartInstance.resize(); |
| | | } |
| | | if (salesAreaChartInstance) { |
| | | salesAreaChartInstance.resize(); |
| | | } |
| | | if (cumulativeSalesVolumeChartInstance) { |
| | | cumulativeSalesVolumeChartInstance.resize(); |
| | | } |
| | | if (cumulativeSalesAmountChartInstance) { |
| | | cumulativeSalesAmountChartInstance.resize(); |
| | | } |
| | | }; |
| | | |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | // 设置é»è®¤æ¥æèå´ä¸ºæè¿3个æ |
| | | const endDate = dayjs(); |
| | | const startDate = endDate.subtract(3, 'month'); |
| | | dateRange.value = [startDate.format('YYYY-MM-DD'), endDate.format('YYYY-MM-DD')]; |
| | | |
| | | // çå¾
DOMæ´æ°ååå§åå¾è¡¨ |
| | | nextTick(() => { |
| | | initCharts(); |
| | | return { |
| | | tooltip: { |
| | | trigger: "item", |
| | | formatter: "{b}: {c} ç«æ¹ç±³ ({d}%)", |
| | | }, |
| | | series: [ |
| | | { |
| | | type: "pie", |
| | | radius: "60%", |
| | | data: types.map((type, index) => ({ |
| | | name: type, |
| | | value: values[index], |
| | | })), |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: "rgba(0, 0, 0, 0.5)", |
| | | }, |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | // æ·»å çªå£å¤§å°ååçå¬ |
| | | window.addEventListener('resize', handleResize); |
| | | }); |
| | | |
| | | // ç»ä»¶å¸è½½æ¶éæ¯å¾è¡¨å®ä¾ |
| | | onBeforeUnmount(() => { |
| | | if (salesVolumeChartInstance) { |
| | | salesVolumeChartInstance.dispose(); |
| | | } |
| | | if (salesAmountChartInstance) { |
| | | salesAmountChartInstance.dispose(); |
| | | } |
| | | if (productTypeChartInstance) { |
| | | productTypeChartInstance.dispose(); |
| | | } |
| | | if (salesAreaChartInstance) { |
| | | salesAreaChartInstance.dispose(); |
| | | } |
| | | if (cumulativeSalesVolumeChartInstance) { |
| | | cumulativeSalesVolumeChartInstance.dispose(); |
| | | } |
| | | if (cumulativeSalesAmountChartInstance) { |
| | | cumulativeSalesAmountChartInstance.dispose(); |
| | | } |
| | | |
| | | // ç§»é¤çªå£å¤§å°ååçå¬ |
| | | window.removeEventListener('resize', handleResize); |
| | | }); |
| | | // éå®åºååå¸å¾è¡¨é
ç½® |
| | | const salesAreaChartOption = computed(() => { |
| | | // æéå®åºååç» |
| | | const areaMap = {}; |
| | | filteredData.value.forEach(item => { |
| | | if (!areaMap[item.salesArea]) { |
| | | areaMap[item.salesArea] = 0; |
| | | } |
| | | areaMap[item.salesArea] += item.salesVolume; |
| | | }); |
| | | |
| | | const areas = Object.keys(areaMap); |
| | | const values = areas.map(area => areaMap[area]); |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: "item", |
| | | formatter: "{b}: {c} ç«æ¹ç±³ ({d}%)", |
| | | }, |
| | | series: [ |
| | | { |
| | | type: "pie", |
| | | radius: "60%", |
| | | data: areas.map((area, index) => ({ |
| | | name: area, |
| | | value: values[index], |
| | | })), |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: "rgba(0, 0, 0, 0.5)", |
| | | }, |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | // 累计ééè¶å¿å¾è¡¨é
ç½® |
| | | const cumulativeSalesVolumeChartOption = computed(() => { |
| | | // æå¨æåç» |
| | | const periodMap = {}; |
| | | let cumulativeValue = 0; |
| | | |
| | | // æå¨ææåº |
| | | const sortedData = [...filteredData.value].sort((a, b) => |
| | | a.period.localeCompare(b.period) |
| | | ); |
| | | |
| | | sortedData.forEach(item => { |
| | | cumulativeValue += item.salesVolume; |
| | | periodMap[item.period] = cumulativeValue; |
| | | }); |
| | | |
| | | const periods = Object.keys(periodMap).sort(); |
| | | const values = periods.map(period => periodMap[period]); |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: "axis", |
| | | formatter: "{b}: {c} ç«æ¹ç±³", |
| | | }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: periods, |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | name: "累计ééï¼ç«æ¹ç±³ï¼", |
| | | }, |
| | | series: [ |
| | | { |
| | | data: values, |
| | | type: "line", |
| | | smooth: true, |
| | | areaStyle: { |
| | | opacity: 0.3, |
| | | }, |
| | | itemStyle: { |
| | | color: "#E6A23C", |
| | | }, |
| | | lineStyle: { |
| | | width: 3, |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | // 累计éå®éé¢è¶å¿å¾è¡¨é
ç½® |
| | | const cumulativeSalesAmountChartOption = computed(() => { |
| | | // æå¨æåç» |
| | | const periodMap = {}; |
| | | let cumulativeValue = 0; |
| | | |
| | | // æå¨ææåº |
| | | const sortedData = [...filteredData.value].sort((a, b) => |
| | | a.period.localeCompare(b.period) |
| | | ); |
| | | |
| | | sortedData.forEach(item => { |
| | | cumulativeValue += item.salesAmount; |
| | | periodMap[item.period] = cumulativeValue; |
| | | }); |
| | | |
| | | const periods = Object.keys(periodMap).sort(); |
| | | const values = periods.map(period => periodMap[period]); |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: "axis", |
| | | formatter: "{b}: {c} ä¸å
", |
| | | }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: periods, |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | name: "累计éå®éé¢ï¼ä¸å
ï¼", |
| | | }, |
| | | series: [ |
| | | { |
| | | data: values, |
| | | type: "bar", |
| | | itemStyle: { |
| | | color: "#F56C6C", |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | // æ¹æ³ |
| | | const goBack = () => { |
| | | router.back(); |
| | | }; |
| | | |
| | | const handleDateChange = () => { |
| | | // å¤çæ¥æåå |
| | | updateCharts(); |
| | | }; |
| | | |
| | | const handleFilterChange = () => { |
| | | // å¤çç鿡件åå |
| | | updateCharts(); |
| | | }; |
| | | |
| | | // åå§åå¾è¡¨ |
| | | const initCharts = () => { |
| | | // åå§åééè¶å¿å¾è¡¨ |
| | | if (salesVolumeChart.value && !salesVolumeChartInstance) { |
| | | salesVolumeChartInstance = echarts.init(salesVolumeChart.value); |
| | | } |
| | | |
| | | // åå§åéå®éé¢è¶å¿å¾è¡¨ |
| | | if (salesAmountChart.value && !salesAmountChartInstance) { |
| | | salesAmountChartInstance = echarts.init(salesAmountChart.value); |
| | | } |
| | | |
| | | // åå§å产åç±»ååå¸å¾è¡¨ |
| | | if (productTypeChart.value && !productTypeChartInstance) { |
| | | productTypeChartInstance = echarts.init(productTypeChart.value); |
| | | } |
| | | |
| | | // åå§åéå®åºååå¸å¾è¡¨ |
| | | if (salesAreaChart.value && !salesAreaChartInstance) { |
| | | salesAreaChartInstance = echarts.init(salesAreaChart.value); |
| | | } |
| | | |
| | | // åå§å累计ééè¶å¿å¾è¡¨ |
| | | if (cumulativeSalesVolumeChart.value && !cumulativeSalesVolumeChartInstance) { |
| | | cumulativeSalesVolumeChartInstance = echarts.init( |
| | | cumulativeSalesVolumeChart.value |
| | | ); |
| | | } |
| | | |
| | | // åå§å累计éå®éé¢è¶å¿å¾è¡¨ |
| | | if (cumulativeSalesAmountChart.value && !cumulativeSalesAmountChartInstance) { |
| | | cumulativeSalesAmountChartInstance = echarts.init( |
| | | cumulativeSalesAmountChart.value |
| | | ); |
| | | } |
| | | |
| | | updateCharts(); |
| | | }; |
| | | |
| | | // æ´æ°å¾è¡¨ |
| | | const updateCharts = () => { |
| | | // æ´æ°ééè¶å¿å¾è¡¨ |
| | | if (salesVolumeChartInstance) { |
| | | salesVolumeChartInstance.setOption(salesVolumeChartOption.value); |
| | | } |
| | | |
| | | // æ´æ°éå®éé¢è¶å¿å¾è¡¨ |
| | | if (salesAmountChartInstance) { |
| | | salesAmountChartInstance.setOption(salesAmountChartOption.value); |
| | | } |
| | | |
| | | // æ´æ°äº§åç±»ååå¸å¾è¡¨ |
| | | if (productTypeChartInstance) { |
| | | productTypeChartInstance.setOption(productTypeChartOption.value); |
| | | } |
| | | |
| | | // æ´æ°éå®åºååå¸å¾è¡¨ |
| | | if (salesAreaChartInstance) { |
| | | salesAreaChartInstance.setOption(salesAreaChartOption.value); |
| | | } |
| | | |
| | | // æ´æ°ç´¯è®¡ééè¶å¿å¾è¡¨ |
| | | if (cumulativeSalesVolumeChartInstance) { |
| | | cumulativeSalesVolumeChartInstance.setOption( |
| | | cumulativeSalesVolumeChartOption.value |
| | | ); |
| | | } |
| | | |
| | | // æ´æ°ç´¯è®¡éå®éé¢è¶å¿å¾è¡¨ |
| | | if (cumulativeSalesAmountChartInstance) { |
| | | cumulativeSalesAmountChartInstance.setOption( |
| | | cumulativeSalesAmountChartOption.value |
| | | ); |
| | | } |
| | | }; |
| | | |
| | | // çå¬çªå£å¤§å°åå |
| | | const handleResize = () => { |
| | | if (salesVolumeChartInstance) { |
| | | salesVolumeChartInstance.resize(); |
| | | } |
| | | if (salesAmountChartInstance) { |
| | | salesAmountChartInstance.resize(); |
| | | } |
| | | if (productTypeChartInstance) { |
| | | productTypeChartInstance.resize(); |
| | | } |
| | | if (salesAreaChartInstance) { |
| | | salesAreaChartInstance.resize(); |
| | | } |
| | | if (cumulativeSalesVolumeChartInstance) { |
| | | cumulativeSalesVolumeChartInstance.resize(); |
| | | } |
| | | if (cumulativeSalesAmountChartInstance) { |
| | | cumulativeSalesAmountChartInstance.resize(); |
| | | } |
| | | }; |
| | | |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | // 设置é»è®¤æ¥æèå´ä¸ºæè¿3个æ |
| | | const endDate = dayjs(); |
| | | const startDate = endDate.subtract(3, "month"); |
| | | dateRange.value = [ |
| | | startDate.format("YYYY-MM-DD"), |
| | | endDate.format("YYYY-MM-DD"), |
| | | ]; |
| | | |
| | | // çå¾
DOMæ´æ°ååå§åå¾è¡¨ |
| | | nextTick(() => { |
| | | initCharts(); |
| | | }); |
| | | |
| | | // æ·»å çªå£å¤§å°ååçå¬ |
| | | window.addEventListener("resize", handleResize); |
| | | }); |
| | | |
| | | // è·å产åç±»åæ ç¾ç±»å |
| | | const getProductTypeType = type => { |
| | | const typeMap = { |
| | | ç å: "primary", |
| | | æ¿æ: "success", |
| | | åæ: "warning", |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }; |
| | | |
| | | // è·åéå®åºåæ ç¾ç±»å |
| | | const getSalesAreaType = area => { |
| | | const typeMap = { |
| | | åä¸: "primary", |
| | | åå: "success", |
| | | åå: "warning", |
| | | 西å: "danger", |
| | | 西å: "info", |
| | | }; |
| | | return typeMap[area] || "info"; |
| | | }; |
| | | |
| | | // ç»ä»¶å¸è½½æ¶éæ¯å¾è¡¨å®ä¾ |
| | | onBeforeUnmount(() => { |
| | | if (salesVolumeChartInstance) { |
| | | salesVolumeChartInstance.dispose(); |
| | | } |
| | | if (salesAmountChartInstance) { |
| | | salesAmountChartInstance.dispose(); |
| | | } |
| | | if (productTypeChartInstance) { |
| | | productTypeChartInstance.dispose(); |
| | | } |
| | | if (salesAreaChartInstance) { |
| | | salesAreaChartInstance.dispose(); |
| | | } |
| | | if (cumulativeSalesVolumeChartInstance) { |
| | | cumulativeSalesVolumeChartInstance.dispose(); |
| | | } |
| | | if (cumulativeSalesAmountChartInstance) { |
| | | cumulativeSalesAmountChartInstance.dispose(); |
| | | } |
| | | |
| | | // ç§»é¤çªå£å¤§å°ååçå¬ |
| | | window.removeEventListener("resize", handleResize); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | /* å¤é¨å®¹å¨ - å æ®æ´ä¸ªè§å£ */ |
| | | .sales-statistics-container { |
| | | position: relative; |
| | | width: 100%; |
| | | /* 页é¢å¨å¸¸è§å¸å±ä¸ï¼æé¡¶æ ï¼é»è®¤åå» 84pxï¼é¿å
å
容被è£å */ |
| | | min-height: calc(100vh - 84px); |
| | | background-color: #f5f7fa; |
| | | overflow: hidden; |
| | | } |
| | | /* å¤é¨å®¹å¨ - å æ®æ´ä¸ªè§å£ */ |
| | | .sales-statistics-container { |
| | | position: relative; |
| | | width: 100%; |
| | | /* 页é¢å¨å¸¸è§å¸å±ä¸ï¼æé¡¶æ ï¼é»è®¤åå» 84pxï¼é¿å
å
容被è£å */ |
| | | min-height: calc(100vh - 84px); |
| | | background-color: #f5f7fa; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* å
é¨å
容åºå - èªéåºå®½åº¦ */ |
| | | .data-dashboard { |
| | | position: relative; |
| | | width: 100%; |
| | | min-height: 100%; |
| | | background-color: #ffffff; |
| | | box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); |
| | | } |
| | | /* å
é¨å
容åºå - èªéåºå®½åº¦ */ |
| | | .data-dashboard { |
| | | position: relative; |
| | | width: 100%; |
| | | min-height: 100%; |
| | | background-color: #ffffff; |
| | | box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .filter-area { |
| | | padding: 20px; |
| | | background-color: #ffffff; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | display: flex; |
| | | gap: 40px; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | } |
| | | .filter-area { |
| | | padding: 20px; |
| | | background-color: #ffffff; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | display: flex; |
| | | gap: 40px; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .filter-section { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | .filter-section { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .filter-label { |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | white-space: nowrap; |
| | | } |
| | | .filter-label { |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .dashboard-content { |
| | | position: relative; |
| | | z-index: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | padding: 20px; |
| | | min-height: 800px; |
| | | overflow: hidden; |
| | | } |
| | | .dashboard-content { |
| | | position: relative; |
| | | z-index: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | padding: 20px; |
| | | min-height: 800px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* è¡å¸å± */ |
| | | .row { |
| | | display: flex; |
| | | gap: 20px; |
| | | align-items: stretch; |
| | | } |
| | | /* è¡å¸å± */ |
| | | .row { |
| | | display: flex; |
| | | gap: 20px; |
| | | align-items: stretch; |
| | | } |
| | | |
| | | /* 第ä¸è¡ï¼4ä¸ªææ å¡ç */ |
| | | .row-1 { |
| | | height: 180px; |
| | | } |
| | | /* 第ä¸è¡ï¼4ä¸ªææ å¡ç */ |
| | | .row-1 { |
| | | height: 180px; |
| | | } |
| | | |
| | | /* 第äºè¡ï¼2个è¶å¿å¾è¡¨ */ |
| | | .row-2 { |
| | | height: 350px; |
| | | } |
| | | /* 第äºè¡ï¼2个è¶å¿å¾è¡¨ */ |
| | | .row-2 { |
| | | height: 350px; |
| | | } |
| | | |
| | | /* 第ä¸è¡ï¼ç´¯è®¡æ°æ®è¶å¿ */ |
| | | .row-3 { |
| | | height: 350px; |
| | | } |
| | | /* 第ä¸è¡ï¼ç´¯è®¡æ°æ®è¶å¿ */ |
| | | .row-3 { |
| | | height: 350px; |
| | | } |
| | | |
| | | /* 第åè¡ï¼è¡¨æ ¼åå¾è¡¨ */ |
| | | .row-4 { |
| | | height: 600px; |
| | | } |
| | | /* 第åè¡ï¼è¡¨æ ¼åå¾è¡¨ */ |
| | | .row-4 { |
| | | height: 600px; |
| | | } |
| | | |
| | | /* å¡çæ ·å¼ */ |
| | | .panel-card { |
| | | background-color: #ffffff; |
| | | border-radius: 8px; |
| | | border: 1px solid #e4e7ed; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | /* å¡çæ ·å¼ */ |
| | | .panel-card { |
| | | background-color: #ffffff; |
| | | border-radius: 8px; |
| | | border: 1px solid #e4e7ed; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .panel-card:hover { |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); |
| | | transform: translateY(-2px); |
| | | } |
| | | .panel-card:hover { |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); |
| | | transform: translateY(-2px); |
| | | } |
| | | |
| | | /* å¡çå¸å± */ |
| | | .card-1 { |
| | | flex: 1; |
| | | } |
| | | /* å¡çå¸å± */ |
| | | .card-1 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-2 { |
| | | flex: 1; |
| | | } |
| | | .card-2 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-3 { |
| | | flex: 1; |
| | | } |
| | | .card-3 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-4 { |
| | | flex: 1; |
| | | } |
| | | .card-4 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-5 { |
| | | flex: 1; |
| | | } |
| | | .card-5 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-6 { |
| | | flex: 1; |
| | | } |
| | | .card-6 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-7 { |
| | | flex: 1; |
| | | } |
| | | .card-7 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-8 { |
| | | flex: 1; |
| | | } |
| | | .card-8 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-9 { |
| | | flex: 1; |
| | | } |
| | | .card-9 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-10 { |
| | | flex: 1; |
| | | } |
| | | .card-10 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-11 { |
| | | flex: 1; |
| | | } |
| | | .card-11 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .panel-title { |
| | | padding: 15px 20px; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | background-color: #fafafa; |
| | | } |
| | | .panel-title { |
| | | padding: 15px 20px; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | background-color: #fafafa; |
| | | } |
| | | |
| | | .chart-container { |
| | | flex: 1; |
| | | padding: 20px; |
| | | } |
| | | .card-1 .panel-title { |
| | | border-left: 4px solid #409eff; |
| | | } |
| | | |
| | | .table-container { |
| | | flex: 1; |
| | | padding: 20px; |
| | | overflow: auto; |
| | | } |
| | | .card-2 .panel-title { |
| | | border-left: 4px solid #67c23a; |
| | | } |
| | | |
| | | .stats-grid { |
| | | flex: 1; |
| | | padding: 15px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | .card-3 .panel-title { |
| | | border-left: 4px solid #e6a23c; |
| | | } |
| | | |
| | | .stat-item { |
| | | background-color: #fafafa; |
| | | border-radius: 8px; |
| | | padding: 15px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 1px solid #e4e7ed; |
| | | min-height: 80px; |
| | | width: 100%; |
| | | } |
| | | .card-4 .panel-title { |
| | | border-left: 4px solid #f56c6c; |
| | | } |
| | | |
| | | .stat-value { |
| | | font-size: 24px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 5px; |
| | | } |
| | | .card-5 .panel-title { |
| | | border-left: 4px solid #409eff; |
| | | } |
| | | |
| | | .stat-unit { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-bottom: 3px; |
| | | } |
| | | .card-6 .panel-title { |
| | | border-left: 4px solid #67c23a; |
| | | } |
| | | |
| | | .stat-change { |
| | | font-size: 12px; |
| | | color: #67c23a; |
| | | } |
| | | .card-7 .panel-title { |
| | | border-left: 4px solid #e6a23c; |
| | | } |
| | | |
| | | /* è¡¨æ ¼æ ·å¼ */ |
| | | :deep(.el-table) { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | } |
| | | .card-8 .panel-title { |
| | | border-left: 4px solid #f56c6c; |
| | | } |
| | | |
| | | :deep(.el-table th) { |
| | | background-color: #fafafa; |
| | | font-weight: 500; |
| | | } |
| | | .card-9 .panel-title { |
| | | border-left: 4px solid #409eff; |
| | | } |
| | | |
| | | :deep(.el-table tr:hover > td) { |
| | | background-color: #ecf5ff; |
| | | } |
| | | .card-10 .panel-title { |
| | | border-left: 4px solid #67c23a; |
| | | } |
| | | |
| | | /* ä¸æéæ©æ¡æ ·å¼ */ |
| | | :deep(.el-select) { |
| | | width: 100%; |
| | | } |
| | | .card-11 .panel-title { |
| | | border-left: 4px solid #e6a23c; |
| | | } |
| | | |
| | | :deep(.el-date-picker) { |
| | | width: 100%; |
| | | } |
| | | .chart-container { |
| | | flex: 1; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .table-container { |
| | | flex: 1; |
| | | padding: 20px; |
| | | overflow: auto; |
| | | } |
| | | |
| | | .stats-grid { |
| | | flex: 1; |
| | | padding: 15px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .stat-item { |
| | | background-color: #fafafa; |
| | | border-radius: 8px; |
| | | padding: 15px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 1px solid #e4e7ed; |
| | | min-height: 80px; |
| | | width: 100%; |
| | | } |
| | | |
| | | .stat-value { |
| | | font-size: 24px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .sales-volume-color { |
| | | color: #409eff; |
| | | text-shadow: 0 2px 4px rgba(64, 158, 255, 0.3); |
| | | } |
| | | |
| | | .sales-amount-color { |
| | | color: #67c23a; |
| | | text-shadow: 0 2px 4px rgba(103, 194, 58, 0.3); |
| | | } |
| | | |
| | | .new-customer-color { |
| | | color: #e6a23c; |
| | | text-shadow: 0 2px 4px rgba(230, 162, 60, 0.3); |
| | | } |
| | | |
| | | .total-customer-color { |
| | | color: #f56c6c; |
| | | text-shadow: 0 2px 4px rgba(245, 108, 108, 0.3); |
| | | } |
| | | |
| | | .stat-unit { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-bottom: 3px; |
| | | } |
| | | |
| | | .stat-change { |
| | | font-size: 12px; |
| | | color: #67c23a; |
| | | } |
| | | |
| | | /* è¡¨æ ¼æ ·å¼ */ |
| | | :deep(.el-table) { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | :deep(.el-table th) { |
| | | background-color: #fafafa; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | :deep(.el-table tr:hover > td) { |
| | | background-color: #ecf5ff; |
| | | } |
| | | |
| | | .data-value { |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | } |
| | | |
| | | /* ä¸æéæ©æ¡æ ·å¼ */ |
| | | :deep(.el-select) { |
| | | width: 100%; |
| | | } |
| | | |
| | | :deep(.el-date-picker) { |
| | | width: 100%; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="dashboard-container"> |
| | | <div class="data-dashboard"> |
| | | <!-- çéåºå --> |
| | | <div class="filter-area"> |
| | | <div class="filter-section"> |
| | | <span class="filter-label">æ¶é´ç»´åº¦ï¼</span> |
| | | <el-radio-group v-model="dateType" |
| | | @change="handleDateTypeChange" |
| | | class="radio-group"> |
| | | <el-radio-button label="month">æåº¦</el-radio-button> |
| | | <el-radio-button label="year">年度</el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | </div> |
| | | <!-- 主è¦å
容åºå --> |
| | | <div class="dashboard-content"> |
| | | <!-- 第ä¸è¡ï¼æ ¸å¿ææ --> |
| | | <div class="row row-1"> |
| | | <div class="panel-card card-1"> |
| | | <div class="panel-title">æ ¸å¿ææ </div> |
| | | <div class="stats-grid"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">å计é</div> |
| | | <div class="stat-value">{{ totalSolidWaste }}</div> |
| | | <div class="stat-unit">å¨</div> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">2022å¹´è³ä»ç´¯è®¡æ¶çº³é</div> |
| | | <div class="stat-value">{{ totalSolidWasteSince2022 }}</div> |
| | | <div class="stat-unit">å¨</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- 第äºè¡ï¼åºåºæ¶çº³è¶å¿ --> |
| | | <div class="row row-2"> |
| | | <div class="panel-card card-2"> |
| | | <div class="panel-title">åºåºæ¶çº³è¶å¿</div> |
| | | <div class="chart-container"> |
| | | <div ref="trendChart" |
| | | style="width: 100%; height: 100%"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- 第ä¸è¡ï¼åºåºç±»ååå¸ --> |
| | | <div class="row row-3"> |
| | | <div class="panel-card card-3"> |
| | | <div class="panel-title">åºåºç±»ååå¸</div> |
| | | <div class="chart-container"> |
| | | <div ref="distributionChart" |
| | | style="width: 100%; height: 100%"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- 第åè¡ï¼æ¶çº³éæç» --> |
| | | <div class="row row-4"> |
| | | <div class="panel-card card-4"> |
| | | <div class="panel-title">æ¶çº³éæç»</div> |
| | | <div class="table-container"> |
| | | <el-table :data="wasteTableData" |
| | | style="width: 100%"> |
| | | <el-table-column prop="time" |
| | | label="æ¶é´" |
| | | width="120" /> |
| | | <el-table-column prop="type" |
| | | label="åºåºç±»å" |
| | | width="120" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getWasteTypeType(scope.row.type)"> |
| | | {{ scope.row.type }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="quantity" |
| | | label="æ¶çº³é" |
| | | align="right"> |
| | | <template #default="scope"> |
| | | <span class="data-value">{{ scope.row.quantity }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="unit" |
| | | label="åä½" |
| | | width="80" /> |
| | | <el-table-column prop="source" |
| | | label="æ¥æº" /> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, onBeforeUnmount, nextTick } from "vue"; |
| | | import * as echarts from "echarts"; |
| | | |
| | | // ç鿡件 |
| | | const dateType = ref("month"); // month æ year |
| | | |
| | | // å¾è¡¨å¼ç¨ |
| | | const trendChart = ref(null); |
| | | const distributionChart = ref(null); |
| | | |
| | | // å¾è¡¨å®ä¾ |
| | | let trendChartInstance = null; |
| | | let distributionChartInstance = null; |
| | | |
| | | // æ¨¡ææ°æ® |
| | | const solidWasteData = ref({ |
| | | month: [ |
| | | { name: "1æ", ç²ç
¤ç°: 200, ç³è: 150, ç³ç°: 100 }, |
| | | { name: "2æ", ç²ç
¤ç°: 220, ç³è: 160, ç³ç°: 110 }, |
| | | { name: "3æ", ç²ç
¤ç°: 190, ç³è: 140, ç³ç°: 95 }, |
| | | { name: "4æ", ç²ç
¤ç°: 230, ç³è: 170, ç³ç°: 115 }, |
| | | { name: "5æ", ç²ç
¤ç°: 240, ç³è: 180, ç³ç°: 120 }, |
| | | { name: "6æ", ç²ç
¤ç°: 225, ç³è: 165, ç³ç°: 112 }, |
| | | ], |
| | | year: [ |
| | | { name: "2022", ç²ç
¤ç°: 2300, ç³è: 1700, ç³ç°: 1100 }, |
| | | { name: "2023", ç²ç
¤ç°: 2500, ç³è: 1800, ç³ç°: 1200 }, |
| | | { name: "2024", ç²ç
¤ç°: 2700, ç³è: 1950, ç³ç°: 1300 }, |
| | | { name: "2025", ç²ç
¤ç°: 2900, ç³è: 2100, ç³ç°: 1400 }, |
| | | ], |
| | | }); |
| | | |
| | | // 计ç®å±æ§ |
| | | const totalSolidWaste = computed(() => { |
| | | const data = solidWasteData.value[dateType.value]; |
| | | if (dateType.value === "month") { |
| | | return data.reduce( |
| | | (sum, item) => sum + item.ç²ç
¤ç° + item.ç³è + item.ç³ç°, |
| | | 0 |
| | | ); |
| | | } else { |
| | | const lastItem = data[data.length - 1]; |
| | | return lastItem.ç²ç
¤ç° + lastItem.ç³è + lastItem.ç³ç°; |
| | | } |
| | | }); |
| | | |
| | | const totalSolidWasteSince2022 = computed(() => { |
| | | const data = solidWasteData.value.year; |
| | | return data.reduce( |
| | | (sum, item) => sum + item.ç²ç
¤ç° + item.ç³è + item.ç³ç°, |
| | | 0 |
| | | ); |
| | | }); |
| | | |
| | | const wasteTableData = computed(() => { |
| | | const data = solidWasteData.value[dateType.value]; |
| | | const result = []; |
| | | |
| | | data.forEach(item => { |
| | | result.push({ |
| | | time: item.name, |
| | | type: "ç²ç
¤ç°", |
| | | quantity: item.ç²ç
¤ç°, |
| | | unit: "å¨", |
| | | source: "ç产è¿ç¨", |
| | | }); |
| | | result.push({ |
| | | time: item.name, |
| | | type: "ç³è", |
| | | quantity: item.ç³è, |
| | | unit: "å¨", |
| | | source: "ç产è¿ç¨", |
| | | }); |
| | | result.push({ |
| | | time: item.name, |
| | | type: "ç³ç°", |
| | | quantity: item.ç³ç°, |
| | | unit: "å¨", |
| | | source: "ç产è¿ç¨", |
| | | }); |
| | | }); |
| | | |
| | | return result; |
| | | }); |
| | | |
| | | // å¾è¡¨é
ç½® |
| | | const trendChartOption = computed(() => { |
| | | const data = solidWasteData.value[dateType.value]; |
| | | return { |
| | | tooltip: { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "shadow", |
| | | }, |
| | | }, |
| | | legend: { |
| | | data: ["ç²ç
¤ç°", "ç³è", "ç³ç°"], |
| | | textStyle: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | grid: { |
| | | left: "3%", |
| | | right: "4%", |
| | | bottom: "3%", |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: data.map(item => item.name), |
| | | axisLabel: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | name: "æ¶çº³é (å¨)", |
| | | axisLabel: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | series: [ |
| | | { |
| | | name: "ç²ç
¤ç°", |
| | | type: "bar", |
| | | data: data.map(item => item.ç²ç
¤ç°), |
| | | itemStyle: { |
| | | color: "#909399", |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç³è", |
| | | type: "bar", |
| | | data: data.map(item => item.ç³è), |
| | | itemStyle: { |
| | | color: "#E6A23C", |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç³ç°", |
| | | type: "bar", |
| | | data: data.map(item => item.ç³ç°), |
| | | itemStyle: { |
| | | color: "#F56C6C", |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | const distributionChartOption = computed(() => { |
| | | const data = solidWasteData.value[dateType.value]; |
| | | const lastItem = data[data.length - 1]; |
| | | |
| | | return { |
| | | tooltip: { |
| | | trigger: "item", |
| | | formatter: "{a} <br/>{b}: {c} ({d}%)", |
| | | }, |
| | | legend: { |
| | | orient: "vertical", |
| | | left: "left", |
| | | textStyle: { |
| | | color: "#333", |
| | | }, |
| | | }, |
| | | series: [ |
| | | { |
| | | name: "åºåºç±»å", |
| | | type: "pie", |
| | | radius: "60%", |
| | | center: ["50%", "50%"], |
| | | data: [ |
| | | { value: lastItem.ç²ç
¤ç°, name: "ç²ç
¤ç°" }, |
| | | { value: lastItem.ç³è, name: "ç³è" }, |
| | | { value: lastItem.ç³ç°, name: "ç³ç°" }, |
| | | ], |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: "rgba(0, 0, 0, 0.5)", |
| | | }, |
| | | }, |
| | | itemStyle: { |
| | | color: function (params) { |
| | | const colors = ["#909399", "#E6A23C", "#F56C6C"]; |
| | | return colors[params.dataIndex]; |
| | | }, |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | // äºä»¶å¤ç |
| | | const handleDateTypeChange = () => { |
| | | updateCharts(); |
| | | }; |
| | | |
| | | // åå§åå¾è¡¨ |
| | | const initCharts = () => { |
| | | if (trendChart.value) { |
| | | trendChartInstance = echarts.init(trendChart.value); |
| | | trendChartInstance.setOption(trendChartOption.value); |
| | | } |
| | | |
| | | if (distributionChart.value) { |
| | | distributionChartInstance = echarts.init(distributionChart.value); |
| | | distributionChartInstance.setOption(distributionChartOption.value); |
| | | } |
| | | }; |
| | | |
| | | // æ´æ°å¾è¡¨ |
| | | const updateCharts = () => { |
| | | if (trendChartInstance) { |
| | | trendChartInstance.setOption(trendChartOption.value); |
| | | } |
| | | |
| | | if (distributionChartInstance) { |
| | | distributionChartInstance.setOption(distributionChartOption.value); |
| | | } |
| | | }; |
| | | |
| | | // è°æ´å¾è¡¨å¤§å° |
| | | const resizeCharts = () => { |
| | | trendChartInstance?.resize(); |
| | | distributionChartInstance?.resize(); |
| | | }; |
| | | |
| | | // çªå£å¤§å°ååå¤ç |
| | | const handleResize = () => { |
| | | // å»¶è¿æ§è¡ï¼ç¡®ä¿DOMæ´æ°å®æ |
| | | setTimeout(() => { |
| | | resizeCharts(); |
| | | }, 100); |
| | | }; |
| | | |
| | | // è·ååºåºç±»åæ ç¾ç±»å |
| | | const getWasteTypeType = type => { |
| | | const typeMap = { |
| | | ç²ç
¤ç°: "info", |
| | | ç³è: "warning", |
| | | ç³ç°: "danger", |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }; |
| | | |
| | | // çå½å¨æé©å |
| | | onMounted(() => { |
| | | // 使ç¨nextTickç¡®ä¿DOMå®å
¨æ¸²æåååå§å |
| | | nextTick(() => { |
| | | // åå§åå¾è¡¨ |
| | | initCharts(); |
| | | }); |
| | | |
| | | window.addEventListener("resize", handleResize); |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | window.removeEventListener("resize", handleResize); |
| | | |
| | | // 鿝å¾è¡¨å®ä¾ |
| | | trendChartInstance?.dispose(); |
| | | distributionChartInstance?.dispose(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | /* å¤é¨å®¹å¨ - å æ®æ´ä¸ªè§å£ */ |
| | | .dashboard-container { |
| | | position: relative; |
| | | width: 100%; |
| | | /* 页é¢å¨å¸¸è§å¸å±ä¸ï¼æé¡¶æ ï¼é»è®¤åå» 84pxï¼é¿å
å
容被è£å */ |
| | | min-height: calc(100vh - 84px); |
| | | background-color: #f5f7fa; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* å
é¨å
容åºå - èªéåºå®½åº¦ */ |
| | | .data-dashboard { |
| | | position: relative; |
| | | width: 100%; |
| | | min-height: 100%; |
| | | background-color: #ffffff; |
| | | box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .filter-area { |
| | | padding: 20px; |
| | | background-color: #ffffff; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | display: flex; |
| | | gap: 40px; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .filter-section { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .filter-label { |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .radio-group { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | /* æé®æ ·å¼ */ |
| | | :deep(.el-radio-button__inner) { |
| | | border-radius: 4px; |
| | | padding: 8px 20px; |
| | | font-size: 14px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | :deep(.el-radio-button__orig-radio:checked + .el-radio-button__inner) { |
| | | background-color: #409eff; |
| | | border-color: #409eff; |
| | | color: #ffffff; |
| | | box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3); |
| | | } |
| | | |
| | | :deep(.el-radio-button__inner:hover) { |
| | | color: #409eff; |
| | | border-color: #c6e2ff; |
| | | } |
| | | |
| | | :deep(.el-radio-button:first-child .el-radio-button__inner) { |
| | | border-radius: 4px 0 0 4px; |
| | | } |
| | | |
| | | :deep(.el-radio-button:last-child .el-radio-button__inner) { |
| | | border-radius: 0 4px 4px 0; |
| | | } |
| | | |
| | | .dashboard-content { |
| | | position: relative; |
| | | z-index: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | padding: 20px; |
| | | min-height: 800px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* è¡å¸å± */ |
| | | .row { |
| | | display: flex; |
| | | gap: 20px; |
| | | align-items: stretch; |
| | | } |
| | | |
| | | /* 第ä¸è¡ï¼æ ¸å¿ææ */ |
| | | .row-1 { |
| | | height: 250px; |
| | | } |
| | | |
| | | /* 第äºè¡ï¼åºåºæ¶çº³è¶å¿ */ |
| | | .row-2 { |
| | | height: 300px; |
| | | } |
| | | |
| | | /* 第ä¸è¡ï¼åºåºç±»ååå¸ */ |
| | | .row-3 { |
| | | height: 300px; |
| | | } |
| | | |
| | | /* 第åè¡ï¼æ¶çº³éæç» */ |
| | | .row-4 { |
| | | min-height: 250px; |
| | | } |
| | | |
| | | /* å¡çæ ·å¼ */ |
| | | .panel-card { |
| | | background-color: #ffffff; |
| | | border-radius: 8px; |
| | | border: 1px solid #e4e7ed; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .panel-card:hover { |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); |
| | | transform: translateY(-2px); |
| | | } |
| | | |
| | | /* å¡çå¸å± */ |
| | | .card-1 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-2 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-3 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-4 { |
| | | flex: 1; |
| | | } |
| | | |
| | | .panel-title { |
| | | padding: 15px 20px; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | background-color: #fafafa; |
| | | } |
| | | |
| | | .card-1 .panel-title { |
| | | border-left: 4px solid #409eff; |
| | | } |
| | | |
| | | .card-2 .panel-title { |
| | | border-left: 4px solid #f56c6c; |
| | | } |
| | | |
| | | .card-3 .panel-title { |
| | | border-left: 4px solid #e6a23c; |
| | | } |
| | | |
| | | .card-4 .panel-title { |
| | | border-left: 4px solid #67c23a; |
| | | } |
| | | |
| | | .chart-container { |
| | | flex: 1; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .table-container { |
| | | flex: 1; |
| | | padding: 20px; |
| | | overflow: auto; |
| | | } |
| | | |
| | | .stats-grid { |
| | | flex: 1; |
| | | padding: 15px; |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | gap: 15px; |
| | | } |
| | | |
| | | .stat-item { |
| | | background-color: #ffffff; |
| | | border-radius: 12px; |
| | | padding: 25px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 2px solid #e4e7ed; |
| | | min-height: 120px; |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .stat-item:hover { |
| | | box-shadow: 0 8px 24px rgba(64, 158, 255, 0.2); |
| | | border-color: #409eff; |
| | | transform: translateY(-4px); |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .stat-value { |
| | | font-size: 32px; |
| | | font-weight: 700; |
| | | color: #409eff; |
| | | margin-bottom: 5px; |
| | | text-shadow: 0 2px 4px rgba(64, 158, 255, 0.3); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .stat-value:hover { |
| | | transform: scale(1.05); |
| | | } |
| | | |
| | | .stat-unit { |
| | | font-size: 12px; |
| | | font-weight: 500; |
| | | color: #606266; |
| | | } |
| | | |
| | | /* è¡¨æ ¼æ ·å¼ */ |
| | | :deep(.el-table) { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | :deep(.el-table th) { |
| | | background-color: #fafafa; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | :deep(.el-table tr:hover > td) { |
| | | background-color: #ecf5ff; |
| | | } |
| | | |
| | | .data-value { |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | } |
| | | </style> |