zhangwencui
6 天以前 f08a81f01bc4a4104dd9ef04e071bb0bd0eb8afe
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 {
    // ç¼–辑模式:调用接口删除
    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>