You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

534 lines
14 KiB
Vue

<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>