✨ feat(mes): 优化工单和任务管理功能,增强表单交互逻辑
调整工单表单和任务列表的展示,新增工单完成操作,优化表单数据处理逻辑,提升用户体验。pull/871/MERGE
parent
cfa787530e
commit
bc098e8999
|
|
@ -180,13 +180,37 @@ const props = defineProps<{
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const getDefaultColor = () => props.colorCode || '#00AEF3'
|
|
||||||
|
|
||||||
// ==================== 列表 ====================
|
// ==================== 列表 ====================
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const list = ref<ProTaskVO[]>([])
|
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 () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
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) => {
|
const openForm = async (type: string, id?: number) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
formType.value = type
|
formType.value = type
|
||||||
resetForm()
|
resetForm()
|
||||||
|
|
||||||
if (type === 'create') {
|
if (type === 'create') {
|
||||||
// 新增时预填工单信息
|
|
||||||
formData.value.workOrderId = props.workOrderId!
|
formData.value.workOrderId = props.workOrderId!
|
||||||
formData.value.routeId = props.routeId!
|
formData.value.routeId = props.routeId!
|
||||||
formData.value.processId = props.processId!
|
formData.value.processId = props.processId!
|
||||||
formData.value.itemId = props.itemId!
|
formData.value.itemId = props.itemId!
|
||||||
formData.value.colorCode = getDefaultColor()
|
formData.value.colorCode = props.colorCode || '#00AEF3'
|
||||||
} else if (id) {
|
} else if (id) {
|
||||||
// 编辑:加载任务数据
|
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
formData.value = await ProTaskApi.getTask(id)
|
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 = () => {
|
const resetForm = () => {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
|
|
@ -333,6 +309,12 @@ const resetForm = () => {
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 监听开始时间变化重新计算结束时间 */
|
||||||
|
watch(
|
||||||
|
() => formData.value.startTime,
|
||||||
|
() => handleDurationChange()
|
||||||
|
)
|
||||||
|
|
||||||
/** 监听 processId 切换重新加载 */
|
/** 监听 processId 切换重新加载 */
|
||||||
watch(
|
watch(
|
||||||
() => props.processId,
|
() => props.processId,
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,18 @@
|
||||||
<MdItemSelect v-model="formData.productId" disabled />
|
<MdItemSelect v-model="formData.productId" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</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-col :span="8">
|
||||||
<el-form-item label="工单数量" prop="quantity">
|
<el-form-item label="工单数量" prop="quantity">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
|
|
@ -141,10 +153,18 @@
|
||||||
:process-id="rp.processId"
|
:process-id="rp.processId"
|
||||||
:item-id="formData.productId!"
|
:item-id="formData.productId!"
|
||||||
:color-code="rp.colorCode"
|
:color-code="rp.colorCode"
|
||||||
:disabled="formType === 'detail'"
|
:disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
<el-button
|
||||||
|
v-if="formType === 'schedule'"
|
||||||
|
@click="handleFinish"
|
||||||
|
type="success"
|
||||||
|
:disabled="formLoading"
|
||||||
|
>
|
||||||
|
完 成
|
||||||
|
</el-button>
|
||||||
<el-button @click="dialogVisible = false">关 闭</el-button>
|
<el-button @click="dialogVisible = false">关 闭</el-button>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
@ -161,6 +181,7 @@ import { MesProWorkOrderSourceTypeEnum, MesProWorkOrderTypeEnum } from '@/views/
|
||||||
import ProTaskList from './ProTaskList.vue'
|
import ProTaskList from './ProTaskList.vue'
|
||||||
|
|
||||||
defineOptions({ name: 'WorkOrderForm2' })
|
defineOptions({ name: 'WorkOrderForm2' })
|
||||||
|
const emit = defineEmits(['success'])
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
|
|
@ -168,6 +189,7 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
const dialogTitle = ref('') // 弹窗的标题
|
const dialogTitle = ref('') // 弹窗的标题
|
||||||
const formLoading = ref(false) // 表单的加载中
|
const formLoading = ref(false) // 表单的加载中
|
||||||
const formType = ref('') // 表单的类型:detail - 详情;schedule - 排产
|
const formType = ref('') // 表单的类型:detail - 详情;schedule - 排产
|
||||||
|
const isReadonly = computed(() => formType.value === 'detail') // 是否只读
|
||||||
const formData = ref<any>({})
|
const formData = ref<any>({})
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
|
|
||||||
|
|
@ -178,8 +200,8 @@ const currentRouteId = ref(0) // 当前工艺路线编号
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
const open = async (type: string, id: number) => {
|
const open = async (type: string, id: number) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
dialogTitle.value = type === 'schedule' ? '生产排产' : '工单详情'
|
|
||||||
formType.value = type
|
formType.value = type
|
||||||
|
dialogTitle.value = { schedule: '生产排产', detail: '工单详情' }[type] || type
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
formData.value = {}
|
formData.value = {}
|
||||||
routeProcessList.value = []
|
routeProcessList.value = []
|
||||||
|
|
@ -197,6 +219,21 @@ const open = async (type: string, id: number) => {
|
||||||
}
|
}
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
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) => {
|
const loadRouteProcesses = async (productId: number) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@
|
||||||
<dict-tag :type="DICT_TYPE.MES_PRO_WORK_ORDER_STATUS" :value="scope.row.status" />
|
<dict-tag :type="DICT_TYPE.MES_PRO_WORK_ORDER_STATUS" :value="scope.row.status" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</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">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
v-if="scope.row.status === MesProWorkOrderStatusEnum.CONFIRMED"
|
v-if="scope.row.status === MesProWorkOrderStatusEnum.CONFIRMED"
|
||||||
|
|
@ -128,15 +128,6 @@
|
||||||
>
|
>
|
||||||
排产
|
排产
|
||||||
</el-button>
|
</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>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
@ -149,7 +140,7 @@
|
||||||
/>
|
/>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
<WorkOrderForm2 ref="formRef" />
|
<WorkOrderForm2 ref="formRef" @success="getWorkOrderList" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
@ -166,10 +157,12 @@ import WorkOrderForm2 from './WorkOrderForm2.vue'
|
||||||
|
|
||||||
defineOptions({ name: 'MesProTask' })
|
defineOptions({ name: 'MesProTask' })
|
||||||
|
|
||||||
const message = useMessage()
|
const { push } = useRouter()
|
||||||
|
|
||||||
const loading = ref(true) // 列表加载状态
|
const loading = ref(true) // 列表加载状态
|
||||||
const workOrderList = ref<ProWorkOrderVO[]>([]) // 工单列表数据
|
const workOrderList = ref<ProWorkOrderVO[]>([]) // 工单列表数据
|
||||||
const total = ref(0) // 总条数
|
const total = ref(0) // 总条数
|
||||||
|
const ganttTasks = ref<any[]>([]) // 甘特图数据
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
|
|
@ -183,9 +176,7 @@ const queryParams = reactive({
|
||||||
type: MesProWorkOrderTypeEnum.SELF // 固定筛选:只查询"自制"的工单
|
type: MesProWorkOrderTypeEnum.SELF // 固定筛选:只查询"自制"的工单
|
||||||
})
|
})
|
||||||
const queryFormRef = ref() // 搜索表单 ref
|
const queryFormRef = ref() // 搜索表单 ref
|
||||||
|
const formRef = ref() // 表单弹窗 ref
|
||||||
const ganttLoading = ref(false) // 甘特图加载状态
|
|
||||||
const ganttTasks = ref<any[]>([]) // 甘特图数据
|
|
||||||
|
|
||||||
/** 查询待排产工单列表(支持父子工单树形展示) */
|
/** 查询待排产工单列表(支持父子工单树形展示) */
|
||||||
const getWorkOrderList = async () => {
|
const getWorkOrderList = async () => {
|
||||||
|
|
@ -199,50 +190,33 @@ const getWorkOrderList = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 加载甘特图预览数据(查询所有任务,供甘特图组件渲染) */
|
/** 加载甘特图预览数据 */
|
||||||
const loadGanttPreview = async () => {
|
const loadGanttPreview = async () => {
|
||||||
ganttLoading.value = true
|
|
||||||
try {
|
try {
|
||||||
ganttTasks.value = await ProTaskApi.getGanttTaskList(queryParams)
|
ganttTasks.value = await ProTaskApi.getGanttTaskList(queryParams)
|
||||||
} finally {
|
} catch {}
|
||||||
ganttLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 搜索 */
|
/** 搜索按钮操作 */
|
||||||
const handleQuery = () => {
|
const handleQuery = () => {
|
||||||
queryParams.pageNo = 1
|
queryParams.pageNo = 1
|
||||||
getWorkOrderList()
|
getWorkOrderList()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 重置 */
|
/** 重置按钮操作 */
|
||||||
const resetQuery = () => {
|
const resetQuery = () => {
|
||||||
queryFormRef.value.resetFields()
|
queryFormRef.value.resetFields()
|
||||||
// 恢复固定筛选
|
|
||||||
queryParams.status = MesProWorkOrderStatusEnum.CONFIRMED
|
queryParams.status = MesProWorkOrderStatusEnum.CONFIRMED
|
||||||
queryParams.type = MesProWorkOrderTypeEnum.SELF
|
queryParams.type = MesProWorkOrderTypeEnum.SELF
|
||||||
// 执行搜索
|
|
||||||
handleQuery()
|
handleQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 打开排产/详情对话框 */
|
/** 打开排产/详情/完成对话框 */
|
||||||
const formRef = ref()
|
|
||||||
const openForm = (type: string, id: number) => {
|
const openForm = (type: string, id: number) => {
|
||||||
formRef.value.open(type, id)
|
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 = () => {
|
const openGanttEdit = () => {
|
||||||
push({ name: 'MesProTaskGanttEdit' })
|
push({ name: 'MesProTaskGanttEdit' })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue