|
|
<template>
|
|
|
<div class='current-task'>
|
|
|
<div v-if='!currentWorkOrder' class='empty-state'>
|
|
|
<el-empty description='暂无当前工单' />
|
|
|
</div>
|
|
|
<div v-else class='task-content'>
|
|
|
<!-- 左侧:工单任务详情 -->
|
|
|
<div class='task-details'>
|
|
|
<div class='details-header'>
|
|
|
<h3>工单任务详情</h3>
|
|
|
</div>
|
|
|
<div class='details-body'>
|
|
|
<div class='detail-item'>
|
|
|
<span class='label'>工单号:</span>
|
|
|
<span class='value plan-code'>{{ currentWorkOrder.planCode }}</span>
|
|
|
</div>
|
|
|
<div class='detail-item'>
|
|
|
<span class='label'>任务状态:</span>
|
|
|
<span class='value status-text'>{{ getStatusText(currentWorkOrder.planStatus) }}</span>
|
|
|
</div>
|
|
|
<div class='detail-item'>
|
|
|
<span class='label'>产品编码:</span>
|
|
|
<span class='value'>{{ (currentWorkOrder as any).materialCode || currentWorkOrder.materialId }}</span>
|
|
|
</div>
|
|
|
<div class='detail-item'>
|
|
|
<span class='label'>工序名称:</span>
|
|
|
<span class='value'>{{ (currentWorkOrder as any).processName || '-' }}</span>
|
|
|
</div>
|
|
|
<div class='detail-item'>
|
|
|
<span class='label'>计划数量:</span>
|
|
|
<span class='value'>{{ currentWorkOrder.planAmount || 0 }}</span>
|
|
|
</div>
|
|
|
<div class='detail-item'>
|
|
|
<span class='label'>完成数量:</span>
|
|
|
<span class='value'>{{ currentWorkOrder.completeAmount || 0 }}</span>
|
|
|
</div>
|
|
|
<div class='detail-item'>
|
|
|
<span class='label'>产品名称:</span>
|
|
|
<span class='value'>{{ currentWorkOrder.materialName || '-' }}</span>
|
|
|
</div>
|
|
|
<div class='detail-item'>
|
|
|
<span class='label'>可报工量:</span>
|
|
|
<span class='value'>{{ reportableQuantity }}</span>
|
|
|
</div>
|
|
|
<div class='detail-item'>
|
|
|
<span class='label'>排产日期:</span>
|
|
|
<span class='value'>{{ parseTime(currentWorkOrder.planBeginTime, '{y}-{m}-{d}') }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 右侧:报工采集 -->
|
|
|
<div class='report-form'>
|
|
|
<div class='form-header'>
|
|
|
<h3>报工采集</h3>
|
|
|
</div>
|
|
|
<div class='form-body'>
|
|
|
<el-form :model='reportForm' label-width='100px'>
|
|
|
<el-form-item label='报工数量:'>
|
|
|
<el-input-number
|
|
|
v-model='reportForm.reportQuantity'
|
|
|
:min='0'
|
|
|
:max='reportableQuantity'
|
|
|
:precision='0'
|
|
|
style='width: 100%'
|
|
|
placeholder='请输入报工数量'
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
<el-form-item label='不良数量:'>
|
|
|
<el-input-number
|
|
|
v-model='reportForm.defectiveQuantity'
|
|
|
:min='0'
|
|
|
:precision='0'
|
|
|
style='width: 100%'
|
|
|
placeholder='请输入不良数量'
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 操作按钮 -->
|
|
|
<div class='action-buttons'>
|
|
|
<el-button
|
|
|
type='primary'
|
|
|
:disabled='!currentWorkOrder || currentWorkOrder.planStatus === "2"'
|
|
|
@click='handleStartWork'
|
|
|
>
|
|
|
开工
|
|
|
</el-button>
|
|
|
<el-button
|
|
|
type='primary'
|
|
|
:disabled='!currentWorkOrder || currentWorkOrder.planStatus !== "2"'
|
|
|
@click='handleStopWork'
|
|
|
>
|
|
|
停工
|
|
|
</el-button>
|
|
|
<el-button
|
|
|
type='primary'
|
|
|
:disabled='!currentWorkOrder || currentWorkOrder.planStatus === "2"'
|
|
|
@click='handleResumeWork'
|
|
|
>
|
|
|
复工
|
|
|
</el-button>
|
|
|
<el-button
|
|
|
type='primary'
|
|
|
:disabled='!currentWorkOrder || !reportForm.reportQuantity'
|
|
|
@click='handleReportWork'
|
|
|
>
|
|
|
报工
|
|
|
</el-button>
|
|
|
<el-button
|
|
|
type='primary'
|
|
|
:disabled='!currentWorkOrder || hasPendingInspection'
|
|
|
@click='handleFirstInspection'
|
|
|
>
|
|
|
首检
|
|
|
</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup name='CurrentTask' lang='ts'>
|
|
|
import { ref, computed, onMounted, getCurrentInstance } from 'vue';
|
|
|
import { listPlanInfo, updatePlanInfo, stopPlanInfo, resumePlanInfo, reportPlanInfo, generateInspectionTask } from '@/api/mes/planInfo';
|
|
|
import { PlanInfoVO, PlanInfoQuery } from '@/api/mes/planInfo/types';
|
|
|
import { getProdProductPlanDetailList, listProductPlanDetail } from '@/api/mes/productPlanDetail';
|
|
|
import { ProductPlanDetailQuery } from '@/api/mes/productPlanDetail/types';
|
|
|
import { listQcInspectionMain } from '@/api/qms/QcInspectionMain';
|
|
|
import { QcInspectionMainQuery } from '@/api/qms/QcInspectionMain/types';
|
|
|
import { parseTime } from '@/utils/ruoyi';
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as any;
|
|
|
|
|
|
const props = defineProps<{
|
|
|
workstationId?: string | number;
|
|
|
}>();
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
reportSuccess: [];
|
|
|
}>();
|
|
|
|
|
|
const loading = ref(false);
|
|
|
const currentWorkOrder = ref<PlanInfoVO | null>(null);
|
|
|
const hasPendingInspection = ref(false); // 是否有未处理的首检任务
|
|
|
|
|
|
const reportForm = ref({
|
|
|
reportQuantity: 0,
|
|
|
defectiveQuantity: 0
|
|
|
});
|
|
|
|
|
|
// 可报工量 = 计划数量 - 完成数量
|
|
|
const reportableQuantity = computed(() => {
|
|
|
if (!currentWorkOrder.value) return 0;
|
|
|
const planAmount = currentWorkOrder.value.planAmount || 0;
|
|
|
const completeAmount = currentWorkOrder.value.completeAmount || 0;
|
|
|
return Math.max(0, planAmount - completeAmount);
|
|
|
});
|
|
|
|
|
|
// 获取状态文本
|
|
|
const getStatusText = (status: string) => {
|
|
|
const statusMap: Record<string, string> = {
|
|
|
'0': '未派工',
|
|
|
'1': '已派工',
|
|
|
'2': '已开始',
|
|
|
'3': '已完成'
|
|
|
};
|
|
|
return statusMap[status] || '未知';
|
|
|
};
|
|
|
|
|
|
// 检查首检任务状态
|
|
|
const checkInspectionTask = async (planCode: string, planDetailId: string | number) => {
|
|
|
try {
|
|
|
const query: QcInspectionMainQuery = {
|
|
|
pageNum: 1,
|
|
|
pageSize: 1,
|
|
|
inspectionType: 0, // 首检
|
|
|
status: 0, // 未处理
|
|
|
planDetailId: planDetailId
|
|
|
};
|
|
|
|
|
|
const res = await listQcInspectionMain(query);
|
|
|
const inspectionList = res.rows || [];
|
|
|
|
|
|
// 检查是否有未处理的首检任务
|
|
|
// 如果有未处理的首检任务,则禁用按钮
|
|
|
hasPendingInspection.value = inspectionList.length > 0;
|
|
|
} catch (error) {
|
|
|
console.error('查询首检任务状态失败:', error);
|
|
|
hasPendingInspection.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 获取当前工单
|
|
|
const getCurrentWorkOrder = async () => {
|
|
|
loading.value = true;
|
|
|
try {
|
|
|
const query: any = {
|
|
|
pageNum: 1,
|
|
|
pageSize: 1,
|
|
|
planStatus: '2', // 已开始状态
|
|
|
releaseType: '3' // 工位类型
|
|
|
};
|
|
|
|
|
|
if (props.workstationId) {
|
|
|
query.releaseId = props.workstationId;
|
|
|
}
|
|
|
|
|
|
const res = await listPlanInfo(query as PlanInfoQuery);
|
|
|
if (res.rows && res.rows.length > 0) {
|
|
|
currentWorkOrder.value = res.rows[0];
|
|
|
|
|
|
// 获取生产信息明细,用于检查首检任务
|
|
|
const detailRes = await getProdProductPlanDetailList({ planId: currentWorkOrder.value.planId });
|
|
|
const detailList = detailRes.data || [];
|
|
|
|
|
|
if (detailList && detailList.length > 0) {
|
|
|
const firstDetail = detailList[detailList.length - 1];
|
|
|
// 检查首检任务状态
|
|
|
await checkInspectionTask(currentWorkOrder.value.planCode, firstDetail.planDetailId);
|
|
|
} else {
|
|
|
hasPendingInspection.value = false;
|
|
|
}
|
|
|
} else {
|
|
|
currentWorkOrder.value = null;
|
|
|
hasPendingInspection.value = false;
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('获取当前工单失败:', error);
|
|
|
currentWorkOrder.value = null;
|
|
|
hasPendingInspection.value = false;
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 开工
|
|
|
const handleStartWork = async () => {
|
|
|
if (!currentWorkOrder.value) return;
|
|
|
|
|
|
try {
|
|
|
await proxy?.$modal.confirm('确认开工?');
|
|
|
loading.value = true;
|
|
|
|
|
|
const updateData = {
|
|
|
...currentWorkOrder.value,
|
|
|
planStatus: '2', // 已开始
|
|
|
realBeginTime: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
|
|
|
};
|
|
|
|
|
|
await updatePlanInfo(updateData);
|
|
|
proxy?.$modal.msgSuccess('开工成功');
|
|
|
await getCurrentWorkOrder();
|
|
|
} catch (error: any) {
|
|
|
if (error !== 'cancel') {
|
|
|
proxy?.$modal.msgError('开工失败');
|
|
|
console.error('开工失败:', error);
|
|
|
}
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 停工
|
|
|
const handleStopWork = async () => {
|
|
|
if (!currentWorkOrder.value) return;
|
|
|
|
|
|
try {
|
|
|
await proxy?.$modal.confirm('确认停工?');
|
|
|
loading.value = true;
|
|
|
|
|
|
// 调用停工接口
|
|
|
await stopPlanInfo(currentWorkOrder.value.planId);
|
|
|
proxy?.$modal.msgSuccess('停工成功');
|
|
|
await getCurrentWorkOrder();
|
|
|
} catch (error: any) {
|
|
|
if (error !== 'cancel') {
|
|
|
proxy?.$modal.msgError('停工失败');
|
|
|
console.error('停工失败:', error);
|
|
|
}
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 复工
|
|
|
const handleResumeWork = async () => {
|
|
|
if (!currentWorkOrder.value) return;
|
|
|
|
|
|
try {
|
|
|
await proxy?.$modal.confirm('确认复工?');
|
|
|
loading.value = true;
|
|
|
|
|
|
// 调用复工接口
|
|
|
await resumePlanInfo(currentWorkOrder.value.planId);
|
|
|
proxy?.$modal.msgSuccess('复工成功');
|
|
|
await getCurrentWorkOrder();
|
|
|
} catch (error: any) {
|
|
|
if (error !== 'cancel') {
|
|
|
proxy?.$modal.msgError('复工失败');
|
|
|
console.error('复工失败:', error);
|
|
|
}
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 报工
|
|
|
const handleReportWork = async () => {
|
|
|
if (!currentWorkOrder.value) return;
|
|
|
|
|
|
if (!reportForm.value.reportQuantity || reportForm.value.reportQuantity <= 0) {
|
|
|
proxy?.$modal.msgWarning('请输入报工数量');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (reportForm.value.reportQuantity > reportableQuantity.value) {
|
|
|
proxy?.$modal.msgWarning('报工数量不能超过可报工量');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
await proxy?.$modal.confirm('确认报工?');
|
|
|
loading.value = true;
|
|
|
|
|
|
// 调用报工接口
|
|
|
const reportData = {
|
|
|
planId: currentWorkOrder.value.planId,
|
|
|
reportQuantity: reportForm.value.reportQuantity,
|
|
|
defectiveQuantity: reportForm.value.defectiveQuantity || 0,
|
|
|
workshopId: currentWorkOrder.value.workshopId
|
|
|
};
|
|
|
|
|
|
await reportPlanInfo(reportData);
|
|
|
proxy?.$modal.msgSuccess('报工成功');
|
|
|
|
|
|
// 清空表单
|
|
|
reportForm.value.reportQuantity = 0;
|
|
|
reportForm.value.defectiveQuantity = 0;
|
|
|
|
|
|
// 刷新当前工单数据
|
|
|
await getCurrentWorkOrder();
|
|
|
|
|
|
// 通知父组件刷新相关数据(待处理任务列表、报工记录等)
|
|
|
emit('reportSuccess');
|
|
|
} catch (error: any) {
|
|
|
if (error !== 'cancel') {
|
|
|
proxy?.$modal.msgError('报工失败');
|
|
|
console.error('报工失败:', error);
|
|
|
}
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 首检
|
|
|
const handleFirstInspection = async () => {
|
|
|
if (!currentWorkOrder.value) return;
|
|
|
|
|
|
try {
|
|
|
// 先检查是否已存在未处理的首检任务
|
|
|
const detailRes = await getProdProductPlanDetailList({ planId: currentWorkOrder.value.planId });
|
|
|
const detailList = detailRes.data || [];
|
|
|
|
|
|
// 如果没有生产信息,提示用户
|
|
|
if (!detailList || detailList.length === 0) {
|
|
|
proxy?.$modal.msgWarning('本工单尚未生成生产信息,无法进行首检');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const firstDetail = detailList[detailList.length - 1];
|
|
|
const planDetailId = firstDetail.planDetailId;
|
|
|
|
|
|
// 检查是否已存在未处理的首检任务
|
|
|
await checkInspectionTask(currentWorkOrder.value.planCode, planDetailId);
|
|
|
|
|
|
if (hasPendingInspection.value) {
|
|
|
proxy?.$modal.msgWarning('已存在未处理的首检任务,无法重复生成');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
await proxy?.$modal.confirm('确认生成首检任务?');
|
|
|
loading.value = true;
|
|
|
|
|
|
// 调用首检接口
|
|
|
const inspectionData = {
|
|
|
planDetailId: planDetailId,
|
|
|
processId: currentWorkOrder.value.processId,
|
|
|
qcInspectionType: '0' // 0首检 1专检 2自检 3互检 4原材料检 5抽检 6成品检 7入库检
|
|
|
};
|
|
|
|
|
|
await generateInspectionTask(inspectionData);
|
|
|
proxy?.$modal.msgSuccess('首检任务生成成功');
|
|
|
|
|
|
// 刷新首检任务状态
|
|
|
await checkInspectionTask(currentWorkOrder.value.planCode, planDetailId);
|
|
|
} catch (error: any) {
|
|
|
if (error !== 'cancel') {
|
|
|
console.error('首检任务生成失败:', error);
|
|
|
}
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 刷新数据
|
|
|
const refresh = () => {
|
|
|
getCurrentWorkOrder();
|
|
|
};
|
|
|
|
|
|
defineExpose({
|
|
|
refresh
|
|
|
});
|
|
|
|
|
|
onMounted(() => {
|
|
|
getCurrentWorkOrder();
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang='scss'>
|
|
|
.current-task {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
|
|
|
.empty-state {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.task-content {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
gap: 20px;
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
.task-details {
|
|
|
flex: 1;
|
|
|
background: #fff;
|
|
|
border-radius: 4px;
|
|
|
padding: 20px;
|
|
|
border: 1px solid #e4e7ed;
|
|
|
|
|
|
.details-header {
|
|
|
margin-bottom: 20px;
|
|
|
padding-bottom: 10px;
|
|
|
border-bottom: 1px solid #e4e7ed;
|
|
|
|
|
|
h3 {
|
|
|
margin: 0;
|
|
|
font-size: 16px;
|
|
|
font-weight: 500;
|
|
|
color: #303133;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.details-body {
|
|
|
.detail-item {
|
|
|
display: flex;
|
|
|
margin-bottom: 15px;
|
|
|
font-size: 14px;
|
|
|
|
|
|
.label {
|
|
|
width: 120px;
|
|
|
color: #606266;
|
|
|
flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
.value {
|
|
|
flex: 1;
|
|
|
color: #303133;
|
|
|
|
|
|
&.plan-code {
|
|
|
color: #67c23a;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
&.status-text {
|
|
|
color: #f56c6c;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.report-form {
|
|
|
width: 400px;
|
|
|
background: #fff;
|
|
|
border-radius: 4px;
|
|
|
padding: 20px;
|
|
|
border: 1px solid #e4e7ed;
|
|
|
|
|
|
.form-header {
|
|
|
margin-bottom: 20px;
|
|
|
padding-bottom: 10px;
|
|
|
border-bottom: 1px solid #e4e7ed;
|
|
|
|
|
|
h3 {
|
|
|
margin: 0;
|
|
|
font-size: 16px;
|
|
|
font-weight: 500;
|
|
|
color: #303133;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.form-body {
|
|
|
:deep(.el-form-item) {
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.action-buttons {
|
|
|
display: flex;
|
|
|
gap: 10px;
|
|
|
padding: 20px;
|
|
|
background: #fff;
|
|
|
border-radius: 4px;
|
|
|
border: 1px solid #e4e7ed;
|
|
|
|
|
|
.el-button {
|
|
|
flex: 1;
|
|
|
height: 40px;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</style>
|