<script lang="ts" setup>
|
import type { FormType } from '../data';
|
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
|
import type { MesProWorkOrderProcessApi } from '#/api/mes/pro/workorder/process';
|
|
import { computed, nextTick, onUnmounted, ref, watch } from 'vue';
|
|
import { useVbenModal } from '@vben/common-ui';
|
import { MesProWorkOrderStatusEnum } from '@vben/constants';
|
import { IconifyIcon } from '@vben/icons';
|
|
import { useSortable } from '@vueuse/integrations/useSortable';
|
import { message, Tag } from 'ant-design-vue';
|
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
import {
|
deleteWorkOrderProcess,
|
getWorkOrderProcessListByWorkOrderId,
|
} from '#/api/mes/pro/workorder/process';
|
import { getRouteProcessListByProduct } from '#/api/mes/pro/route/process';
|
import { $t } from '#/locales';
|
|
import { useProcessGridColumns } from '../data';
|
import ProcessForm from './process-form.vue';
|
|
const props = defineProps<{
|
formType: FormType;
|
workOrder: MesProWorkOrderApi.WorkOrder;
|
workOrderId?: number;
|
productId?: number;
|
}>();
|
|
const emit = defineEmits<{
|
processListChange: [list: MesProWorkOrderProcessApi.WorkOrderProcess[]];
|
}>();
|
|
// 工序列表数据
|
const list = ref<MesProWorkOrderProcessApi.WorkOrderProcess[]>([]);
|
|
const isEditable = computed(() =>
|
['create', 'update'].includes(props.formType) &&
|
(props.workOrder?.status === MesProWorkOrderStatusEnum.PREPARE ||
|
props.formType === 'create'),
|
);
|
|
const isCreateMode = computed(() => props.formType === 'create');
|
|
// 拖拽排序
|
const sortableInstance = ref<any>(null);
|
|
const [ProcessFormModal, processFormModalApi] = useVbenModal({
|
connectedComponent: ProcessForm,
|
destroyOnClose: true,
|
});
|
|
const [Grid, gridApi] = useVbenVxeGrid({
|
gridOptions: {
|
autoResize: true,
|
border: true,
|
columns: useProcessGridColumns(isEditable.value),
|
data: [],
|
minHeight: 240,
|
pagerConfig: { enabled: false },
|
rowConfig: { isHover: true, keyField: 'processId' },
|
showOverflow: true,
|
toolbarConfig: { enabled: false },
|
} as VxeTableGridOptions<MesProWorkOrderProcessApi.WorkOrderProcess>,
|
});
|
|
/** 初始化/销毁拖拽排序 */
|
async function initSortable() {
|
destroySortable();
|
if (!isEditable.value || list.value.length === 0) return;
|
await nextTick();
|
sortableInstance.value = useSortable(
|
'.workorder-process-list .vxe-table--body-wrapper:not(.fixed-right--wrapper) .vxe-table--body tbody',
|
list.value,
|
{
|
animation: 150,
|
draggable: '.vxe-body--row',
|
handle: '.drag-handle',
|
onEnd: ({ newDraggableIndex, oldDraggableIndex }) => {
|
if (
|
newDraggableIndex !== undefined &&
|
oldDraggableIndex !== undefined &&
|
oldDraggableIndex !== newDraggableIndex
|
) {
|
// 重新排序数组
|
list.value.splice(
|
newDraggableIndex,
|
0,
|
list.value.splice(oldDraggableIndex, 1)[0]!,
|
);
|
// 更新 sort 字段
|
list.value.forEach((item, index) => {
|
item.sort = index + 1;
|
});
|
gridApi.setGridOptions({ data: list.value });
|
emit('processListChange', list.value);
|
}
|
},
|
},
|
);
|
}
|
|
/** 销毁拖拽排序实例 */
|
function destroySortable() {
|
if (sortableInstance.value) {
|
sortableInstance.value.stop?.();
|
sortableInstance.value = null;
|
}
|
}
|
|
/** 加载工序列表 */
|
async function getList() {
|
gridApi.setLoading(true);
|
try {
|
if (isCreateMode.value) {
|
// 新增模式:从工艺路线获取
|
if (!props.productId) {
|
list.value = [];
|
} else {
|
// 调用工艺路线接口获取工序列表
|
const routeProcessList = await getRouteProcessListByProduct(props.productId);
|
// 转换为工单工序格式
|
list.value = routeProcessList.map((item) => ({
|
processId: item.processId,
|
processCode: item.processCode,
|
processName: item.processName,
|
sort: item.sort,
|
nextProcessId: item.nextProcessId,
|
nextProcessName: item.nextProcessName,
|
linkType: item.linkType,
|
prepareTime: item.prepareTime,
|
waitTime: item.waitTime,
|
colorCode: item.colorCode,
|
keyFlag: item.keyFlag,
|
checkFlag: item.checkFlag,
|
backflushFlag: item.backflushFlag,
|
outputItemId: item.outputItemId,
|
outputItemCode: item.outputItemCode,
|
outputItemName: item.outputItemName,
|
remark: item.remark,
|
}));
|
}
|
} else {
|
// 编辑模式:从工单工序获取
|
if (!props.workOrderId) {
|
list.value = [];
|
} else {
|
list.value = await getWorkOrderProcessListByWorkOrderId(props.workOrderId);
|
}
|
}
|
gridApi.setGridOptions({ data: list.value });
|
emit('processListChange', list.value);
|
// 初始化拖拽排序
|
initSortable();
|
} finally {
|
gridApi.setLoading(false);
|
}
|
}
|
|
/** 添加工序 */
|
function handleCreate() {
|
processFormModalApi
|
.setData({
|
workOrderId: props.workOrderId,
|
productId: props.productId || props.workOrder?.productId,
|
processList: list.value,
|
})
|
.open();
|
}
|
|
/** 编辑工序 */
|
function handleEdit(row: MesProWorkOrderProcessApi.WorkOrderProcess) {
|
processFormModalApi
|
.setData({
|
id: row.id,
|
workOrderId: props.workOrderId,
|
productId: props.productId || props.workOrder?.productId,
|
row,
|
processList: list.value,
|
})
|
.open();
|
}
|
|
/** 新增/编辑成功回调 */
|
function handleFormSuccess(data: MesProWorkOrderProcessApi.WorkOrderProcess, isEdit: boolean) {
|
if (isCreateMode.value) {
|
if (isEdit) {
|
const index = list.value.findIndex((item) => item.processId === data.processId);
|
if (index > -1) {
|
list.value[index] = data;
|
}
|
} else {
|
list.value.push(data);
|
}
|
gridApi.setGridOptions({ data: list.value });
|
emit('processListChange', list.value);
|
initSortable();
|
} else {
|
getList();
|
}
|
}
|
|
/** 删除工序 */
|
function handleDelete(row: MesProWorkOrderProcessApi.WorkOrderProcess) {
|
if (isCreateMode.value) {
|
// 新增模式:从列表删除
|
const index = list.value.findIndex((item) => item.processId === row.processId);
|
if (index > -1) {
|
list.value.splice(index, 1);
|
// 更新 sort 字段
|
list.value.forEach((item, idx) => {
|
item.sort = idx + 1;
|
});
|
gridApi.setGridOptions({ data: list.value });
|
emit('processListChange', list.value);
|
initSortable();
|
}
|
} else {
|
// 编辑模式:调用接口删除
|
deleteWorkOrderProcess(row.id!).then(() => {
|
message.success($t('ui.actionMessage.deleteSuccess', [row.processName]));
|
getList();
|
});
|
}
|
}
|
|
// 组件卸载时销毁排序实例
|
onUnmounted(() => {
|
destroySortable();
|
});
|
|
// 监听产品/工单变化
|
watch(
|
() => [props.productId, props.workOrderId],
|
() => {
|
getList();
|
},
|
{ immediate: true },
|
);
|
</script>
|
|
<template>
|
<div class="workorder-process-list">
|
<ProcessFormModal @success="handleFormSuccess" />
|
<div v-if="isEditable" class="mb-3 flex items-center justify-start">
|
<TableAction
|
:actions="[
|
{
|
label: '添加工序',
|
type: 'primary',
|
onClick: handleCreate,
|
},
|
]"
|
/>
|
</div>
|
<Grid class="w-full" table-title="工序">
|
<template #dragHandle>
|
<IconifyIcon
|
icon="ic:round-drag-indicator"
|
class="drag-handle cursor-move text-xl text-gray-400"
|
/>
|
</template>
|
<template #keyFlag="{ row }">
|
<Tag v-if="row.keyFlag" color="red">关键</Tag>
|
<span v-else>-</span>
|
</template>
|
<template #checkFlag="{ row }">
|
<Tag v-if="row.checkFlag" color="blue">质检</Tag>
|
<span v-else>-</span>
|
</template>
|
<template #backflushFlag="{ row }">
|
<Tag v-if="row.backflushFlag" color="green">是</Tag>
|
<Tag v-else color="default">否</Tag>
|
</template>
|
<template #actions="{ row }">
|
<TableAction
|
:actions="[
|
{
|
label: $t('common.edit'),
|
type: 'link',
|
ifShow: isEditable,
|
onClick: handleEdit.bind(null, row),
|
},
|
{
|
label: $t('common.delete'),
|
type: 'link',
|
danger: true,
|
ifShow: isEditable,
|
popConfirm: {
|
title: $t('ui.actionMessage.deleteConfirm', [row.processName]),
|
confirm: handleDelete.bind(null, row),
|
},
|
},
|
]"
|
/>
|
</template>
|
</Grid>
|
</div>
|
</template>
|