From fc36e9f71f1238e0c4d65ebb897bb54a68204463 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期四, 02 四月 2026 14:50:11 +0800
Subject: [PATCH] 生产报表联调,并优化页面
---
src/views/productionManagement/largeScreen/index.vue | 324 ++++++++++++++++++++++++++++++---------------
src/api/productionManagement/productionStatistic.js | 50 +++++++
2 files changed, 263 insertions(+), 111 deletions(-)
diff --git a/src/api/productionManagement/productionStatistic.js b/src/api/productionManagement/productionStatistic.js
new file mode 100644
index 0000000..7722d71
--- /dev/null
+++ b/src/api/productionManagement/productionStatistic.js
@@ -0,0 +1,50 @@
+import request from "@/utils/request.js";
+
+// 鑾峰彇鐢熶骇宸ュ崟鏁伴噺缁熻鏁版嵁
+export function findProductWorkOrderCountStatistics() {
+ return request({
+ url: "/productStatistics/workOrderCount",
+ method: "get",
+ });
+}
+
+// 鑾峰彇鐢熶骇璐ㄩ噺缁熻鏁版嵁
+export function findProductQualityStatistics() {
+ return request({
+ url: "/productStatistics/qualityStatistics",
+ method: "get",
+ });
+}
+
+// 鑾峰彇鐢熶骇鏁伴噺缁熻鏁版嵁
+export function findProductProductionStatistics() {
+ return request({
+ url: "/productStatistics/productionStatistics",
+ method: "get",
+ });
+}
+
+// 鑾峰彇浜у搧浜у嚭鍒嗘瀽鏁版嵁
+export function findProductOutputCategoryPieData() {
+ return request({
+ url: "/productStatistics/productOutputCategoryPieData",
+ method: "get",
+ });
+}
+
+// 鑾峰彇涓嶈壇鍘熷洜鍒嗘瀽缁熻鏁版嵁
+export function findProductDefectReasonAnalysis() {
+ return request({
+ url: "/productStatistics/defectReasonAnalysis",
+ method: "get",
+ });
+}
+
+// 鑾峰彇宸ュ簭涓嶈壇鐜囧垎鏋�
+export function findProductProcessDefectRateAnalysis(params) {
+ return request({
+ url: "/productStatistics/processDefectRateAnalysis",
+ method: "get",
+ params
+ });
+}
\ No newline at end of file
diff --git a/src/views/productionManagement/largeScreen/index.vue b/src/views/productionManagement/largeScreen/index.vue
index eece56d..4815bc5 100644
--- a/src/views/productionManagement/largeScreen/index.vue
+++ b/src/views/productionManagement/largeScreen/index.vue
@@ -146,8 +146,8 @@
/>
</div>
<div class="category-cards">
- <div v-for="(it, idx) in categoryPieData" :key="it.name" class="category-card">
- <div class="category-name">浜у搧澶х被{{ idx + 1 }}</div>
+ <div v-for="(it, idx) in categoryPieData" :key="idx" class="category-card">
+ <div class="category-name">{{ it.name }}</div>
<div class="category-val">{{ it.value }}</div>
</div>
</div>
@@ -205,22 +205,26 @@
<div class="bottom-row">
<div class="panel panel-full">
<div class="panel-header">
- <div class="panel-title">宸ュ簭涓嶈壇鍘熷洜鍒嗘瀽</div>
+ <div class="panel-title">宸ュ簭涓嶈壇鐜囧垎鏋�</div>
<div class="panel-actions">
- <div class="panel-action">骞翠唤</div>
+ <div class="panel-action">绛涢�夋椂闂磋寖鍥�</div>
<el-date-picker
- v-model="currentYear"
- type="year"
- format="YYYY骞�"
- value-format="YYYY"
- :clearable="false"
- class="panel-year-picker"
+ v-model="dateRange"
+ type="daterange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ :clearable="true"
+ class="panel-date-picker"
size="small"
+ @change="handleDateChange"
/>
</div>
</div>
<div class="panel-body">
- <div class="bottom-chart-wrap">
+ <div class="bottom-chart-wrap" v-loading="isLoading" element-loading-text="鍔犺浇涓�...">
<div class="chart-unit">鍗曚綅锛�%</div>
<Echarts
:chartStyle="{ width: '100%', height: '260px' }"
@@ -241,7 +245,16 @@
</template>
<script setup>
-import { computed, ref } from 'vue'
+import {
+ findProductQualityStatistics,
+ findProductWorkOrderCountStatistics,
+ findProductProductionStatistics,
+ findProductOutputCategoryPieData,
+ findProductDefectReasonAnalysis,
+ findProductProcessDefectRateAnalysis
+} from '@/api/productionManagement/productionStatistic.js'
+
+import {computed, onMounted, ref} from 'vue'
import * as echarts from 'echarts'
import Echarts from '@/components/Echarts/echarts.vue'
import { ElSelect, ElOption, ElDatePicker } from 'element-plus'
@@ -253,24 +266,20 @@
}
const kpi = ref({
- workOrderTotal: 378,
- workOrderDoing: 108,
- workOrderDone: 238,
- passRate: 98,
- ngRate: 8,
- scrapTotal: 38,
- productionAmount: 989878,
- productionAmountTrend: 16,
- productionQty: 19878,
- productionQtyTrend: 16,
- productionCost: 69878,
- productionCostTrend: -16,
- supplierCount: 48,
- supplierCountTrend: 16,
- totalOutput: 1000,
+ workOrderTotal: 0,
+ workOrderDoing: 0,
+ workOrderDone: 0,
+ passRate: 0,
+ ngRate: 0,
+ scrapTotal: 0,
+ productionAmount: 0, // 鎬荤敓浜ф�婚噺
+ productionAmountTrend: 0, // 鎬荤敓浜ф�婚噺杈冧笂鏈堣秼鍔�
+ productionCost: 0, // 鐢熶骇鎬绘秷鑰�
+ productionCostTrend: 0, // 鐢熶骇鎬绘秷鑰楄緝涓婃湀瓒嬪娍
+ supplierCount: 0, // 鎬讳緵搴斿晢
+ supplierCountTrend: 0, // 鎬讳緵搴斿晢杈冧笂鏈堣秼鍔�
})
-
-const currentYear = ref('2025')
+const dateRange = ref([])
function formatMoney(n) {
const num = Number(n || 0)
@@ -280,6 +289,10 @@
const axisTooltip = {
trigger: 'axis',
axisPointer: { type: 'shadow' },
+ confine: true, // 闄愬埗鍦ㄥ浘琛ㄥ尯鍩熷唴鏄剧ず
+ position: 'top', // 鍥哄畾鏄剧ず鍦ㄩ《閮�
+ enterable: true, // 鍏佽榧犳爣杩涘叆鎻愮ず妗嗗尯鍩�
+ extraCssText: 'max-height: 200px; overflow-y: auto; padding: 10px;', // 娣诲姞鏈�澶ч珮搴﹀拰婊氬姩鏉�
formatter: (params) => {
const first = params?.[0]
const x = first?.axisValueLabel || ''
@@ -296,13 +309,7 @@
// 涓儴宸︿晶锛氶ゼ鐘跺浘锛堟寜浜у搧澶х被缁熻浜у嚭鏁伴噺锛�
const categoryPieColors = ['#2D5BFF', '#4E8AFF', '#00A4ED', '#26C6DA', '#7C3AED', '#F59E0B', '#EF4444', '#10B981']
-const categoryPieData = ref([
- { name: '浜у搧澶х被1', value: 600 },
- { name: '浜у搧澶х被2', value: 200 },
- { name: '浜у搧澶х被3', value: 200 },
- { name: '浜у搧澶х被4', value: 200 },
- { name: '浜у搧澶х被5', value: 200 },
-])
+const categoryPieData = ref([])
const categoryPieLegend = computed(() => {
// 璁捐鍥句腑楗煎浘鏈韩鏄剧ず鍗犳瘮鏍囩锛屼笉棰濆灞曠ず鍥句緥
@@ -347,13 +354,9 @@
// 涓儴鍙充晶锛氱幆褰㈤ゼ鍥�
const ngColors = ['#2D5BFF', '#26C6DA', '#F59E0B', '#7C3AED', '#60A5FA', '#10B981']
-const ngReasonData = ref([
- { name: '涓嶈壇鍘熷洜1', value: 300 },
- { name: '涓嶈壇鍘熷洜3', value: 200 },
- { name: '涓嶈壇鍘熷洜4', value: 100 },
- { name: '涓嶈壇鍘熷洜5', value: 100 },
- { name: '涓嶈壇鍘熷洜6', value: 100 },
-])
+const ngReasonData = ref([])
+const ngRateData = ref([])
+const isLoading = ref(false)
const ngTotal = computed(() => ngReasonData.value.reduce((s, it) => s + Number(it.value || 0), 0))
const ngRows = computed(() => {
@@ -385,40 +388,59 @@
// 搴曢儴锛氭姌绾垮浘锛堝勾浠借仈鍔ㄧ殑姣忎釜宸ュ簭涓嶈壇鐜囷級
const lineGrid = { left: '4%', right: '4%', top: 60, bottom: 30, containLabel: true }
const lineLegend = computed(() => {
+ const processes = chartData.value.processes || []
+ const processNames = processes.map(p => p.name)
return {
show: true,
top: 8,
+ type: 'scroll',
+ orient: 'horizontal',
textStyle: { color: '#666' },
- data: ['骞冲潎涓嶈壇鐜�', '宸ュ簭A', '宸ュ簭B', '宸ュ簭C'],
+ data: ['骞冲潎涓嶈壇鐜�', ...processNames],
+ pageIconSize: 10,
+ pageTextStyle: { color: '#666' },
+ pageButtonItemGap: 5,
+ pageButtonGap: 10
}
})
-const chartDataByYear = {
- '2023': {
- x: ['2023/02/21', '2023/03/02', '2023/03/13', '2023/03/24', '2023/04/04', '2023/04/15', '2023/04/28'],
- bar: [30, 32, 35, 40, 38, 42, 39],
- lineA: [45, 48, 52, 60, 55, 62, 58],
- lineB: [30, 32, 38, 45, 40, 48, 42],
- lineC: [20, 24, 30, 35, 32, 38, 33],
- },
- '2024': {
- x: ['2024/02/21', '2024/03/02', '2024/03/13', '2024/03/24', '2024/04/04', '2024/04/15', '2024/04/28'],
- bar: [28, 30, 33, 37, 35, 39, 36],
- lineA: [42, 46, 50, 58, 53, 60, 56],
- lineB: [28, 31, 36, 43, 38, 46, 40],
- lineC: [18, 21, 27, 33, 29, 35, 30],
- },
- '2025': {
- x: ['2025/02/21', '2025/03/02', '2025/03/13', '2025/03/24', '2025/04/04', '2025/04/15', '2025/04/28'],
- bar: [32, 34, 37, 42, 40, 45, 41],
- lineA: [45, 49, 54, 61, 57, 64, 60],
- lineB: [31, 33, 39, 47, 41, 50, 44],
- lineC: [21, 25, 31, 37, 34, 40, 35],
- },
-}
-
const chartData = computed(() => {
- return chartDataByYear[currentYear.value] || chartDataByYear['2025']
+ const data = ngRateData.value
+ if (!data || data.length === 0) {
+ return {
+ x: [],
+ bar: [],
+ processes: []
+ }
+ }
+
+ // Extract data from the API response
+ const x = data.map(item => item.date)
+ const bar = data.map(item => item.averageDefectRate) // Convert to percentage
+
+ // Extract process names and their data
+ const processNames = new Set()
+ data.forEach(item => {
+ item.processes.forEach(process => {
+ Object.keys(process).forEach(name => processNames.add(name))
+ })
+ })
+
+ const processes = Array.from(processNames).map(name => {
+ return {
+ name,
+ data: data.map(item => {
+ const process = item.processes.find(p => p[name] !== undefined)
+ return process ? process[name] : 0 // Convert to percentage
+ })
+ }
+ })
+
+ return {
+ x,
+ bar,
+ processes
+ }
})
const lineXAxis = computed(() => {
@@ -443,48 +465,118 @@
]
})
-const lineSeries = computed(() => [
- {
- name: '骞冲潎涓嶈壇鐜�',
- type: 'bar',
- barWidth: 18,
- data: chartData.value.bar,
- itemStyle: {
- color: 'rgba(59, 130, 246, 0.15)',
- borderRadius: [4, 4, 0, 0],
+const lineSeries = computed(() => {
+ const series = [
+ {
+ name: '骞冲潎涓嶈壇鐜�',
+ type: 'bar',
+ barWidth: 18,
+ data: chartData.value.bar,
+ itemStyle: {
+ color: 'rgba(59, 130, 246, 0.15)',
+ borderRadius: [4, 4, 0, 0],
+ },
},
- },
- {
- name: '宸ュ簭A',
- type: 'line',
- smooth: true,
- data: chartData.value.lineA,
- symbol: 'circle',
- symbolSize: 6,
- lineStyle: { width: 2, color: '#3b82f6' },
- itemStyle: { color: '#3b82f6' },
- },
- {
- name: '宸ュ簭B',
- type: 'line',
- smooth: true,
- data: chartData.value.lineB,
- symbol: 'circle',
- symbolSize: 6,
- lineStyle: { width: 2, color: '#f59e0b' },
- itemStyle: { color: '#f59e0b' },
- },
- {
- name: '宸ュ簭C',
- type: 'line',
- smooth: true,
- data: chartData.value.lineC,
- symbol: 'circle',
- symbolSize: 6,
- lineStyle: { width: 2, color: '#10b981' },
- itemStyle: { color: '#10b981' },
- },
-])
+ ]
+
+ // Add process lines with different colors
+ const colors = ['#3b82f6', '#f59e0b', '#10b981', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316']
+ chartData.value.processes.forEach((process, index) => {
+ series.push({
+ name: process.name,
+ type: 'line',
+ smooth: true,
+ data: process.data,
+ symbol: 'circle',
+ symbolSize: 6,
+ lineStyle: { width: 2, color: colors[index % colors.length] },
+ itemStyle: { color: colors[index % colors.length] },
+ })
+ })
+
+ return series
+})
+
+// 鑾峰彇鐢熶骇宸ュ崟鏁伴噺缁熻鏁版嵁
+const fetchProductWorkOrderCountStatistic = () => {
+ findProductWorkOrderCountStatistics().then((res) => {
+ const data = res.data
+ kpi.value.workOrderTotal = data.totalCount
+ kpi.value.workOrderDoing = data.inProgressCount
+ kpi.value.workOrderDone = data.completedCount
+ })
+}
+
+// 鑾峰彇鐢熶骇璐ㄩ噺缁熻鏁版嵁
+const fetchProductQualityStatistics = () => {
+ findProductQualityStatistics().then((res) => {
+ const data = res.data
+ kpi.value.passRate = data.qualifiedRate
+ kpi.value.ngRate = data.defectRate
+ kpi.value.scrapTotal = data.scrapCount
+ })
+}
+
+// 鑾峰彇鐢熶骇鏁伴噺缁熻鏁版嵁
+const fetchProductProductionStatistics = () => {
+ findProductProductionStatistics().then((res) => {
+ const data = res.data
+ kpi.value.productionAmount = data.productionOutput
+ kpi.value.productionAmountTrend = data.productionOutputMonthlyChange
+ kpi.value.productionCost = data.productionConsumption
+ kpi.value.productionCostTrend = data.productionConsumptionMonthlyChange
+ kpi.value.supplierCount = data.supplierCount
+ kpi.value.supplierCountTrend = data.supplierCountMonthlyChange
+ })
+}
+
+// 鑾峰彇浜у搧浜у嚭鍒嗘瀽鏁版嵁
+const fetchProductOutputCategoryPieData = () => {
+ findProductOutputCategoryPieData().then((res) => {
+ categoryPieData.value = res.data
+ })
+}
+// 鑾峰彇涓嶈壇鍘熷洜鍒嗘瀽缁熻鏁版嵁
+const fetchProductDefectReasonAnalysis = () => {
+ findProductDefectReasonAnalysis().then((res) => {
+ ngReasonData.value = res.data
+ })
+}
+
+// 鑾峰彇宸ュ簭涓嶈壇鐜囧垎鏋�
+const fetchProductProcessDefectRateAnalysis = (dateRange) => {
+ isLoading.value = true
+ const params = dateRange ? {
+ startDate: dateRange[0],
+ endDate: dateRange[1]
+ } : {}
+ findProductProcessDefectRateAnalysis(params).then((res) => {
+ ngRateData.value = res.data
+ }).finally(() => {
+ isLoading.value = false
+ })
+}
+
+// 澶勭悊鏃ユ湡閫夋嫨鍙樺寲
+const handleDateChange = (range) => {
+ fetchProductProcessDefectRateAnalysis(range)
+}
+
+onMounted(() => {
+ // 鍒濆鍖栨椂鑾峰彇鐢熶骇宸ュ崟鏁伴噺缁熻鏁版嵁
+ fetchProductWorkOrderCountStatistic()
+ // 鍒濆鍖栨椂鑾峰彇鐢熶骇璐ㄩ噺缁熻鏁版嵁
+ fetchProductQualityStatistics()
+ // 鍒濆鍖栨椂鑾峰彇鐢熶骇鏁伴噺缁熻鏁版嵁
+ fetchProductProductionStatistics()
+ // 鍒濆鍖栨椂鑾峰彇浜у搧浜у嚭鍒嗘瀽鏁版嵁
+ fetchProductOutputCategoryPieData()
+ // 鍒濆鍖栨椂鑾峰彇涓嶈壇鍘熷洜鍒嗘瀽缁熻鏁版嵁
+ fetchProductDefectReasonAnalysis()
+ // 鍒濆鍖栨椂鑾峰彇宸ュ簭涓嶈壇鐜囧垎鏋�
+ fetchProductProcessDefectRateAnalysis()
+})
+
</script>
<style scoped>
@@ -858,6 +950,16 @@
white-space: nowrap;
}
+.panel-date-picker {
+ width: 240px;
+ margin-left: 6px;
+}
+
+.panel-date-picker {
+ width: 240px;
+ margin-left: 6px;
+}
+
.panel-title {
font-weight: 800;
color: #030303;
@@ -1086,4 +1188,4 @@
grid-template-columns: 1fr;
}
}
-</style>
+</style>
\ No newline at end of file
--
Gitblit v1.9.3