zhangwencui
2026-04-20 f12d684876ffbe90fa95c3113b1e6c4308ae4615
src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -1,9 +1,10 @@
<template>
  <div class="app-container">
    <PageHeader content="工艺路线项目" />
    <!-- 工艺路线信息展示 -->
    <el-card v-if="routeInfo.processRouteCode" class="route-info-card" shadow="hover">
    <el-card v-if="routeInfo.processRouteCode"
             class="route-info-card"
             shadow="hover">
      <div class="route-info">
        <div class="info-item">
          <div class="info-label-wrapper">
@@ -37,7 +38,8 @@
            <span class="info-value">{{ routeInfo.bomNo || '-' }}</span>
          </div>
        </div>
        <div class="info-item full-width" v-if="routeInfo.description">
        <div class="info-item full-width"
             v-if="routeInfo.description">
          <div class="info-label-wrapper">
            <span class="info-label">描述</span>
          </div>
@@ -47,23 +49,21 @@
        </div>
      </div>
    </el-card>
    <!-- 表格视图 -->
    <div v-if="viewMode === 'table'" class="section-header">
    <div v-if="viewMode === 'table'"
         class="section-header">
      <div class="section-title">工艺路线项目列表</div>
      <div class="section-actions">
        <el-button
            icon="Grid"
        <el-button icon="Grid"
            @click="toggleView"
            style="margin-right: 10px;"
        >
                   style="margin-right: 10px;">
          卡片视图
        </el-button>
        <el-button type="primary" @click="handleAdd">新增</el-button>
        <el-button type="primary"
                   @click="handleAdd">新增</el-button>
      </div>
    </div>
    <el-table
        v-if="viewMode === 'table'"
    <el-table v-if="viewMode === 'table'"
        ref="tableRef"
        v-loading="tableLoading"
        border
