yyb
21 小时以前 a4ac4f696eca98e7f6918288321ff1b1784fc803
新增入库审批功能,优化审批人选择逻辑,支持动态添加审批节点
已修改3个文件
284 ■■■■■ 文件已修改
src/views/collaborativeApproval/approvalProcess/index.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index.vue
@@ -18,6 +18,8 @@
                   name="6"></el-tab-pane>
      <el-tab-pane label="发货审批"
                   name="7"></el-tab-pane>
      <el-tab-pane label="入库审批"
                   name="9"></el-tab-pane>
    </el-tabs>
    <div class="search_form">
      <div>
@@ -52,12 +54,12 @@
        <el-button @click="handleOut">审批人维护</el-button>
        <el-button type="primary"
                   @click="openForm('add')"
                   v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7">新增</el-button>
                   v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7 && currentApproveType !== 9">新增</el-button>
        <el-button @click="handleExport">导出</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete"
                   v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7">删除</el-button>
                   v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7 && currentApproveType !== 9">删除</el-button>
      </div>
    </div>
    <div class="table_list">
@@ -187,6 +189,7 @@
    const isReimburseType = currentApproveType.value === 4; // 报销管理
    const isQuotationType = currentApproveType.value === 6; // 报价审批
    const isPurchaseType = currentApproveType.value === 5; // 采购审批
    const isInboundType = currentApproveType.value === 9; // 入库审批
    // 基础列配置
    const baseColumns = [
@@ -237,6 +240,8 @@
          ? "报价单号"
          : isPurchaseType
          ? "采购合同号"
          : isInboundType
          ? "入库单号"
          : "审批事由",
        prop: "approveReason",
      },
@@ -289,6 +294,7 @@
          currentApproveType.value === 5 ||
          currentApproveType.value === 6 ||
          currentApproveType.value === 7 ||
          currentApproveType.value === 9 ||
          row.approveStatus == 2 ||
          row.approveStatus == 1 ||
          row.approveStatus == 4,
@@ -402,6 +408,7 @@
      5: "/approveProcess/exportFive",
      6: "/approveProcess/exportSix",
      7: "/approveProcess/exportSeven",
      9: "/approveProcess/exportNine",
    };
    const url = urlMap[type] || urlMap[0];
    const nameMap = {
@@ -413,6 +420,7 @@
      5: "采购申请审批表",
      6: "报价审批表",
      7: "发货审批表",
      9: "入库审批表",
    };
    const fileName = nameMap[type] || nameMap[0];
    proxy.download(url, {}, `${fileName}.xlsx`);
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -88,13 +88,13 @@
                               clearable :precision="2" :disabled="supplierQuantityDisabled"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检测单位:" prop="checkCompany">
              <el-input v-model="form.checkCompany" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检测结果:" prop="checkResult">
              <el-select v-model="form.checkResult">
@@ -103,8 +103,6 @@
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
              <el-select v-model="form.checkName" placeholder="请选择" clearable style="width: 100%">
@@ -112,6 +110,8 @@
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检测日期:" prop="checkTime">
              <el-date-picker
@@ -123,6 +123,47 @@
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item>
              <template #label>
                <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
                  <span>审批人选择:</span>
                  <el-button type="primary"
                             size="small"
                             @click="addApproverNode"
                             icon="Plus">新增节点</el-button>
                </div>
              </template>
              <div class="approver-nodes-container">
                <div v-for="(node, index) in approverNodes"
                     :key="node.id"
                     class="approver-node-item">
                  <div class="approver-node-header">
                    <span class="approver-node-label">审批节点 {{ index + 1 }}</span>
                    <el-button v-if="approverNodes.length > 1"
                               type="danger"
                               size="small"
                               text
                               @click="removeApproverNode(index)"
                               icon="Delete">删除</el-button>
                  </div>
                  <el-select v-model="node.userId"
                             placeholder="请选择审批人"
                             filterable
                             style="width: 100%">
                    <el-option
                      v-for="item in userList"
                      :key="item.userId"
                      :label="item.nickName"
                      :value="item.userId"
                    />
                  </el-select>
                </div>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
