huminmin
昨天 5f6cce2e7d5d752c00ec25b5658857c7e0beea46
src/views/productionManagement/processRoute/ItemsForm.vue
@@ -3,10 +3,18 @@
    <el-dialog
        v-model="isShow"
        title="工艺路线项目"
        width="800"
        width="800px"
        @close="closeModal"
    >
      <el-button type="primary" @click="isShowProductSelectDialog = true" class="mb5">选择产品</el-button>
      <el-button
          type="primary"
          @click="isShowProductSelectDialog = true"
          class="mb5"
          style="margin-bottom: 10px;"
      >
        选择产品
      </el-button>
      <el-table
          ref="multipleTable"
          v-loading="tableLoading"
@@ -16,10 +24,17 @@
          row-key="id"
          tooltip-effect="dark"
          class="lims-table"
          style="cursor: move;"
      >
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column v-for="(item, index) in tableColumn" :key="index" :label="item.label" :width="item.width" show-overflow-tooltip>
        <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"
@@ -27,7 +42,7 @@
                :type="op.type"
                :link="op.link"
                size="small"
                @click="op.clickFun(scope.row)"
                @click.stop="op.clickFun(scope.row)"
            >
              {{ op.name }}
            </el-button>
@@ -35,7 +50,11 @@
          <template #default="scope" v-else>
            <template v-if="item.prop === 'processId'">
              <el-select v-model="scope.row[item.prop]" style="width: 100%">
              <el-select
                  v-model="scope.row[item.prop]"
                  style="width: 100%;"
                  @mousedown.stop
              >
                <el-option
                    v-for="process in processOptions"
                    :key="process.id"
@@ -45,10 +64,9 @@
              </el-select>
            </template>
            <template v-else>
              {{ scope.row[item.prop] }}
              {{ scope.row[item.prop] || '-' }}
            </template>
          </template>
        </el-table-column>
      </el-table>
@@ -59,31 +77,42 @@
        </div>
      </template>
    </el-dialog>
  </div>
  <ProductSelectDialog v-if="isShowProductSelectDialog" v-model:model-value="isShowProductSelectDialog" @confirm="handelSelectProducts" />
    <ProductSelectDialog
        v-model="isShowProductSelectDialog"
        @confirm="handelSelectProducts"
    />
  </div>
