Files
rd_mes_uniapp/pages/mes/jobReport/pipelineReport.vue
2025-12-18 14:11:48 +08:00

710 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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

<template>
<view class="container">
<uni-segmented-control :current="current" :values="items" active-color="#2979FF" @clickItem="current = (current + 1) % 2" />
<t-gap />
<view v-if="current === 0" class="segmented-wrapper">
<uni-forms label-position="left" :label-width="75" label-align="right">
<uni-forms-item label="生产工单" name="pwoCode">
<uni-easyinput v-model="pwoCode" suffixIcon="scan" @iconClick="handleScanPwoCode" @confirm="fetchPwoJobList"
placeholder="请输入工单编码" />
</uni-forms-item>
<uni-forms-item label="产线" name="proLineId">
<uni-data-select v-model="proLineId" :localdata="proLineOptions" :disabled="!pwoCode"
@change="handleSelectProLine" :clear="false" placeholder="请选择产线" />
</uni-forms-item>
<uni-forms-item label="开始作业" name="startJobCode">
<uni-data-select v-model="startJobCode" :localdata="startJobOptions" :disabled="!pwoCode"
@change="handleJobChange($event, 'start')" :clear="false" placeholder="请选择开始作业" />
</uni-forms-item>
<uni-forms-item label="结束作业" name="endJobCode">
<uni-data-select v-model="endJobCode" :localdata="endJobOptions" :disabled="!pwoCode"
@change="handleJobChange($event, 'end')" :clear="false" placeholder="请选择结束作业" />
</uni-forms-item>
<uni-forms-item label="合格数量">
<uni-number-box v-model="mesJobReportOnceRequestDTO.pass.reportNumber" />
</uni-forms-item>
<uni-forms-item label="不良数量">
<uni-number-box v-model="mesJobReportOnceRequestDTO.defect.reportNumber" />
</uni-forms-item>
<uni-forms-item label="不良原因" v-if="mesJobReportOnceRequestDTO.defect.reportNumber">
<uni-data-checkbox v-model="mesJobReportOnceRequestDTO.defect.reason" :localdata="defectOptions"
placeholder="请选择不良原因" />
</uni-forms-item>
<uni-forms-item label="报废数量">
<uni-number-box v-model="mesJobReportOnceRequestDTO.scrap.reportNumber" />
</uni-forms-item>
<uni-forms-item label="报废原因" v-if="mesJobReportOnceRequestDTO.scrap.reportNumber">
<uni-data-checkbox v-model="mesJobReportOnceRequestDTO.scrap.reason" :localdata="scrapOptions"
placeholder="请选择报废原因" />
</uni-forms-item>
</uni-forms>
</view>
<view v-if="current === 1" class="segmented-wrapper">
<uni-card v-for="(item, index) in selectedJobs" :key="index">
<template #title>
<view class="header">
<view class="jobInfo">
<uni-tag :text="String(index + 1)" />
<text class="jobCode">
{{ item.pwoJobCode }}
</text>
<uni-tag :text="item.status" :type="item.labelStyle" />
</view>
</view>
</template>
<view class="card-list">
<view class="card-item">
<span class="card-item__label">目标产品编码</span>
<span class="card-item__value">{{ item.ptNoTar || '/' }}</span>
</view>
<view class="card-item">
<span class="card-item__label">目标产品名称</span>
<span class="card-item__value">{{ item.ptTitleTar || '/' }}</span>
</view>
<view class="card-item">
<span class="card-item__label">制程名称</span>
<span class="card-item__value">{{ item.opTitle || '/' }}</span>
</view>
</view>
<view class="card-data">
<view class="card-data-item">
<span class="card-data-item__label">完成数量</span>
<span class="card-data-item__value finish">
{{ typeof item.finishQty === 'number' ? item.finishQty : '/' }}
</span>
</view>
<view class="card-data-item">
<span class="card-data-item__label">目标数量</span>
<span class="card-data-item__value">
{{ typeof item.targetNum === 'number' ? item.targetNum : '/' }}
</span>
</view>
</view>
<template #actions>
<view class="actions">
<uni-forms-item label="加工人员" :labelWidth="80">
<uni-easyinput @change="validateEmpCode(index)"
v-model="item.createByCode">
<template #right>
<uni-tag :text="item.createByName" />
<uni-icons type="scan" size="30" @click="handleScanEmpCode(index)" />
</template>
</uni-easyinput>
</uni-forms-item>
</view>
</template>
</uni-card>
<u-empty v-if="selectedJobs.length == 0" mode="list" />
<view class="btns">
<u-button v-if="startJobCode && endJobCode" type="primary" @click="submit" class="pri-btn">提交</u-button>
</view>
</view>
</view>
</template>
<script>
import { getConfigKey } from '@/api/system/config';
import { listPwo } from '@/api/mes/pwo';
import {
listPwoJob,
addReport,
getEquipment,
addPipelineReport
} from "@/api/mes/jobReport";
import {
listEmpEqpHistory,
listEmployee,
listConversion
} from "@/api/mes/jobIn";
import { getDicts } from "@/api/system/dict/dictData";
import { listProLine } from "@/api/basic/proLine";
import { listEmployeeInProLines } from "@/api/basic/employee";
import { setProductionLine } from '@/api/mes/pwo';
export default {
data() {
return {
proLineEmployeeDataSet: 1,
empSelectOptions: [],
proLineMap: {},
items: ['报工信息', '作业列表'],
current: 0,
dataSet: null,
isCollapse: false,
pwoCode: null,
pwoId: null,
proLineId: null,
startJobCode: null,
endJobCode: null,
mesJobReportOnceRequestDTO: {
defect: { reportNumber: 0, reason: null },
loss: { reportNumber: 0, reason: null },
pass: { reportNumber: 0, reason: null },
scrap: { reportNumber: 0, reason: null }
},
rawList: [],
pwoJobList: [],
startJobOptions: [],
endJobOptions: [],
proLineOptions: [],
defectOptions: [],
scrapOptions: [],
statusMap: {},
selectedJobs: [],
startIndex: null,
endIndex: null,
}
},
async mounted() {
// 获取产线人员对应列表数据源
await getConfigKey('mes.proline-employee-dataset').then(res => {
this.proLineEmployeeDataSet = +res.msg;
});
// 获取产线列表
this.fetchProLineOptions();
// 获取作业状态列表
this.fetchJobStatusList();
// 获取不良原因列表
this.fetchDefectOptions();
// 获取产线人员对应列表
this.getEmpOptions();
console.log('PWO100022136');
},
methods: {
// 获取产线列表
fetchProLineOptions() {
this.$modal.loading('加载产线列表...');
listProLine().then(res => {
this.$modal.closeLoading();
res.rows
.filter(item => !item.delStatus)
.forEach(item => {
this.proLineOptions.push({
text: item.productLineTitle,
value: item.id,
proLineCode: item.productLineCode
})
})
}).catch(err => {
this.$modal.closeLoading();
this.$modal.alert('加载产线列表失败', err.msg || err);
})
},
// 获取作业状态列表
fetchJobStatusList() {
// this.$modal.loading('加载作业类型...');
getDicts('mes_job_status').then(res => {
// this.$modal.closeLoading();
res.data.forEach(item => {
this.statusMap[item.dictValue] = [item.dictLabel, item.listClass];
})
}).catch(err => {
// this.$modal.closeLoading();
this.$modal.alert('加载作业列表失败', err.msg || err);
})
},
// 获取不良原因列表
async fetchDefectOptions() {
try {
await getDicts("mes_job_report_defect").then(res => {
this.defectOptions = res.data.map(dict => {
return {
text: dict.dictLabel,
value: dict.dictValue,
diasble: false
}
});
})
await getDicts("mes_job_report_scrap").then(res => {
this.scrapOptions = res.data.map(dict => {
return {
text: dict.dictLabel,
value: dict.dictValue,
diasble: false
}
});
})
} catch (err) {
this.$modal.msgError('加载不良原因列表失败');
console.error(`加载不良原因列表失败:\n${err}`);
}
},
// 获取作业列表
async fetchPwoJobList() {
if (!this.pwoCode) return;
this.resetForm();
this.$modal.loading('获取工单信息...');
await listPwo({ pwoCode: this.pwoCode }).then(res => {
this.$modal.closeLoading();
if (res.total === 1) {
this.proLineId = res.rows[0].proLineId;
}
}).catch(err => {
this.$modal.closeLoading();
this.$modal.msgError('获取工单信息失败');
console.error(`获取工单信息失败:\n${err}`);
return;
})
this.$modal.loading('获取作业列表...');
await listPwoJob({
pwoCode: this.pwoCode
}).then(res => {
this.$modal.closeLoading();
this.rawList = res.rows;
this.pwoId = res.rows[0].pwoId;
res.rows.forEach((item, index) => {
this.isCollapse = true;
this.pwoJobList.push({
pwoJobCode: item.code,
status: this.statusMap[item.status][0],
labelStyle: this.statusMap[item.status][1] || 'default',
ptTitleTar: item.ptTitleTar,
ptNoTar: item.ptNoTar,
opTitle: item.opTitle,
targetNum: item.targetNum,
finishQty: item.finishQty,
createByCode: null
})
this.startJobOptions.push({
text: `${item.code}(${item.opTitle})`,
value: item.code,
disable: index === res.rows.length - 1
})
this.endJobOptions.push({
text: `${item.code}(${item.opTitle})`,
value: item.code,
disable: index === 0
})
})
}).catch(err => {
this.$modal.closeLoading();
console.log(err)
this.$modal.alert('获取作业列表失败', err.msg || err);
})
},
// 扫描工单编码
handleScanPwoCode() {
const _this = this;
this.$modal.scanCode((res) => {
if (!res.result) {
_this.$modal.msg('未扫描到有效值,请重试');
return;
}
_this.pwoCode = res.result;
_this.fetchPwoJobList();
});
},
// 扫描员工编码
handleScanEmpCode(index) {
const _this = this;
this.$modal.scanCode((res) => {
if (!res.result) {
_this.$modal.msg('未扫描到有效值,请重试');
return;
}
_this.selectedJobs[index].createByCode = res.result;
_this.validateEmpCode(index);
});
},
// 绑定产线
async handleSelectProLine(e) {
if (!e) return;
await setProductionLine({
pwoId: [this.pwoId],
productionLineId: this.proLineId
}).then((res) => {
if (res.code == 200) {
this.$modal.msgSuccess("绑定成功");
}
}).catch(err => {
console.error(`绑定失败:${err.msg}`);
this.proLineId = null;
});
this.renderEmpInProLine();
},
// 根据产线人员对应关系填写人员编码
async renderEmpInProLine() {
let proLineChildren = this.proLineMap[this.proLineId];
proLineChildren = [];
if (this.proLineEmployeeDataSet === 0) {
console.log(this.empSelectOptions)
this.empSelectOptions
.find(item => item.proLineId == this.proLineId)?.children
.forEach(item => {
proLineChildren.push({
name: item.empName,
empId: item.value,
empCode: item.empCode,
lineNum: item.serialNum,
});
})
} else {
await listProductLineEmployee(this.proLineId).then((res) => {
res.rows.forEach(item => {
proLineChildren.push({
name: item.employeeName,
empId: item.employeeId,
empCode: item.employeeCode,
lineNum: item.serialNum,
});
});
})
}
console.log(proLineChildren);
this.selectedJobs.forEach((job, index) => {
const emp = proLineChildren.find(item => item.lineNum == index + 1);
const empName = emp?.name;
const empCode = emp?.empCode;
const empId = emp?.empId;
this.$set(job, 'createByCode', empCode || null);
this.$set(job, 'createByName', empName || null);
this.$set(job, 'createById', empId || null);
console.log(`${index + 1}个作业的员工名称为${empName}id为${empId}`);
})
},
// 获取产线人员对应关系
async getEmpOptions() {
// 一人员一产线
if (this.proLineEmployeeDataSet === 1) {
const proLineMap = new Map(); // 用于存储产线信息
await listProLine().then((res) => {
res.rows.forEach(item => {
const { id, productLineTitle } = item;
// 如果产线不存在,初始化产线
proLineMap.set(id, {
proLineId: id,
label: productLineTitle,
children: []
});
})
})
await listEmployee().then((res) => {
res.rows.forEach(item => {
const { empCode, name, proLineId,id } = item;
// 添加员工到对应产线
if (proLineId) {
proLineMap.get(proLineId).children.push({
value: id,
label: empCode + ': ' + name,
empId: id,
empName: name,
empCode: empCode
});
}
});
});
// 将Map转换为数组
this.empSelectOptions = Array.from(proLineMap.values());
} else {
// 一人员多产线
listEmployeeInProLines().then(res => {
if (!res.data) return;
res.data.forEach(proLine => {
const children = proLine.employees.map(emp => {
return {
value: emp.id,
label: emp.employeeCode + ': ' + emp.employeeName,
empId: emp.id,
empName: emp.employeeName,
empCode: emp.employeeCode,
serialNum: emp.serialNum
}
})
this.empSelectOptions.push({
proLineId: proLine.id,
label: proLine.productLineTitle,
children
})
})
})
}
},
// 校验员工编码
validateEmpCode(index) {
if (!this.selectedJobs[index].createByCode) return;
listEmployee({
empCode: this.selectedJobs[index].createByCode
}).then(res => {
if (res.total === 0) {
this.$modal.msgError(`员工不存在,请重新输入!`);
this.selectedJobs[index].createByCode = null;
} else if (res.total > 1) {
this.$modal.msgError(`员工编码异常,请联系管理员!`);
this.selectedJobs[index].createByCode = null;
} else {
this.selectedJobs[index].createByName = res.rows[0].name;
this.selectedJobs[index].createById = res.rows[0].id;
}
}).catch(err => {
this.$modal.msgError(`校验失败,请重试!\n${err}`);
this.selectedJobs[index].createByCode = null;
})
},
// 重置表单
resetForm() {
this.isCollapse = false;
this.proLineId = null;
this.startJobCode = null;
this.endJobCode = null;
this.mesJobReportOnceRequestDTO = {
defect: { reportNumber: 0, reason: null },
loss: { reportNumber: 0, reason: null },
pass: { reportNumber: 0, reason: null },
scrap: { reportNumber: 0, reason: null }
};
this.pwoJobList = [];
this.startJobOptions = [];
this.endJobOptions = [];
this.selectedJobs = [];
this.startIndex = null;
this.endIndex = null;
},
// 校验表单
async validateForm() {
try {
if (!this.pwoCode) throw new Error("请填写工单编码");
if (!this.startJobCode) throw new Error("请选择起始作业");
if (!this.endJobCode) throw new Error("请选择结束作业");
if (!this.proLineId) {
await this.$modal.confirm('未选择产线,是否继续提交').then(res => {
if (res.cancel) {
throw new Error("取消提交");
}
})
}
// 校验报工数量
let hasReport = false;
for (const key in this.mesJobReportOnceRequestDTO) {
const item = this.mesJobReportOnceRequestDTO[key];
const { reportNumber, reason } = item;
if (reportNumber !== 0) {
hasReport = true;
}
// 除了 pass数量不为0 且 原因为空时抛错
if (key !== "pass" && reportNumber !== 0 && (reason == null || reason === "")) {
throw new Error("请选择不良原因");
}
}
if (!hasReport) {
throw new Error("请填写报工数量");
}
this.selectedJobs.forEach((item, index) => {
if (!item.createByCode) {
throw new Error(`请填写第 ${index + 1} 条作业的加工人员`);
}
})
return true;
} catch (err) {
this.$modal.msg(String(err).replace('Error: ', ''));
console.error(`表单校验失败:\n${err}`);
return false;
}
},
// 切换作业
handleJobChange(e, type) {
if (!e) return;
switch (type) {
case 'start':
this.startIndex = this.pwoJobList.findIndex(item => item.pwoJobCode === e);
this.endJobOptions.forEach((job, i) => {
job.disable = i <= this.startIndex;
})
break;
case 'end':
this.endIndex = this.pwoJobList.findIndex(item => item.pwoJobCode === e);
this.startJobOptions.forEach((job, i) => {
job.disable = i >= this.endIndex;
})
break;
}
this.selectedJobs = [];
if (typeof this.startIndex === 'number' && typeof this.endIndex === 'number') {
this.selectedJobs = this.pwoJobList.slice(this.startIndex, this.endIndex + 1);
}
this.renderEmpInProLine();
},
// 提交
async submit() {
const res = await this.validateForm();
if (!res) return;
const submitList = this.rawList.slice(this.startIndex, this.endIndex + 1)
this.selectedJobs.forEach((item, index) => {
submitList[this.startIndex + index] = Object.assign({}, submitList[this.startIndex + index], item);
})
const data = {
reasonFrom: "0",
endTime: this.$time.getTime(),
endPwoJobId: this.rawList[this.endIndex].id,
startPwoJobId: this.rawList[this.startIndex].id,
productLineId: this.proLineId,
mesJobReportList: submitList,
mesJobReportOnceRequestDTO: this.mesJobReportOnceRequestDTO
}
addPipelineReport(data).then(() => {
this.$modal.msgSuccess('提交成功');
setTimeout(() => {
this.$tab.switchTab("/pages/work/index");
}, 1000);
}).catch(err => {
this.$modal.alert('提交失败', err.msg);
console.error(`提交失败:\n${err}`);
})
}
}
}
</script>
<style scoped lang="scss">
.container {
/* #ifdef H5 */
height: calc(100vh - 44px);
/* #endif */
/* #ifndef H5 */
height: 100vh;
/* #endif */
padding: 10px;
display: flex;
flex-direction: column;
.card-list__title {
background: transparent;
border-top: 1px solid #d9d9d9;
margin-top: 5px;
}
.segmented-wrapper {
flex: 1;
overflow: scroll;
.u-empty {
height: 100%;
}
}
.uni-card {
margin: 0 0 10px !important;
padding: 0 !important;
box-shadow: none !important;
border-radius: 11px;
.header {
display: flex;
align-items: center;
padding: 8px;
border-bottom: 1px solid #d9d9d9;
.jobInfo {
display: flex;
align-items: center;
flex: 1;
.jobCode {
font-size: 16px;
font-weight: bold;
flex: 1;
}
.uni-tag {
box-sizing: border-box;
height: 100%;
text-align: center;
margin-right: 8px;
}
}
}
.card-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 4px;
.card-item {
display: flex;
flex-direction: column;
align-items: flex-start;
.card-item__label {
font-size: 13px;
}
.card-item__value {
font-weight: bold;
font-size: 15px;
color: #333;
}
}
}
.card-data {
display: flex;
margin-top: 10px;
background-color: #f1f1f1;
border-radius: 4px;
padding: 6px 0;
.card-data-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.card-data-item__label {
font-size: 13px;
}
.card-data-item__value {
font-weight: bold;
font-size: 15px;
color: #333;
&.finish {
color: #059669;
}
}
}
}
.actions {
padding: 0 10px;
}
}
.btns {
padding-top: 5px;
}
}
::v-deep .uni-numbox__value {
flex: 1;
height: 36px;
}
::v-deep .uni-forms-item.is-direction-left {
align-items: baseline;
}
</style>