@@ -71,162 +71,397 @@
        :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
        row-key="id"
        tooltip-effect="dark"
        class="lims-table"
    >
      <el-table-column align="center" label="序号" width="60" type="index" />
      <el-table-column label="工序名称" prop="processId" width="200">
              class="lims-table">
      <el-table-column align="center"
                       label="序号"
                       width="60"
                       type="index" />
      <el-table-column label="工序名称"
                       prop="processId"
                       width="200">
        <template #default="scope">
          {{ getProcessName(scope.row.processId) || '-' }}
        </template>
      </el-table-column>
      <el-table-column label="产品名称" prop="productName" min-width="160" />
      <el-table-column label="规格名称" prop="model" min-width="140" />
      <el-table-column label="单位" prop="unit" width="100" />
      <el-table-column label="是否质检" prop="isQuality" width="100">
      <el-table-column label="参数列表"
                       min-width="160">
        <template #default="scope">
          <el-button type="primary"
                     link
                     size="small"
                     @click="handleViewParams(scope.row)">参数列表</el-button>
        </template>
      </el-table-column>
      <el-table-column label="产品名称"
                       prop="productName"
                       min-width="160" />
      <el-table-column label="规格名称"
                       prop="model"
                       min-width="140" />
      <el-table-column label="单位"
                       prop="unit"
                       width="100" />
      <el-table-column label="是否质检"
                       prop="isQuality"
                       width="100">
        <template #default="scope">
          {{scope.row.isQuality ? "是" : "否"}}
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" fixed="right" width="150">
      <el-table-column label="操作"
                       align="center"
                       fixed="right"
                       width="150">
        <template #default="scope">
          <el-button type="primary" link size="small" @click="handleEdit(scope.row)" :disabled="scope.row.isComplete">编辑</el-button>
          <el-button type="danger" link size="small" @click="handleDelete(scope.row)" :disabled="scope.row.isComplete">删除</el-button>
          <el-button type="primary"
                     link
                     size="small"
                     @click="handleEdit(scope.row)"
                     :disabled="scope.row.isComplete">编辑</el-button>
          <el-button type="danger"
                     link
                     size="small"
                     @click="handleDelete(scope.row)"
                     :disabled="scope.row.isComplete">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 卡片视图 -->
    <template v-else>
      <div class="section-header">
        <div class="section-title">工艺路线项目列表</div>
        <div class="section-actions">
          <el-button
              icon="Menu"
          <el-button icon="Menu"
              @click="toggleView"
              style="margin-right: 10px;"
          >
                     style="margin-right: 10px;">
            表格视图
          </el-button>
          <el-button type="primary" @click="handleAdd">新增</el-button>
          <el-button type="primary"
                     @click="handleAdd">新增</el-button>
        </div>
      </div>
      <div v-loading="tableLoading" class="card-container">
        <div
            ref="cardsContainer"
            class="cards-wrapper"
        >
        <div
            v-for="(item, index) in tableData"
      <div v-loading="tableLoading"
           class="card-container">
        <div ref="cardsContainer"
             class="cards-wrapper">
          <div v-for="(item, index) in tableData"
            :key="item.id || index"
            class="process-card"
            :data-index="index"
        >
               :data-index="index">
          <!-- 序号圆圈 -->
          <div class="card-header">
            <div class="card-number">{{ index + 1 }}</div>
            <div class="card-process-name">{{ getProcessName(item.processId) || '-' }}</div>
          </div>
          <!-- 产品信息 -->
          <div class="card-content">
            <div v-if="item.productName" class="product-info">
              <div v-if="item.productName"
                   class="product-info">
              <div class="product-name">{{ item.productName }}</div>
              <div v-if="item.model" class="product-model">
                <div v-if="item.model"
                     class="product-model">
                {{ item.model }}
                <!-- <span v-if="item.unit" class="product-unit">{{ item.unit }}</span> -->
              </div>
              <el-tag type="primary" class="product-tag" v-if="item.isQuality">质检</el-tag>
                <el-tag type="primary"
                        class="product-tag"
                        v-if="item.isQuality">质检</el-tag>
            </div>
            <div v-else class="product-info empty">暂无产品信息</div>
              <div v-else
                   class="product-info empty">暂无产品信息</div>
          </div>
          <!-- 操作按钮 -->
          <div class="card-footer">
            <el-button type="primary" link size="small" @click="handleEdit(item)" :disabled="item.isComplete">编辑</el-button>
            <el-button type="danger" link size="small" @click="handleDelete(item)" :disabled="item.isComplete">删除</el-button>
              <el-button type="primary"
                         link
                         size="small"
                         @click="handleEdit(item)"
                         :disabled="item.isComplete">编辑</el-button>
              <el-button type="info"
                         link
                         size="small"
                         @click="handleViewParams(item)">参数列表</el-button>
              <el-button type="danger"
                         link
                         size="small"
                         @click="handleDelete(item)"
                         :disabled="item.isComplete">删除</el-button>
          </div>
        </div>
      </div>
      </div>
    </template>
    <!-- 新增/编辑弹窗 -->
    <el-dialog
        v-model="dialogVisible"
        :title="operationType === 'add' ? '新增工艺路线项目' : '编辑工艺路线项目'"
        width="500px"
        @close="closeDialog"
    >
      <el-form
          ref="formRef"
          :model="form"
          :rules="rules"
          label-width="120px"
      >
        <el-form-item label="工序" prop="processId">
          <el-select
              v-model="form.processId"
              placeholder="请选择工序"
    <div class="section-BOM">
      <div class="section-header">
        <div class="section-title">BOM</div>
        <div class="section-actions">
          <el-button type="primary"
                     @click="toggleBomEdit">
            {{ bomDataValue.isEdit ? '取消' : '编辑' }}
          </el-button>
          <el-button v-if=" bomDataValue.isEdit"
                     type="success"
                     @click="saveBomChanges">保存</el-button>
        </div>
      </div>
      <div>
        <!-- BOM表格 -->
        <el-table :data="bomTableData"
                  border
                  :preserve-expanded-content="false"
                  :default-expand-all="true"
                  style="width: 100%">
          <el-table-column type="expand">
            <template #default="props">
              <el-form ref="bomFormRef"
                       :model="bomDataValue">
                <el-table :data="props.row.bomList"
                          row-key="tempId"
                          default-expand-all
                          :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
                          style="width: 100%">
                  <el-table-column prop="productName"
                                   label="产品" />
                  <el-table-column prop="model"
                                   label="规格">
                    <template #default="{ row }">
                      <el-form-item v-if="bomDataValue.isEdit"
                                    :rules="[{ required: true, message: '请选择规格', trigger: ['blur','change'] }]"
                                    style="margin: 0">
                        <el-select v-model="row.model"
                                   placeholder="请选择规格"
              clearable
                                   :disabled="!bomDataValue.isEdit"
              style="width: 100%"
          >
            <el-option
                v-for="process in processOptions"
                :key="process.id"
                :label="process.name"
                :value="process.id"
            />
                                   @visible-change="(v) => { if (v) openBomProductDialog(row.tempId) }">
                          <el-option v-if="row.model"
                                     :label="row.model"
                                     :value="row.model" />
          </el-select>
        </el-form-item>
        <el-form-item label="产品名称" prop="productModelId">
          <el-button type="primary" @click="showProductSelectDialog = true">
                      <span v-else>{{ row.model }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column prop="processName"
                                   label="消耗工序">
                    <template #default="{ row }">
                      <el-form-item v-if="bomDataValue.isEdit"
                                    :rules="[{ required: true, message: '请选择消耗工序', trigger: 'change' }]"
                                    style="margin: 0">
                        <el-select v-model="row.processId"
                                   placeholder="请选择"
                                   filterable
                                   clearable
                                   :disabled="!bomDataValue.isEdit"
                                   style="width: 100%">
                          <el-option v-for="process in processOptions"
                                     :key="process.id"
                                     :label="process.name"
                                     :value="process.id" />
                        </el-select>
                      </el-form-item>
                      <span v-else>{{ row.processName }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column prop="unitQuantity"
                                   label="单位产出所需数量">
                    <template #default="{ row }">
                      <el-form-item v-if="bomDataValue.isEdit"
                                    :rules="[{ required: true, message: '请输入单位产出所需数量', trigger: ['blur','change'] }]"
                                    style="margin: 0">
                        <el-input-number v-model="row.unitQuantity"
                                         :min="0"
                                         :precision="2"
                                         :step="1"
                                         controls-position="right"
                                         style="width: 100%"
                                         :disabled="!bomDataValue.isEdit" />
                      </el-form-item>
                      <span v-else>{{ row.unitQuantity }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column v-if="pageType === 'order'"
                                   prop="demandedQuantity"
                                   label="需求总量">
                    <template #default="{ row }">
                      <el-form-item v-if="bomDataValue.isEdit"
                                    :rules="[{ required: true, message: '请输入需求总量', trigger: ['blur','change'] }]"
                                    style="margin: 0">
                        <el-input-number v-model="row.demandedQuantity"
                                         :min="0"
                                         :precision="2"
                                         :step="1"
                                         controls-position="right"
                                         style="width: 100%"
                                         :disabled="!bomDataValue.isEdit" />
                      </el-form-item>
                      <span v-else>{{ row.demandedQuantity }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column prop="unit"
                                   label="单位">
                    <template #default="{ row }">
                      <el-form-item v-if="bomDataValue.isEdit"
                                    :rules="[{ required: true, message: '请输入单位', trigger: ['blur','change'] }]"
                                    style="margin: 0">
                        <el-input v-model="row.unit"
                                  placeholder="请输入单位"
                                  clearable
                                  :disabled="!bomDataValue.isEdit" />
                      </el-form-item>
                      <span v-else>{{ row.unit }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column label="操作"
                                   fixed="right"
                                   width="180">
                    <template #default="{ row }">
                      <el-button v-if="bomDataValue.isEdit"
                                 type="danger"
                                 text
                                 size="small"
                                 @click="removeBomItem(row.tempId)">删除</el-button>
                      <el-button v-if="bomDataValue.isEdit"
                                 type="primary"
                                 text
                                 size="small"
                                 @click="addBomItem2(row.tempId)">添加子项</el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </el-form>
            </template>
          </el-table-column>
          <el-table-column label="BOM编号"
                           prop="bomNo" />
          <el-table-column label="产品名称"
                           prop="productName" />
          <el-table-column label="规格型号"
                           prop="model" />
        </el-table>
        <!-- <div v-if="bomDataValue.isEdit"
             style="text-align: center;border: 1px solid #e4e7ed;padding: 10px;transition: all 0.3s ease;cursor: pointer;"
             :class="{'hover-effect': bomDataValue.isEdit}">
          <el-button type="primary"
                     text
                     @click="addBomItem">
            <el-icon style="vertical-align: middle;margin-right: 5px;">
              <Plus />
            </el-icon>
            添加
          </el-button>
        </div> -->
      </div>
    </div>
    <!-- 新增/编辑弹窗 -->
    <el-dialog v-model="dialogVisible"
               :title="operationType === 'add' ? '新增工艺路线项目' : '编辑工艺路线项目'"
               width="500px"
               @close="closeDialog">
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="120px">
        <el-form-item label="工序"
                      prop="processId">
          <el-select v-model="form.processId"
                     placeholder="请选择工序"
                     clearable
                     style="width: 100%">
            <el-option v-for="process in processOptions"
                       :key="process.id"
                       :label="process.name"
                       :value="process.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="产品名称"
                      prop="productModelId">
          <el-button type="primary"
                     @click="showProductSelectDialog = true">
            {{ form.productName && form.model 
              ? `${form.productName} - ${form.model}` 
              : '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item label="单位" prop="unit">
          <el-input
              v-model="form.unit"
        <el-form-item label="单位"
                      prop="unit">
          <el-input v-model="form.unit"
              :placeholder="form.productModelId ? '根据选择的产品自动带出' : '请先选择产品'" 
              clearable 
              :disabled="true"
          />
                    :disabled="true" />
        </el-form-item>
        <el-form-item label="是否质检" prop="isQuality">
          <el-switch v-model="form.isQuality" :active-value="true" inactive-value="false"/>
        <el-form-item label="是否质检"
                      prop="isQuality">
          <el-switch v-model="form.isQuality"
                     :active-value="true"
                     inactive-value="false" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
        <el-button type="primary"
                   @click="handleSubmit"
                   :loading="submitLoading">确定</el-button>
        <el-button @click="closeDialog">取消</el-button>
      </template>
    </el-dialog>
    <!-- 产品选择对话框 -->
    <ProductSelectDialog
        v-model="showProductSelectDialog"
    <ProductSelectDialog v-model="showProductSelectDialog"
        @confirm="handleProductSelect"
        single
    />
                         single />
    <!-- BOM产品选择对话框 -->
    <ProductSelectDialog v-model="bomDataValue.showProductDialog"
                         @confirm="handleBomProductSelect"
                         single />
    <!-- 参数列表对话框 -->
    <!-- :editable="!routeInfo.status" -->
    <ProcessParamListDialog v-model="showParamListDialog"
                            :title="`${currentProcess ? (currentProcess.processName || getProcessName(currentProcess.processId)) : ''} - 参数列表`"
                            :route-id="routeId"
                            :order-id="orderId"
                            :process="currentProcess"
                            :page-type="pageType"
                            :param-list="paramList"
                            @refresh="refreshParamList" />
  </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 { findProcessRouteItemList, addOrUpdateProcessRouteItem, sortProcessRouteItem, batchDeleteProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
import { findProductProcessRouteItemList, deleteRouteItem, addRouteItem, addOrUpdateProductProcessRouteItem, sortRouteItem } from "@/api/productionManagement/productProcessRoute.js";
  import ProcessParamListDialog from "@/components/ProcessParamListDialog.vue";
  import {
    findProcessRouteItemList,
    addOrUpdateProcessRouteItem,
    sortProcessRouteItem,
    batchDeleteProcessRouteItem,
    getProcessParamList,
  } from "@/api/productionManagement/processRouteItem.js";
  import {
    findProductProcessRouteItemList,
    deleteRouteItem,
    addRouteItem,
    findProcessParamListOrder,
    addOrUpdateProductProcessRouteItem,
    sortRouteItem,
  } from "@/api/productionManagement/productProcessRoute.js";
import { processList } from "@/api/productionManagement/productionProcess.js";
import { useRoute } from 'vue-router'
import { ElMessageBox } from 'element-plus'
import Sortable from 'sortablejs'
  import {
    queryList2,
    queryList,
    add2,
  } from "@/api/productionManagement/productStructure.js";
  import { useRoute } from "vue-router";
  import { ElMessageBox, ElMessage } from "element-plus";
  import Sortable from "sortablejs";
const route = useRoute()
  const route = useRoute();
const { proxy } = getCurrentInstance() || {};
const routeId = computed(() => route.query.id);
@@ -236,28 +471,40 @@
const tableLoading = ref(false);
const tableData = ref([]);
const dialogVisible = ref(false);
const operationType = ref('add'); // add | edit
  const operationType = ref("add"); // add | edit
const formRef = ref(null);
const submitLoading = ref(false);
const cardsContainer = ref(null);
const tableRef = ref(null);
const viewMode = ref('table'); // table | card
  const viewMode = ref("card"); // table | card
const routeInfo = ref({
  processRouteCode: '',
  productName: '',
  model: '',
  bomNo: '',
  description: ''
    processRouteCode: "",
    productName: "",
    model: "",
    bomNo: "",
    description: "",
});
const processOptions = ref([]);
const showProductSelectDialog = ref(false);
  const showParamListDialog = ref(false);
  const currentProcess = ref(null);
  const paramList = ref([]);
  const bomTableData = ref([]);
  const bomFormRef = ref(null);
  const bomDataValue = ref({
    dataList: [],
    showProductDialog: false,
    currentRowName: null,
    loading: false,
    isEdit: false,
  });
let tableSortable = null;
let cardSortable = null;
// 切换视图
const toggleView = () => {
  viewMode.value = viewMode.value === 'table' ? 'card' : 'table';
    viewMode.value = viewMode.value === "table" ? "card" : "table";
  // 切换视图后重新初始化拖拽排序
  nextTick(() => {
    initSortable();
@@ -276,15 +523,17 @@
});
const rules = {
  processId: [{ required: true, message: '请选择工序', trigger: 'change' }],
  productModelId: [{ required: true, message: '请选择产品', trigger: 'change' }],
    processId: [{ required: true, message: "请选择工序", trigger: "change" }],
    productModelId: [
      { required: true, message: "请选择产品", trigger: "change" },
    ],
};
// 根据工序ID获取工序名称
const getProcessName = (processId) => {
  if (!processId) return '';
  const getProcessName = processId => {
    if (!processId) return "";
  const process = processOptions.value.find(p => p.id === processId);
  return process ? process.name : '';
    return process ? process.name : "";
};
// 获取列表
@@ -325,24 +574,92 @@
// 获取工艺路线详情(从路由参数获取)
const getRouteInfo = () => {
  routeInfo.value = {
    processRouteCode: route.query.processRouteCode || '',
    productName: route.query.productName || '',
    model: route.query.model || '',
    bomNo: route.query.bomNo || '',
    description: route.query.description || ''
      processRouteCode: route.query.processRouteCode || "",
      productName: route.query.productName || "",
      model: route.query.model || "",
      bomNo: route.query.bomNo || "",
      description: route.query.description || "",
      status: !(route.query.status == 1 || route.query.status === "false"),
  };
    if (pageType.value === "order") {
      queryList2(route.query.orderId)
        .then(res => {
          if (res.data) {
            // 为BOM数据设置tempId
            const setTempIdRecursively = items => {
              items.forEach(item => {
                item.tempId = item.id || new Date().getTime();
                if (item.children && item.children.length > 0) {
                  setTempIdRecursively(item.children);
                }
              });
            };
            setTempIdRecursively(res.data);
            bomTableData.value = [
              {
                bomNo: routeInfo.value.bomNo,
                dictLabel: routeInfo.value.dictLabel,
                productCode: "",
                productName: routeInfo.value.productName,
                model: routeInfo.value.model,
                bomList: res.data,
              },
            ];
            // 保存原始BOM数据
            bomDataValue.value.dataList = res.data;
          }
        })
        .catch(err => {
          console.error("获取BOM数据失败:", err);
        });
    } else {
      queryList(Number(route.query.bomId))
        .then(res => {
          if (res.data) {
            // 为BOM数据设置tempId
            const setTempIdRecursively = items => {
              items.forEach(item => {
                item.tempId = item.id || new Date().getTime();
                if (item.children && item.children.length > 0) {
                  setTempIdRecursively(item.children);
                }
              });
            };
            setTempIdRecursively(res.data);
            bomTableData.value = [
              {
                bomNo: routeInfo.value.bomNo,
                dictLabel: routeInfo.value.dictLabel,
                productCode: "",
                productName: routeInfo.value.productName,
                model: routeInfo.value.model,
                bomList: res.data,
              },
            ];
            // 保存原始BOM数据
            bomDataValue.value.dataList = res.data;
          }
        })
        .catch(err => {
          console.error("获取BOM数据失败:", err);
        });
    }
};
// 新增
const handleAdd = () => {
  operationType.value = 'add';
    operationType.value = "add";
  resetForm();
  dialogVisible.value = true;
};
// 编辑
const handleEdit = (row) => {
  operationType.value = 'edit';
  const handleEdit = row => {
    operationType.value = "edit";
  form.value = {
    id: row.id,
    routeId: routeId.value,
@@ -357,33 +674,33 @@
};
// 删除
const handleDelete = (row) => {
  ElMessageBox.confirm('确认删除该工艺路线项目?', '提示', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning'
  const handleDelete = row => {
    ElMessageBox.confirm("确认删除该工艺路线项目?", "提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
  })
    .then(() => {
      // 生产订单下使用 productProcessRoute 的删除接口(路由后拼接 id),其它情况使用工艺路线项目批量删除接口
      const deletePromise =
        pageType.value === 'order'
          pageType.value === "order"
          ? deleteRouteItem(row.id)
          : batchDeleteProcessRouteItem([row.id]);
      deletePromise
        .then(() => {
          proxy?.$modal?.msgSuccess('删除成功');
            proxy?.$modal?.msgSuccess("删除成功");
          getList();
        })
        .catch(() => {
          proxy?.$modal?.msgError('删除失败');
            proxy?.$modal?.msgError("删除失败");
        });
    })
    .catch(() => {});
};
// 产品选择
const handleProductSelect = (products) => {
  const handleProductSelect = products => {
  if (products && products.length > 0) {
    const product = products[0];
    form.value.productModelId = product.id;
@@ -392,21 +709,21 @@
    form.value.unit = product.unit || "";
    showProductSelectDialog.value = false;
    // 触发表单验证
    formRef.value?.validateField('productModelId');
      formRef.value?.validateField("productModelId");
  }
};
// 提交
const handleSubmit = () => {
  formRef.value.validate((valid) => {
    formRef.value.validate(valid => {
    if (valid) {
      submitLoading.value = true;
      
      if (operationType.value === 'add') {
        if (operationType.value === "add") {
        // 新增:传单个对象,包含dragSort字段
        // dragSort = 当前列表长度 + 1,表示新增记录排在最后
        const dragSort = tableData.value.length + 1;
        const isOrderPage = pageType.value === 'order';
          const isOrderPage = pageType.value === "order";
        const addPromise = isOrderPage
          ? addRouteItem({
@@ -427,19 +744,19 @@
        addPromise
          .then(() => {
            proxy?.$modal?.msgSuccess('新增成功');
              proxy?.$modal?.msgSuccess("新增成功");
            closeDialog();
            getList();
          })
          .catch(() => {
            proxy?.$modal?.msgError('新增失败');
              proxy?.$modal?.msgError("新增失败");
          })
          .finally(() => {
            submitLoading.value = false;
          });
      } else {
        // 编辑:生产订单下使用 productProcessRoute/updateRouteItem,其它情况使用工艺路线项目更新接口
        const isOrderPage = pageType.value === 'order';
          const isOrderPage = pageType.value === "order";
        
        const updatePromise = isOrderPage
          ? addOrUpdateProductProcessRouteItem({
@@ -458,12 +775,12 @@
        updatePromise
          .then(() => {
            proxy?.$modal?.msgSuccess('修改成功');
              proxy?.$modal?.msgSuccess("修改成功");
            closeDialog();
            getList();
          })
          .catch(() => {
            proxy?.$modal?.msgError('修改失败');
              proxy?.$modal?.msgError("修改失败");
          })
          .finally(() => {
            submitLoading.value = false;
@@ -493,26 +810,286 @@
  resetForm();
};
  // 查看参数列表
  const handleViewParams = row => {
    currentProcess.value = row;
    const query = {
      routeItemId: row.id,
      orderId: orderId.value,
    };
    const apiPromise =
      pageType.value === "order"
        ? findProcessParamListOrder(query)
        : getProcessParamList(query);
    apiPromise
      .then(res => {
        paramList.value = res.data || [];
        showParamListDialog.value = true;
      })
      .catch(err => {
        console.error("获取参数列表失败:", err);
        proxy?.$modal?.msgError("获取参数列表失败");
      });
  };
  // 刷新参数列表
  const refreshParamList = () => {
    if (currentProcess.value) {
      handleViewParams(currentProcess.value);
    }
  };
  // BOM相关方法
  // 切换BOM编辑模式
  const toggleBomEdit = () => {
    bomDataValue.value.isEdit = !bomDataValue.value.isEdit;
    if (!bomDataValue.value.isEdit) {
      // 取消编辑时重新加载数据
      getRouteInfo();
    }
  };
  // 添加BOM项
  const addBomItem = () => {
    if (bomTableData.value.length > 0) {
      const newItem = {
        parentId: "",
        parentTempId: "",
        productName: "",
        productId: "",
        model: undefined,
        productModelId: undefined,
        processId: "",
        processName: "",
        unitQuantity: 0,
        demandedQuantity: 0,
        unit: "",
        children: [],
        tempId: new Date().getTime(),
      };
      bomTableData.value[0].bomList.push(newItem);
    }
  };
  // 添加BOM子项
  const addBomItem2 = tempId => {
    const addChildItem = (items, tempId) => {
      for (let i = 0; i < items.length; i++) {
        const item = items[i];
        if (item.tempId === tempId) {
          if (!item.children) {
            item.children = [];
          }
          item.children.push({
            parentId: item.id || "",
            parentTempId: item.tempId || "",
            productName: "",
            productId: "",
            model: undefined,
            productModelId: undefined,
            processId: "",
            processName: "",
            unitQuantity: 0,
            demandedQuantity: 0,
            unit: "",
            children: [],
            tempId: new Date().getTime(),
          });
          return true;
        }
        if (item.children && item.children.length > 0) {
          if (addChildItem(item.children, tempId)) {
            return true;
          }
        }
      }
      return false;
    };
    if (bomTableData.value.length > 0) {
      addChildItem(bomTableData.value[0].bomList, tempId);
    }
  };
  // 删除BOM项
  const removeBomItem = tempId => {
    if (bomTableData.value.length > 0) {
      const removeFromList = (items, tempId) => {
        for (let i = 0; i < items.length; i++) {
          const item = items[i];
          if (item.tempId === tempId) {
            items.splice(i, 1);
            return true;
          }
          if (item.children && item.children.length > 0) {
            if (removeFromList(item.children, tempId)) {
              return true;
            }
          }
        }
        return false;
      };
      removeFromList(bomTableData.value[0].bomList, tempId);
    }
  };
  // 打开BOM产品选择对话框
  const openBomProductDialog = tempId => {
    bomDataValue.value.currentRowName = tempId;
    bomDataValue.value.showProductDialog = true;
  };
  // 处理BOM产品选择
  const handleBomProductSelect = products => {
    if (products && products.length > 0) {
      const product = products[0];
      const updateProductInfo = (items, tempId, productData) => {
        for (let i = 0; i < items.length; i++) {
          const item = items[i];
          if (item.tempId === tempId) {
            item.productName = productData.productName;
            item.model = productData.model;
            item.productModelId = productData.id;
            item.unit = productData.unit || "";
            return true;
          }
          if (item.children && item.children.length > 0) {
            if (updateProductInfo(item.children, tempId, productData)) {
              return true;
            }
          }
        }
        return false;
      };
      if (bomTableData.value.length > 0) {
        updateProductInfo(
          bomTableData.value[0].bomList,
          bomDataValue.value.currentRowName,
          product
        );
      }
      bomDataValue.value.showProductDialog = false;
    }
  };
  // 保存BOM更改
  const saveBomChanges = () => {
    const validateBomData = (items, isTopLevel = false) => {
      for (let i = 0; i < items.length; i++) {
        const item = items[i];
        if (!item.productModelId) {
          ElMessage.error("请选择产品");
          return false;
        }
        if (!isTopLevel && !item.processId) {
          ElMessage.error("请选择消耗工序");
          return false;
        }
        if (
          item.unitQuantity === undefined ||
          item.unitQuantity === null ||
          item.unitQuantity === 0
        ) {
          ElMessage.error("请填写单位产出所需数量");
          return false;
        }
        if (
          pageType.value === "order" &&
          (item.demandedQuantity === undefined ||
            item.demandedQuantity === null ||
            item.demandedQuantity === 0)
        ) {
          ElMessage.error("请输入需求总量");
          return false;
        }
        if (item.children && item.children.length > 0) {
          if (!validateBomData(item.children, false)) {
            return false;
          }
        }
      }
      return true;
    };
    if (bomTableData.value.length > 0) {
      if (!validateBomData(bomTableData.value[0].bomList, true)) {
        return;
      }
    }
    const processBomItem = (item, parentId = null, parentTempId = null) => {
      const cleanItem = {
        id: item.id || null,
        orderId: Number(orderId.value) || null,
        parentId: parentId,
        parentTempId: parentTempId || null,
        productModelId: item.productModelId || null,
        processId: item.processId || null,
        unitQuantity: item.unitQuantity || 0,
        demandedQuantity: item.demandedQuantity || 0,
        unit: item.unit || "",
        tempId: item.tempId || new Date().getTime(),
        bomId: Number(route.query.bomId) || null,
        children: [],
      };
      if (item.children && item.children.length > 0) {
        cleanItem.children = item.children.map(child =>
          processBomItem(child, item.id, item.tempId)
        );
      }
      return cleanItem;
    };
    const saveData = {
      orderId: Number(orderId.value),
      bomId: Number(route.query.bomId),
      children: bomTableData.value[0].bomList.map(item => processBomItem(item)),
    };
    const savePromise =
      pageType.value === "order" ? add2(saveData) : add(saveData);
    savePromise
      .then(() => {
        proxy?.$modal?.msgSuccess("保存成功");
        bomDataValue.value.isEdit = false;
        getRouteInfo();
      })
      .catch(err => {
        console.error("保存BOM失败:", err);
        proxy?.$modal?.msgError("保存失败");
      });
  };
// 初始化拖拽排序
const initSortable = () => {
  destroySortable();
  
  if (viewMode.value === 'table') {
    if (viewMode.value === "table") {
    // 表格视图的拖拽排序
    if (!tableRef.value) return;
    
    const tbody = tableRef.value.$el.querySelector('.el-table__body tbody') ||
        tableRef.value.$el.querySelector('.el-table__body-wrapper > table > tbody');
      const tbody =
        tableRef.value.$el.querySelector(".el-table__body tbody") ||
        tableRef.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 || !tableData.value[evt.oldIndex]) return;
        ghostClass: "sortable-ghost",
        handle: ".el-table__row",
        filter: ".el-button, .el-select",
        onEnd: evt => {
          if (evt.oldIndex === evt.newIndex || !tableData.value[evt.oldIndex])
            return;
        // 重新排序数组
        const moveItem = tableData.value.splice(evt.oldIndex, 1)[0];
@@ -524,15 +1101,15 @@
        
        // 调用排序接口
        if (moveItem.id) {
          const isOrderPage = pageType.value === 'order';
            const isOrderPage = pageType.value === "order";
          const sortPromise = isOrderPage
            ? sortRouteItem({
                id: moveItem.id,
                dragSort: dragSort
                  dragSort: dragSort,
              })
            : sortProcessRouteItem({
                id: moveItem.id,
                dragSort: dragSort
                  dragSort: dragSort,
              });
          sortPromise
@@ -543,17 +1120,17 @@
                  item.dragSort = index + 1;
                }
              });
              proxy?.$modal?.msgSuccess('排序成功');
                proxy?.$modal?.msgSuccess("排序成功");
            })
            .catch((err) => {
              .catch(err => {
              // 排序失败,恢复原数组
              tableData.value.splice(newIndex, 1);
              tableData.value.splice(evt.oldIndex, 0, moveItem);
              proxy?.$modal?.msgError('排序失败');
                proxy?.$modal?.msgError("排序失败");
              console.error("排序失败:", err);
            });
        }
      }
        },
    });
  } else {
    // 卡片视图的拖拽排序
@@ -561,11 +1138,12 @@
    cardSortable = new Sortable(cardsContainer.value, {
      animation: 150,
      ghostClass: 'sortable-ghost',
      handle: '.process-card',
      filter: '.el-button',
      onEnd: (evt) => {
        if (evt.oldIndex === evt.newIndex || !tableData.value[evt.oldIndex]) return;
        ghostClass: "sortable-ghost",
        handle: ".process-card",
        filter: ".el-button",
        onEnd: evt => {
          if (evt.oldIndex === evt.newIndex || !tableData.value[evt.oldIndex])
            return;
        // 重新排序数组
        const moveItem = tableData.value.splice(evt.oldIndex, 1)[0];
@@ -577,15 +1155,15 @@
        
        // 调用排序接口
        if (moveItem.id) {
          const isOrderPage = pageType.value === 'order';
            const isOrderPage = pageType.value === "order";
          const sortPromise = isOrderPage
            ? sortRouteItem({
                id: moveItem.id,
                dragSort: dragSort
                  dragSort: dragSort,
              })
            : sortProcessRouteItem({
                id: moveItem.id,
                dragSort: dragSort
                  dragSort: dragSort,
              });
          sortPromise
@@ -596,17 +1174,17 @@
                  item.dragSort = index + 1;
                }
              });
              proxy?.$modal?.msgSuccess('排序成功');
                proxy?.$modal?.msgSuccess("排序成功");
            })
            .catch((err) => {
              .catch(err => {
              // 排序失败,恢复原数组
              tableData.value.splice(newIndex, 1);
              tableData.value.splice(evt.oldIndex, 0, moveItem);
              proxy?.$modal?.msgError('排序失败');
                proxy?.$modal?.msgError("排序失败");
              console.error("排序失败:", err);
            });
        }
      }
        },
    });
  }
};
@@ -802,7 +1380,7 @@
}
.section-title::before {
  content: '';
    content: "";
  position: absolute;
  left: 0;
  top: 50%;
@@ -871,7 +1449,7 @@
}
.info-label::after {
  content: '';
    content: "";
  position: absolute;
  left: 0;
  bottom: 0;
@@ -893,4 +1471,15 @@
  line-height: 1.5;
  word-break: break-all;
}
  .section-BOM {
    margin-top: 20px;
  }
  .hover-effect:hover {
    border-color: #409eff;
    background-color: #ecf5ff;
    transform: translateY(-2px);
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  }
</style>