@@ -180,6 +221,7 @@
    quantity: "",
    checkCompany: "",
    checkResult: "",
    reviewName: "",
  },
  rules: {
    checkTime: [{required: true, message: "请输入", trigger: "blur"},],
@@ -229,6 +271,14 @@
const testStandardOptions = ref([]); // 指标选择下拉框数据
const modelOptions = ref([]);
const userList = ref([]); // 检验员下拉列表
const approverNodes = ref([{ id: 1, userId: null }]);
let nextApproverId = 2;
const addApproverNode = () => {
  approverNodes.value.push({ id: nextApproverId++, userId: null });
};
const removeApproverNode = index => {
  approverNodes.value.splice(index, 1);
};
// 编辑时:productMainId 或 purchaseLedgerId 任一有值则供应商、数量置灰
const supplierQuantityDisabled = computed(() => {
@@ -265,7 +315,10 @@
    quantity: "",
    checkCompany: "",
    checkResult: "",
    reviewName: "",
  }
  approverNodes.value = [{ id: 1, userId: null }];
  nextApproverId = 2;
  testStandardOptions.value = [];
  tableData.value = [];
  // 先确保产品树已加载,否则编辑时产品/规格型号无法反显
@@ -274,6 +327,25 @@
    // 先保存 testStandardId,避免被清空
    const savedTestStandardId = row.testStandardId;
    form.value = {...row}
    if (form.value.approveUserIds) {
      const ids = String(form.value.approveUserIds)
        .split(",")
        .map(id => id.trim())
        .filter(Boolean);
      if (ids.length > 0) {
        approverNodes.value = ids.map((id, index) => ({
          id: index + 1,
          userId: Number(id),
        }));
        nextApproverId = ids.length + 1;
      }
    } else if (form.value.reviewName) {
      const matchedReviewer = userList.value.find(item => item.nickName === form.value.reviewName);
      if (matchedReviewer?.userId) {
        approverNodes.value = [{ id: 1, userId: matchedReviewer.userId }];
        nextApproverId = 2;
      }
    }
    currentProductId.value = row.productId || 0
    // 关键:编辑时加载规格型号下拉选项,才能反显 productModelId
    if (currentProductId.value) {
@@ -397,13 +469,29 @@
const submitForm = () => {
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
      if (hasEmptyApprover) {
        proxy.$modal.msgError("请为所有审批节点选择审批人!");
        return;
      }
      const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
      const firstApproverName =
        userList.value.find(item => String(item.userId) === String(approverNodes.value[0]?.userId))
          ?.nickName || "";
      form.value.inspectType = 0
            if (operationType.value === "add") {
                tableData.value.forEach((item) => {
                    delete item.id
                })
            }
      const data = {...form.value, qualityInspectParams: tableData.value}
      const data = {
        ...form.value,
        reviewName: firstApproverName,
        approveUserIds,
        // 兼容后端不同字段命名
        auditName: firstApproverName,
        qualityInspectParams: tableData.value,
      }
      if (operationType.value === "add") {
        qualityInspectAdd(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
@@ -464,6 +552,8 @@
// 关闭弹框
const closeDia = () => {
  proxy.resetForm("formRef");
  approverNodes.value = [{ id: 1, userId: null }];
  nextApproverId = 2;
  tableData.value = [];
  testStandardOptions.value = [];
  form.value.testStandardId = '';
@@ -488,4 +578,54 @@
  cursor: not-allowed;
}
.approver-nodes-container {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  padding: 16px;
  background-color: #f8f9fa;
  border-radius: 4px;
  border: 1px solid #e4e7ed;
}
.approver-node-item {
  flex: 0 0 calc(33.333% - 12px);
  min-width: 200px;
  padding: 12px;
  background-color: #fff;
  border-radius: 4px;
  border: 1px solid #dcdfe6;
  transition: all 0.3s;
}
.approver-node-item:hover {
  border-color: #409eff;
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
}
.approver-node-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}
.approver-node-label {
  font-size: 13px;
  font-weight: 500;
  color: #606266;
}
@media (max-width: 1200px) {
  .approver-node-item {
    flex: 0 0 calc(50% - 8px);
  }
}
@media (max-width: 768px) {
  .approver-node-item {
    flex: 0 0 100%;
  }
}
</style>
src/views/salesManagement/salesLedger/index.vue
@@ -1675,6 +1675,46 @@
               title="选择入库产品"
               width="60%"
               :close-on-click-modal="false">
      <div style="margin-bottom: 12px;">
        <el-form>
          <el-form-item required>
            <template #label>
              <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
                <span>审批人选择:</span>
                <el-button type="primary"
                           size="small"
                           @click="addStockApproverNode"
                           icon="Plus">新增节点</el-button>
              </div>
            </template>
            <div class="approver-nodes-container">
              <div v-for="(node, index) in stockApproverNodes"
                   :key="node.id"
                   class="approver-node-item">
                <div class="approver-node-header">
                  <span class="approver-node-label">审批节点 {{ index + 1 }}</span>
                  <el-button v-if="stockApproverNodes.length > 1"
                             type="danger"
                             size="small"
                             text
                             @click="removeStockApproverNode(index)"
                             icon="Delete">删除</el-button>
                </div>
                <el-select v-model="node.userId"
                           placeholder="请选择审批人"
                           filterable
                           clearable
                           style="width: 100%;">
                  <el-option v-for="item in stockApproverOptions"
                             :key="item.userId"
                             :label="item.userName"
                             :value="item.userId" />
                </el-select>
              </div>
            </div>
          </el-form-item>
        </el-form>
      </div>
      <el-table :data="stockProductList"
                border
                stripe
@@ -1815,6 +1855,15 @@
  const selectedStockProductIds = ref([]);
  const stockLoading = ref(false);
  const currentStockLedgerId = ref(null);
  const stockApproverOptions = ref([]);
  const stockApproverNodes = ref([{ id: 1, userId: null }]);
  let nextStockApproverId = 2;
  const addStockApproverNode = () => {
    stockApproverNodes.value.push({ id: nextStockApproverId++, userId: null });
  };
  const removeStockApproverNode = index => {
    stockApproverNodes.value.splice(index, 1);
  };
  const ledgerQrDialogVisible = ref(false);
  const ledgerQrCompositeUrl = ref("");
@@ -2957,22 +3006,36 @@
    currentStockLedgerId.value = id;
    selectedStockProductIds.value = [];
    stockProductList.value = [];
    stockApproverNodes.value = [{ id: 1, userId: null }];
    nextStockApproverId = 2;
    stockDialogVisible.value = true;
    stockLoading.value = true;
    try {
      const approverRes = await approveUserList({ approveType: 9 });
      stockApproverOptions.value = Array.isArray(approverRes?.data)
        ? approverRes.data.map(item => ({
            userId: item.userId,
            userName: item.userName,
          }))
        : [];
      const res = await productList({ salesLedgerId: id, type: 1 });
      stockProductList.value = [];
      stockProductList.value =
        res.data.filter(item => item.productStockStatus == 0 || item.productStockStatus == 1) || [];
    } catch (e) {
      proxy?.$modal?.msgError?.("获取产品列表失败");
      proxy?.$modal?.msgError?.("获取产品或审批人失败");
    } finally {
      stockLoading.value = false;
    }
  };
  const submitStock = async () => {
    const hasEmptyApprover = stockApproverNodes.value.some(node => !node.userId);
    if (hasEmptyApprover) {
      ElMessage.warning("请为所有审批节点选择审批人");
      return;
    }
    if (selectedStockProductIds.value.length === 0) {
      ElMessage.warning("请选择至少一个产品进行入库");
      return;
@@ -2990,9 +3053,16 @@
    proxy?.$modal?.loading?.("正在入库,请稍候...");
    try {
      const approveUserIds = stockApproverNodes.value.map(node => node.userId).join(",");
      const approveUserName = stockApproverNodes.value
        .map(node => stockApproverOptions.value.find(item => String(item.userId) === String(node.userId))?.userName)
        .filter(Boolean)
        .join(",");
      await salesStock({
        salesLedgerId: currentStockLedgerId.value,
        salesLedgerProducts: selectedStockProductIds.value,
        approveUserIds,
        approveUserName,
      });
      proxy?.$modal?.msgSuccess?.("入库成功");
      stockDialogVisible.value = false;
@@ -4793,4 +4863,54 @@
  .ledger-qr-save-btn {
    margin-bottom: 12px;
  }
  .approver-nodes-container {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    padding: 16px;
    background-color: #f8f9fa;
    border-radius: 4px;
    border: 1px solid #e4e7ed;
  }
  .approver-node-item {
    flex: 0 0 calc(33.333% - 12px);
    min-width: 200px;
    padding: 12px;
    background-color: #fff;
    border-radius: 4px;
    border: 1px solid #dcdfe6;
    transition: all 0.3s;
  }
  .approver-node-item:hover {
    border-color: #409eff;
    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
  }
  .approver-node-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
  }
  .approver-node-label {
    font-size: 13px;
    font-weight: 500;
    color: #606266;
  }
  @media (max-width: 1200px) {
    .approver-node-item {
      flex: 0 0 calc(50% - 8px);
    }
  }
  @media (max-width: 768px) {
    .approver-node-item {
      flex: 0 0 100%;
    }
  }
</style>