zhangwencui
8 小时以前 e545f964005b0c80b29a4417ca8275570778ac75
src/views/productionManagement/processRoute/index.vue
@@ -59,15 +59,41 @@
          </div>
        </div>
        <div class="card-body">
          <div class="route-desc">{{ route.routeDesc || '暂无描述' }}</div>
          <el-button type="primary"
                     link
          <div class="route-meta">
            <span class="meta-item">
              <el-icon>
                <Box />
              </el-icon>
              <span class="meta-label">产品:</span>
              <span class="meta-value">{{ route.productName }} - {{ route.productModelName }}</span>
            </span>
            <span class="meta-item">
              <el-icon>
                <Document />
              </el-icon>
              <span class="meta-label">BOM:</span>
              <span class="meta-value">{{ route.bomId || '-' }}</span>
            </span>
            <span class="meta-item">
              <el-icon>
                <Document />
              </el-icon>
              <span class="meta-label">路线描述:</span>
              <span class="meta-value">{{ route.routeDesc || '暂无描述' }}</span>
            </span>
          </div>
          <div class="expand-btn-wrapper">
            <el-button class="expand-btn"
                       :class="{ expanded: route.expanded }"
                       type="primary"
                       text
                     @click="toggleExpand(route)">
            {{ route.expanded ? '收起' : '展开工序路线' }}
              <span class="btn-text">{{ route.expanded ? '收起工序路线' : '展开工序路线' }}</span>
            <el-icon class="expand-icon">
              <component :is="route.expanded ? 'ArrowUp' : 'ArrowDown'" />
            </el-icon>
          </el-button>
          </div>
        </div>
        <div v-if="route.expanded"
             class="process-route">
