feat(mes): 优化工单和任务管理功能,增强表单交互逻辑

调整工单表单和任务列表的展示,新增工单完成操作,优化表单数据处理逻辑,提升用户体验。
pull/871/MERGE
YunaiV 2026-04-04 17:03:45 +08:00
parent cfa787530e
commit bc098e8999
3 changed files with 104 additions and 111 deletions

View File

@ -180,13 +180,37 @@ const props = defineProps<{
const { t } = useI18n()
const message = useMessage()
const getDefaultColor = () => props.colorCode || '#00AEF3'
// ==================== ====================
const loading = ref(false)
const list = ref<ProTaskVO[]>([])
/** 查询任务列表(按工单 + 路线 + 工序过滤) */
// ==================== ====================
const dialogVisible = ref(false)
const dialogTitle = computed(() => {
const titles: Record<string, string> = {
create: '新增生产任务',
update: '编辑生产任务',
detail: '生产任务详情'
}
return titles[formType.value] || ''
})
const formLoading = ref(false)
const formType = ref('')
const isDetail = computed(() => formType.value === 'detail')
const formData = ref<ProTaskVO>({} as unknown as ProTaskVO)
const formRules = reactive({
workstationId: [{ required: true, message: '工作站不能为空', trigger: 'change' }],
quantity: [
{ required: true, message: '排产数量不能为空', trigger: 'blur' },
{ type: 'number', min: 0.01, message: '排产数量必须大于 0', trigger: 'blur' }
],
startTime: [{ required: true, message: '开始时间不能为空', trigger: 'change' }],
duration: [{ required: true, message: '生产时长不能为空', trigger: 'blur' }]
})
const formRef = ref()
/** 查询任务列表 */
const getList = async () => {
loading.value = true
try {
@ -203,87 +227,18 @@ const getList = async () => {
}
}
/** 删除任务 */
const handleDelete = async (id: number) => {
try {
await message.delConfirm()
await ProTaskApi.deleteTask(id)
message.success(t('common.delSuccess'))
await getList()
} catch {}
}
// ==================== / ====================
const dialogVisible = ref(false) //
const formLoading = ref(false) //
const formType = ref('') // create - update - detail -
const dialogTitle = computed(() => {
const typeMap: Record<string, string> = {
detail: '生产任务详情',
create: '新增生产任务',
update: '编辑生产任务'
}
return typeMap[formType.value] || ''
})
const formData = ref<ProTaskVO>({
id: undefined,
workOrderId: undefined,
workstationId: undefined,
routeId: undefined,
processId: undefined,
itemId: undefined,
quantity: undefined,
startTime: undefined,
duration: 1,
endTime: undefined,
colorCode: undefined,
status: undefined,
remark: undefined
} as unknown as ProTaskVO)
const formRules = reactive({
workstationId: [{ required: true, message: '工作站不能为空', trigger: 'change' }],
quantity: [
{ required: true, message: '排产数量不能为空', trigger: 'blur' },
{ type: 'number', min: 0.01, message: '排产数量必须大于 0', trigger: 'blur' }
],
startTime: [{ required: true, message: '开始时间不能为空', trigger: 'change' }],
duration: [{ required: true, message: '生产时长不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref
const isDetail = computed(() => formType.value === 'detail')
/** 计算结束时间:开始时间 + 生产时长 * 8小时 */
const handleDurationChange = () => {
if (formData.value.startTime && formData.value.duration) {
const start =
typeof formData.value.startTime === 'number'
? formData.value.startTime
: new Date(formData.value.startTime).getTime()
formData.value.endTime = start + formData.value.duration * 8 * 3600 * 1000
}
}
/** 监听开始时间变化也重新计算 */
watch(
() => formData.value.startTime,
() => handleDurationChange()
)
/** 打开表单弹窗 */
const openForm = async (type: string, id?: number) => {
dialogVisible.value = true
formType.value = type
resetForm()
if (type === 'create') {
//
formData.value.workOrderId = props.workOrderId!
formData.value.routeId = props.routeId!
formData.value.processId = props.processId!
formData.value.itemId = props.itemId!
formData.value.colorCode = getDefaultColor()
formData.value.colorCode = props.colorCode || '#00AEF3'
} else if (id) {
//
formLoading.value = true
try {
formData.value = await ProTaskApi.getTask(id)
@ -313,6 +268,27 @@ const submitForm = async () => {
}
}
/** 删除任务 */
const handleDelete = async (id: number) => {
try {
await message.delConfirm()
await ProTaskApi.deleteTask(id)
message.success(t('common.delSuccess'))
await getList()
} catch {}
}
/** 计算结束时间:开始时间 + 生产时长 × 8小时 */
const handleDurationChange = () => {
if (formData.value.startTime && formData.value.duration) {
const start =
typeof formData.value.startTime === 'number'
? formData.value.startTime
: new Date(formData.value.startTime).getTime()
formData.value.endTime = start + formData.value.duration * 8 * 3600 * 1000
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
@ -333,6 +309,12 @@ const resetForm = () => {
formRef.value?.resetFields()
}
/** 监听开始时间变化重新计算结束时间 */
watch(
() => formData.value.startTime,
() => handleDurationChange()
)
/** 监听 processId 切换重新加载 */
watch(
() => props.processId,

View File

@ -51,6 +51,18 @@
<MdItemSelect v-model="formData.productId" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="规格型号" prop="productSpec">
<el-input v-model="formData.productSpec" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="单位" prop="unitMeasureName">
<el-input v-model="formData.unitMeasureName" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="工单数量" prop="quantity">
<el-input-number
@ -141,10 +153,18 @@
:process-id="rp.processId"
:item-id="formData.productId!"
:color-code="rp.colorCode"
:disabled="formType === 'detail'"
:disabled="isReadonly"
/>
</el-card>
<template #footer>
<el-button
v-if="formType === 'schedule'"
@click="handleFinish"
type="success"
:disabled="formLoading"
>
</el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
@ -161,6 +181,7 @@ import { MesProWorkOrderSourceTypeEnum, MesProWorkOrderTypeEnum } from '@/views/
import ProTaskList from './ProTaskList.vue'
defineOptions({ name: 'WorkOrderForm2' })
const emit = defineEmits(['success'])
const message = useMessage() //
@ -168,6 +189,7 @@ const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') //
const formLoading = ref(false) //
const formType = ref('') // detail - schedule -
const isReadonly = computed(() => formType.value === 'detail') //
const formData = ref<any>({})
const formRef = ref() // Ref
@ -178,8 +200,8 @@ const currentRouteId = ref(0) // 当前工艺路线编号
/** 打开弹窗 */
const open = async (type: string, id: number) => {
dialogVisible.value = true
dialogTitle.value = type === 'schedule' ? '生产排产' : '工单详情'
formType.value = type
dialogTitle.value = { schedule: '生产排产', detail: '工单详情' }[type] || type
formLoading.value = true
formData.value = {}
routeProcessList.value = []
@ -197,6 +219,21 @@ const open = async (type: string, id: number) => {
}
defineExpose({ open }) // open
/** 完成工单 */
const handleFinish = async () => {
try {
await message.confirm('确认要完成该工单吗?完成后工单下所有任务将标记为已完成。')
formLoading.value = true
await ProWorkOrderApi.finishWorkOrder(formData.value.id!)
message.success('工单已完成')
dialogVisible.value = false
emit('success')
} catch {
} finally {
formLoading.value = false
}
}
/** 加载工艺路线工序列表 */
const loadRouteProcesses = async (productId: number) => {
try {

View File

@ -117,7 +117,7 @@
<dict-tag :type="DICT_TYPE.MES_PRO_WORK_ORDER_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" fixed="right">
<el-table-column label="操作" align="center" width="100" fixed="right">
<template #default="scope">
<el-button
v-if="scope.row.status === MesProWorkOrderStatusEnum.CONFIRMED"
@ -128,15 +128,6 @@
>
排产
</el-button>
<el-button
v-if="scope.row.status === MesProWorkOrderStatusEnum.CONFIRMED"
link
type="success"
@click="handleFinish(scope.row.id)"
v-hasPermi="['mes:pro-task:update']"
>
完成
</el-button>
</template>
</el-table-column>
</el-table>
@ -149,7 +140,7 @@
/>
</ContentWrap>
<WorkOrderForm2 ref="formRef" />
<WorkOrderForm2 ref="formRef" @success="getWorkOrderList" />
</template>
<script setup lang="ts">
@ -166,10 +157,12 @@ import WorkOrderForm2 from './WorkOrderForm2.vue'
defineOptions({ name: 'MesProTask' })
const message = useMessage()
const { push } = useRouter()
const loading = ref(true) //
const workOrderList = ref<ProWorkOrderVO[]>([]) //
const total = ref(0) //
const ganttTasks = ref<any[]>([]) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
@ -183,9 +176,7 @@ const queryParams = reactive({
type: MesProWorkOrderTypeEnum.SELF // ""
})
const queryFormRef = ref() // ref
const ganttLoading = ref(false) //
const ganttTasks = ref<any[]>([]) //
const formRef = ref() // ref
/** 查询待排产工单列表(支持父子工单树形展示) */
const getWorkOrderList = async () => {
@ -199,50 +190,33 @@ const getWorkOrderList = async () => {
}
}
/** 加载甘特图预览数据(查询所有任务,供甘特图组件渲染) */
/** 加载甘特图预览数据 */
const loadGanttPreview = async () => {
ganttLoading.value = true
try {
ganttTasks.value = await ProTaskApi.getGanttTaskList(queryParams)
} finally {
ganttLoading.value = false
}
} catch {}
}
/** 搜索 */
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getWorkOrderList()
}
/** 重置 */
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
//
queryParams.status = MesProWorkOrderStatusEnum.CONFIRMED
queryParams.type = MesProWorkOrderTypeEnum.SELF
//
handleQuery()
}
/** 打开排产/详情对话框 */
const formRef = ref()
/** 打开排产/详情/完成对话框 */
const openForm = (type: string, id: number) => {
formRef.value.open(type, id)
}
/** 完成工单 */
const handleFinish = async (id: number) => {
try {
await message.confirm('确认要完成该工单吗?')
await ProWorkOrderApi.finishWorkOrder(id)
message.success('工单已完成')
await getWorkOrderList()
} catch {}
}
/** 打开甘特图编辑页面 */
const { push } = useRouter()
const openGanttEdit = () => {
push({ name: 'MesProTaskGanttEdit' })
}