zhangwencui
5 天以前 6f32ae6d2545c8279032105bf263eb2552fe6a09
src/views/productionManagement/productionOrder/ProcessRouteItemForm.vue
@@ -1,32 +1,23 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
    <el-dialog v-model="isShow"
        title="工艺路线项目"
        width="800px"
        @close="closeModal"
    >
               @close="closeModal">
      <div class="operate-button">
        <el-button
            type="primary"
        <el-button type="primary"
            @click="isShowProductSelectDialog = true"
            class="mb5"
            style="margin-bottom: 10px;"
        >
                   style="margin-bottom: 10px;">
          选择产品
        </el-button>
        <el-switch
            v-model="isTable"
        <el-switch v-model="isTable"
            inline-prompt
            active-text="表格"
            inactive-text="列表"
            @change="handleViewChange"
        />
                   @change="handleViewChange" />
      </div>
      <el-table
          v-if="isTable"
      <el-table v-if="isTable"
          ref="multipleTable"
          v-loading="tableLoading"
          border
@@ -35,47 +26,40 @@
          row-key="id"
          tooltip-effect="dark"
          class="lims-table"
          style="cursor: move;"
      >
        <el-table-column align="center" label="序号" width="60">
                style="cursor: move;">
        <el-table-column align="center"
                         label="序号"
                         width="60">
          <template #default="scope">
            {{ scope.$index + 1 }}
          </template>
        </el-table-column>
        <el-table-column
            v-for="(item, index) in tableColumn"
        <el-table-column v-for="(item, index) in tableColumn"
            :key="index"
            :label="item.label"
            :width="item.width"
            show-overflow-tooltip
        >
          <template #default="scope" v-if="item.dataType === 'action'">
            <el-button
                v-for="(op, opIndex) in item.operation"
                         show-overflow-tooltip>
          <template #default="scope"
                    v-if="item.dataType === 'action'">
            <el-button v-for="(op, opIndex) in item.operation"
                :key="opIndex"
                :type="op.type"
                :link="op.link"
                size="small"
                @click.stop="op.clickFun(scope.row)"
            >
                       @click.stop="op.clickFun(scope.row)">
              {{ op.name }}
            </el-button>
          </template>
          <template #default="scope" v-else>
          <template #default="scope"
                    v-else>
            <template v-if="item.prop === 'processId'">
              <el-select
                  v-model="scope.row[item.prop]"
              <el-select v-model="scope.row[item.prop]"
                  style="width: 100%;"
                  @mousedown.stop
              >
                <el-option
                    v-for="process in processOptions"
                         @mousedown.stop>
                <el-option v-for="process in processOptions"
                    :key="process.id"
                    :label="process.name"
                    :value="process.id"
                />
                           :value="process.id" />
              </el-select>
            </template>
            <template v-else>
@@ -84,90 +68,90 @@
          </template>
        </el-table-column>
      </el-table>
      <!-- 使用普通div替代el-steps -->
      <div
          v-else
      <div v-else
          ref="stepsContainer"
          class="mb5 custom-steps"
          style="padding: 10px 0; display: flex; flex-wrap: nowrap; gap: 20px; align-items: flex-start;"
      >
        <div
            v-for="(item, index) in routeItems"
           style="padding: 10px 0; display: flex; flex-wrap: nowrap; gap: 20px; align-items: flex-start;">
        <div v-for="(item, index) in routeItems"
            :key="item.id"
            class="custom-step draggable-step"
            :data-id="item.id"
            style="cursor: move; flex: 0 0 auto; min-width: 220px;"
        >
             style="cursor: move; flex: 0 0 auto; min-width: 220px;">
          <div class="step-content">
            <div class="step-number">{{ index + 1 }}</div>
            <el-card
                :header="item.productName"
            <el-card :header="item.productName"
                class="step-card"
                style="cursor: move;"
            >
                     style="cursor: move;">
              <div class="step-card-content">
                <p>{{ item.model }}</p>
                <p>{{ item.unit }}</p>
                <el-select
                    v-model="item.processId"
                <el-select v-model="item.processId"
                    style="width: 100%;"
                    @mousedown.stop
                >
                  <el-option
                      v-for="process in processOptions"
                           @mousedown.stop>
                  <el-option v-for="process in processOptions"
                      :key="process.id"
                      :label="process.name"
                      :value="process.id"
                  />
                             :value="process.id" />
                </el-select>
              </div>
              <template #footer>
                <div class="step-card-footer">
                  <el-button type="danger" link size="small" @click.stop="removeItemByID(item.id)">删除</el-button>
                  <el-button type="danger"
                             link
                             size="small"
                             @click.stop="removeItemByID(item.id)">删除</el-button>
                </div>
              </template>
            </el-card>
          </div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button type="primary"
                     @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <ProductSelectDialog
        v-model="isShowProductSelectDialog"
        @confirm="handelSelectProducts"
    />
    <ProductSelectDialog v-model="isShowProductSelectDialog"
                         @confirm="handelSelectProducts" />
  </div>
