Files
xgy-oa/klp-ui/src/views/aps/lineCapability/index.vue
砂糖 d3da84f65e fix: 修复产线能力查询参数错误及调整分页默认值
修复产线能力查询时lineId参数传递错误的问题,增加参数校验
调整分页组件默认的pageSizes选项,增加10的选项
修改物料线圈查询状态参数为0
优化线圈吞吐记录展示方式,改为行点击触发
移除用餐记录中不必要的部门字典依赖
2026-03-09 10:34:05 +08:00

494 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container line-capability-page" v-loading="loading">
<el-row :gutter="12" class="mb12">
<el-col :span="6"><div class="kpi-card"><div class="k">能力规则</div><div class="v">{{ total }}</div></div></el-col>
<el-col :span="6"><div class="kpi-card"><div class="k">启用规则</div><div class="v">{{ enabledCount }}</div></div></el-col>
<el-col :span="6"><div class="kpi-card"><div class="k">产线数量</div><div class="v">{{ lineTotal }}</div></div></el-col>
<el-col :span="6"><div class="kpi-card"><div class="k">平均每小时产能</div><div class="v">{{ avgCapacityText }}</div></div></el-col>
</el-row>
<el-card shadow="never" class="mb12">
<div slot="header" class="card-title">产线管理</div>
<el-row :gutter="10" class="mb8">
<el-col :span="7">
<el-input v-model="lineQuery.keyword" size="small" clearable placeholder="搜索产线名称/编号" @keyup.enter.native="loadLineList">
<el-button slot="append" icon="el-icon-search" @click="loadLineList" />
</el-input>
</el-col>
<el-col :span="17" class="tr">
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openLineForm()">新增产线</el-button>
<el-button size="mini" icon="el-icon-refresh" @click="loadLineList">刷新</el-button>
</el-col>
</el-row>
<KLPTable :data="lineList" max-height="260" @row-click="onLineRowClick">
<el-table-column label="产线名称" prop="lineName" min-width="140" />
<el-table-column label="产线编号" prop="lineCode" min-width="120" />
<el-table-column label="日产能" prop="capacity" width="110" />
<el-table-column label="单位" prop="unit" width="90" />
<el-table-column label="操作" width="160" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" @click.stop="openLineForm(scope.row)">修改</el-button>
<el-button size="mini" type="text" @click.stop="removeLine(scope.row)">删除</el-button>
</template>
</el-table-column>
</KLPTable>
</el-card>
<el-card shadow="never">
<div slot="header" class="card-title">产线能力设置</div>
<el-form :inline="true" size="small" class="mb8">
<el-form-item label="产线">
<el-select v-model="queryParams.lineId" clearable filterable placeholder="全部产线" style="width: 220px" @change="handleQuery">
<el-option v-for="line in lineList" :key="line.lineId" :label="line.lineName || line.lineCode || ('产线' + line.lineId)" :value="line.lineId" />
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="queryParams.isEnabled" clearable placeholder="全部" style="width: 120px" @change="handleQuery">
<el-option :value="1" label="启用" />
<el-option :value="0" label="停用" />
</el-select>
</el-form-item>
<el-form-item>
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openCapabilityForm">新增能力</el-button>
<el-button size="mini" icon="el-icon-search" @click="handleQuery">查询</el-button>
<el-button size="mini" icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<KLPTable :data="lineCapabilityList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="45" />
<el-table-column label="产线" prop="lineName" min-width="130" />
<el-table-column label="产品" min-width="210" show-overflow-tooltip>
<template slot-scope="scope">{{ formatProduct(scope.row) }}</template>
</el-table-column>
<el-table-column label="工序" min-width="180" show-overflow-tooltip>
<template slot-scope="scope">{{ formatProcess(scope.row) }}</template>
</el-table-column>
<el-table-column label="每小时产能" prop="capacityPerHour" width="120" />
<el-table-column label="准备时长(分钟)" prop="setupMinutes" width="130" />
<el-table-column label="优先级" prop="priority" width="90" />
<el-table-column label="状态" prop="isEnabledName" width="90" />
<el-table-column label="操作" width="170" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="openCapabilityForm(scope.row)">修改</el-button>
<el-button size="mini" type="text" @click="removeCapability(scope.row)">删除</el-button>
</template>
</el-table-column>
</KLPTable>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
</el-card>
<el-dialog :title="lineDialog.title" :visible.sync="lineDialog.visible" width="560px" append-to-body>
<el-form ref="lineFormRef" :model="lineDialog.form" label-width="110px" :rules="lineRules">
<el-form-item label="产线名称" prop="lineName"><el-input v-model="lineDialog.form.lineName" /></el-form-item>
<el-form-item label="产线编号" prop="lineCode"><el-input v-model="lineDialog.form.lineCode" /></el-form-item>
<el-form-item label="日产能"><el-input-number v-model="lineDialog.form.capacity" :min="0" :step="1" style="width: 100%" /></el-form-item>
<el-form-item label="单位"><el-input v-model="lineDialog.form.unit" /></el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" :loading="buttonLoading" @click="submitLine">确定</el-button>
<el-button @click="lineDialog.visible = false">取消</el-button>
</div>
</el-dialog>
<el-dialog :title="title" :visible.sync="open" width="720px" append-to-body>
<el-form ref="capabilityFormRef" :model="form" :rules="rules" label-width="130px">
<el-form-item label="产线" prop="lineId">
<el-select v-model="form.lineId" filterable placeholder="请选择产线" style="width: 100%" @change="onFormLineChange">
<el-option v-for="line in lineList" :key="line.lineId" :label="line.lineName || line.lineCode || ('产线' + line.lineId)" :value="line.lineId" />
</el-select>
</el-form-item>
<el-form-item label="产品">
<el-input :value="form.productDisplay" readonly style="width: calc(100% - 120px); margin-right: 8px" />
<el-button @click="openProductSelector">选择</el-button>
<el-button type="text" @click="clearProduct">清空</el-button>
</el-form-item>
<el-form-item label="工序">
<el-input :value="form.processDisplay" readonly style="width: calc(100% - 120px); margin-right: 8px" />
<el-button @click="openProcessSelector">选择</el-button>
<el-button type="text" @click="clearProcess">清空</el-button>
</el-form-item>
<el-form-item label="每小时产能" prop="capacityPerHour"><el-input-number v-model="form.capacityPerHour" :min="0" :precision="2" :step="0.1" style="width: 100%" /></el-form-item>
<el-form-item label="准备时长(分钟)"><el-input-number v-model="form.setupMinutes" :min="0" :step="1" style="width: 100%" /></el-form-item>
<el-form-item label="优先级"><el-input-number v-model="form.priority" :min="1" :step="1" style="width: 100%" /></el-form-item>
<el-form-item label="是否启用"><el-radio-group v-model="form.isEnabled"><el-radio :label="1">启用</el-radio><el-radio :label="0">停用</el-radio></el-radio-group></el-form-item>
<el-form-item label="备注"><el-input v-model="form.remark" type="textarea" :rows="3" /></el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" :loading="buttonLoading" @click="submitForm">确定</el-button>
<el-button @click="open = false">取消</el-button>
</div>
</el-dialog>
<el-dialog title="选择产品" :visible.sync="productSelector.visible" width="980px" append-to-body>
<el-form :inline="true" size="small">
<el-form-item><el-input v-model="productSelector.keyword" placeholder="产品名称/编号" @keyup.enter.native="fetchProductOptions" /></el-form-item>
<el-form-item><el-button type="primary" size="mini" @click="fetchProductOptions">搜索</el-button></el-form-item>
</el-form>
<KLPTable :data="productSelector.list" max-height="360">
<el-table-column prop="productName" label="产品名称" min-width="150" />
<el-table-column prop="productCode" label="产品编号" min-width="120" />
<el-table-column prop="specification" label="规格" min-width="120" />
<el-table-column prop="material" label="材质" min-width="100" />
<el-table-column label="操作" width="90" fixed="right">
<template slot-scope="scope"><el-button size="mini" type="text" @click="selectProduct(scope.row)">选择</el-button></template>
</el-table-column>
</KLPTable>
<pagination v-show="productSelector.total > 0" :total="productSelector.total" :page.sync="productSelector.pageNum" :limit.sync="productSelector.pageSize" @pagination="fetchProductOptions" />
</el-dialog>
<el-dialog title="选择工序" :visible.sync="processSelector.visible" width="900px" append-to-body>
<el-form :inline="true" size="small">
<el-form-item><el-input v-model="processSelector.keyword" placeholder="工序名称/编码" @keyup.enter.native="fetchProcessOptions" /></el-form-item>
<el-form-item><el-button type="primary" size="mini" @click="fetchProcessOptions">搜索</el-button></el-form-item>
</el-form>
<KLPTable :data="processSelector.list" max-height="360">
<el-table-column prop="processName" label="工序名称" min-width="150" />
<el-table-column prop="processCode" label="工序编码" min-width="120" />
<el-table-column prop="standardTime" label="标准工时" width="110" />
<el-table-column label="操作" width="90" fixed="right">
<template slot-scope="scope"><el-button size="mini" type="text" @click="selectProcess(scope.row)">选择</el-button></template>
</el-table-column>
</KLPTable>
<pagination v-show="processSelector.total > 0" :total="processSelector.total" :page.sync="processSelector.pageNum" :limit.sync="processSelector.pageSize" @pagination="fetchProcessOptions" />
</el-dialog>
</div>
</template>
<script>
import { listLineCapability, getLineCapability, delLineCapability, addLineCapability, updateLineCapability } from '@/api/aps/aps'
import { listProductionLine, addProductionLine, updateProductionLine, delProductionLine } from '@/api/wms/productionLine'
import { listProduct } from '@/api/wms/product'
import { listProcesse } from '@/api/wms/craft'
export default {
name: 'ApsLineCapability',
data() {
return {
loading: false,
buttonLoading: false,
total: 0,
lineTotal: 0,
ids: [],
lineCapabilityList: [],
lineList: [],
lineQuery: { keyword: '' },
queryParams: { pageNum: 1, pageSize: 20, lineId: undefined, isEnabled: undefined },
open: false,
title: '',
form: {},
lineDialog: { visible: false, title: '', form: {} },
productSelector: { visible: false, keyword: '', pageNum: 1, pageSize: 10, total: 0, list: [] },
processSelector: { visible: false, keyword: '', pageNum: 1, pageSize: 10, total: 0, list: [] },
rules: {
lineId: [{ required: true, message: '请选择产线', trigger: 'change' }],
capacityPerHour: [{ required: true, message: '请输入每小时产能', trigger: 'blur' }]
},
lineRules: {
lineName: [{ required: true, message: '请输入产线名称', trigger: 'blur' }],
lineCode: [{ required: true, message: '请输入产线编号', trigger: 'blur' }]
}
}
},
computed: {
enabledCount() {
return (this.lineCapabilityList || []).filter(i => Number(i.isEnabled) === 1).length
},
avgCapacityText() {
if (!this.lineCapabilityList.length) return '-'
const sum = this.lineCapabilityList.reduce((s, i) => s + Number(i.capacityPerHour || 0), 0)
return (sum / this.lineCapabilityList.length).toFixed(2)
}
},
created() {
this.resetFormData()
this.loadLineList().then(() => this.getList())
},
methods: {
formatProduct(row) {
if (!row.productName) return '通用'
return `${row.productName}${row.productCode ? '' + row.productCode + '' : ''}`
},
formatProcess(row) {
if (!row.processName) return '通用'
return `${row.processName}${row.processCode ? '' + row.processCode + '' : ''}`
},
resetFormData() {
this.form = {
capabilityId: undefined,
lineId: undefined,
productId: undefined,
processId: undefined,
productDisplay: '',
processDisplay: '',
capacityPerHour: undefined,
setupMinutes: 0,
priority: 999,
isEnabled: 1,
remark: ''
}
},
async loadLineList() {
const res = await listProductionLine({ pageNum: 1, pageSize: 500, lineName: this.lineQuery.keyword || undefined })
this.lineList = res.rows || []
this.lineTotal = res.total || this.lineList.length
},
onLineRowClick(row) {
this.queryParams.lineId = row.lineId
console.log(row);
// this.handleQuery()
},
getList() {
this.loading = true
listLineCapability(this.queryParams).then(res => {
this.lineCapabilityList = res.rows || []
this.total = res.total || 0
}).finally(() => { this.loading = false })
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.queryParams = { pageNum: 1, pageSize: 20, lineId: undefined, isEnabled: undefined }
this.getList()
},
handleSelectionChange(selection) {
this.ids = (selection || []).map(i => i.capabilityId)
},
openCapabilityForm(row) {
this.resetFormData()
if (!row) {
this.title = '新增产线能力'
if (this.queryParams.lineId) this.form.lineId = this.queryParams.lineId
this.open = true
return
}
console.log(row);
const lineId = row.lineId ?? this.queryParams.lineId
if (!lineId) {
this.$modal.msgError('请选择产线')
return
}
this.loading = true
getLineCapability(lineId).then(res => {
const d = res.data || {}
this.form = {
...this.form,
...d,
productDisplay: d.productName ? `${d.productName}${d.productCode ? '' + d.productCode + '' : ''}` : '',
processDisplay: d.processName ? `${d.processName}${d.processCode ? '' + d.processCode + '' : ''}` : ''
}
this.title = '修改产线能力'
this.open = true
}).finally(() => { this.loading = false })
},
onFormLineChange(lineId) {
const line = (this.lineList || []).find(i => String(i.lineId) === String(lineId))
const cap = Number((line && line.capacity) || 0)
if (cap > 0 && (!this.form.capacityPerHour || Number(this.form.capacityPerHour) <= 0)) {
this.form.capacityPerHour = Number((cap / 24).toFixed(2))
}
},
submitForm() {
this.$refs.capabilityFormRef.validate(valid => {
if (!valid) return
this.buttonLoading = true
const payload = {
capabilityId: this.form.capabilityId,
lineId: this.form.lineId,
productId: this.form.productId,
processId: this.form.processId,
capacityPerHour: this.form.capacityPerHour,
setupMinutes: this.form.setupMinutes,
priority: this.form.priority,
isEnabled: this.form.isEnabled,
remark: this.form.remark
}
const req = payload.capabilityId ? updateLineCapability(payload) : addLineCapability(payload)
req.then(() => {
this.$modal.msgSuccess(payload.capabilityId ? '修改成功' : '新增成功')
this.open = false
this.getList()
}).finally(() => { this.buttonLoading = false })
})
},
removeCapability(row) {
this.$modal.confirm('确认删除该能力规则吗?').then(() => delLineCapability(row.capabilityId)).then(() => {
this.$modal.msgSuccess('删除成功')
this.getList()
})
},
openLineForm(row) {
this.lineDialog.visible = true
this.lineDialog.title = row ? '修改产线' : '新增产线'
this.lineDialog.form = row ? { ...row } : { lineName: '', lineCode: '', capacity: 0, unit: '' }
},
submitLine() {
this.$refs.lineFormRef.validate(valid => {
if (!valid) return
const payload = { ...this.lineDialog.form }
this.buttonLoading = true
const req = payload.lineId ? updateProductionLine(payload) : addProductionLine(payload)
req.then(() => {
this.$modal.msgSuccess(payload.lineId ? '产线修改成功' : '产线新增成功')
this.lineDialog.visible = false
return this.loadLineList()
}).then(() => this.getList()).finally(() => { this.buttonLoading = false })
})
},
removeLine(row) {
this.$modal.confirm('确认删除该产线吗?').then(() => delProductionLine(row.lineId)).then(() => {
this.$modal.msgSuccess('删除成功')
return this.loadLineList()
}).then(() => this.getList())
},
openProductSelector() {
this.productSelector.visible = true
this.productSelector.pageNum = 1
this.fetchProductOptions()
},
fetchProductOptions() {
listProduct({
pageNum: this.productSelector.pageNum,
pageSize: this.productSelector.pageSize,
productName: this.productSelector.keyword || undefined,
productCode: this.productSelector.keyword || undefined
}).then(res => {
this.productSelector.list = res.rows || []
this.productSelector.total = res.total || 0
})
},
selectProduct(row) {
this.form.productId = row.productId
this.form.productDisplay = `${row.productName || ''}${row.productCode ? '' + row.productCode + '' : ''}`
this.productSelector.visible = false
},
clearProduct() {
this.form.productId = undefined
this.form.productDisplay = ''
},
openProcessSelector() {
this.processSelector.visible = true
this.processSelector.pageNum = 1
this.fetchProcessOptions()
},
fetchProcessOptions() {
listProcesse({
pageNum: this.processSelector.pageNum,
pageSize: this.processSelector.pageSize,
processName: this.processSelector.keyword || undefined,
processCode: this.processSelector.keyword || undefined
}).then(res => {
this.processSelector.list = res.rows || []
this.processSelector.total = res.total || 0
})
},
selectProcess(row) {
this.form.processId = row.processId
this.form.processDisplay = `${row.processName || ''}${row.processCode ? '' + row.processCode + '' : ''}`
this.processSelector.visible = false
},
clearProcess() {
this.form.processId = undefined
this.form.processDisplay = ''
}
}
}
</script>
<style scoped lang="scss">
.line-capability-page {
padding: 12px;
background: transparent;
}
.line-capability-page > .el-row,
.line-capability-page > .el-card {
background: #fff;
}
.mb12 { margin-bottom: 12px; }
.mb8 { margin-bottom: 8px; }
.tr { text-align: right; }
.kpi-card {
border: 1px solid #e9edf5;
border-radius: 10px;
background: #fff;
padding: 12px;
box-shadow: 0 1px 4px rgba(30, 41, 59, 0.04);
}
.kpi-card .k {
font-size: 12px;
color: #7c8798;
}
.kpi-card .v {
margin-top: 8px;
font-size: 24px;
line-height: 1;
font-weight: 700;
color: #1f2d3d;
}
.card-title {
font-weight: 700;
color: #344054;
font-size: 14px;
position: relative;
padding-left: 10px;
}
.card-title::before {
content: '';
position: absolute;
left: 0;
top: 3px;
width: 3px;
height: 14px;
border-radius: 2px;
background: #4f8ff7;
}
::v-deep .el-card {
border: 1px solid #e9edf5;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(30, 41, 59, 0.04);
}
::v-deep .el-card__header {
padding: 10px 14px;
border-bottom: 1px solid #eef2f8;
background: #fff;
}
::v-deep .el-card__body {
padding: 12px 14px;
}
::v-deep .el-table,
::v-deep .el-table::before,
::v-deep .el-table__fixed::before,
::v-deep .el-table__fixed-right::before {
border-color: #edf1f7;
}
::v-deep .el-table th.is-leaf,
::v-deep .el-table td {
border-bottom: 1px solid #f1f4f9;
}
::v-deep .el-table th {
background: #fff;
color: #5d6b82;
font-weight: 600;
}
::v-deep .el-table tbody tr:hover > td {
background: #f9fbff !important;
}
</style>