From f08a81f01bc4a4104dd9ef04e071bb0bd0eb8afe Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 23 七月 2026 10:50:19 +0800
Subject: [PATCH] Merge branch 'dev_pro2.0' of http://114.132.189.42:9002/r/mom-pro2-before into dev_pro2.0

---
 src/views/mes/pro/workorder/modules/process-list.vue |  304 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 304 insertions(+), 0 deletions(-)

diff --git a/src/views/mes/pro/workorder/modules/process-list.vue b/src/views/mes/pro/workorder/modules/process-list.vue
new file mode 100644
index 0000000..35ac698
--- /dev/null
+++ b/src/views/mes/pro/workorder/modules/process-list.vue
@@ -0,0 +1,304 @@
+<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 {
+    // 缂栬緫妯″紡锛氳皟鐢ㄦ帴鍙e垹闄�
+    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>
\ No newline at end of file

--
Gitblit v1.9.3