feat: update MES pages and tracking docs

This commit is contained in:
2026-06-29 18:27:00 +08:00
parent ebee02d103
commit e0e1b5d2bb
59 changed files with 8405 additions and 1398 deletions

View File

@@ -1,9 +1,9 @@
<template>
<div class="app-container">
<el-card style="height: 850px">
<div style="margin-top: 7px; margin-bottom: 7px">
<el-input v-model="equipmentName" placeholder="设备名称" size="small" clearable style="width:200px;margin-left: 0px"/>
<el-select v-model="equipmentType" placeholder="设备类型" filterable size="small" style="width:200px" clearable >
<div class="app-container device-information-page">
<el-card class="device-card">
<div class="filter-bar">
<el-input v-model="equipmentName" placeholder="生产用名" size="small" clearable class="filter-control"/>
<el-select v-model="equipmentType" placeholder="设备类型" filterable size="small" clearable class="filter-control">
<el-option
v-for="item in basedatalx"
:key="item.id"
@@ -11,63 +11,52 @@
:value="item.id"
/>
</el-select>
<!-- <el-input v-model="opName" placeholder="工位" size="small" clearable style="width:200px;margin-left: 0px"/> -->
<el-button type="primary" icon="el-icon-search" size="small" style="margin-left: 20px" plain @click="pageSize = 20;pageCurrent = 1;searchTable()">查询</el-button>
<el-button type="primary" icon="el-icon-search" size="small" plain @click="pageSize = 20; pageCurrent = 1; searchTable()">查询</el-button>
<el-tooltip effect="dark" content="添加新设备" placement="top">
<el-button type="distribution" icon="el-icon-circle-plus-outline" size="small" style="margin-left: 10px" @click="ADD()">设备</el-button>
<el-button type="distribution" icon="el-icon-circle-plus-outline" size="small" @click="ADD()">设备</el-button>
</el-tooltip>
</div>
<div>
<el-table :data="tableData" border size="small" stripe highlight-current-row height="740px" style="width: 1048px">
<el-table-column label="设备名称" prop="设备名称" align="center" width="150px" >
<template slot-scope="scope">
{{ scope.row.设备名称 }}
</template>
</el-table-column>
<el-table-column label="设备型号" prop="设备型号" align="center" width="150px" >
<template slot-scope="scope">
{{ scope.row.设备型号 }}
</template>
</el-table-column>
<el-table-column label="设备类型" prop="设备类型名称" align="center" width="150px" >
<template slot-scope="scope">
{{ scope.row.设备类型名称 }}
</template>
</el-table-column>
<!-- <el-table-column label="加工工艺" align="center" width="150px">
<template slot-scope="scope">
{{ scope.row.加工工艺 }}
</template>
</el-table-column> -->
<!-- <el-table-column label="工位" align="center" width="80px">
<template slot-scope="scope">
{{ scope.row.工位号 }}
</template>
</el-table-column> -->
<!-- <el-table-column label="工时单价(元/小时)" align="center" width="140px">
<template slot-scope="scope">
{{ scope.row.工时单价 }}
</template>
</el-table-column>
<el-table-column label="月设备加工能力" align="center" width="140px">
<template slot-scope="scope">
{{ scope.row.月设备加工能力 }}
</template>
</el-table-column>
<el-table-column label="点检类型名称" align="center" width="137px">
<template slot-scope="scope">
{{ scope.row.点检表类型名称 }}
</template>
</el-table-column> -->
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="text" size="mini" icon="el-icon-edit" style="margin-right:10px" @click="handleEdit(scope.row)"/>
<el-button type="text" size="mini" icon="el-icon-delete" @click="handleDelete(scope.row)"/>
</template>
</el-table-column>
</el-table>
</div>
<div class="block" style="margin-top: 10px;">
<el-table
:data="sortedTableData"
:row-class-name="tableRowClassName"
border
size="small"
stripe
highlight-current-row
height="740px"
class="device-table">
<el-table-column label="设备名称" prop="名称" align="center" width="150" show-overflow-tooltip/>
<el-table-column label="公司编码(财务)" prop="公司编码" align="center" width="140" show-overflow-tooltip/>
<el-table-column label="生产用名" prop="设备名称" align="center" width="150" show-overflow-tooltip/>
<el-table-column label="规格/型号" prop="设备型号" align="center" width="150" show-overflow-tooltip/>
<el-table-column label="设备类型" prop="设备类型名称" align="center" width="130" show-overflow-tooltip/>
<el-table-column label="设备信息" prop="设备信息" align="center" width="160" show-overflow-tooltip/>
<el-table-column label="使用部门" prop="使用部门" align="center" width="120" show-overflow-tooltip/>
<el-table-column label="入账日期" prop="入账日期" align="center" width="120">
<template slot-scope="scope">{{ formatDate(scope.row.入账日期) }}</template>
</el-table-column>
<el-table-column label="单位" prop="单位" align="center" width="80" show-overflow-tooltip/>
<el-table-column label="数量" prop="数量" align="center" width="90" show-overflow-tooltip/>
<el-table-column label="状态" prop="状态" align="center" width="100" show-overflow-tooltip/>
<el-table-column label="维修记录" prop="维修记录" align="center" width="160" show-overflow-tooltip/>
<el-table-column label="维保记录" prop="维保记录" align="center" width="160" show-overflow-tooltip/>
<el-table-column label="工位绑定" prop="工位绑定" align="center" width="180" show-overflow-tooltip>
<template slot-scope="scope">{{ formatStationNames(scope.row.工位绑定) }}</template>
</el-table-column>
<el-table-column label="主要设备" prop="主要设备" align="center" width="90" show-overflow-tooltip/>
<el-table-column label="设备标准开机工时(小时)" prop="设备标准开机工时" align="center" width="170" show-overflow-tooltip/>
<el-table-column label="特种设备" prop="特种设备" align="center" width="90" show-overflow-tooltip/>
<el-table-column label="证书有效期至" prop="证书有效期至" align="center" width="130">
<template slot-scope="scope">{{ formatDate(scope.row.证书有效期至) }}</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="90">
<template slot-scope="scope">
<el-button type="text" size="mini" icon="el-icon-edit" style="margin-right:10px" @click="handleEdit(scope.row)"/>
<el-button type="text" size="mini" icon="el-icon-delete" @click="handleDelete(scope.row)"/>
</template>
</el-table-column>
</el-table>
<div class="pagination-bar">
<el-pagination
:current-page="pageCurrent"
:page-sizes="[10, 20, 30, 40, 100]"
@@ -79,22 +68,32 @@
/>
</div>
</el-card>
<el-dialog :title="title" :visible.sync="dialogFormVisible" center style="min-height: 300px" width="700px">
<el-form ref="form" :model="form" :rules="rules" label-position="right" label-width="110px" class="demo-ruleForm">
<el-dialog :title="title" :visible.sync="dialogFormVisible" center width="980px">
<el-form ref="form" :model="form" :rules="rules" label-position="right" label-width="150px" class="device-form">
<el-row>
<el-col :span="12">
<el-form-item label="设备名称" prop="设备名称" >
<el-input v-model="form.设备名称" placeholder="设备名称" size="small" style="width:200px" clearable />
<el-form-item label="设备名称" prop="名称">
<el-input v-model="form.名称" placeholder="设备名称" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备型号" prop="设备型号" >
<el-input v-model="form.设备型号" placeholder="设备型号" size="small" style="width:200px" clearable />
<el-form-item label="公司编码(财务)" prop="公司编码">
<el-input v-model="form.公司编码" :disabled="title === '编辑设备'" placeholder="公司编码(财务)" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备类型" prop="设备类型" >
<el-select v-model="form.设备类型" placeholder="设备类型" filterable size="small" style="width:200px" clearable >
<el-form-item label="生产用名" prop="设备名称">
<el-input v-model="form.设备名称" placeholder="生产用名" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规格/型号" prop="设备型号">
<el-input v-model="form.设备型号" placeholder="规格/型号" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备类型" prop="设备类型">
<el-select v-model="form.设备类型" placeholder="设备类型" filterable size="small" clearable>
<el-option
v-for="item in basedatalx"
:key="item.id"
@@ -104,55 +103,92 @@
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- <el-row>
<el-col :span = "12">
<el-form-item label="加工工艺" prop="加工工艺" >
<el-select v-model="form.加工工艺" placeholder="加工工艺" filterable size="small" style="width:200px" clearable >
<el-col :span="12">
<el-form-item label="设备信息" prop="设备信息">
<el-input v-model="form.设备信息" placeholder="设备信息" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="使用部门" prop="使用部门">
<el-input v-model="form.使用部门" placeholder="使用部门" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入账日期" prop="入账日期">
<el-date-picker v-model="form.入账日期" type="date" placeholder="入账日期" size="small"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位" prop="单位">
<el-input v-model="form.单位" placeholder="单位" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="数量" prop="数量">
<el-input v-positive="'float'" v-model="form.数量" type="number" placeholder="数量" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态" prop="状态">
<el-input v-model="form.状态" placeholder="状态" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="维修记录" prop="维修记录">
<el-input v-model="form.维修记录" placeholder="维修记录" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="维保记录" prop="维保记录">
<el-input v-model="form.维保记录" placeholder="维保记录" size="small" clearable/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工位绑定" prop="工位绑定">
<el-select v-model="form.工位绑定" placeholder="工位绑定" filterable multiple collapse-tags size="small" clearable>
<el-option
v-for="item in process"
v-for="item in stationOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span = "12">
<el-form-item label="工位号" prop="工位号" >
<el-input v-model="form.工位号" placeholder="工位号" size="small" style="width:200px" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span = "12">
<el-form-item label="工时单价" prop="工时单价" >
<el-input v-positive="'float'" v-model="form.工时单价" class="工时单价" size="small" style="width:200px" placeholder="工时单价" type="number"/>
</el-form-item>
</el-col>
<el-col :span = "12">
<el-form-item label="月设备加工能力" prop="月设备加工能力" >
<el-input v-positive="'float'" v-model="form.月设备加工能力" class="工时单价" size="small" style="width:200px" placeholder="月设备加工能力" type="number"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span = "12">
<el-form-item label="点检类型" prop="点检类型" >
<el-select v-model="form.点检类型" placeholder="选择点检类型" filterable size="small" style="width:200px" clearable >
<el-option
v-for="item in routineCheckType"
:key="item.value"
:label="item.label"
:value="item.value"/>
<el-col :span="12">
<el-form-item label="主要设备" prop="主要设备">
<el-select v-model="form.主要设备" placeholder="主要设备" size="small" clearable>
<el-option label="是" value="是"/>
<el-option label="否" value="否"/>
</el-select>
</el-form-item>
</el-col>
</el-row> -->
<el-col :span="12">
<el-form-item label="设备标准开机工时" prop="设备标准开机工时">
<el-input v-positive="'float'" v-model="form.设备标准开机工时" type="number" placeholder="小时" size="small" clearable>
<template slot="append">小时</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="特种设备" prop="特种设备">
<el-select v-model="form.特种设备" placeholder="特种设备" size="small" clearable>
<el-option label="是" value="是"/>
<el-option label="否" value="否"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="证书有效期至" prop="证书有效期至">
<el-date-picker v-model="form.证书有效期至" type="date" placeholder="证书有效期至" size="small"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="callOff('form')">取消</el-button>
<el-button v-show="title==='新增设备'" type="primary" @click="submitAdd('form')">确定</el-button>
<el-button v-show="title==='编辑设备'" type="primary" @click="submitEdit('form')">确定</el-button>
<el-button v-show="title === '新增设备'" type="primary" @click="submitAdd('form')">确定</el-button>
<el-button v-show="title === '编辑设备'" type="primary" @click="submitEdit('form')">确定</el-button>
</div>
</el-dialog>
</div>
@@ -160,80 +196,82 @@
<script>
import { mapGetters } from 'vuex'
const emptyForm = () => ({
名称: '',
公司编码: '',
设备名称: '',
设备型号: '',
设备类型: '',
设备信息: '',
使用部门: '',
入账日期: null,
单位: '',
数量: '',
状态: '',
维修记录: '',
维保记录: '',
工位绑定: [],
主要设备: '',
设备标准开机工时: '',
特种设备: '',
证书有效期至: null
})
export default {
name: 'DeviceInformation',
data() {
const validateCompanyCode = (rule, value, callback) => {
if (this.title === '新增设备' && !value) {
callback(new Error('请输入公司编码'))
} else {
callback()
}
}
return {
routineCheckType: [],
dialogFormVisible: false,
title: '',
equipmentValue: '',
equipmentName: '',
equipmentType: '',
opName: '',
process: [],
form: {
设备名称: '',
设备型号: '',
设备类型: ''
// 加工工艺: '',
// 工位号: '',
// 点检类型: '',
// 工时单价: '',
// 月设备加工能力: ''
},
form: emptyForm(),
basedatalx: [],
stationOptions: [],
rules: {
设备名称: [{ required: true, message: '请输入设备名称' }],
// 设备型号: [{ required: true, message: '请输入设备型号' }],
// 加工工艺: [{ required: true, message: '请选择加工工艺' }],
设备类型: [{ required: true, message: '请选择' }]
公司编码: [{ validator: validateCompanyCode, trigger: 'blur' }],
设备名称: [{ required: true, message: '请输入生产用名', trigger: 'blur' }]
},
original: '',
total: 0, // 分页总数
tableData: [], // 设备信息表
pageCurrent: 1, // 分页当前页
pageSize: 20 // 分页每页显示条数
total: 0,
tableData: [],
pageCurrent: 1,
pageSize: 20
}
},
computed: {
...mapGetters([
'id',
'token'
])
]),
sortedTableData() {
return [...this.tableData].sort((a, b) => {
const aLevel = this.getCertificateLevel(a.证书有效期至)
const bLevel = this.getCertificateLevel(b.证书有效期至)
if (aLevel !== bLevel) return aLevel - bLevel
return this.getDateTime(a.证书有效期至) - this.getDateTime(b.证书有效期至)
})
}
},
created() {
this.searchTable()
this.getbasedata()
// this.getRoutineCheckType()
this.getStationOptions()
},
methods: {
// getRoutineCheckType() {
// var param = []
// var Data = this.CreateData('11', '点检类型_查询', param)
// this.ExecDatabase(Data).then(response => {
// for (let i = 0; i < response.data.length; i++) {
// this.routineCheckType.push({
// label: response.data[i].点检表类型名称,
// value: response.data[i].点检表类型流水号
// })
// }
// })
// },
// getProcess() {
// var param = []
// var Data = this.CreateData('11', '工艺名称_查询', param)
// this.ExecDatabase(Data).then(response => {
// for (let i = 0; i < response.data.length; i++) {
// this.process.push({
// label: response.data[i].工艺名称,
// value: response.data[i].工艺编号
// })
// }
// })
// },
getbasedata() {
var Data = this.CreateData('11', '查询字典')
this.ExecDatabase(Data).then(response => {
this.basedatalx = []
response.data.forEach(element => {
if (element.数据类别 == '设备类型') {
this.basedatalx.push({
@@ -244,11 +282,19 @@ export default {
})
})
},
getStationOptions() {
var Data = this.CreateData('11', 'MES_登录_工位与名称_查询', [])
this.ExecDatabase(Data).then(response => {
this.stationOptions = (response.data || []).map(item => ({
label: item.指南工位号 || item.工位名称 || item.工位号,
value: item.工位号
}))
})
},
searchTable() {
let equipmentName_check, equipmentType_check
this.equipmentName ? equipmentName_check = 1 : equipmentName_check = 0
this.equipmentType ? equipmentType_check = 1 : equipmentType_check = 0
// this.opName ? opName_check = 1 : opName_check = 0
var param = []
param[0] = ['设备名称_check', equipmentName_check]
param[1] = ['设备名称', this.equipmentName]
@@ -256,43 +302,52 @@ export default {
param[3] = ['设备类型_check', equipmentType_check]
var Data = this.CreateData('11', '设备管理_设备信息_查询', param, this.pageSize, this.pageCurrent)
this.ExecDatabase(Data).then(response => {
this.tableData = response.data.rows
this.total = response.data.total
this.tableData = response.data.rows || []
this.total = response.data.total || 0
})
},
ADD() {
this.title = '新增设备'
if (this.$refs['form'] !== undefined) {
this.$refs['form'].resetFields()
if (this.$refs.form !== undefined) {
this.$refs.form.resetFields()
}
this.form.设备名称 = ''
this.form.设备型号 = ''
this.form.设备类型 = ''
// this.form.加工工艺 = ''
// this.form.工位号 = ''
// this.form.月设备加工能力 = ''
// this.form.点检类型 = 1
// this.form.工时单价 = ''
this.form = emptyForm()
this.dialogFormVisible = true
},
handleEdit(row) {
this.title = '编辑设备'
if (this.$refs['form'] !== undefined) {
this.$refs['form'].resetFields()
if (this.$refs.form !== undefined) {
this.$refs.form.resetFields()
}
this.equipmentValue = row.设备流水号
this.form.设备名称 = row.设备名称
this.form.设备型号 = row.设备型号
console.log(this.basedatalx)
this.form.设备类型 = parseInt(row.设备类型)
// this.form.加工工艺 = Number(row.工艺编号)
// this.form.工位号 = row.工位号
// this.original = row.工位号
// this.form.点检类型 = Number(row.点检表类型)
// this.form.月设备加工能力 = row.月设备加工能力
// this.form.工时单价 = row.工时单价
const editForm = {
名称: row.名称 || '',
公司编码: row.公司编码 || '',
设备名称: row.设备名称 || '',
设备型号: row.设备型号 || '',
设备类型: row.设备类型 ? parseInt(row.设备类型) : '',
设备信息: row.设备信息 || '',
使用部门: row.使用部门 || '',
入账日期: this.parseDateValue(row.入账日期 || row['入账日期']),
单位: row.单位 || '',
数量: row.数量 || '',
状态: row.状态 || '',
维修记录: row.维修记录 || '',
维保记录: row.维保记录 || '',
工位绑定: this.parseStationBinding(row.工位绑定),
主要设备: row.主要设备 || '',
设备标准开机工时: row.设备标准开机工时 || '',
特种设备: row.特种设备 || '',
证书有效期至: this.parseDateValue(row.证书有效期至 || row['证书有效期至'])
}
Object.keys(editForm).forEach(key => {
this.$set(this.form, key, editForm[key])
})
this.dialogFormVisible = true
this.$nextTick(() => {
this.$set(this.form, '入账日期', editForm.入账日期)
this.$set(this.form, '证书有效期至', editForm.证书有效期至)
})
},
callOff(formName) {
this.dialogFormVisible = false
@@ -301,21 +356,7 @@ export default {
submitAdd(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
// var param = []
// param[0] = ['工位', this.form.工位号]
// var Data = this.CreateData('11', '设备管理_设备信息_验证工位号是否重复', param)
// this.ExecDatabase(Data).then(response => {
// if (Number(response.data[0].Column1) === 0) {
var param = []
param[0] = ['设备名称', this.form.设备名称]
param[1] = ['设备型号', this.form.设备型号]
param[2] = ['设备类型', this.form.设备类型]
// param[2] = ['加工工艺', this.form.加工工艺]
// param[3] = ['工位', this.form.工位号]
// param[4] = ['工时单价', this.form.工时单价]
// param[5] = ['月设备加工能力', this.form.月设备加工能力]
// param[6] = ['点检类型', this.form.点检类型]
var Data = this.CreateData('12', '设备管理_设备信息_增加', param)
var Data = this.CreateData('12', '设备管理_设备信息_增加', this.buildSubmitParams())
this.ExecDatabase(Data).then(response => {
if (response.data[0].result === '1') {
this.$message.success('添加成功')
@@ -325,74 +366,49 @@ export default {
this.$message.error('添加失败')
}
})
// } else {
// // this.$message.warning('工位号重复')
// }
// })
}
})
},
submitEdit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
if (this.form.工位号 === this.original) {
var param = []
param[0] = ['设备名称', this.form.设备名称]
param[1] = ['设备型号', this.form.设备型号]
param[2] = ['设备流水号', this.equipmentValue]
param[3] = ['设备类型', this.form.设备类型]
// param[2] = ['加工工艺', this.form.加工工艺]
// param[3] = ['工位', this.form.工位号]
// param[4] = ['工时单价', this.form.工时单价]
// param[5] = ['月设备加工能力', this.form.月设备加工能力]
// param[6] = ['设备流水号', this.equipmentValue]
// param[7] = ['原工位', this.original]
// param[8] = ['点检类型', this.form.点检类型]
var Data = this.CreateData('12', '设备管理_设备信息_编辑', param)
this.ExecDatabase(Data).then(response => {
if (response.data[0].result === '1') {
this.$message.success('编辑成功')
this.searchTable()
this.dialogFormVisible = false
} else {
this.$message.error('编辑失败')
}
})
} else {
// var param1 = []
// param1[0] = ['工位', this.form.工位号]
// var Data1 = this.CreateData('11', '设备管理_设备信息_验证工位号是否重复', param1)
// this.ExecDatabase(Data1).then(response => {
// if (Number(response.data[0].Column1) === 0) {
var param = []
param[0] = ['设备名称', this.form.设备名称]
param[1] = ['设备型号', this.form.设备型号]
param[2] = ['设备流水号', this.equipmentValue]
param[3] = ['设备类型', this.form.设备类型]
// param[2] = ['加工工艺', this.form.加工工艺]
// param[3] = ['工位', this.form.工位号]
// param[4] = ['工时单价', this.form.工时单价]
// param[5] = ['月设备加工能力', this.form.月设备加工能力]
// param[6] = ['设备流水号', this.equipmentValue]
// param[7] = ['原工位', this.original]
var Data = this.CreateData('12', '设备管理_设备信息_编辑', param)
this.ExecDatabase(Data).then(response => {
if (response.data[0].result === '1') {
this.$message.success('编辑成功')
this.searchTable()
this.dialogFormVisible = false
} else {
this.$message.error('编辑失败')
}
})
// } else {
// // this.$message.warning('工位号重复')
// }
// })
}
var param = this.buildSubmitParams()
param.unshift(['设备流水号', this.equipmentValue])
var Data = this.CreateData('12', '设备管理_设备信息_编辑', param)
this.ExecDatabase(Data).then(response => {
if (response.data[0].result === '1') {
this.$message.success('编辑成功')
this.searchTable()
this.dialogFormVisible = false
} else {
this.$message.error('编辑失败')
}
})
}
})
},
buildSubmitParams() {
return [
['名称', this.form.名称],
['公司编码', this.form.公司编码],
['设备名称', this.form.设备名称],
['设备型号', this.form.设备型号],
['设备类型', this.form.设备类型],
['设备信息', this.form.设备信息],
['使用部门', this.form.使用部门],
['入账日期', this.dateParam(this.form.入账日期)],
['单位', this.form.单位],
['数量', this.nullIfEmpty(this.form.数量)],
['状态', this.form.状态],
['维修记录', this.form.维修记录],
['维保记录', this.form.维保记录],
['工位绑定', (this.form.工位绑定 || []).join(',')],
['主要设备', this.form.主要设备],
['设备标准开机工时', this.nullIfEmpty(this.form.设备标准开机工时)],
['特种设备', this.form.特种设备],
['证书有效期至', this.dateParam(this.form.证书有效期至)]
]
},
handleDelete(row) {
this.$confirm('是否删除?', '提示', {
confirmButtonText: '确定',
@@ -426,8 +442,119 @@ export default {
handleCurrentChanges(val) {
this.pageCurrent = val
this.searchTable()
},
tableRowClassName({ row }) {
const level = this.getCertificateLevel(row.证书有效期至)
if (level === 0) return 'certificate-expired-row'
if (level === 1) return 'certificate-warning-row'
return ''
},
getCertificateLevel(value) {
const time = this.getDateTime(value)
if (!time) return 2
const today = new Date()
today.setHours(0, 0, 0, 0)
const warningEnd = new Date(today)
warningEnd.setDate(warningEnd.getDate() + 30)
if (time < today.getTime()) return 0
if (time <= warningEnd.getTime()) return 1
return 2
},
getDateTime(value) {
if (!value) return 0
const date = new Date(value)
if (Number.isNaN(date.getTime())) return 0
date.setHours(0, 0, 0, 0)
return date.getTime()
},
formatDate(value) {
if (!value) return ''
if (typeof value === 'string') {
const timestampMatch = value.match(/\/Date\((\d+)\)\//)
if (timestampMatch) return this.formatDate(new Date(Number(timestampMatch[1])))
const match = value.match(/\d{4}[-/.]\d{1,2}[-/.]\d{1,2}/)
return match ? match[0].replace(/[/.]/g, '-') : ''
}
const date = new Date(value)
if (Number.isNaN(date.getTime())) return ''
const month = `${date.getMonth() + 1}`.padStart(2, '0')
const day = `${date.getDate()}`.padStart(2, '0')
return `${date.getFullYear()}-${month}-${day}`
},
parseStationBinding(value) {
if (!value) return []
if (Array.isArray(value)) return value
return `${value}`.split(/[,]/).map(item => item.trim()).filter(item => item)
},
formatStationNames(value) {
const values = this.parseStationBinding(value)
if (values.length === 0) return ''
return values.map(value => {
const option = this.stationOptions.find(item => item.value === value)
return option ? option.label : value
}).join(',')
},
nullIfEmpty(value) {
return value === '' || value === undefined ? null : value
},
dateParam(value) {
if (value === null || value === undefined || value === '') return ''
return this.formatDate(value)
},
parseDateValue(value) {
if (!value) return null
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value
const text = this.formatDate(value)
if (!text) return null
const parts = text.split('-').map(item => Number(item))
if (parts.length !== 3 || parts.some(item => Number.isNaN(item))) return null
return new Date(parts[0], parts[1] - 1, parts[2])
},
debugDateValue(label, value) {
if (process.env.NODE_ENV !== 'production') {
console.log(`[DeviceInformation] ${label}:`, value, this.parseDateValue(value))
}
}
}
}
</script>
<style scoped>
.device-card {
height: 850px;
}
.filter-bar {
display: flex;
align-items: center;
gap: 10px;
margin-top: 7px;
margin-bottom: 7px;
}
.filter-control {
width: 200px;
}
.device-table {
width: 100%;
}
.pagination-bar {
margin-top: 10px;
}
.device-form ::v-deep .el-input,
.device-form ::v-deep .el-select,
.device-form ::v-deep .el-date-editor {
width: 260px;
}
.device-information-page ::v-deep .certificate-expired-row td {
background-color: #fde2e2 !important;
}
.device-information-page ::v-deep .certificate-warning-row td {
background-color: #fdf6d3 !important;
}
</style>

View File

@@ -0,0 +1,544 @@
<template>
<div class="timely-kit-page">
<el-card>
<div class="search-container" @keyup.enter="searchTable">
<span class="show-text">生产订单:</span>
<el-input v-model="orderNo" clearable placeholder="请输入生产订单" class="filter-control" />
<span class="show-text">销售订单:</span>
<el-input v-model="contractNo" clearable placeholder="请输入销售订单" class="filter-control" />
<span class="show-text">物料编码:</span>
<el-input v-model="productCode" clearable placeholder="请输入产品编码" class="filter-control" />
<span class="show-text">计划号:</span>
<el-input v-model="planNo" clearable placeholder="请输入计划号" class="filter-control" />
<span class="show-text">发货状态:</span>
<el-select v-model="shipmentStatus" clearable placeholder="请选择" class="filter-control">
<el-option label="全部" value="全部" />
<el-option label="已下达发货通知" value="已发货" />
<el-option label="未下达发货通知" value="未发货" />
</el-select>
<span class="show-text">齐套:</span>
<el-select v-model="complete" clearable placeholder="请选择" class="small-filter">
<el-option label="全部" value="全部" />
<el-option label="是" value="是" />
<el-option label="否" value="否" />
</el-select>
<span class="show-text">完成状态:</span>
<el-select v-model="checkStatus" clearable placeholder="请选择" class="filter-control">
<el-option label="全部" value="" />
<el-option label="已完成" value="已完成" />
<el-option label="未完成" value="未完成" />
</el-select>
<span class="show-text">发料状态:</span>
<el-select v-model="sendStatus" clearable placeholder="请选择" class="filter-control">
<el-option label="全部" value="全部" />
<el-option label="发料齐套" value="发料齐套" />
<el-option label="发料缺件" value="发料缺件" />
<el-option label="未发料" value="未发料" />
</el-select>
<span class="show-text">自制件属性:</span>
<el-select v-model="selfStatus" clearable placeholder="请选择" class="filter-control">
<el-option label="全部" value="全部" />
<el-option label="机加" value="机加" />
<el-option label="装配" value="装配" />
</el-select>
<span class="show-text">计划开始:</span>
<el-date-picker
v-model="planStart"
clearable
format="yyyy-MM-dd"
placeholder="选择日期"
type="date"
value-format="yyyy-MM-dd"
class="filter-control"
/>
<el-button :disabled="loading" icon="el-icon-search" plain type="primary" @click="searchTable">查询</el-button>
</div>
<el-table
v-loading="loading"
:data="tableData"
:height="'78vh'"
border
element-loading-background="rgba(0, 0, 0, 0.2)"
element-loading-spinner="el-icon-loading"
highlight-current-row
size="mini"
style="width: 100%; margin-top: 16px"
tooltip-effect="dark"
>
<el-table-column type="expand">
<template slot-scope="scope">
<el-table
:data="scope.row.缺件明细"
border
size="mini"
class="detail-table"
:row-class-name="detailRowClassName"
>
<el-table-column align="center" label="序号" type="index" width="50" />
<el-table-column align="center" label="物料编码" prop="缺件物料编码" min-width="180" show-overflow-tooltip />
<el-table-column align="center" label="物料名称" prop="缺件物料名称" width="150" show-overflow-tooltip />
<el-table-column align="center" label="缺件数量" prop="缺件数量" width="90" />
<el-table-column align="center" label="属性" prop="缺件属性" width="90" />
<el-table-column align="center" label="生产订单号" prop="缺件生产订单号" width="100" />
<el-table-column align="center" label="进度" prop="进度" width="130" show-overflow-tooltip />
<el-table-column align="center" label="工艺路线/采购状态" min-width="520">
<template slot-scope="detail">
<div v-if="detail.row.缺件属性 === '外购'" class="purchase-route">
{{ detail.row.工艺路线 }}
</div>
<div v-else class="process-route" v-html="formatProcessRoute(detail.row)" />
</template>
</el-table-column>
<el-table-column align="center" label="预计完成时间" width="130">
<template slot-scope="detail">{{ formatDate(detail.row.预计完成时间) }}</template>
</el-table-column>
<el-table-column align="center" label="采购员" prop="采购员" width="90" show-overflow-tooltip />
<el-table-column align="center" label="采购未清数量" prop="采购未清数量" width="110" />
<el-table-column align="center" label="到货草稿" prop="到货草稿" width="90" />
<el-table-column align="center" label="质检状态" width="90">
<template slot-scope="detail">
<div :style="getStatusStyle(detail.row.质检状态)" class="score-cell">{{ detail.row.质检状态 }}</div>
</template>
</el-table-column>
</el-table>
</template>
</el-table-column>
<el-table-column align="center" label="序号" type="index" width="50" />
<el-table-column align="center" prop="合同号" label="销售订单" width="110" show-overflow-tooltip />
<el-table-column align="center" prop="计划号" label="计划号" width="100" show-overflow-tooltip />
<el-table-column align="center" prop="订单号" label="生产订单" width="90" />
<el-table-column align="center" prop="产品名称" label="产品名称" width="160" show-overflow-tooltip />
<el-table-column align="center" prop="产品编码" label="产品编码" min-width="210" show-overflow-tooltip />
<el-table-column align="center" prop="计划数量订单" label="计划数量" width="80" />
<el-table-column align="center" prop="入库数量" label="入库数量" width="80" />
<el-table-column align="center" label="要求完工日" width="120">
<template slot-scope="scope">{{ formatDate(scope.row.要求完工日期) }}</template>
</el-table-column>
<el-table-column align="center" label="要求发货日" width="120">
<template slot-scope="scope">{{ formatDate(scope.row.要求发货日期) }}</template>
</el-table-column>
<el-table-column align="center" label="预计齐套时间" width="130">
<template slot-scope="scope">{{ formatDate(scope.row.预计齐套时间) }}</template>
</el-table-column>
<el-table-column align="center" label="齐套" width="80">
<template slot-scope="scope">
<div :style="getStatusStyle(scope.row.齐套)" class="score-cell">{{ scope.row.齐套 }}</div>
</template>
</el-table-column>
<el-table-column align="center" label="拣配状态" width="90">
<template slot-scope="scope">
<div :style="getStatusStyle(scope.row.拣配状态)" class="score-cell">{{ scope.row.拣配状态 }}</div>
</template>
</el-table-column>
<el-table-column align="center" label="发料状态" width="90">
<template slot-scope="scope">
<div :style="getStatusStyle(scope.row.发料状态)" class="score-cell">{{ scope.row.发料状态 }}</div>
</template>
</el-table-column>
<el-table-column align="center" label="装配开工" width="90">
<template slot-scope="scope">
<div :style="getStatusStyle(scope.row.装配开工)" class="score-cell">{{ scope.row.装配开工 }}</div>
</template>
</el-table-column>
<el-table-column align="center" prop="完成数量" label="送检数量" width="90" />
<el-table-column align="center" label="终检" width="70">
<template slot-scope="scope">
<div :style="getStatusStyle(scope.row.终检)" class="score-cell">{{ scope.row.终检 }}</div>
</template>
</el-table-column>
<el-table-column align="center" label="工艺" width="70">
<template slot-scope="scope">
<div :style="getStatusStyle(scope.row.生产工艺)" class="score-cell">{{ scope.row.生产工艺 }}</div>
</template>
</el-table-column>
<el-table-column align="center" label="缺件项" width="80">
<template slot-scope="scope">{{ scope.row.缺件明细.length }}</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script>
export default {
name: 'TimelyKitTrackingResult',
data() {
return {
orderNo: '',
contractNo: '',
productCode: '',
productName: '',
processName: '',
planNo: '',
shipmentStatus: '全部',
complete: '全部',
checkStatus: '',
sendStatus: '全部',
selfStatus: '全部',
planStart: '',
tableData: [],
loading: false
}
},
created() {
this.searchTable()
},
methods: {
async searchTable() {
this.loading = true
this.tableData = []
const param = []
param.push(['订单号', this.orderNo || 0])
param.push(['物料类型', '自制件'])
param.push(['合同号', this.contractNo])
param.push(['产品编码', this.productCode])
param.push(['产品名称', this.productName])
param.push(['工序名称', this.processName])
param.push(['计划开始', ''])
param.push(['计划完成', this.planStart])
param.push(['排产状态', '全部'])
param.push(['指派对象', '全部'])
param.push(['计划号', this.planNo])
param.push(['发货状态', this.shipmentStatus])
param.push(['齐套', this.complete])
param.push(['检验状态', this.checkStatus])
param.push(['发料状态', this.sendStatus])
param.push(['自制件属性', this.selfStatus])
const data = this.CreateData('11', '生产管理_及时齐套跟踪结果_查询', param)
try {
const response = await this.ExecDatabase(data)
this.tableData = this.mergeRows(response.data || [])
if (this.tableData.length === 0) {
this.$message.info('未查询到及时齐套跟踪结果')
}
} catch (error) {
console.error('及时齐套跟踪结果查询失败:', error)
this.$message.error('查询失败')
} finally {
this.loading = false
}
},
mergeRows(rows) {
const map = new Map()
rows.forEach(row => {
const key = [
row.订单号 || '',
row.合同号 || '',
row.计划号 || '',
row.产品编码 || ''
].join('|')
if (!map.has(key)) {
map.set(key, {
计划号: row.计划号,
合同号: row.合同号,
订单号: row.订单号,
产品编码: row.产品编码,
产品名称: row.产品名称,
计划数量订单: this.formatQuantity(row.计划数量订单),
入库数量: this.formatQuantity(row.入库数量),
要求完工日期: row.要求完工日期,
要求发货日期: row.要求发货日期,
预计齐套时间: row.预计齐套时间,
齐套: row.齐套,
拣配状态: row.拣配状态,
发料状态: row.发料状态,
装配开工: row.装配开工,
完成数量: this.formatQuantity(row.完成数量),
终检: row.终检,
生产工艺: row.生产工艺,
缺件明细: []
})
}
const detailKey = [
row.缺件生产订单号 || '',
row.缺件物料编码 || '',
row.缺件属性 || ''
].join('|')
const current = map.get(key)
if (current.缺件明细.some(item => item.detailKey === detailKey)) {
return
}
current.缺件明细.push({
detailKey,
缺件物料编码: row.缺件物料编码,
缺件物料名称: row.缺件物料名称,
缺件数量: this.formatQuantity(row.缺件数量),
缺件属性: row.缺件属性,
缺件生产订单号: row.缺件生产订单号,
进度: row.进度,
工艺路线: row.工艺路线,
工艺路线明细: this.parseRouteDetails(row.工艺路线明细),
预计完成时间: row.预计完成时间,
采购员: row.采购员,
采购未清数量: this.formatQuantity(row.采购未清数量),
到货草稿: row.到货草稿,
质检状态: row.质检状态,
SAP单据: row.SAP单据,
SAP行号: row.SAP行号
})
})
return Array.from(map.values())
},
detailRowClassName() {
return 'detail-row'
},
parseRouteDetails(value) {
if (!value) return []
if (Array.isArray(value)) return value
try {
const parsed = JSON.parse(value)
return Array.isArray(parsed) ? parsed : []
} catch (error) {
return []
}
},
toProcessNumber(value) {
const num = Number(value)
return Number.isFinite(num) ? num : 0
},
isValidProcessDate(value) {
if (!value) return false
const date = new Date(value)
return !Number.isNaN(date.getTime()) && date > new Date('1900-01-01 00:00:00')
},
getProcessStatusNum(value) {
const num = parseInt(value)
if (Number.isFinite(num)) return num
const statusMap = {
辅助开始: 2,
辅助结束: 3,
开始加工: 4,
暂停: 5,
报工: 6
}
return statusMap[value] || 0
},
isProcessComplete(process) {
const progress = this.toProcessNumber(process.进度)
const planCount = this.toProcessNumber(process.计划数量)
const finishCount = this.toProcessNumber(process.完成数量)
const checkedCount = this.toProcessNumber(process.收检合格数) + this.toProcessNumber(process.收检不合格数)
return progress >= 100 ||
(planCount > 0 && finishCount >= planCount) ||
(planCount > 0 && checkedCount >= planCount)
},
getProcessClass(process, index, processes) {
if (this.isProcessComplete(process)) {
return 'process-route-blue'
}
const statusNum = this.getProcessStatusNum(process.加工状态)
if (this.isValidProcessDate(process.开工时间) || [2, 4].includes(statusNum) || this.toProcessNumber(process.进度) > 0) {
return 'process-route-green'
}
if (index > 0 && this.isProcessComplete(processes[index - 1])) {
return 'process-route-orange'
}
return 'process-route-red'
},
getRouteProcessName(process) {
const name = String(process.工序名称 || '')
const assignee = String(process.指派对象 || '')
if (!assignee || name.includes(`(${assignee})`) || name.includes(`${assignee}`)) {
return name
}
return `${name}(${assignee})`
},
escapeProcessHtml(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
},
formatProcessRoute(detail) {
const processes = Array.isArray(detail.工艺路线明细) ? detail.工艺路线明细 : []
if (processes.length === 0) {
return this.escapeProcessHtml(detail.工艺路线 || '')
}
const maxPerRow = 8
const rows = Math.ceil(processes.length / maxPerRow)
let html = '<div class="process-route-wrap"><table class="process-route-table"><tbody>'
for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
const startIndex = rowIndex * maxPerRow
const rowProcesses = processes.slice(startIndex, startIndex + maxPerRow)
if (rowIndex > 0) {
html += `<tr class="process-route-spacer"><td colspan="${maxPerRow * 2 - 1}"></td></tr>`
}
html += '<tr>'
rowProcesses.forEach((process, index) => {
const className = this.getProcessClass(process, startIndex + index, processes)
const date = this.formatDate(process.计划开始时间 || process.计划完成时间)
html += `<td class="process-route-date ${className}">${this.escapeProcessHtml(date)}</td>`
if (index < rowProcesses.length - 1) html += '<td class="process-route-arrow-hidden"></td>'
})
html += '</tr><tr>'
rowProcesses.forEach((process, index) => {
const className = this.getProcessClass(process, startIndex + index, processes)
html += `<td class="${className}">${this.escapeProcessHtml(this.getRouteProcessName(process))}</td>`
if (index < rowProcesses.length - 1) html += '<td class="process-route-arrow">→</td>'
})
html += '</tr><tr>'
rowProcesses.forEach((process, index) => {
const className = this.getProcessClass(process, startIndex + index, processes)
const count = this.formatQuantity(process.收检合格数)
html += `<td class="process-route-count ${className}">合格${this.escapeProcessHtml(count)}</td>`
if (index < rowProcesses.length - 1) html += '<td class="process-route-arrow-hidden"></td>'
})
html += '</tr>'
}
html += '</tbody></table></div>'
return html
},
formatDate(value) {
if (!value) return ''
return String(value).split('T')[0].split(' ')[0]
},
formatQuantity(value) {
if (value === null || value === undefined || value === '') return ''
const numberValue = Number(value)
if (Number.isNaN(numberValue)) return value
return Number.isInteger(numberValue) ? String(numberValue) : String(numberValue).replace(/0+$/, '').replace(/\.$/, '')
},
getStatusStyle(value) {
if (value === '是' || value === '发料齐套' || value === '已拣配' || value === '已开工' || value === '已完成') {
return {
backgroundColor: '#f0f9eb',
color: '#67c23a'
}
}
if (value === '否' || value === '发料缺件' || value === '未拣配' || value === '待执行' || value === '未完成') {
return {
backgroundColor: '#fdf6ec',
color: '#e6a23c'
}
}
return {}
}
}
}
</script>
<style scoped>
.timely-kit-page {
padding: 0;
}
.search-container {
display: flex;
flex-wrap: wrap;
gap: 8px 0;
}
.show-text {
line-height: 32px;
margin-right: 5px;
white-space: nowrap;
}
.filter-control {
margin-right: 10px;
width: 150px;
}
.small-filter {
margin-right: 10px;
width: 100px;
}
.score-cell {
border-radius: 3px;
padding: 2px 4px;
}
.detail-table {
margin: 4px 0;
}
.purchase-route {
line-height: 22px;
text-align: left;
white-space: normal;
}
.process-route {
overflow-x: auto;
padding: 2px 0;
}
::v-deep .process-route-wrap {
min-width: 480px;
}
::v-deep .process-route-table {
border-collapse: separate;
border-spacing: 0;
margin: 0 auto;
}
::v-deep .process-route-table td {
border-radius: 2px;
line-height: 20px;
min-width: 72px;
padding: 2px 5px;
text-align: center;
white-space: nowrap;
}
::v-deep .process-route-date,
::v-deep .process-route-count {
font-size: 12px;
}
::v-deep .process-route-arrow,
::v-deep .process-route-arrow-hidden {
background: transparent;
color: #606266;
min-width: 18px;
width: 18px;
}
::v-deep .process-route-arrow-hidden {
color: transparent;
}
::v-deep .process-route-spacer td {
height: 6px;
line-height: 6px;
padding: 0;
}
::v-deep .process-route-green {
background: #f0f9eb;
color: #67c23a;
}
::v-deep .process-route-blue {
background: #ecf5ff;
color: #409eff;
}
::v-deep .process-route-orange {
background: #fdf6ec;
color: #e6a23c;
}
::v-deep .process-route-red {
background: #fef0f0;
color: #f56c6c;
}
::v-deep .detail-row td {
background-color: #f0f9eb !important;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -399,7 +399,7 @@
>
检验记录
</div> -->
<!-- <div class="grid-btn" style="margin-left: 50px" @click="Abnormalcontrol()">异常管控</div> -->
<div class="grid-btn" style="margin-left: 50px" @click="Abnormalcontrol()">异常管控</div>
<div
class="grid-btn"
style="margin-left: 50px"
@@ -1275,6 +1275,15 @@
<el-button type="primary" @click="abnormalForm()"> </el-button>
</div>
</el-dialog>
<el-dialog
:visible.sync="abnormalControlDialogVisible"
title="异常管控"
width="1200px"
center
append-to-body
:close-on-click-modal="false">
<abnormal-control-panel :initial-selected-row="selectedRow" />
</el-dialog>
<el-dialog
:visible.sync="addRecordDialogVisible"
title="新增工时记录"
@@ -1841,7 +1850,11 @@ import { MESDownFile } from '@/utils/request'
import { postB1 } from '@/api/b1s';
import { getB1 } from '@/api/b1s';
import { posttm } from '@/api/tm';
import AbnormalControlPanel from '@/views/ProductionManagement/components/AbnormalControlPanel.vue';
export default {
components: {
AbnormalControlPanel
},
data() {
return {
TaskAID: "",
@@ -2015,6 +2028,7 @@ export default {
currentInspectionRecord: {}, // 当前选中的检验记录
abnormalControlDialogVisible: false,
//异常管控相关数据
orderNo: '',
contractNo: '',
@@ -3479,6 +3493,9 @@ async workendForm() {
this.abnormalVisible = true;
this.abnormalTable();
},
Abnormalcontrol() {
this.abnormalControlDialogVisible = true;
},
abnormalTable() {
var param = [];
param.push(["操作人", this.$store.state.user.name]);

View File

@@ -2131,6 +2131,15 @@
</el-button>
</div>
</el-dialog>
<el-dialog
:visible.sync="abnormalControlDialogVisible"
title="异常管控"
width="1200px"
center
append-to-body
:close-on-click-modal="false">
<abnormal-control-panel :initial-selected-row="selectedRow" />
</el-dialog>
<el-dialog :visible.sync="Abnormalcontrolpop"
title="异常管控"
width="1200px"
@@ -2271,9 +2280,6 @@
<el-input type="textarea" v-model="newProcessForm.description" :rows="3"
placeholder="请输入流程详细描述"></el-input>
</el-form-item>
<el-form-item label="上传附件" prop="title">
<el-button type="primary" icon="el-icon-circle-plus-outline" size="small" @click="uploadShowPDF" style="width: 100px;">点击上传</el-button>
</el-form-item>
<!-- 审批流程设置 -->
<div class="form-section" v-if="newProcessForm.type">
<div class="form-section-title">
@@ -2327,7 +2333,7 @@
<el-table v-loading="loading" :data="tableData5" :height="'40vh'" border
element-loading-background="rgba(0, 0, 0, 0.2)" element-loading-spinner="el-icon-loading"
highlight-current-row size="mini" style="width: 100%;" tooltip-effect="dark"
@row-click="handleRowClick">
@row-click="handleAbnormalControlTaskRowClick">
<!-- <el-table-column align="center" label="序号" type="index" width="50" fixed /> -->
<el-table-column align="center" prop="产品名称" label="物料名称" width="190" fixed/>
@@ -2492,23 +2498,6 @@
<el-button type="primary" @click="confirmComplete" :loading="completeLoading" style="width: auto;"> </el-button>
</span>
</el-dialog>
<!--上传文件-->
<!-- <el-dialog :visible.sync="imgDialogFormVisible" title="上传附件" center width="460px">
<el-upload
ref="uploadPDF"
:multiple="true"
:auto-upload="false"
style="margin-left: 30px"
drag>
<i class="el-icon-upload"/>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div slot="tip" class="el-upload__tip">上传文件且不超过100mb</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button @click="imgDialogFormVisible=false">取消</el-button>
<el-button type="primary" style="width: 70px" @click="submitPicturePDF()">确定</el-button>
</div>
</el-dialog> -->
<!-- 退库弹窗 -->
<el-dialog
:visible.sync="ReturnWarehousepop"
@@ -2803,7 +2792,11 @@ import { postB1 } from '@/api/b1s';
import { getB1 } from '@/api/b1s';
import { posttm } from '@/api/tm';
import { seepdf, initPDFKeyboardShortcuts, setZoom } from '@/utils/pdf';
import AbnormalControlPanel from '@/views/ProductionManagement/components/AbnormalControlPanel.vue';
export default {
components: {
AbnormalControlPanel
},
data() {
return {
TaskAID:'',
@@ -2973,6 +2966,7 @@ export default {
currentInspectionRecord: {}, // 当前选中的检验记录
abnormalControlDialogVisible: false,
//异常管控相关数据
orderNo: '',
contractNo: '',
@@ -2990,8 +2984,6 @@ export default {
tableProcessName: '',
selectedRow: '',
loading: false,
imgDialogFormVisible: false,
// 转办相关数据
transferDialogVisible: false,
transferLoading: false,
@@ -3160,11 +3152,7 @@ export default {
this.getTypeList();
this.getOperationPersonList();
this.searchTable4();
this.searchTable1();
this.getUserList();
this.getProcessedTasks();
this.initPauseState()
this.loadOrderList('')
this.getBind()
@@ -4043,20 +4031,23 @@ confirmOvertime() {
// ⭐ 直接弹出加班确认弹窗
overtimeResult = await this.showOvertimeDialog()
if (overtimeResult && overtimeResult.persons && overtimeResult.persons.length > 0) {
// 添加调机人员到加班人员列表
const allOvertimePersons = [...overtimeResult.persons]
if (this.selectedOperatorsName && !allOvertimePersons.includes(this.selectedOperatorsName)) {
allOvertimePersons.push(this.selectedOperatorsName)
}
ordinaryPersons = overtimeResult.ordinaryPersons ? overtimeResult.ordinaryPersons.join(',') : ''
specialPersons = overtimeResult.specialPersons ? overtimeResult.specialPersons.join(',') : ''
if (overtimeResult) {
isOvertime = overtimeResult.isOvertime || 0
isRestDay = overtimeResult.isRestDay || 0
isLegalRestDay = overtimeResult.isLegalRestDay || 0
selectedShiftName = overtimeResult.selectedShift || this.shift
shiftStandardHours = this.shiftStandardHours || 0
shiftStandardHours = overtimeResult.shiftStandardHours || this.shiftStandardHours || 0
if (overtimeResult.persons && overtimeResult.persons.length > 0) {
// 添加调机人员到加班人员列表
const allOvertimePersons = [...overtimeResult.persons]
if (this.selectedOperatorsName && !allOvertimePersons.includes(this.selectedOperatorsName)) {
allOvertimePersons.push(this.selectedOperatorsName)
}
ordinaryPersons = overtimeResult.ordinaryPersons ? overtimeResult.ordinaryPersons.join(',') : ''
specialPersons = overtimeResult.specialPersons ? overtimeResult.specialPersons.join(',') : ''
}
} else if (overtimeResult === null || overtimeResult === undefined) {
// 用户关闭了弹窗,询问是否继续
const continueReport = await this.$confirm('未记录加班信息,是否继续辅助结束?', '提示', {
@@ -4447,15 +4438,18 @@ async proReportForm() {
// 每次都弹出加班确认弹框
overtimeResult = await this.showOvertimeDialog()
if (overtimeResult && overtimeResult.persons && overtimeResult.persons.length > 0) {
overtimePersons = overtimeResult.persons.join(',') // 所有人员
ordinaryPersons = overtimeResult.ordinaryPersons.join(',') // 普通人员
specialPersons = overtimeResult.specialPersons.join(',') // 特殊人员
if (overtimeResult) {
isOvertime = overtimeResult.isOvertime
isRestDay = overtimeResult.isRestDay
isLegalRestDay = overtimeResult.isLegalRestDay
selectedShiftName = overtimeResult.selectedShift || this.shift
shiftStandardHours = this.shiftStandardHours || 8
shiftStandardHours = overtimeResult.shiftStandardHours || this.shiftStandardHours || 8
if (overtimeResult.persons && overtimeResult.persons.length > 0) {
overtimePersons = overtimeResult.persons.join(',') // 所有人员
ordinaryPersons = overtimeResult.ordinaryPersons ? overtimeResult.ordinaryPersons.join(',') : '' // 普通人员
specialPersons = overtimeResult.specialPersons ? overtimeResult.specialPersons.join(',') : '' // 特殊人员
}
} else if (overtimeResult === null || overtimeResult === undefined) {
// 用户关闭了弹窗,询问是否继续
const continueReport = await this.$confirm('未记录加班信息,是否继续报工?', '提示', {
@@ -6047,21 +6041,6 @@ async proReportForm() {
//异常管控相关函数
uploadShowPDF(val) {
this.imgDialogFormVisible = true
},
submitPicturePDF() {
// 修改这里:确保参数名与后台一致
this.DataPDF = {
num: this.uploadValue, // 取消注释,使用实际值
tableName: '流程表',
worker: this.$store.state.user.name, // 取消注释
uploadTime: this.currentTime // 取消注释
}
console.log('上传参数:', JSON.stringify(this.DataPDF))
this.$refs.uploadPDF.submit()
this.imgDialogFormVisible = false
},
getProcessedTasks() {
var params = []
params.push(['审批人ID', this.$store.state.user.id])
@@ -6202,7 +6181,7 @@ async proReportForm() {
},
// 数据处理方法
handleRowClick(row) {
handleAbnormalControlTaskRowClick(row) {
this.selectedRow = row
this.tableOrderNum = row.订单号
this.tableProductCode = row.物料编码
@@ -6423,7 +6402,7 @@ submitNewProcess() {
return new Date(deadline) - new Date() < 24 * 60 * 60 * 1000;
},
Abnormalcontrol(){
this.Abnormalcontrolpop = true
this.abnormalControlDialogVisible = true
},
ReturnWarehouse(){
this.ReturnWarehousepop = true

View File

@@ -0,0 +1,298 @@
<template>
<div class="settlement-workhours">
<el-card>
<div class="search-container">
<span class="show-text">生产订单号:</span>
<el-input v-model="searchForm.orderNo" placeholder="请输入生产订单号" clearable style="width: 140px; margin-right: 10px" />
<span class="show-text">物料编码:</span>
<el-input v-model="searchForm.productCode" placeholder="请输入物料编码" clearable style="width: 160px; margin-right: 10px" />
<span class="show-text">工位:</span>
<el-input v-model="searchForm.station" placeholder="请输入工位" clearable style="width: 120px; margin-right: 10px" />
<span class="show-text">数据条数:</span>
<el-input v-model="searchForm.datanum" placeholder="数据条数" clearable style="width: 100px; margin-right: 10px" />
<span class="show-text">开始日期:</span>
<el-date-picker
v-model="searchForm.startDate"
type="date"
placeholder="选择开始日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
style="width: 150px; margin-right: 10px" />
<span class="show-text">结束日期:</span>
<el-date-picker
v-model="searchForm.endDate"
type="date"
placeholder="选择结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
style="width: 150px; margin-right: 10px" />
<el-button :loading="loading" icon="el-icon-search" plain type="primary" @click="handleSearch">查询</el-button>
</div>
<el-table
v-loading="loading"
:data="tableData"
:height="tableHeight"
border
size="mini"
row-key="id"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
default-expand-all
class="custom-tree-table"
:row-class-name="tableRowClassName"
style="width: 100%">
<el-table-column prop="taskDescription" label="任务描述" min-width="380" show-overflow-tooltip>
<template slot-scope="scope">
<span :class="{ 'bold-text': scope.row.level === 1 }">{{ scope.row.taskDescription || '' }}</span>
</template>
</el-table-column>
<el-table-column prop="productionOrderNo" label="生产订单号" width="130" align="center" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.productionOrderNo || '' }}
</template>
</el-table-column>
<el-table-column prop="station" label="工位" width="110" align="center" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.station || '' }}
</template>
</el-table-column>
<el-table-column prop="reportWorkHours" label="报工工时" width="100" align="center">
<template slot-scope="scope">
<span :class="{ 'bold-text': scope.row.level <= 2 }">{{ scope.row.reportWorkHours || '' }}</span>
</template>
</el-table-column>
<el-table-column prop="auxiliaryWorkHours" label="主辅工时" width="100" align="center">
<template slot-scope="scope">
<span :class="{ 'bold-text': scope.row.level <= 2 }">{{ scope.row.auxiliaryWorkHours || '' }}</span>
</template>
</el-table-column>
<el-table-column prop="reportFinishQty" label="报工完成数" width="110" align="center">
<template slot-scope="scope">
<span :class="{ 'bold-text': scope.row.level <= 2 }">{{ scope.row.reportFinishQty || '' }}</span>
</template>
</el-table-column>
<el-table-column prop="auxiliaryFinishQty" label="辅助完成数" width="110" align="center">
<template slot-scope="scope">
<span :class="{ 'bold-text': scope.row.level <= 2 }">{{ scope.row.auxiliaryFinishQty || '' }}</span>
</template>
</el-table-column>
<el-table-column prop="totalWorkHours" label="合计工时" width="100" align="center">
<template slot-scope="scope">
<span :class="{ 'bold-text': scope.row.level <= 2 }">{{ scope.row.totalWorkHours || '' }}</span>
</template>
</el-table-column>
<el-table-column prop="clampingTime" label="装夹时间" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.clampingTime || '' }}
</template>
</el-table-column>
<el-table-column prop="programTime" label="程序时间" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.programTime || '' }}
</template>
</el-table-column>
<el-table-column prop="settlementReportPieceHours" label="结算报工单件工时" width="140" align="center">
<template slot-scope="scope">
{{ scope.row.settlementReportPieceHours || '' }}
</template>
</el-table-column>
<el-table-column prop="settlementAuxiliaryPieceHours" label="结算辅助单件工时" width="140" align="center">
<template slot-scope="scope">
{{ scope.row.settlementAuxiliaryPieceHours || '' }}
</template>
</el-table-column>
<el-table-column prop="operatorNames" label="操作人" width="180" align="center" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.operatorNames || '' }}
</template>
</el-table-column>
</el-table>
<div class="summary-footer">
<span>报工工时<strong>{{ totals.reportWorkHours }}</strong> 小时 | 主辅工时<strong>{{ totals.auxiliaryWorkHours }}</strong> 小时 | 合计工时<strong>{{ totals.totalWorkHours }}</strong> 小时</span>
</div>
</el-card>
</div>
</template>
<script>
export default {
name: 'SettlementWorkhours',
data() {
return {
searchForm: {
contractNo: '',
productCode: '',
orderNo: '',
station: '',
startDate: '',
endDate: '',
datanum: 100
},
loading: false,
tableHeight: '68vh',
tableData: []
}
},
computed: {
totals() {
const totals = this.tableData.reduce((sum, row) => {
sum.reportWorkHours += this.parseNumber(row.reportWorkHours)
sum.auxiliaryWorkHours += this.parseNumber(row.auxiliaryWorkHours)
sum.totalWorkHours += this.parseNumber(row.totalWorkHours)
return sum
}, {
reportWorkHours: 0,
auxiliaryWorkHours: 0,
totalWorkHours: 0
})
return {
reportWorkHours: totals.reportWorkHours.toFixed(2),
auxiliaryWorkHours: totals.auxiliaryWorkHours.toFixed(2),
totalWorkHours: totals.totalWorkHours.toFixed(2)
}
}
},
methods: {
parseNumber(value) {
const numberValue = Number(value)
return Number.isFinite(numberValue) ? numberValue : 0
},
handleSearch() {
this.loadData()
},
loadData() {
this.loading = true
const param = [
['合同号', this.searchForm.contractNo || ''],
['产品编码', this.searchForm.productCode || ''],
['订单号', this.searchForm.orderNo || ''],
['工位', this.searchForm.station || ''],
['开始日期', this.searchForm.startDate || ''],
['结束日期', this.searchForm.endDate || ''],
['数据条数', this.searchForm.datanum || '']
]
const Data = this.CreateData('11', '生产管理_结算工时_查询', param)
this.ExecDatabase(Data).then(res => {
this.tableData = this.buildTree(res.data || [])
this.loading = false
}).catch(() => {
this.loading = false
})
},
buildTree(flatData) {
const materialMap = new Map()
flatData.forEach((row, index) => {
if (Number(row.Level) !== 1) return
materialMap.set(row.materialCode, {
id: `m-${index}`,
level: 1,
materialCode: row.materialCode,
materialDesc: row.materialDesc,
taskDescription: row.taskDescription,
reportWorkHours: row.reportWorkHours,
auxiliaryWorkHours: row.auxiliaryWorkHours,
reportFinishQty: row.reportFinishQty,
auxiliaryFinishQty: row.auxiliaryFinishQty,
totalWorkHours: row.totalWorkHours,
clampingTime: row.clampingTime,
programTime: row.programTime,
settlementReportPieceHours: row.settlementReportPieceHours,
settlementAuxiliaryPieceHours: row.settlementAuxiliaryPieceHours,
recordCount: row.recordCount,
children: []
})
})
flatData.forEach((row, index) => {
if (Number(row.Level) !== 2) return
const parent = materialMap.get(row.materialCode)
if (!parent) return
parent.children.push({
id: `o-${index}`,
level: 2,
materialCode: row.materialCode,
materialDesc: row.materialDesc,
taskDescription: row.taskDescription,
productionOrderNo: row.productionOrderNo,
station: row.station,
reportWorkHours: row.reportWorkHours,
auxiliaryWorkHours: row.auxiliaryWorkHours,
reportFinishQty: row.reportFinishQty,
auxiliaryFinishQty: row.auxiliaryFinishQty,
totalWorkHours: row.totalWorkHours,
clampingTime: row.clampingTime,
programTime: row.programTime,
settlementReportPieceHours: row.settlementReportPieceHours,
settlementAuxiliaryPieceHours: row.settlementAuxiliaryPieceHours,
operatorNames: row.operatorNames,
recordCount: row.recordCount
})
})
return Array.from(materialMap.values())
},
tableRowClassName({ row }) {
if (row.level === 1) return 'material-row'
return ''
}
}
}
</script>
<style scoped>
.settlement-workhours {
padding: 10px;
}
.search-container {
margin-bottom: 10px;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.show-text {
margin-right: 5px;
font-size: 14px;
color: #606266;
}
.bold-text {
font-weight: 700;
color: #303133;
}
::v-deep .el-table .material-row {
background: #f5f7fa !important;
}
::v-deep .el-table .material-row:hover > td {
background-color: #f5f7fa !important;
}
.summary-footer {
margin-top: 10px;
padding: 10px;
background: #f5f7fa;
border: 1px solid #ebeef5;
border-radius: 4px;
font-size: 14px;
color: #606266;
}
</style>

View File

@@ -563,6 +563,108 @@ export default {
getWorkHoursType(row) {
return row.工时类型 || row.workType || row.WorkType || row.workHoursType || row.WorkHoursType || '未分类'
},
getStartTimeValue(row) {
if (!row) return Number.MAX_SAFE_INTEGER
const value = row.startTime || row.planStart || row.开始时间 || row.dateLabel || ''
const text = String(value).trim()
if (!text) return Number.MAX_SAFE_INTEGER
const normalizedText = text.includes('T') ? text : text.replace(/-/g, '/')
const time = new Date(normalizedText).getTime()
return Number.isNaN(time) ? Number.MAX_SAFE_INTEGER : time
},
getEarliestStartTime(row) {
if (row && Array.isArray(row.children) && row.children.length > 0) {
return Math.min(...row.children.map(child => this.getEarliestStartTime(child)))
}
return this.getStartTimeValue(row)
},
sortRowsByStartTime(rows) {
return (rows || []).map(row => {
if (Array.isArray(row.children) && row.children.length > 0) {
row.children = this.sortRowsByStartTime(row.children)
}
return row
}).sort((a, b) => {
const diff = this.getEarliestStartTime(a) - this.getEarliestStartTime(b)
if (diff !== 0) return diff
return String(a.id || '').localeCompare(String(b.id || ''), 'zh-CN')
})
},
parseReportQty(value) {
const sourceValue = value === null || value === undefined ? 0 : value
const qty = Number(String(sourceValue).replace(/,/g, ''))
return Number.isFinite(qty) ? qty : 0
},
formatReportQty(value) {
if (!Number.isFinite(value)) return '0'
return Number.isInteger(value) ? String(value) : String(Number(value.toFixed(6)))
},
getTaskAID(row) {
if (!row) return ''
return row.TaskAID || row.taskAID || row.TaskId || row.taskId || row.任务ID || row.AID || ''
},
getReportQtyGroupKey(row) {
const taskAID = this.getTaskAID(row)
const startTime = row && (row.startTime || row.开始时间 || '')
if (!taskAID || !startTime) return ''
return `${taskAID}_${startTime}`
},
isAssemblyAssignee(row) {
const assignee = row && (row.指派对象 || row.工位名称 || row.assignee || '')
return String(assignee).includes('装配')
},
buildTaskStartReportQtyMap(rows) {
const qtyMap = new Map()
;(rows || []).forEach(row => {
if (Number(row.Level) !== 3) return
const key = this.getReportQtyGroupKey(row)
if (!key) return
const reportQty = row.reportQty === null || row.reportQty === undefined ? row.报工数量 : row.reportQty
const qty = this.parseReportQty(reportQty)
const current = qtyMap.has(key) ? qtyMap.get(key) : Number.NEGATIVE_INFINITY
qtyMap.set(key, Math.max(current, qty))
})
return qtyMap
},
getTaskStartReportQty(row, qtyMap) {
if (this.isAssemblyAssignee(row)) return '0'
const key = this.getReportQtyGroupKey(row)
if (!key || !qtyMap || !qtyMap.has(key)) return row.reportQty
return this.formatReportQty(qtyMap.get(key))
},
recalculateOperatorReportQty(treeData) {
;(treeData || []).forEach(operator => {
const operatorQtyMap = new Map()
let operatorHasTaskStartKey = false
;(operator.children || []).forEach(date => {
const dateQtyMap = new Map()
let dateHasTaskStartKey = false
;(date.children || []).forEach(detail => {
const key = detail.reportQtyGroupKey
if (!key) return
const qty = this.parseReportQty(detail.reportQty)
dateHasTaskStartKey = true
operatorHasTaskStartKey = true
const currentDateQty = dateQtyMap.has(key) ? dateQtyMap.get(key) : Number.NEGATIVE_INFINITY
const currentOperatorQty = operatorQtyMap.has(key) ? operatorQtyMap.get(key) : Number.NEGATIVE_INFINITY
dateQtyMap.set(key, Math.max(currentDateQty, qty))
operatorQtyMap.set(key, Math.max(currentOperatorQty, qty))
})
if (dateHasTaskStartKey) {
const dateQty = Array.from(dateQtyMap.values()).reduce((sum, qty) => sum + qty, 0)
date.reportQty = this.formatReportQty(dateQty)
}
})
if (operatorHasTaskStartKey) {
const operatorQty = Array.from(operatorQtyMap.values()).reduce((sum, qty) => sum + qty, 0)
operator.reportQty = this.formatReportQty(operatorQty)
}
})
},
getCurrentSummaryItems() {
const items = []
if (this.activeTab === 'project') {
@@ -684,7 +786,7 @@ export default {
}
})
return Array.from(level1Map.values())
return this.sortRowsByStartTime(Array.from(level1Map.values()))
},
loadOperatorData() {
@@ -702,7 +804,7 @@ export default {
const Data = this.CreateData('11', '工时统计_作业者工时_查询', param)
this.ExecDatabase(Data).then(res => {
this.operatorTableData = this.buildOperatorTree(res.data || [])
this.operatorTableData = this.buildOperatorTree(res.data || [], true)
this.loading = false
}).catch(() => {
this.loading = false
@@ -730,9 +832,10 @@ export default {
})
},
buildOperatorTree(flatData) {
buildOperatorTree(flatData, useTaskStartMaxReportQty = false) {
const level1Map = new Map()
const level2Map = new Map()
const reportQtyMap = useTaskStartMaxReportQty ? this.buildTaskStartReportQtyMap(flatData) : null
flatData.forEach((row, index) => {
if (row.Level === 1) {
@@ -761,9 +864,13 @@ export default {
const key = `${row.operator}_${row.dateLabel}`
const parent = level2Map.get(key)
if (parent) {
const taskAID = this.getTaskAID(row)
const reportQtyGroupKey = this.getReportQtyGroupKey(row)
const reportQty = useTaskStartMaxReportQty ? this.getTaskStartReportQty(row, reportQtyMap) : row.reportQty
parent.children.push({
id: `o-${index}`,
level: 3,
TaskAID: taskAID,
contractNo: row.contractNo,
orderNo: row.orderNo,
productCode: row.productCode,
@@ -775,7 +882,8 @@ export default {
班次: row.班次,
报工类型: row.报工类型,
指派对象: row.指派对象,
reportQty: row.reportQty,
reportQty,
reportQtyGroupKey,
startTime: row.startTime,
endTime: row.endTime,
workHours: row.workHours,
@@ -794,7 +902,11 @@ export default {
}
})
return Array.from(level1Map.values())
const treeData = this.sortRowsByStartTime(Array.from(level1Map.values()))
if (useTaskStartMaxReportQty) {
this.recalculateOperatorReportQty(treeData)
}
return treeData
},
loadTaskData() {
@@ -872,7 +984,7 @@ export default {
}
})
return Array.from(level1Map.values())
return this.sortRowsByStartTime(Array.from(level1Map.values()))
},
tableRowClassName({ row }) {

View File

@@ -45,60 +45,77 @@
:row-class-name="tableRowClassName"
style="width: 100%">
<el-table-column prop="taskAID" label="任务描述" width="500" show-overflow-tooltip>
<el-table-column prop="taskDescription" label="任务描述" width="360" show-overflow-tooltip>
<template slot-scope="scope">
<span :class="{'bold-text': scope.row.level === 1}">
{{ scope.row.level === 1 ? scope.row.taskAID :
scope.row.level === 2 ? scope.row.dateLabel :
scope.row.level === 3 ? `${scope.row.dateLabel} (${scope.row.shift})` : '' }}
{{ scope.row.level === 1 ? scope.row.taskDescription : '' }}
</span>
</template>
</el-table-column>
<!-- <el-table-column prop="reportCount" label="报工次数" width="80" align="center">
<el-table-column prop="dateLabel" label="日期" width="130" align="center" show-overflow-tooltip>
<template slot-scope="scope">
<span :class="{'bold-text': scope.row.level <= 2}">{{ scope.row.reportCount || '' }}</span>
<span :class="{'bold-text': scope.row.level === 2}">
{{ scope.row.level === 2 ? `日期${scope.row.dateLabel || ''}` : scope.row.dateLabel || '' }}
</span>
</template>
</el-table-column> -->
</el-table-column>
<el-table-column prop="shift" label="班次" width="100" align="center" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.shift || '' }}
</template>
</el-table-column>
<el-table-column prop="operatorNames" label="操作人" width="160" align="center" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.operatorNames || '' }}
</template>
</el-table-column>
<el-table-column prop="productionOrderNo" label="生产订单号" width="140" align="center" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.productionOrderNo || '' }}
</template>
</el-table-column>
<el-table-column prop="reportType" label="报工类型" width="110" align="center" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.reportType || '' }}
</template>
</el-table-column>
<el-table-column prop="reportQty" label="报工数量" width="90" align="center">
<template slot-scope="scope">
<span :class="{'bold-text': scope.row.level <= 2}">{{ scope.row.reportQty || '' }}</span>
</template>
</el-table-column>
<el-table-column prop="工位名称" label="工位名称" width="90" align="center">
<el-table-column prop="station" label="工位" width="90" align="center" show-overflow-tooltip>
<template slot-scope="scope">
<span :class="{'bold-text': scope.row.level <= 2}">{{ scope.row.工位名称 || '' }}</span>
{{ scope.row.station || '' }}
</template>
</el-table-column>
<el-table-column prop="actualWorkHours" label="实际工时" width="90" align="center">
<el-table-column prop="workHours" label="工时" width="90" align="center">
<template slot-scope="scope">
{{ scope.row.actualWorkHours || '' }}
<span :class="{'bold-text': scope.row.level <= 2}">{{ scope.row.workHours || '' }}</span>
</template>
</el-table-column>
<el-table-column prop="auxiliaryWorkHours" label="辅助工时" width="90" align="center">
<el-table-column prop="programTime" label="程序时间" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.auxiliaryWorkHours || '' }}
{{ scope.row.programTime || '' }}
</template>
</el-table-column>
<el-table-column prop="avgActualWorkHours" label="平均程序工时" width="100" align="center">
<el-table-column prop="pieceWorkHours" label="单件工时" width="110" align="center">
<template slot-scope="scope">
{{ scope.row.pieceWorkHours || '' }}
</template>
</el-table-column>
<el-table-column prop="avgActualWorkHours" label="平均实际工时" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.avgActualWorkHours || '' }}
</template>
</el-table-column>
<el-table-column prop="avgAuxiliaryWorkHours" label="平均辅助工时" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.avgAuxiliaryWorkHours || '' }}
</template>
</el-table-column>
<el-table-column prop="avgAuxiliaryWorkHours" label="工艺工时" width="100" align="center">
<el-table-column prop="processWorkHours" label="工艺工时" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.processWorkHours || '' }}
</template>
@@ -140,8 +157,7 @@
<!-- 底部汇总 -->
<div class="summary-footer">
<span>实际工时<strong>{{ currentTotalActualHours }}</strong> 小时 |
总辅助工时<strong>{{ currentTotalAuxiliaryHours }}</strong> 小时</span>
<span>总工时<strong>{{ currentTotalWorkHours }}</strong> 小时</span>
</div>
</el-card>
</div>
@@ -181,15 +197,9 @@ export default {
},
computed: {
// 计算当前Tab的总工时
currentTotalActualHours() {
currentTotalWorkHours() {
const total = this.processTableData.reduce((sum, item) => {
return sum + (parseFloat(item.actualWorkHours) || 0)
}, 0)
return total.toFixed(1)
},
currentTotalAuxiliaryHours() {
const total = this.processTableData.reduce((sum, item) => {
return sum + (parseFloat(item.auxiliaryWorkHours) || 0)
return sum + (parseFloat(item.workHours) || 0)
}, 0)
return total.toFixed(1)
}
@@ -224,6 +234,12 @@ export default {
handleTabChange() {
this.loadProcessData()
},
formatReportType(value) {
const reportType = String(value || '')
if (reportType.includes('辅助')) return '调机'
if (reportType.includes('报工')) return '正常'
return reportType
},
loadProcessData() {
this.loading = true
@@ -254,13 +270,13 @@ export default {
level1Map.set(row.TaskAID, {
id: `p1-${index}`,
level: 1,
taskAID: row.taskDescription,
TaskAID: row.TaskAID,
taskDescription: row.taskDescription,
reportCount: row.reportCount,
reportQty: '',
actualWorkHours: row.actualWorkHours,
auxiliaryWorkHours: row.auxiliaryWorkHours,
avgActualWorkHours: row.avgActualWorkHours,
avgAuxiliaryWorkHours: row.avgAuxiliaryWorkHours,
reportQty: row.reportQty,
workHours: row.workHours || row.actualWorkHours,
pieceWorkHours: row.pieceWorkHours,
processWorkHours: row.processWorkHours,
children: []
})
} else if (row.Level === 2) {
@@ -269,46 +285,45 @@ export default {
level2Map.set(key, {
id: `p2-${index}`,
level: 2,
taskAID: row.TaskAID,
TaskAID: row.TaskAID,
taskDescription: row.taskDescription,
dateLabel: row.dateLabel,
shift: '',
operatorNames: '',
productionOrderNo: '',
reportType: '',
reportCount: row.reportCount,
reportQty: '',
actualWorkHours: '',
auxiliaryWorkHours: '',
avgActualWorkHours: row.avgActualWorkHours,
avgAuxiliaryWorkHours: row.avgAuxiliaryWorkHours,
contractNo: '',
orderNo: '',
productCode: '',
productName: '',
processName: '',
reportQty: row.reportQty,
station: '',
workHours: row.workHours || row.actualWorkHours,
programTime: '',
pieceWorkHours: row.pieceWorkHours,
processWorkHours: row.processWorkHours,
startTime: '',
children: []
})
} else if (row.Level === 3) {
// 第三层:按TaskAID、日期和班次汇总
// 第三层:明细
const parentKey = `${row.TaskAID}_${row.dateLabel}`
const parent = level2Map.get(parentKey)
if (parent) {
parent.children.push({
id: `p3-${index}`,
level: 3,
taskAID: row.taskDescription,
TaskAID: row.TaskAID,
taskDescription: row.taskDescription,
dateLabel: row.dateLabel,
shift: row.shift,
reportCount: row.reportCount,
operatorNames: row.operatorNames,
productionOrderNo: row.productionOrderNo,
reportType: this.formatReportType(row.reportType),
reportQty: row.reportQty,
actualWorkHours: row.actualWorkHours,
auxiliaryWorkHours: row.auxiliaryWorkHours,
avgActualWorkHours: row.avgActualWorkHours,
avgAuxiliaryWorkHours: row.avgAuxiliaryWorkHours,
contractNo: row.contractNo,
orderNo: row.orderNo,
productCode: row.productCode,
productName: row.productName,
processName: row.processName,
processWorkHours:row.processWorkHours,
工位名称:row.工位名称,
station: row.station || row.工位名称,
workHours: row.workHours || row.actualWorkHours,
programTime: row.programTime,
pieceWorkHours: row.pieceWorkHours,
processWorkHours: row.processWorkHours,
startTime: row.startTime
})
}
}
@@ -316,7 +331,7 @@ export default {
// 构建树形结构
level2Map.forEach(l2Item => {
const parent = level1Map.get(l2Item.taskAID)
const parent = level1Map.get(l2Item.TaskAID)
if (parent) {
parent.children.push(l2Item)
}
@@ -410,4 +425,4 @@ export default {
::v-deep .el-table__expand-icon {
color: #606266;
}
</style>
</style>

View File

@@ -0,0 +1,994 @@
<template>
<div class="abnormal-control-panel">
<div class="workflow-container">
<!-- 统计信息 -->
<el-row :gutter="20" style="margin-bottom: 20px;">
<el-col :span="8">
<el-card class="stats-card" @click.native="switchTab('pending')">
<div class="stats-number">{{ stats.pending }}</div>
<div class="stats-label">待处理任务</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card class="stats-card" @click.native="switchTab('initiated')">
<div class="stats-number">{{ stats.initiated }}</div>
<div class="stats-label">我发起的</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card class="stats-card" @click.native="switchTab('processed')">
<div class="stats-number">{{ stats.completed }}</div>
<div class="stats-label">已完成</div>
</el-card>
</el-col>
</el-row>
<!-- 选项卡切换 -->
<el-tabs v-model="activeTab" type="border-card" class="workflow-tabs" @tab-click="handleTabClick">
<!-- 待我处理 -->
<el-tab-pane label="待我处理" name="pending">
<div class="filter-bar">
<el-input placeholder="搜索流程名称" v-model="filters.pending.keyword" class="filter-item" style="width: 200px;" clearable>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<el-select v-model="filters.pending.priority" placeholder="优先级" class="filter-item" clearable>
<el-option label="紧急" value="urgent"></el-option>
<el-option label="高" value="high"></el-option>
<el-option label="普通" value="normal"></el-option>
</el-select>
</div>
<div v-if="filteredPendingTasks.length > 0">
<el-card v-for="task in filteredPendingTasks" :key="task.节点ID" class="process-card">
<div slot="header" class="clearfix">
<el-tag :type="getPriorityType(task.优先级)" class="priority-tag">
{{ getPriorityText(task.优先级) }}
</el-tag>
<span v-if="task.是否转办" class="node-status status-transferred">转办</span>
<el-button style="float: right; padding: 3px 0" type="text" @click="handleTask(task)">处理</el-button>
<el-button size="mini" icon="el-icon-s-fold" @click="showTransferDialog(task)">转办</el-button>
</div>
<div>
<p><strong>流程名称:</strong> {{ task.流程标题 }}</p>
<p><strong>申请人:</strong> {{ task.申请人 }}</p>
<p><strong>申请时间:</strong> {{ task.申请时间 }}</p>
<p><strong>当前节点:</strong> {{ task.当前节点 }}</p>
<p><strong>描述:</strong> {{ task.流程描述 }}</p>
<p><strong>已关联生产订单:</strong> {{ task.已关联计划号 }}</p>
<p><strong>物料名称:</strong> {{ task.零件名称 }}</p>
<p><strong>物料编号:</strong> {{ task.零件编号 }}</p>
<p><strong>工序名称:</strong> {{ task.工序名称 }}</p>
</div>
</el-card>
</div>
<div v-else class="empty-state">
<el-empty description="暂无待处理任务"></el-empty>
</div>
</el-tab-pane>
<!-- 我发起的 -->
<el-tab-pane label="我发起的" name="initiated">
<div class="filter-bar">
<el-input placeholder="搜索流程名称" v-model="filters.initiated.keyword" class="filter-item" style="width: 200px;" clearable>
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
<el-select v-model="filters.initiated.status" placeholder="状态" class="filter-item" clearable>
<el-option label="审批中" value="processing"></el-option>
<el-option label="已完成" value="completed"></el-option>
<el-option label="已拒绝" value="rejected"></el-option>
</el-select>
<el-button type="primary" icon="el-icon-plus" @click="showCreateDialog" style="width: auto;">发起新流程</el-button>
</div>
<el-table :data="filteredInitiatedProcesses" style="width: 100%">
<el-table-column prop="流程标题" label="流程名称" width="150"></el-table-column>
<el-table-column prop="发起时间" label="发起时间" width="200"></el-table-column>
<el-table-column prop="当前节点" label="当前节点" width="120"></el-table-column>
<el-table-column prop="状态" label="状态" width="100">
<template slot-scope="scope">
<el-tag :type="getStatusType(scope.row.状态)">
{{ getStatusText(scope.row.状态) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template slot-scope="scope">
<el-button type="text" @click="viewProcessDetail(scope.row)">查看进度</el-button>
<el-button v-if="scope.row.状态 === 'processing'" type="text" @click="showCompleteDialog(scope.row)">完结</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 我已处理 -->
<el-tab-pane label="我已处理" name="processed">
<el-table :data="processedTasks" style="width: 100%">
<el-table-column prop="流程名称" label="流程名称" width="150"></el-table-column>
<el-table-column prop="申请人" label="申请人" width="100"></el-table-column>
<el-table-column prop="处理时间" label="处理时间" width="200"></el-table-column>
<el-table-column prop="处理结果" label="处理结果" width="100">
<template slot-scope="scope">
<el-tag v-if="scope.row.处理结果 === 'transferred'">
转办
</el-tag>
<el-tag v-else :type="scope.row.处理结果 === 'approved' ? 'success' : 'danger'">
{{ scope.row.处理结果 === 'approved' ? '同意' : '拒绝' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="审批意见" label="审批意见"></el-table-column>
<el-table-column label="操作" width="100">
<template slot-scope="scope">
<el-button type="text" @click="viewProcessDetail(scope.row)">查看详情</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 发起流程对话框 -->
<el-dialog title="发起新流程" :visible.sync="createDialogVisible" width="1000px" :close-on-click-modal="false" :append-to-body="true">
<el-form :model="newProcessForm" label-width="120px" :rules="formRules" ref="processForm">
<!-- 基础信息 -->
<div class="form-section">
<div class="form-section-title">
<i class="el-icon-info"></i>基础信息
</div>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="优先级" prop="priority">
<el-select v-model="newProcessForm.priority" placeholder="请选择优先级" style="width: 100%">
<el-option label="普通" value="normal"></el-option>
<el-option label="高" value="high"></el-option>
<el-option label="紧急" value="urgent"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="流程标题" prop="title">
<el-input v-model="newProcessForm.title" placeholder="请输入流程标题"></el-input>
</el-form-item>
<el-form-item label="流程描述" prop="description">
<el-input type="textarea" v-model="newProcessForm.description" :rows="3"
placeholder="请输入流程详细描述"></el-input>
</el-form-item>
<!-- 审批流程设置 -->
<div class="form-section" v-if="newProcessForm.type">
<div class="form-section-title">
<i class="el-icon-s-check"></i>审批流程设置
</div>
<el-form-item label="审批人" prop="approverIds">
<el-select
v-model="newProcessForm.approverIds"
multiple
filterable
clearable
collapse-tags
placeholder="请选择审批人"
style="width: 100%;">
<el-option
v-for="user in userList"
:key="user.id"
:label="user.name"
:value="user.id">
</el-option>
</el-select>
</el-form-item>
</div>
</div>
<div class="form-section">
<div class="form-section-title">
<i class="el-icon-info"></i>绑定任务信息
</div>
<el-form-item v-if="selectedRow" label="已关联生产订单">
<el-input v-model="tableOrderNum" placeholder="" readonly></el-input>
</el-form-item>
<el-form-item v-if="selectedRow" label="物料名称">
<el-input v-model="tableProductName" placeholder="" readonly></el-input>
</el-form-item>
<el-form-item v-if="selectedRow" label="物料编号">
<el-input v-model="tableProductCode" placeholder="" readonly></el-input>
</el-form-item>
<el-form-item v-if="selectedRow" label="工序名称">
<el-input v-model="tableProcessName" placeholder="" readonly></el-input>
</el-form-item>
<div class="search-container">
<span class="show-text">生产订单:</span>
<el-input v-model="orderNo" placeholder="请输入生产订单" clearable style="width: 150px;margin-right: 10px" />
<span class="show-text">产品编码:</span>
<el-input v-model="productCode" placeholder="请输入产品编码" clearable style="width: 150px;margin-right: 10px" />
<span class="show-text">产品名称:</span>
<el-input v-model="productName" placeholder="请输入产品名称" clearable style="width: 150px;margin-right: 10px" />
<el-button :disabled="loading" icon="el-icon-search" plain type="primary" @click="searchTable3()">查询</el-button>
</div>
<div class="custom-tabs">
<el-table v-loading="loading" :data="tableData5" :height="'40vh'" border
element-loading-background="rgba(0, 0, 0, 0.2)" element-loading-spinner="el-icon-loading"
highlight-current-row size="mini" style="width: 100%;" tooltip-effect="dark"
@row-click="handleRowClick">
<el-table-column align="center" label="序号" type="index" width="50" fixed />
<el-table-column align="center" prop="合同号" label="销售订单" width="120" fixed />
<el-table-column align="center" prop="订单号" label="生产订单" width="100" fixed />
<el-table-column align="center" prop="产品编码" label="零件编码" width="220" />
<el-table-column align="center" prop="产品名称" label="产品名称" width="190" />
<el-table-column align="center" prop="计划数量" label="计划数" width="80" />
<el-table-column align="center" prop="工序名称" label="工序名称" width="120" />
<el-table-column align="center" prop="指派对象编号" label="指派对象编号" width="120" />
<el-table-column align="center" prop="指派对象" label="指派对象" width="120" />
<el-table-column align="center" prop="指派数量" label="指派数量" width="120" />
<el-table-column align="center" prop="计划开始时间" label="计划开始时间" width="120" />
<el-table-column align="center" prop="计划完成时间" label="计划完成时间" width="120" />
<el-table-column align="center" prop="最晚开始时间" label="最晚开始时间" width="120" />
<el-table-column align="center" label="要求完工日期" width="120">
<template slot-scope="scope">
{{ formatDate(scope.row.要求完工日期) }}
</template>
</el-table-column>
</el-table>
</div>
</div>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="createDialogVisible = false"> </el-button>
<el-button type="primary" @click="submitNewProcess"> </el-button>
</span>
</el-dialog>
<!-- 处理任务对话框 -->
<el-dialog title="处理任务" :visible.sync="handleDialogVisible" width="700px" :append-to-body="true">
<div v-if="currentTask">
<el-descriptions :column="2" border>
<el-descriptions-item label="申请人">{{ currentTask.申请人 }}</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ currentTask.申请时间 }}</el-descriptions-item>
<el-descriptions-item label="优先级">
<el-tag :type="getPriorityType(currentTask.优先级)">
{{ getPriorityText(currentTask.优先级) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="描述" :span="2">{{ currentTask.流程描述 }}</el-descriptions-item>
<el-descriptions-item label="已关联生产订单">{{ currentTask.已关联计划号 }}</el-descriptions-item>
<el-descriptions-item label="物料名称">{{ currentTask.零件名称 }}</el-descriptions-item>
<el-descriptions-item label="物料编号">{{ currentTask.零件编号 }}</el-descriptions-item>
<el-descriptions-item label="工序名称">{{ currentTask.工序名称 }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-form :model="handleForm" label-width="100px" ref="approvalForm">
<el-form-item label="处理意见" required>
<el-radio-group v-model="handleForm.result">
<el-radio label="approved">同意</el-radio>
<el-radio label="rejected">拒绝</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审批意见">
<el-input type="textarea" v-model="handleForm.comment" :rows="3" placeholder="请输入审批意见"></el-input>
</el-form-item>
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="handleDialogVisible = false"> </el-button>
<el-button type="primary" @click="submitHandle(currentTask)"> </el-button>
</span>
</el-dialog>
<!-- 查看流程进度对话框 -->
<el-dialog title="流程进度详情" :visible.sync="detailDialogVisible" width="800px" :append-to-body="true">
<div v-if="currentProcess">
<el-descriptions :column="2" border>
<el-descriptions-item label="流程名称">{{ currentProcess.processInfo.流程标题 }}</el-descriptions-item>
<el-descriptions-item label="发起人">{{ currentProcess.processInfo.发起人姓名 }}</el-descriptions-item>
<el-descriptions-item label="发起时间">{{ currentProcess.processInfo.创建时间 }}</el-descriptions-item>
<el-descriptions-item label="当前状态">
<el-tag :type="getStatusType(currentProcess.processInfo.状态)">
{{ getStatusText(currentProcess.processInfo.状态) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="已关联生产订单">{{ currentProcess.processInfo.已关联计划号 }}</el-descriptions-item>
<el-descriptions-item label="物料名称">{{ currentProcess.processInfo.零件名称 }}</el-descriptions-item>
<el-descriptions-item label="物料编号">{{ currentProcess.processInfo.零件编号 }}</el-descriptions-item>
<el-descriptions-item label="工序名称">{{ currentProcess.processInfo.工序名称 }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<h4>审批流程</h4>
<el-steps :active="currentProcess.processInfo.当前步骤" align-center style="margin: 20px 0;">
<el-step v-for="(step, index) in currentProcess.steps" :key="index"
:title="step.标题" :description="step.描述"></el-step>
</el-steps>
<el-divider></el-divider>
<h4>审批记录</h4>
<el-timeline style="margin-top: 20px;">
<el-timeline-item v-if="record.处理结果 != 'transferred'" v-for="(record, index) in currentProcess.approvalRecords"
:key="index" :timestamp="record.时间" :type="record.处理结果 === 'approved' ? 'success' : 'danger'"
:icon="record.处理结果 === 'approved' ? 'el-icon-check' : 'el-icon-close'">
<el-card>
<h4>{{ record.审批人姓名 }} - {{ record.处理结果 === 'approved' ? '同意' : '拒绝' }}</h4>
<p>{{ record.审批意见 || '无备注' }}</p>
</el-card>
</el-timeline-item>
<el-timeline-item v-if="record.处理结果 == 'transferred'" v-for="(record, index) in currentProcess.approvalRecords"
:key="index" :timestamp="record.时间" :type="'success'"
:icon="'el-icon-check'">
<el-card>
<h4>{{ record.审批人姓名 }}-转办</h4>
<p>{{ record.审批意见 || '无备注' }}</p>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</el-dialog>
<!-- 转办对话框 -->
<el-dialog title="转办流程" :visible.sync="transferDialogVisible" width="700px" :append-to-body="true">
<el-form :model="transferForm" label-width="100px">
<el-form-item label="目标人员" required>
<el-select v-model="transferForm.newApproverId" placeholder="请选择人员" style="width: 100%">
<el-option v-for="user in users" :key="user.id"
:label="user.name" :value="user.id">
<span>{{ user.name }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="转办原因" required>
<el-input type="textarea" v-model="transferForm.reason" :rows="3" placeholder="请输入转办原因"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="transferDialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmTransfer" :loading="transferLoading"> </el-button>
</span>
</el-dialog>
<!-- 完结对话框 -->
<el-dialog title="完结流程" :visible.sync="completeDialogVisible" width="700px" class="complete-dialog" :append-to-body="true">
<el-form :model="completeForm" label-width="120px">
<el-form-item label="流程名称">
<div>{{ selectProcess ? selectProcess.流程标题 : '' }}</div>
</el-form-item>
<el-form-item label="完结原因" required>
<el-input type="textarea" v-model="completeForm.reason" :rows="3"
placeholder="请输入完结原因"></el-input>
</el-form-item>
<el-form-item label="完结结果" required>
<el-radio-group v-model="completeForm.result">
<el-radio label="completed">完成</el-radio>
<el-radio label="cancelled">取消</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="completeDialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmComplete" :loading="completeLoading" style="width: auto;"> </el-button>
</span>
</el-dialog>
</div>
</div>
</template>
<script>
export default {
name: 'WorkflowManagement',
props: {
initialSelectedRow: {
type: Object,
default: null
}
},
data() {
return {
// 搜索条件
orderNo: '',
contractNo: '',
productCode: '',
productName: '',
processName: '',
nature: '',
completionTime: [],
productionStatus: '',
tableData5: [],
activeTab: 'pending',
tableOrderNum: '',
tableProductCode: '',
tableProductName: '',
tableProcessName: '',
selectedRow: '',
loading: false,
// 转办相关数据
transferDialogVisible: false,
transferLoading: false,
// 完结相关数据
completeDialogVisible: false,
completeLoading: false,
currentTask: null,
currentProcess: null,
selectProcess: null,
transferForm: {
newApproverId: '',
newApproverName: '',
reason: ''
},
completeForm: {
reason: '',
result: 'completed'
},
createDialogVisible: false,
handleDialogVisible: false,
detailDialogVisible: false,
submitting: false,
filters: {
pending: { keyword: '', priority: '', type: '' },
initiated: { keyword: '', status: '' }
},
newProcessForm: {
type: '',
priority: 'normal',
title: '',
description: '',
approverIds: []
},
formRules: {
title: [{ required: true, message: '请输入流程标题', trigger: 'blur' }],
description: [{ required: true, message: '请输入流程描述', trigger: 'blur' }],
approverIds: [{ type: 'array', required: true, message: '请选择审批人', trigger: 'change' }]
},
handleForm: {
result: 'approved',
comment: ''
},
userList: [],
stats: {
pending: 0,
initiated: 0,
completed: 0,
},
// 数据
pendingTasks: [],
initiatedProcesses: [],
processedTasks: [],
users: [],
};
},
computed: {
filteredPendingTasks() {
let tasks = this.pendingTasks;
if (this.filters.pending.keyword) {
tasks = tasks.filter(task =>
task.流程标题.includes(this.filters.pending.keyword)
);
}
if (this.filters.pending.priority) {
tasks = tasks.filter(task => task.优先级 == this.filters.pending.priority);
}
return tasks;
},
filteredInitiatedProcesses() {
let processes = this.initiatedProcesses;
if (this.filters.initiated.keyword) {
processes = processes.filter(process =>
process.流程标题.includes(this.filters.initiated.keyword)
);
}
if (this.filters.initiated.status) {
processes = processes.filter(process => process.状态 === this.filters.initiated.status);
}
return processes;
}
},
created() {
this.applyInitialSelectedRow();
this.searchTable();
this.searchTable1();
this.getUserList();
this.getProcessedTasks();
this.searchTable3();
},
watch: {
initialSelectedRow: {
handler() {
this.applyInitialSelectedRow();
},
deep: true
}
},
methods: {
// 数据查询方法
switchTab(tabName) {
this.activeTab = tabName
this.refreshActiveTab(tabName)
},
handleTabClick(tab) {
this.refreshActiveTab(tab.name)
},
refreshActiveTab(tabName = this.activeTab) {
if (tabName === 'pending') {
this.searchTable1()
} else if (tabName === 'initiated') {
this.searchTable()
} else if (tabName === 'processed') {
this.getProcessedTasks()
}
},
getProcessedTasks() {
var params = []
params.push(['审批人ID', this.$store.state.user.id])
const data = this.CreateData('11', 'sp_获取最近处理任务', params)
this.ExecDatabase(data).then((response) => {
this.processedTasks = response.data || []
this.stats.completed = this.processedTasks.length
})
},
getUserList() {
const data = this.CreateData('11', '人员管理_人员信息查询')
this.ExecDatabase(data).then((response) => {
response.data.forEach(item => {
this.users.push({ id: item.Personnel_Number, name: item.Personnel_Name })
this.userList.push({ id: item.Personnel_Number, name: item.Personnel_Name })
});
console.log( this.userList)
}).catch(() => {
this.$message.error('处理失败')
})
},
searchTable() {
var params = []
params.push(['发起人ID', this.$store.state.user.id])
params.push(['状态', null])
var data = this.CreateData('11', 'sp_获取我的流程', params)
this.ExecDatabase(data).then(response => {
this.loading = false
this.initiatedProcesses = response.data || [];
this.stats.initiated = this.initiatedProcesses.length
}).catch(() => {
this.loading = false
})
},
searchTable1() {
var params = []
params.push(['审批人ID', this.$store.state.user.id])
var data = this.CreateData('11', 'sp_获取待处理任务', params)
this.ExecDatabase(data).then(response => {
this.loading = false
this.pendingTasks = response.data || [];
this.stats.pending = this.pendingTasks.length
}).catch(() => {
this.loading = false
})
},
searchTable3() {
this.loading = true
var params = []
params.push(['订单号', this.orderNo || 0])
params.push(['物料类型', '自制件'])
params.push(['合同号', this.contractNo])
params.push(['产品编码', this.productCode])
params.push(['产品名称', this.productName])
params.push(['工序名称', this.processName])
params.push(['性质', this.nature])
params.push(['要求完工日期1', this.completionTime ? this.completionTime[0] : ''])
params.push(['要求完工日期2', this.completionTime ? this.completionTime[1] : ''])
params.push(['排产状态', this.productionStatus])
params.push(['工位',''])
var data = this.CreateData('11', 'MES_ProductAdmin_Query', params)
this.ExecDatabase(data).then(response => {
this.loading = false
this.tableData5 = response.data || []
}).catch(() => {
this.loading = false
})
},
// 对话框控制方法
showCreateDialog() {
this.createDialogVisible = true;
this.resetNewProcessForm();
this.applyInitialSelectedRow();
},
showTransferDialog(task) {
this.currentTask = task;
this.transferForm = {
newApproverId: '',
newApproverName: '',
reason: ''
};
this.transferDialogVisible = true;
},
showCompleteDialog(process) {
this.selectProcess = process;
this.completeForm = {
reason: '',
result: 'completed'
};
this.completeDialogVisible = true;
},
handleTask(task) {
this.currentTask = task;
this.handleForm = {
result: 'approved',
comment: ''
};
this.handleDialogVisible = true;
},
// 表单操作方法
resetNewProcessForm() {
this.newProcessForm = {
type: true,
priority: 'normal',
title: '',
description: '',
approverIds: []
};
},
getSelectedApprovers() {
const selectedIds = this.newProcessForm.approverIds || [];
const approvers = selectedIds.map(id => {
return this.userList.find(user => String(user.id) === String(id));
}).filter(Boolean);
return approvers.filter((user, index, arr) => {
return arr.findIndex(item => String(item.id) === String(user.id)) === index;
});
},
// 数据处理方法
applyInitialSelectedRow() {
if (!this.initialSelectedRow || Object.keys(this.initialSelectedRow).length === 0) return
this.handleRowClick(this.initialSelectedRow)
this.orderNo = this.tableOrderNum
this.productCode = this.tableProductCode
this.productName = this.tableProductName
this.processName = this.tableProcessName
},
handleRowClick(row) {
this.selectedRow = row
this.tableOrderNum = row.订单号 || row.计划号 || row.生产订单 || ''
this.tableProductCode = row.产品编码 || row.零件编码 || row.物料编码 || ''
this.tableProductName = row.产品名称 || row.零件名称 || row.物料名称 || ''
this.tableProcessName = row.工序名称 || ''
},
formatDate(date) {
if (!date) return ''
return date.split(' ').length > 1 ? date.split(' ')[0] : date
},
// 提交处理方法
// 调用简化版存储过程的方法
submitNewProcess() {
if(!this.selectedRow){
return this.$message.warning('请选择绑定信息');
}
this.$refs.processForm.validate((valid) => {
if (valid) {
this.submitting = true;
const approvers = this.getSelectedApprovers();
if (approvers.length === 0) {
this.submitting = false;
return this.$message.warning('请选择审批人');
}
const approverId = approvers.map(user => user.id).join(',');
const approverName = approvers.map(user => user.name).join(',');
const params = []
params.push(['流程标题', this.newProcessForm.title])
params.push(['流程描述', this.newProcessForm.description])
params.push(['优先级', this.newProcessForm.priority || 'normal'])
params.push(['发起人ID', this.$store.state.user.id])
params.push(['发起人姓名', this.$store.state.user.name])
params.push(['审批人ID', approverId])
params.push(['审批人姓名', approverName])
params.push(['新流程ID', '0'])
params.push(['已关联计划号', this.tableOrderNum || ''])
params.push(['零件名称', this.tableProductName || ''])
params.push(['零件编号', this.tableProductCode || ''])
params.push(['工序名称', this.tableProcessName || ''])
console.log('简化版参数:', params);
// 调用简化版存储过程
const data = this.CreateData('11', 'sp_创建流程_简化版', params)
this.ExecDatabase(data).then((response) => {
console.log('简化版返回:', response);
if(response.data && response.data.length > 0){
const result = response.data[0];
if(result.结果 == 1){
this.$message.success('流程创建成功流程ID: ' + result.新流程ID);
this.createDialogVisible = false;
this.resetNewProcessForm();
this.searchTable();
this.searchTable1();
} else {
this.$message.error('创建失败: ' + (result.消息 || '未知错误'));
}
}
this.submitting = false;
}).catch((error) => {
console.error('API调用错误:', error);
this.$message.error('保存失败');
this.submitting = false;
})
}
});
},
submitHandle(task) {
const params = []
params.push(['流程ID', task.流程ID])
params.push(['节点ID', task.节点ID])
params.push(['审批人ID', this.$store.state.user.id])
params.push(['审批人姓名', this.$store.state.user.name])
params.push(['处理结果', this.$refs.approvalForm.model.result])
params.push(['审批意见', this.$refs.approvalForm.model.comment])
params.push(['成功',''])
const data = this.CreateData('12', 'sp_处理审批', params)
this.ExecDatabase(data).then((response) => {
this.$message.success('处理完成');
this.handleDialogVisible = false;
this.searchTable1();
this.getProcessedTasks();
}).catch(() => {
this.$message.error('处理失败')
})
},
confirmTransfer() {
if (!this.transferForm.newApproverId) {
this.$message.warning('请选择目标人员');
return;
}
this.users.forEach(user => {
if(user.id == this.transferForm.newApproverId){
this.transferForm.newApproverName = user.name
}
});
if (!this.transferForm.reason) {
this.$message.warning('请输入转办原因');
return;
}
this.transferLoading = true;
const params = []
params.push(['流程ID', this.currentTask.流程ID])
params.push(['节点ID', this.currentTask.节点ID])
params.push(['原审批人ID', this.$store.state.user.id])
params.push(['原审批人姓名', this.$store.state.user.name])
params.push(['新审批人ID', this.transferForm.newApproverId])
params.push(['新审批人姓名', this.transferForm.newApproverName])
params.push(['转办原因', this.transferForm.reason])
params.push(['成功', ''])
const data = this.CreateData('12', 'sp_转办流程', params)
this.ExecDatabase(data).then((response) => {
this.$message.success('转办成功!');
this.transferDialogVisible = false;
this.searchTable1();
}).catch(() => {
this.$message.error('保存失败')
}).finally(() => {
this.transferLoading = false;
})
},
confirmComplete() {
if (!this.completeForm.reason) {
this.$message.warning('请输入完结原因');
return;
}
const params = []
params.push(['流程ID', this.selectProcess.流程ID])
params.push(['发起人ID', this.$store.state.user.id])
params.push(['发起人姓名', this.$store.state.user.name])
params.push(['完成类型', this.completeForm.result])
params.push(['完成原因', this.completeForm.reason])
params.push(['成功', ''])
const data = this.CreateData('11', 'sp_完成流程', params)
this.ExecDatabase(data).then((response) => {
this.$message.success('操作成功!');
this.completeDialogVisible = false;
this.searchTable();
}).catch(() => {
this.$message.error('保存失败')
}).finally(() => {
this.completeLoading = false;
})
},
// 查看详情方法
viewProcessDetail(process) {
var params = []
params.push(['流程ID', process.流程ID])
var data = this.CreateData('11', 'sp_获取流程详情', params)
this.ExecDatabase(data).then(response => {
this.加载中 = false
const result = JSON.parse(response.data[0]["JSON_F52E2B61-18A1-11d1-B105-00805F49916B"])
const processInfo = JSON.parse(result.流程信息);
const steps = JSON.parse(result.步骤信息);
var approvalRecords = ''
if(result.审批记录){
approvalRecords = JSON.parse(result.审批记录);
}
this.currentProcess = {
'processInfo': processInfo,
'steps': steps,
'approvalRecords': approvalRecords,
}
console.log(this.currentProcess)
this.detailDialogVisible = true;
}).catch(() => {
this.加载中 = false
})
},
// 工具方法
getPriorityType(priority) {
const map = { urgent: 'danger', high: 'warning', normal: 'success' };
return map[priority] || 'info';
},
getPriorityText(priority) {
const map = { urgent: '紧急', high: '高', normal: '普通' };
return map[priority] || '普通';
},
getStatusType(status) {
const map = { processing: 'warning', completed: 'success', rejected: 'danger', cancelled: 'info' };
return map[status] || 'info';
},
getStatusText(status) {
const map = { processing: '审批中', completed: '已完成', rejected: '已拒绝', cancelled: '已取消' };
return map[status] || status;
},
isUrgent(deadline) {
return new Date(deadline) - new Date() < 24 * 60 * 60 * 1000;
},
}
};
</script>
<style>
.workflow-container {
padding: 20px;
min-height: 100%;
}
.page-header {
background: #fff;
padding: 20px;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.workflow-tabs {
background: #fff;
border-radius: 4px;
overflow: hidden;
}
.filter-bar {
padding: 15px;
background: #fff;
margin-bottom: 15px;
border-radius: 4px;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.filter-item {
margin-right: 15px;
margin-bottom: 10px;
}
.process-card {
margin-bottom: 15px;
cursor: pointer;
transition: all 0.3s;
}
.process-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.priority-tag {
margin-left: 10px;
}
.footer-buttons {
margin-top: 20px;
text-align: right;
}
.empty-state {
text-align: center;
padding: 40px 0;
color: #909399;
}
.stats-card {
text-align: center;
padding: 20px;
cursor: pointer;
transition: all 0.3s;
}
.stats-card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
.stats-number {
font-size: 24px;
font-weight: bold;
color: #409EFF;
margin: 10px 0;
}
.stats-label {
color: #909399;
}
.transfer-actions {
margin: 10px 0;
padding: 10px;
background: #f5f7fa;
border-radius: 4px;
}
.complete-dialog .el-dialog__body {
max-height: 60vh;
overflow-y: auto;
}
.node-status {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
margin-left: 10px;
}
.status-transferred {
background: #fdf6ec;
color: #e6a23c;
}
</style>