</template>
<script setup>
import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue";
  import {
    ref,
    computed,
    getCurrentInstance,
    onMounted,
    onUnmounted,
    nextTick,
  } from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import { findProductProcessRouteItemList, addOrUpdateProductProcessRouteItem } from "@/api/productionManagement/productProcessRoute.js";
  import {
    findProductProcessRouteItemList,
    addOrUpdateProductProcessRouteItem,
    deleteRouteItem,
  } from "@/api/productionManagement/productProcessRoute.js";
import { processList } from "@/api/productionManagement/productionProcess.js";
import Sortable from 'sortablejs';
  import Sortable from "sortablejs";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
    default: false
      default: false,
  },
  record: {
    type: Object,
    required: true,
    default: () => ({})
  }
      default: () => ({}),
    },
});
const emit = defineEmits(['update:visible', 'completed']);
  const emit = defineEmits(["update:visible", "completed"]);
const processOptions = ref([]);
const tableLoading = ref(false);
@@ -184,8 +168,8 @@
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  }
      emit("update:visible", val);
    },
});
const tableColumn = ref([
@@ -204,49 +188,84 @@
        name: "删除",
        type: "danger",
        link: true,
        clickFun: (row) => {
          clickFun: row => {
            console.log(row.id, "删除");
            const dragSortx = routeItems.value.findIndex(
              item => item.dragSort === row.dragSort
            );
          const idx = routeItems.value.findIndex(item => item.id === row.id);
          if (idx > -1) {
            removeItem(idx)
            console.log(idx, "idx");
            if (row.id) {
              deleteRouteItemByIds({ id: row.id }, idx);
            } else {
              removeItem(dragSortx);
          }
        }
      }
    ]
  }
          },
        },
      ],
    },
]);
const removeItem = (index) => {
  const removeItem = index => {
    console.log("软删除", index);
  routeItems.value.splice(index, 1);
    updateDragSort();
  nextTick(() => initSortable());
};
const removeItemByID = (id) => {
  const removeItemByID = id => {
  const idx = routeItems.value.findIndex(item => item.id === id);
  if (idx > -1) {
    routeItems.value.splice(idx, 1);
      updateDragSort();
    nextTick(() => initSortable());
  }
  };
  const deleteRouteItemByIds = (ids, index) => {
    deleteRouteItem(ids).then(res => {
      routeItems.value.splice(index, 1);
      updateDragSort();
      nextTick(() => initSortable());
    });
};
const closeModal = () => {
  isShow.value = false;
};
const handelSelectProducts = (products) => {
  const updateDragSort = () => {
    routeItems.value.forEach((item, index) => {
      item.dragSort = index + 1;
    });
    routeItems.value = [...routeItems.value];
    console.log("更新后的数组:", routeItems.value);
  };
  const handelSelectProducts = products => {
  destroySortable();
  const newData = products.map(({ id, ...product }) => ({
    // 计算新的dragSort值起始点
    const maxDragSort =
      routeItems.value.length > 0
        ? Math.max(...routeItems.value.map(item => item.dragSort || 0))
        : 0;
    const newData = products.map(({ id, ...product }, index) => ({
    ...product,
    productModelId: id,
    routeId: props.record.id,
    id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
    processId: undefined
      // id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
      processId: undefined,
      dragSort: maxDragSort + index + 1,
  }));
  console.log('选择产品前数组:', routeItems.value);
    console.log("选择产品前数组:", routeItems.value);
  routeItems.value.push(...newData);
  routeItems.value = [...routeItems.value];
  console.log('选择产品后数组:', routeItems.value);
    updateDragSort();
    console.log("选择产品后数组:", routeItems.value);
  // 延迟初始化,确保DOM完全渲染
  nextTick(() => {
@@ -271,7 +290,7 @@
        tableLoading.value = false;
        routeItems.value = res.data.map(item => ({
          ...item,
          processId: item.processId === 0 ? undefined : item.processId
          processId: item.processId === 0 ? undefined : item.processId,
        }));
        // 延迟初始化,确保DOM完全渲染
        nextTick(() => {
@@ -305,11 +324,11 @@
  addOrUpdateProductProcessRouteItem({
    routeId: props.record.id,
    processRouteItem: routeItems.value.map(({ id, ...item }) => item)
      processRouteItem: routeItems.value,
  })
      .then(res => {
        isShow.value = false;
        emit('completed');
        emit("completed");
        proxy?.$modal?.msgSuccess("提交成功");
      })
      .catch(err => {
@@ -333,24 +352,28 @@
  if (isTable.value) {
    if (!multipleTable.value) return;
    const tbody = multipleTable.value.$el.querySelector('.el-table__body tbody') ||
        multipleTable.value.$el.querySelector('.el-table__body-wrapper > table > tbody');
      const tbody =
        multipleTable.value.$el.querySelector(".el-table__body tbody") ||
        multipleTable.value.$el.querySelector(
          ".el-table__body-wrapper > table > tbody"
        );
    if (!tbody) return;
    tableSortable = new Sortable(tbody, {
      animation: 150,
      ghostClass: 'sortable-ghost',
      handle: '.el-table__row',
      filter: '.el-button, .el-select',
      onEnd: (evt) => {
        if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex]) return;
        ghostClass: "sortable-ghost",
        handle: ".el-table__row",
        filter: ".el-button, .el-select",
        onEnd: evt => {
          if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex])
            return;
        // 使用数组 splice 方法重新排序,与表格模式保持一致
        const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
        routeItems.value.splice(evt.newIndex, 0, moveItem);
        routeItems.value = [...routeItems.value];
        console.log('排序后数组:', routeItems.value);
      }
          updateDragSort();
          console.log("排序后数组:", routeItems.value);
        },
    });
  } else {
    if (!stepsContainer.value) return;
@@ -358,37 +381,38 @@
    // 修改:直接使用stepsContainer.value作为拖拽容器
    const stepsList = stepsContainer.value;
    if (!stepsList) {
      console.warn('未找到步骤条拖拽容器');
        console.warn("未找到步骤条拖拽容器");
      return;
    }
    // 修改:简化拖拽配置
    stepsSortable = new Sortable(stepsList, {
      animation: 150,
      ghostClass: 'sortable-ghost',
      draggable: '.draggable-step', // 可拖拽元素
      handle: '.draggable-step, .step-card', // 拖拽手柄
      filter: '.el-button, .el-select, .el-input', // 过滤按钮/选择器
        ghostClass: "sortable-ghost",
        draggable: ".draggable-step", // 可拖拽元素
        handle: ".draggable-step, .step-card", // 拖拽手柄
        filter: ".el-button, .el-select, .el-input", // 过滤按钮/选择器
      forceFallback: true,
      fallbackClass: 'sortable-fallback',
        fallbackClass: "sortable-fallback",
      preventOnFilter: true,
      scroll: true,
      scrollSensitivity: 30,
      scrollSpeed: 10,
      bubbleScroll: true,
      onEnd: (evt) => {
        if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex]) return;
        onEnd: evt => {
          if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex])
            return;
        // 使用数组 splice 方法重新排序
        const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
        routeItems.value.splice(evt.newIndex, 0, moveItem);
        routeItems.value = [...routeItems.value];
      }
          updateDragSort();
        },
    });
    // 调试:打印容器和实例,确认绑定成功
    console.log('步骤条拖拽容器:', stepsList);
    console.log('Sortable实例:', stepsSortable);
      console.log("步骤条拖拽容器:", stepsList);
      console.log("Sortable实例:", stepsSortable);
  }
};
@@ -412,7 +436,7 @@
defineExpose({
  closeModal,
  handleSubmit,
  isShow
    isShow,
});
</script>