更新精工车间生产计划和车间分配页面

This commit is contained in:
Developer
2026-06-02 11:27:43 +08:00
parent bed9f863a0
commit c2b6fb1fbf
2 changed files with 441 additions and 14 deletions

View File

@@ -12,14 +12,18 @@
<el-input v-model="orderNumberTypeValue" style="width:200px;margin-left: 10px" placeholder="订单编号" size="small" clearable/>
<el-input v-model="partNameValue" style="width:150px;margin-left: 10px" placeholder="零件名称或图号" size="small" clearable/>
<el-input v-model="productNameValue" style="width:150px;margin-left: 10px" placeholder="产品名称" size="small" clearable/>
<el-select v-model="statusValue" style="width:100px;margin-left: 10px" placeholder="状态" size="small">
<el-select v-model="statusValue" style="width:100px;margin-left: 10px" placeholder="状态" size="small" clearable>
<el-option label="派工中" value="派工中"/>
<el-option label="生产中" value="生产中"/>
<el-option label="已完成" value="已完成"/>
</el-select>
<el-button icon="el-icon-search" type="primary" plain size="small" style="margin-left: 10px" @click="searchTable1()">查询</el-button>
<el-button icon="el-icon-data-analysis" type="warning" plain size="small" style="margin-left: 10px" @click="openDashboard()">可视化看板</el-button>
<span v-if="monthCompletionRate !== ''" style="margin-left: 20px; font-size: 14px; color: #606266">
本月完成率<el-tag :type="monthCompletionRateTag" size="medium" effect="dark">{{ monthCompletionRate }}</el-tag>
</span>
</div>
<el-table :data="tableData2" height="750px" style="width: 2200px" size="mini" highlight-current-row border>
<el-table :data="filteredTableData" height="750px" style="width: 2200px" size="mini" highlight-current-row border>
<el-table-column width="50px" align="center" label="序号" show-overflow-tooltip fixed>
<template slot-scope="scope">
{{ scope.$index + 1 }}
@@ -144,6 +148,72 @@
</el-table-column>
</el-table>
</el-card>
<el-dialog title="生产计划可视化看板" :visible.sync="dashboardVisible" width="1200px" top="30px" :close-on-click-modal="false" @close="closeDashboard">
<el-row :gutter="10" style="margin-bottom: 15px">
<el-col :span="6">
<el-card shadow="hover" style="text-align: center; padding: 10px 0; cursor: pointer" @click.native="cardViewMode = cardViewMode === 'status' ? 'orderType' : 'status'">
<div v-if="cardViewMode === 'status'" style="color: #909399; font-size: 12px">订单总数</div>
<div v-else style="color: #909399; font-size: 12px">订单总数</div>
<div v-if="cardViewMode === 'status'" style="color: #303133; font-size: 24px; font-weight: bold">{{ dashboardStats.total }}</div>
<div v-else style="color: #303133; font-size: 24px; font-weight: bold">{{ dashboardStats.total }}</div>
<div style="color: #C0C4CC; font-size: 10px; margin-top: 4px">点击切换</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" style="text-align: center; padding: 10px 0; cursor: pointer" @click.native="cardViewMode = cardViewMode === 'status' ? 'orderType' : 'status'">
<template v-if="cardViewMode === 'status'">
<div style="color: #909399; font-size: 12px">派工中</div>
<div style="color: #409EFF; font-size: 24px; font-weight: bold">{{ dashboardStats.pending }}</div>
</template>
<template v-else>
<div style="color: #909399; font-size: 12px">销售 / 补货派工中</div>
<div style="font-size: 24px; font-weight: bold"><span style="color: #409EFF">{{ orderTypeStats.salesPending }}</span><span style="color: #C0C4CC; font-size: 14px"> / </span><span style="color: #E6A23C">{{ orderTypeStats.restockPending }}</span></div>
</template>
<div style="color: #C0C4CC; font-size: 10px; margin-top: 4px">点击切换</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" style="text-align: center; padding: 10px 0; cursor: pointer" @click.native="cardViewMode = cardViewMode === 'status' ? 'orderType' : 'status'">
<template v-if="cardViewMode === 'status'">
<div style="color: #909399; font-size: 12px">生产中</div>
<div style="color: #67C23A; font-size: 24px; font-weight: bold">{{ dashboardStats.producing }}</div>
</template>
<template v-else>
<div style="color: #909399; font-size: 12px">销售 / 补货生产中</div>
<div style="font-size: 24px; font-weight: bold"><span style="color: #409EFF">{{ orderTypeStats.salesProducing }}</span><span style="color: #C0C4CC; font-size: 14px"> / </span><span style="color: #E6A23C">{{ orderTypeStats.restockProducing }}</span></div>
</template>
<div style="color: #C0C4CC; font-size: 10px; margin-top: 4px">点击切换</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" style="text-align: center; padding: 10px 0; cursor: pointer" @click.native="cardViewMode = cardViewMode === 'status' ? 'orderType' : 'status'">
<template v-if="cardViewMode === 'status'">
<div style="color: #909399; font-size: 12px">已完成</div>
<div style="color: #909399; font-size: 24px; font-weight: bold">{{ dashboardStats.completed }}</div>
</template>
<template v-else>
<div style="color: #909399; font-size: 12px">销售 / 补货已完成</div>
<div style="font-size: 24px; font-weight: bold"><span style="color: #409EFF">{{ orderTypeStats.salesCompleted }}</span><span style="color: #C0C4CC; font-size: 14px"> / </span><span style="color: #E6A23C">{{ orderTypeStats.restockCompleted }}</span></div>
</template>
<div style="color: #C0C4CC; font-size: 10px; margin-top: 4px">点击切换</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="8">
<div id="statusPieChart" style="width: 100%; height: 400px"/>
</el-col>
<el-col :span="16">
<div id="completionBarChart" style="width: 100%; height: 400px"/>
</el-col>
</el-row>
<el-row :gutter="10" style="margin-top: 15px">
<el-col :span="24">
<div id="processStackChart" style="width: 100%; height: 350px"/>
</el-col>
</el-row>
</el-dialog>
</div>
</template>
@@ -154,29 +224,97 @@ export default {
data() {
return {
monthValue: '',
orderNumberTypeValue: '', // 订单编号
partNameValue: '', // 零件名称或图号
productNameValue: '', // 产品名称
statusValue: '', // 状态
orderNumberTypeValue: '',
partNameValue: '',
productNameValue: '',
statusValue: '',
loading: false,
tableData2: []
tableData2: [],
dashboardVisible: false,
statusPieChart: null,
completionBarChart: null,
processStackChart: null,
pieViewMode: 'status',
cardViewMode: 'status'
}
},
computed: {
...mapGetters([
'id',
'token'
])
]),
monthCompletionRate() {
if (!this.tableData2 || this.tableData2.length === 0) return ''
var completed = 0
this.tableData2.forEach(function(row) {
if (row.状态 === '已完成') completed++
})
var avg = Math.round(completed / this.tableData2.length * 100)
return avg + '%'
},
filteredTableData() {
if (!this.statusValue) return this.tableData2
var val = this.statusValue
return this.tableData2.filter(function(row) {
return row.状态 === val
})
},
monthCompletionRateTag() {
var rate = parseInt(this.monthCompletionRate) || 0
if (rate === 100) return 'info'
if (rate >= 50) return 'success'
return 'warning'
},
dashboardStats() {
var total = this.tableData2.length
var pending = 0
var producing = 0
var completed = 0
this.tableData2.forEach(function(row) {
if (row.状态 === '已完成') completed++
else if (row.状态 === '生产中') producing++
else pending++
})
return { total: total, pending: pending, producing: producing, completed: completed }
},
orderTypeStats() {
var salesTotal = 0
var restockTotal = 0
var salesPending = 0
var restockPending = 0
var salesProducing = 0
var restockProducing = 0
var salesCompleted = 0
var restockCompleted = 0
this.tableData2.forEach(function(row) {
var isRestock = row.收货人 === null
if (isRestock) {
restockTotal++
if (row.状态 === '已完成') restockCompleted++
else if (row.状态 === '生产中') restockProducing++
else restockPending++
} else {
salesTotal++
if (row.状态 === '已完成') salesCompleted++
else if (row.状态 === '生产中') salesProducing++
else salesPending++
}
})
return {
salesTotal: salesTotal, restockTotal: restockTotal,
salesPending: salesPending, restockPending: restockPending,
salesProducing: salesProducing, restockProducing: restockProducing,
salesCompleted: salesCompleted, restockCompleted: restockCompleted
}
}
},
// updated() {
// this.$nextTick(() => {
// this.$refs['table'].doLayout()
// })
// },
created() {
this.getNowMonth()
this.searchTable1()
},
beforeDestroy() {
this.disposeCharts()
},
methods: {
getNowMonth() {
const nowDate = new Date()
@@ -285,6 +423,295 @@ export default {
delete group._completedProcesses
})
return Object.values(grouped)
},
openDashboard() {
if (this.tableData2.length === 0) {
this.$message.warning('暂无数据,请先查询')
return
}
this.dashboardVisible = true
this.$nextTick(function() {
this.drawStatusPieChart()
this.drawCompletionBarChart()
this.drawProcessStackChart()
})
},
closeDashboard() {
this.dashboardVisible = false
},
disposeCharts() {
if (this.statusPieChart) {
this.statusPieChart.dispose()
this.statusPieChart = null
}
if (this.completionBarChart) {
this.completionBarChart.dispose()
this.completionBarChart = null
}
if (this.processStackChart) {
this.processStackChart.dispose()
this.processStackChart = null
}
},
drawStatusPieChart() {
var self = this
this.statusPieChart = this.$echarts.init(document.getElementById('statusPieChart'))
this.statusPieChart.clear()
this.statusPieChart.off('click')
this.statusPieChart.on('click', function() {
self.pieViewMode = self.pieViewMode === 'status' ? 'orderType' : 'status'
self.updatePieChart()
})
this.updatePieChart()
},
updatePieChart() {
if (!this.statusPieChart) return
this.statusPieChart.clear()
if (this.pieViewMode === 'status') {
var stats = this.dashboardStats
this.statusPieChart.setOption({
title: {
text: '订单状态分布(点击切换)',
left: 'center',
textStyle: { fontSize: 14 }
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c}单 ({d}%)'
},
legend: {
bottom: 10,
data: ['派工中', '生产中', '已完成']
},
color: ['#409EFF', '#67C23A', '#909399'],
series: [{
type: 'pie',
radius: ['40%', '65%'],
center: ['50%', '45%'],
avoidLabelOverlap: true,
label: {
show: true,
formatter: '{b}\n{c}单'
},
data: [
{ value: stats.pending, name: '派工中' },
{ value: stats.producing, name: '生产中' },
{ value: stats.completed, name: '已完成' }
]
}]
})
} else {
var salesCount = 0
var restockCount = 0
this.tableData2.forEach(function(row) {
if (row.收货人 === null) {
restockCount++
} else {
salesCount++
}
})
this.statusPieChart.setOption({
title: {
text: '订单类型分布(点击切换)',
left: 'center',
textStyle: { fontSize: 14 }
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c}单 ({d}%)'
},
legend: {
bottom: 10,
data: ['销售订单', '补货订单']
},
color: ['#409EFF', '#E6A23C'],
series: [{
type: 'pie',
radius: ['40%', '65%'],
center: ['50%', '45%'],
avoidLabelOverlap: true,
label: {
show: true,
formatter: '{b}\n{c}单'
},
data: [
{ value: salesCount, name: '销售订单' },
{ value: restockCount, name: '补货订单' }
]
}]
})
}
},
drawCompletionBarChart() {
var orderLabels = []
var completionRates = []
var planCounts = []
this.tableData2.forEach(function(row) {
var label = row.MK订单编号 || row.订单号 || ''
if (label.length > 10) label = label.substring(0, 10) + '...'
orderLabels.push(label)
completionRates.push(parseInt(row.完成率) || 0)
planCounts.push(row.计划数 || 0)
})
this.completionBarChart = this.$echarts.init(document.getElementById('completionBarChart'))
this.completionBarChart.clear()
this.completionBarChart.setOption({
title: {
text: '各订单完成率',
left: 'center',
textStyle: { fontSize: 14 }
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function(params) {
var tip = params[0].axisValue + '<br/>'
params.forEach(function(p) {
tip += p.marker + p.seriesName + ': ' + p.value + (p.seriesName === '完成率' ? '%' : '') + '<br/>'
})
return tip
}
},
legend: {
bottom: 0,
data: ['完成率', '计划数']
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
top: '15%',
containLabel: true
},
dataZoom: [
{ type: 'inside', start: 0, end: 100 },
{ type: 'slider', start: 0, end: 100, height: 20, bottom: 30 }
],
xAxis: {
type: 'category',
data: orderLabels,
axisLabel: { rotate: 30, fontSize: 10 }
},
yAxis: [
{
type: 'value',
name: '完成率(%)',
max: 100,
axisLabel: { formatter: '{value}%' }
},
{
type: 'value',
name: '计划数',
axisLabel: { formatter: '{value}' }
}
],
series: [
{
name: '完成率',
type: 'bar',
yAxisIndex: 0,
data: completionRates,
itemStyle: {
color: function(params) {
var val = params.value
if (val === 100) return '#67C23A'
if (val >= 50) return '#E6A23C'
return '#F56C6C'
}
},
label: {
show: true,
position: 'top',
formatter: '{c}%'
}
},
{
name: '计划数',
type: 'line',
yAxisIndex: 1,
data: planCounts,
itemStyle: { color: '#409EFF' },
lineStyle: { width: 2 },
symbolSize: 6
}
]
})
},
drawProcessStackChart() {
var opKeys = ['OP010', 'OP020', 'OP030', 'OP040', 'OP050', 'OP060', 'OP070', 'OP080', 'OP090', 'OP100', 'OP110']
var opLabels = []
var hasProcess = []
var noProcess = []
var self = this
opKeys.forEach(function(key) {
opLabels.push(key)
var count = 0
var empty = 0
self.tableData2.forEach(function(row) {
if (row[key] && row[key] !== '') {
count++
} else {
empty++
}
})
hasProcess.push(count)
noProcess.push(empty)
})
this.processStackChart = this.$echarts.init(document.getElementById('processStackChart'))
this.processStackChart.clear()
this.processStackChart.setOption({
title: {
text: '各工序覆盖情况',
left: 'center',
textStyle: { fontSize: 14 }
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function(params) {
var tip = params[0].axisValue + '<br/>'
params.forEach(function(p) {
tip += p.marker + p.seriesName + ': ' + p.value + '单<br/>'
})
return tip
}
},
legend: {
bottom: 0,
data: ['已安排工序', '未安排工序']
},
grid: {
left: '3%',
right: '4%',
bottom: '12%',
top: '15%',
containLabel: true
},
xAxis: {
type: 'category',
data: opLabels
},
yAxis: {
type: 'value',
name: '订单数'
},
series: [
{
name: '已安排工序',
type: 'bar',
stack: 'total',
data: hasProcess,
itemStyle: { color: '#67C23A' }
},
{
name: '未安排工序',
type: 'bar',
stack: 'total',
data: noProcess,
itemStyle: { color: '#E6E6E6' }
}
]
})
}
}
}

View File

@@ -262,7 +262,7 @@
@click="editData(scope.row)">编辑
</el-button>
<el-button
:disabled="Number(scope.row.是否加工中)===1 || Number(scope.row.完成数) > 0 || Number(scope.row.报废数量) > 0 || scope.row.姓名==='外协'"
:disabled="Number(scope.row.是否加工中)===1 || Number(scope.row.完成数) > 0 || Number(scope.row.报废数量) > 0 "
type="text"
icon="el-icon-delete"
size="mini"