@@ -189,6 +215,28 @@
               :rules="routeRules"
               ref="routeFormRef"
               label-width="120px">
        <el-form-item label="产品名称"
                      prop="productModelId">
          <el-button type="primary"
                     @click="showProductSelectDialog = true">
            {{ routeForm.productName && routeForm.productModelName
              ? `${routeForm.productName} - ${routeForm.productModelName}`
              : '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item label="BOM"
                      prop="bomId">
          <el-select v-model="routeForm.bomId"
                     placeholder="请选择BOM"
                     clearable
                     :disabled="!routeForm.productModelId || bomOptions.length === 0"
                     style="width: 100%">
            <el-option v-for="item in bomOptions"
                       :key="item.id"
                       :label="item.bomNo || `BOM-${item.id}`"
                       :value="item.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="路线编码"
                      prop="routeCode">
          <el-input v-model="routeForm.routeCode"
@@ -222,6 +270,10 @@
        </span>
      </template>
    </el-dialog>
    <!-- 产品选择弹窗 -->
    <ProductSelectDialog v-model="showProductSelectDialog"
                         @confirm="handleProductSelect"
                         single />
    <!-- 工序新增/编辑对话框 -->
    <el-dialog v-model="processDialogVisible"
               :title="isProcessEdit ? '编辑工序' : '新增工序'"
@@ -441,7 +493,7 @@
</template>
<script setup>
  import { ref, reactive } from "vue";
  import { ref, reactive, getCurrentInstance } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import {
    Plus,
@@ -453,12 +505,23 @@
    Search,
    Check,
    Close,
    Box,
    Document,
  } from "@element-plus/icons-vue";
  import { listType } from "@/api/system/dict/type";
  import { getByModel } from "@/api/productionManagement/productBom.js";
  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
  // 工艺路线列表
  const routeList = ref([]);
  const dictTypes = ref([]);
  // 获取全局实例
  const { proxy } = getCurrentInstance();
  // 产品选择和BOM相关
  const showProductSelectDialog = ref(false);
  const bomOptions = ref([]);
  // 工艺路线对话框
  const routeDialogVisible = ref(false);
@@ -466,12 +529,20 @@
  const routeFormRef = ref(null);
  const routeForm = reactive({
    id: null,
    productModelId: null,
    productName: "",
    productModelName: "",
    bomId: null,
    routeCode: "",
    routeName: "",
    routeDesc: "",
    status: "1",
  });
  const routeRules = {
    productModelId: [
      { required: true, message: "请选择产品", trigger: "change" },
    ],
    bomId: [{ required: true, message: "请选择BOM", trigger: "change" }],
    routeCode: [{ required: true, message: "请输入路线编码", trigger: "blur" }],
    routeName: [{ required: true, message: "请输入路线名称", trigger: "blur" }],
  };
@@ -537,6 +608,10 @@
    routeList.value = [
      {
        id: 1,
        productModelId: 1,
        productName: "标准砌块",
        productModelName: "3.5型",
        bomId: 1,
        routeCode: "ROUTE001",
        routeName: "标准砌块生产线",
        routeDesc: "标准砌块生产流程",
@@ -606,6 +681,10 @@
      },
      {
        id: 2,
        productModelId: 2,
        productName: "板材",
        productModelName: "5.0型",
        bomId: 2,
        routeCode: "ROUTE002",
        routeName: "板材生产线",
        routeDesc: "板材生产流程",
@@ -651,16 +730,25 @@
  const handleAddRoute = () => {
    isRouteEdit.value = false;
    routeForm.id = null;
    routeForm.productModelId = null;
    routeForm.productName = "";
    routeForm.productModelName = "";
    routeForm.bomId = null;
    routeForm.routeCode = "";
    routeForm.routeName = "";
    routeForm.routeDesc = "";
    routeForm.status = "1";
    bomOptions.value = [];
    routeDialogVisible.value = true;
  };
  const handleEditRoute = route => {
    isRouteEdit.value = true;
    routeForm.id = route.id;
    routeForm.productModelId = route.productModelId;
    routeForm.productName = route.productName;
    routeForm.productModelName = route.productModelName;
    routeForm.bomId = route.bomId;
    routeForm.routeCode = route.routeCode;
    routeForm.routeName = route.routeName;
    routeForm.routeDesc = route.routeDesc;
@@ -687,6 +775,42 @@
        getRouteList();
      }
    });
  };
  // 产品选择处理
  const handleProductSelect = async products => {
    if (products && products.length > 0) {
      const product = products[0];
      // 先查询BOM列表(必选)
      try {
        const res = await getByModel(product.id);
        // 处理返回的BOM数据:可能是数组、对象或包含data字段
        let bomList = [];
        if (Array.isArray(res)) {
          bomList = res;
        } else if (res && res.data) {
          bomList = Array.isArray(res.data) ? res.data : [res.data];
        } else if (res && typeof res === "object") {
          bomList = [res];
        }
        if (bomList.length > 0) {
          routeForm.productModelId = product.id;
          routeForm.productName = product.productName;
          routeForm.productModelName = product.model;
          routeForm.bomId = undefined; // 重置BOM选择
          bomOptions.value = bomList;
          showProductSelectDialog.value = false;
          // 触发表单验证更新
          proxy.$refs["routeFormRef"]?.validateField("productModelId");
        } else {
          proxy.$modal.msgError("该产品没有BOM,请先创建BOM");
        }
      } catch (error) {
        // 如果接口返回404或其他错误,说明没有BOM
        proxy.$modal.msgError("该产品没有BOM,请先创建BOM");
      }
    }
  };
  const handleApproveRoute = route => {
@@ -1062,8 +1186,96 @@
        margin-bottom: 12px;
      }
      .route-meta {
        display: flex;
        gap: 24px;
        margin-bottom: 12px;
        padding: 10px 14px;
        background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%);
        border-radius: 8px;
        border-left: 3px solid #409eff;
        .meta-item {
          display: flex;
          align-items: center;
          gap: 6px;
          font-size: 13px;
          margin-right: 40px;
          .el-icon {
            font-size: 14px;
            color: #409eff;
          }
          .meta-label {
            color: #909399;
            font-weight: 500;
          }
          .meta-value {
            color: #303133;
            font-weight: 600;
          }
        }
      }
      .expand-btn-wrapper {
        display: flex;
        justify-content: center;
        margin-top: 8px;
        .expand-btn {
          padding: 8px 20px;
          border-radius: 20px;
          background: linear-gradient(135deg, #ecf5ff 0%, #d9ecff 100%);
          border: 1px solid #b3d8ff;
          transition: all 0.3s ease;
          .btn-text {
            font-size: 13px;
            font-weight: 500;
            color: #409eff;
            margin-right: 6px;
          }
      .expand-icon {
        margin-left: 4px;
            font-size: 14px;
            color: #409eff;
            transition: transform 0.3s ease;
          }
          &:hover {
            background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
            border-color: #409eff;
            box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
            .btn-text,
            .expand-icon {
              color: #fff;
            }
          }
          &.expanded {
            background: linear-gradient(135deg, #f0f9eb 0%, #e1f3d8 100%);
            border-color: #a5d69a;
            .btn-text,
            .expand-icon {
              color: #67c23a;
            }
            &:hover {
              background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
              border-color: #67c23a;
              box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3);
              .btn-text,
              .expand-icon {
                color: #fff;
              }
            }
          }
        }
      }
    }