</template>
<script setup>
import {ref, computed, getCurrentInstance, onMounted} from "vue";
import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import {findProcessRouteItemList, addOrUpdateProcessRouteItem} from "@/api/productionManagement/processRouteItem.js";
import { findProcessRouteItemList, addOrUpdateProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
import { processList } from "@/api/productionManagement/productionProcess.js";
import Sortable from 'sortablejs';
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
    default: false
  },
  record: {
    type: Object,
    required: true,
    default: () => ({})
  }
});
const emit = defineEmits(['update:visible', 'completed']);
const processOptions = ref([]);
const tableLoading = ref(false);
const isShowProductSelectDialog = ref(false);
const routeItems = ref([]);
let sortable = null;
const multipleTable = ref(null);
const isShow = computed({
  get() {
@@ -91,119 +120,166 @@
  },
  set(val) {
    emit('update:visible', val);
  },
  }
});
const tableColumn = ref([
  {
    label: "产品名称",
    prop: "productName",
  },
  {
    label: "规格名称",
    prop: "model",
  },
  {
    label: "单位",
    prop: "unit",
  },
  {
    label: "工序名称",
    prop: "processId",
  },
  { label: "产品名称", prop: "productName", width: 180 },
  { label: "规格名称", prop: "model", width: 150 },
  { label: "单位", prop: "unit", width: 80 },
  { label: "工序名称", prop: "processId", width: 180 },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 100,
    operation: [
      {
        name: "删除",
        type: "danger",
        link: true,
        clickFun: (row) => {
          const index = routeItems.value.indexOf(row);
          if (index !== -1) {
            routeItems.value.splice(index, 1);
          const idx = routeItems.value.findIndex(item => item.id === row.id);
          if (idx > -1) {
            routeItems.value.splice(idx, 1);
          }
        }
      },
      }
    ]
  }
])
const tableLoading = ref(false);
const isShowProductSelectDialog = ref(false)
const routeItems = ref([])
let { proxy } = getCurrentInstance()
]);
const closeModal = () => {
  isShow.value = false;
};
const handelSelectProducts = (products) => {
  const data = products.map(({ id, ...product }) => ({
  const newData = products.map(({ id, ...product }) => ({
    ...product,
    productModelId: id,
    routeId: props.record.id
    routeId: props.record.id,
    id: `${Date.now()}-${Math.random().toString(36).slice(2)}`, // 生成无特殊字符的ID
    processId: undefined
  }));
  routeItems.value.push(...newData);
  routeItems.value.push(...data);
}
  nextTick(() => initSortable());
};
const findProcessRouteItems = () => {
  tableLoading.value = true;
  findProcessRouteItemList({routeId: props.record.id}).then(res => {
    tableLoading.value = false;
    routeItems.value = res.data.map(item => ({
      ...item,
      processId: item.processId === 0 ? undefined : item.processId
    }))
  }).catch(err => {
    tableLoading.value = false;
  })
  findProcessRouteItemList({ routeId: props.record.id })
      .then(res => {
        tableLoading.value = false;
        routeItems.value = res.data.map(item => ({
          ...item,
          processId: item.processId === 0 ? undefined : item.processId
        }));
        nextTick(() => initSortable());
      })
      .catch(err => {
        tableLoading.value = false;
        console.error("获取列表失败:", err);
      });
};
const findProcessList = () => {
  processList({}).then(res => {
    processOptions.value = res.data
  })
}
  processList({})
      .then(res => {
        processOptions.value = res.data;
      })
      .catch(err => {
        console.error("获取工序失败:", err);
      });
};
const { proxy } = getCurrentInstance() || {};
const handleSubmit = () => {
  if (routeItems.value.length === 0) {
    proxy.$modal.msgError("请添加路线项目");
    proxy?.$modal?.msgError("请添加路线项目");
    return;
  }
  // 是否有未选择的工序
  const hasUnselectedProcess = routeItems.value.some(item => !item.processId);
  if (hasUnselectedProcess) {
    proxy.$modal.msgError("请选择工序");
  const hasEmptyProcess = routeItems.value.some(item => !item.processId);
  if (hasEmptyProcess) {
    proxy?.$modal?.msgError("请为所有项目选择工序");
    return;
  }
  addOrUpdateProcessRouteItem({routeId: props.record.id, processRouteItem: routeItems.value}).then(res => {
    // 关闭模态框
    isShow.value = false;
    // 告知父组件已完成
    emit('completed');
    proxy.$modal.msgSuccess("提交成功");
  addOrUpdateProcessRouteItem({
    routeId: props.record.id,
    processRouteItem: routeItems.value.map(({ id, ...item }) => item)
  })
      .then(res => {
        isShow.value = false;
        emit('completed');
        proxy?.$modal?.msgSuccess("提交成功");
      })
      .catch(err => {
        proxy?.$modal?.msgError(`提交失败:${err.msg || "网络异常"}`);
      });
};
const initSortable = () => {
  if (sortable) {
    sortable.destroy();
    sortable = null;
  }
  if (!multipleTable.value) return;
  const tbody = multipleTable.value.$el.querySelector('.el-table__body tbody') ||
      multipleTable.value.$el.querySelector('.el-table__body-wrapper > table > tbody');
  if (!tbody) return;
  sortable = new Sortable(tbody, {
    animation: 150,
    ghostClass: 'sortable-ghost',
    handle: '.el-table__row',
    filter: '.el-button, .el-select',
    onEnd: (evt) => {
      const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
      routeItems.value.splice(evt.newIndex, 0, moveItem);
    }
  });
};
onMounted(() => {
  findProcessRouteItems();
  findProcessList();
});
onUnmounted(() => {
  if (sortable) {
    sortable.destroy();
  }
});
// 修复:暴露方法时避免语法错误
defineExpose({
  closeModal,
  handleSubmit,
  isShow,
  isShow
});
onMounted(() => {
  findProcessRouteItems()
  findProcessList()
})
</script>
<style scoped>
:deep(.sortable-ghost) {
  opacity: 0.6;
  background-color: #f5f7fa !important;
}
:deep(.el-table__row) {
  transition: background-color 0.2s;
}
:deep(.el-table__row:hover) {
  background-color: #f9fafc !important;
}
.mb5 {
  margin-bottom: 5px;
}
</style>