Merge branch 'dev_河南_鹤壁天沐玻璃厂' of http://114.132.189.42:9002/r/product-inventory-management into dev_河南_鹤壁天沐玻璃厂
已修改9个文件
4511 ■■■■■ 文件已修改
multiple/config.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/collaborativeApproval/approvalProcess.js 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/index.vue 267 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue 379 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue 318 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index.vue 451 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 1998 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesQuotation/index.vue 940 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
multiple/config.json
@@ -10,8 +10,8 @@
  "HBTM": {
    "env": {
      "VITE_APP_TITLE": "鹤壁天沐信息管理系统",
      "VITE_BASE_API": "http://36.133.46.107:9030",
      "VITE_JAVA_API": "http://36.133.46.107:9031"
      "VITE_BASE_API": "http://1.15.17.182:9028",
      "VITE_JAVA_API": "http://1.15.17.182:9029"
    },
    "screen": "screen/login-background.png",
    "logo": "logo/Logo.png",
src/api/collaborativeApproval/approvalProcess.js
@@ -61,3 +61,27 @@
        method: 'get',
    })
}
// 维护审批人新增-更新
export function addApproveUser(query) {
    return request({
        url: '/approveUser/add',
        method: 'post',
        data: query,
    })
}
// 删除审批人
export function deleteApproveUser(query) {
    return request({
        url: '/approveUser/del',
        method: 'delete',
        data: query,
    })
}
// 查询审批人
export function approveUserList(query) {
    return request({
        url: '/approveUser/getList',
        method: 'get',
        params: query,
    })
}
src/views/basicData/product/index.vue
@@ -2,26 +2,20 @@
  <div class="app-container product-view">
    <div class="left">
      <div>
        <el-input
          v-model="search"
        <el-input v-model="search"
          style="width: 210px"
          placeholder="输入关键字进行搜索"
          @input="searchFilter"
          @change="searchFilter"
          @clear="searchFilter"
          clearable
          prefix-icon="Search"
        />
        <el-button
          type="primary"
                  prefix-icon="Search" />
        <el-button type="primary"
          @click="openProDia('addOne')"
          style="margin-left: 10px"
          >新增产品大类</el-button
        >
                   style="margin-left: 10px">新增产品大类</el-button>
      </div>
      <div ref="containerRef">
        <el-tree
          ref="tree"
        <el-tree ref="tree"
          v-loading="treeLoad"
          :data="list"
          @node-click="handleNodeClick"
@@ -32,8 +26,7 @@
          highlight-current
          node-key="id"
          class="product-tree-scroll"
          style="height: calc(100vh - 190px); overflow-y: auto"
        >
                 style="height: calc(100vh - 190px); overflow-y: auto">
          <template #default="{ node, data }">
            <div class="custom-tree-node">
              <span class="tree-node-content">
@@ -44,23 +37,22 @@
                <span class="tree-node-label">{{ data.label }}</span>
              </span>
              <div>
                <el-button
                  type="primary"
                <el-button type="primary"
                  link
                  @click="openProDia('edit', data)"
                >
                           @click="openProDia('edit', data)">
                  编辑
                </el-button>
                <el-button type="primary" link @click="openProDia('add', data)" :disabled="node.level >= 3">
                <el-button type="primary"
                           link
                           @click="openProDia('add', data)"
                           :disabled="node.level >= 3">
                  添加产品
                </el-button>
                <el-button
                  v-if="!node.childNodes.length"
                <el-button v-if="!node.childNodes.length"
                  style="margin-left: 4px"
                  type="danger"
                  link
                  @click="remove(node, data)"
                >
                           @click="remove(node, data)">
                  删除
                </el-button>
              </div>
@@ -70,117 +62,134 @@
      </div>
    </div>
    <div class="right">
      <div style="margin-bottom: 10px" v-if="isShowButton">
        <el-button type="primary" @click="openModelDia('add')">
      <div style="margin-bottom: 10px"
           v-if="isShowButton">
        <el-button type="primary"
                   @click="openModelDia('add')">
          新增规格型号
        </el-button>
        <ImportExcel :product-id="currentId" @uploadSuccess="getModelList" />
        <el-button
          type="danger"
        <ImportExcel :product-id="currentId"
                     @uploadSuccess="getModelList" />
        <el-button type="danger"
          @click="handleDelete"
          style="margin-left: 10px"
          plain
        >
                   plain>
          删除
        </el-button>
      </div>
      <PIMTable
        rowKey="id"
      <PIMTable rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
        @pagination="pagination"
      ></PIMTable>
                @pagination="pagination"></PIMTable>
    </div>
    <el-dialog v-model="productDia" title="产品" width="400px" @keydown.enter.prevent>
      <el-form
        :model="form"
    <el-dialog v-model="productDia"
               title="产品"
               width="400px"
               @keydown.enter.prevent>
      <el-form :model="form"
        label-width="140px"
        label-position="top"
        :rules="rules"
        ref="formRef"
      >
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品名称:" prop="productName">
              <el-input
                v-model="form.productName"
            <el-form-item label="产品名称:"
                          prop="productName">
              <el-input v-model="form.productName"
                placeholder="请输入产品名称"
                maxlength="20"
                show-word-limit
                clearable
                @keydown.enter.prevent
              />
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeProDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <el-dialog
      v-model="modelDia"
    <el-dialog v-model="modelDia"
      title="规格型号"
      width="400px"
      @close="closeModelDia"
      @keydown.enter.prevent
    >
      <el-form
        :model="modelForm"
               @keydown.enter.prevent>
      <el-form :model="modelForm"
        label-width="140px"
        label-position="top"
        :rules="modelRules"
        ref="modelFormRef"
      >
               ref="modelFormRef">
        <el-row>
          <el-col :span="24">
            <el-form-item label="规格型号:" prop="model">
              <el-input
                v-model="modelForm.model"
            <el-form-item label="规格型号:"
                          prop="model">
              <el-input v-model="modelForm.model"
                placeholder="请输入规格型号"
                clearable
                @keydown.enter.prevent
              />
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="厚度:" prop="thickness">
              <el-input
                v-model="modelForm.thickness"
            <el-form-item label="厚度:"
                          prop="thickness">
              <el-input v-model="modelForm.thickness"
                placeholder="请输入厚度"
                clearable
                @keydown.enter.prevent
                @blur="modelForm.thickness = formatThicknessTo15(modelForm.thickness)"
              />
                        @blur="modelForm.thickness = formatThicknessTo15(modelForm.thickness)" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="单位:" prop="unit">
              <el-input
                v-model="modelForm.unit"
            <el-form-item label="单位:"
                          prop="unit">
              <el-input v-model="modelForm.unit"
                placeholder="请输入单位"
                clearable
                @keydown.enter.prevent
              />
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitModelForm">确认</el-button>
          <el-button type="primary"
                     @click="submitModelForm">确认</el-button>
          <el-button @click="closeModelDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 二维码对话框 -->
    <el-dialog v-model="qrCodeDialog"
               title="产品二维码"
               width="400px">
      <div class="qrcode-container">
        <img v-if="qrCodeUrl"
             :src="qrCodeUrl"
             class="qrcode-image" />
        <div v-else
             class="loading">生成中...</div>
      </div>
      <div style="text-align: center;">
        {{ qrCodeName }}
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="qrCodeDialog = false">关闭</el-button>
          <el-button type="primary"
                     @click="saveQrCodeAsImage"
                     :disabled="!qrCodeUrl">保存为图片</el-button>
        </div>
      </template>
    </el-dialog>
@@ -188,8 +197,10 @@
</template>
<script setup>
import { ref } from "vue";
  import { ref, getCurrentInstance, toRefs, reactive } from "vue";
import { ElMessageBox } from "element-plus";
  import QRCode from "qrcode";
  import { saveAs } from "file-saver";
import {
  addOrEditProduct,
  addOrEditProductModel,
@@ -206,6 +217,9 @@
const productDia = ref(false);
const modelDia = ref(false);
  const qrCodeDialog = ref(false);
  const qrCodeUrl = ref("");
  const currentProductId = ref("");
const modelOperationType = ref("");
const search = ref("");
const currentId = ref("");
@@ -223,7 +237,7 @@
    label: "厚度",
    prop: "thickness",
    // 列表展示时统一保留 15 位小数
    formatData: (val) => formatThicknessTo15(val),
      formatData: val => formatThicknessTo15(val),
  },
  {
    label: "单位",
@@ -237,8 +251,15 @@
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openModelDia("edit", row);
          },
        },
        {
          name: "生成二维码",
          type: "text",
          clickFun: row => {
            generateQrcode(row);
        },
      },
    ],
@@ -275,7 +296,7 @@
const { form, rules, modelForm, modelRules } = toRefs(data);
// 把厚度格式化成固定 15 位小数(用于展示/提交)
const formatThicknessTo15 = (val) => {
  const formatThicknessTo15 = val => {
  if (val === null || val === undefined) return "";
  const s = String(val).trim();
  if (s === "") return "";
@@ -287,14 +308,14 @@
const getProductTreeList = () => {
  treeLoad.value = true;
  productTreeList()
    .then((res) => {
      .then(res => {
      list.value = res;
      list.value.forEach((a) => {
        list.value.forEach(a => {
        expandedKeys.value.push(a.label);
      });
      treeLoad.value = false;
    })
    .catch((err) => {
      .catch(err => {
      treeLoad.value = false;
    });
};
@@ -324,7 +345,7 @@
};
// 提交产品名称修改
const submitForm = () => {
  proxy.$refs.formRef.validate((valid) => {
    proxy.$refs.formRef.validate(valid => {
    if (valid) {
      if (operationType.value === "add") {
        form.value.parentId = currentId.value;
@@ -336,7 +357,7 @@
        form.value.id = currentId.value;
        form.value.parentId = "";
      }
      addOrEditProduct(form.value).then((res) => {
        addOrEditProduct(form.value).then(res => {
        proxy.$modal.msgSuccess("提交成功");
        closeProDia();
        getProductTreeList();
@@ -362,7 +383,7 @@
    .then(() => {
      tableLoading.value = true;
      delProduct(ids)
        .then((res) => {
          .then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getProductTreeList();
        })
@@ -374,6 +395,7 @@
      proxy.$modal.msg("已取消");
    });
};
  const fatherName = ref("");
// 选择产品
const handleNodeClick = (val, node, el) => {
  // 判断是否为叶子节点
@@ -381,16 +403,19 @@
  // 只有叶子节点才执行以下逻辑
  currentId.value = val.id;
  currentParentId.value = val.parentId;
    fatherName.value = val.label;
  getModelList();
};
// 提交规格型号修改
const submitModelForm = () => {
  proxy.$refs.modelFormRef.validate((valid) => {
    proxy.$refs.modelFormRef.validate(valid => {
    if (valid) {
      modelForm.value.productId = currentId.value;
      modelForm.value.thickness = formatThicknessTo15(modelForm.value.thickness);
      addOrEditProductModel(modelForm.value).then((res) => {
        modelForm.value.thickness = formatThicknessTo15(
          modelForm.value.thickness
        );
        addOrEditProductModel(modelForm.value).then(res => {
        proxy.$modal.msgSuccess("提交成功");
        closeModelDia();
        getModelList();
@@ -404,12 +429,12 @@
  modelDia.value = false;
};
// 表格选择数据
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  selectedRows.value = selection;
};
// 查询规格型号
const pagination = (obj) => {
  const pagination = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getModelList();
@@ -420,7 +445,7 @@
    id: currentId.value,
    current: page.current,
    size: page.size,
  }).then((res) => {
    }).then(res => {
    console.log("res", res);
    tableData.value = res.records;
    page.total = res.total;
@@ -431,7 +456,7 @@
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
      ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
@@ -444,7 +469,7 @@
    .then(() => {
      tableLoading.value = true;
      delProductModel(ids)
        .then((res) => {
          .then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getModelList();
        })
@@ -495,6 +520,57 @@
  // 没匹配到返回false
  return false;
};
  const qrCodeName = ref("");
  // 生成二维码
  const generateQrcode = async row => {
    try {
      currentProductId.value = row.id;
      qrCodeName.value = fatherName.value + "-" + row.model;
      // 使用row.id生成二维码
      const qrCodeData = row.id.toString();
      // 生成二维码URL
      qrCodeUrl.value = await QRCode.toDataURL(qrCodeData, {
        width: 300,
        margin: 1,
      });
      // 打开二维码对话框
      qrCodeDialog.value = true;
    } catch (error) {
      console.error("生成二维码失败:", error);
      proxy.$modal.msgError("生成二维码失败");
    }
  };
  // 保存二维码为图片
  const saveQrCodeAsImage = () => {
    if (!qrCodeUrl.value) return;
    try {
      // 从Data URL创建Blob
      const blob = dataURLToBlob(qrCodeUrl.value);
      // 使用file-saver保存图片
      saveAs(blob, `${qrCodeName.value}.png`);
      proxy.$modal.msgSuccess("保存成功");
    } catch (error) {
      console.error("保存图片失败:", error);
      proxy.$modal.msgError("保存图片失败");
    }
  };
  // 将Data URL转换为Blob
  const dataURLToBlob = dataURL => {
    const arr = dataURL.split(",");
    const mime = arr[0].match(/:(.*?);/)[1];
    const bstr = atob(arr[1]);
    let n = bstr.length;
    const u8arr = new Uint8Array(n);
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], { type: mime });
  };
getProductTreeList();
</script>
@@ -563,4 +639,23 @@
.product-tree-scroll::-webkit-scrollbar-thumb:hover {
  background: #909399;
}
  /* 二维码样式 */
  .qrcode-container {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 20px;
    min-height: 300px;
  }
  .qrcode-image {
    max-width: 100%;
    height: auto;
  }
  .loading {
    font-size: 16px;
    color: #606266;
  }
</style>
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -1,118 +1,135 @@
<template>
  <div>
    <el-dialog
      v-model="dialogFormVisible"
    <el-dialog v-model="dialogFormVisible"
      :title="operationType === 'approval' ? '审批' : '详情'"
      width="700px"
      @close="closeDia"
    >
            <el-form :model="form" label-width="140px" label-position="top" ref="formRef">
               @close="closeDia">
      <el-form :model="form"
               label-width="140px"
               label-position="top"
               ref="formRef">
                <el-row>
                    <el-col :span="24">
                        <el-form-item label="流程编号:" prop="approveId">
                            <el-input v-model="form.approveId" placeholder="自动编号" clearable disabled/>
            <el-form-item label="流程编号:"
                          prop="approveId">
              <el-input v-model="form.approveId"
                        placeholder="自动编号"
                        clearable
                        disabled />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="24">
                        <el-form-item label="申请部门:">
                            <el-select
                                disabled
              <el-select disabled
                                v-model="form.approveDeptId"
                                placeholder="选择部门"
                            >
                                <el-option
                                    v-for="user in productOptions"
                         placeholder="选择部门">
                <el-option v-for="user in productOptions"
                                    :key="user.deptId"
                                    :label="user.deptName"
                                    :value="user.deptId"
                                />
                           :value="user.deptId" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row v-if="!isQuotationApproval && !isPurchaseApproval">
                    <el-col :span="24">
                        <el-form-item :label="props.approveType == 5 ? '采购合同号:' : '审批事由:'" prop="approveReason">
                            <el-input v-model="form.approveReason" placeholder="请输入" clearable type="textarea" disabled/>
            <el-form-item :label="props.approveType == 5 ? '采购合同号:' : '审批事由:'"
                          prop="approveReason">
              <el-input v-model="form.approveReason"
                        placeholder="请输入"
                        clearable
                        type="textarea"
                        disabled />
                        </el-form-item>
                    </el-col>
                </el-row>
                <!-- 审批人选择(动态节点) -->
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="申请人:" prop="approveUser">
                            <el-select
                                v-model="form.approveUser"
            <el-form-item label="申请人:"
                          prop="approveUser">
              <el-input v-model="form.approveUserName"
                        clearable
                        disabled />
              <!-- <el-select v-model="form.approveUser"
                                placeholder="选择人员"
                                disabled
                            >
                                <el-option
                                    v-for="user in userList"
                         disabled>
                <el-option v-for="user in userList"
                                    :key="user.userId"
                                    :label="user.nickName"
                                    :value="user.userId"
                                />
                            </el-select>
                           :value="user.userId" />
              </el-select> -->
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="申请日期:" prop="approveTime">
                            <el-date-picker
                                v-model="form.approveTime"
            <el-form-item label="申请日期:"
                          prop="approveTime">
              <el-date-picker v-model="form.approveTime"
                                type="date"
                                placeholder="请选择日期"
                                value-format="YYYY-MM-DD"
                                format="YYYY-MM-DD"
                                clearable
                                style="width: 100%"
                                disabled
                            />
                              disabled />
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
      <!-- 报价审批:展示报价详情(复用销售报价"查看详情对话框"内容结构) -->
      <div v-if="isQuotationApproval" style="margin: 10px 0 18px;">
      <div v-if="isQuotationApproval"
           style="margin: 10px 0 18px;">
        <el-divider content-position="left">报价详情</el-divider>
        <el-skeleton :loading="quotationLoading" animated>
        <el-skeleton :loading="quotationLoading"
                     animated>
          <template #template>
            <el-skeleton-item variant="h3" style="width: 30%" />
            <el-skeleton-item variant="text" style="width: 100%" />
            <el-skeleton-item variant="text" style="width: 100%" />
            <el-skeleton-item variant="h3"
                              style="width: 30%" />
            <el-skeleton-item variant="text"
                              style="width: 100%" />
            <el-skeleton-item variant="text"
                              style="width: 100%" />
          </template>
          <template #default>
            <el-empty v-if="!currentQuotation || !currentQuotation.quotationNo" description="未查询到对应报价详情" />
            <el-empty v-if="!currentQuotation || !currentQuotation.quotationNo"
                      description="未查询到对应报价详情" />
            <template v-else>
              <el-descriptions :column="2" border>
              <el-descriptions :column="2"
                               border>
                <el-descriptions-item label="报价单号">{{ currentQuotation.quotationNo }}</el-descriptions-item>
                <el-descriptions-item label="客户名称">{{ currentQuotation.customer }}</el-descriptions-item>
                <el-descriptions-item label="业务员">{{ currentQuotation.salesperson }}</el-descriptions-item>
                <el-descriptions-item label="报价日期">{{ currentQuotation.quotationDate }}</el-descriptions-item>
                <el-descriptions-item label="有效期至">{{ currentQuotation.validDate }}</el-descriptions-item>
                <el-descriptions-item label="付款方式">{{ currentQuotation.paymentMethod }}</el-descriptions-item>
                <el-descriptions-item label="报价总额" :span="2">
                <el-descriptions-item label="报价总额"
                                      :span="2">
                  <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
                    ¥{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }}
                  </span>
                </el-descriptions-item>
              </el-descriptions>
              <div style="margin-top: 20px;">
                <h4>产品明细</h4>
                <el-table :data="currentQuotation.products || []" border style="width: 100%">
                  <el-table-column prop="product" label="产品名称" />
                  <el-table-column prop="specification" label="规格型号" />
                  <el-table-column prop="unit" label="单位" />
                  <el-table-column prop="unitPrice" label="单价">
                <el-table :data="currentQuotation.products || []"
                          border
                          style="width: 100%">
                  <el-table-column prop="product"
                                   label="产品名称" />
                  <el-table-column prop="specification"
                                   label="规格型号" />
                  <el-table-column prop="unit"
                                   label="单位" />
                  <el-table-column prop="unitPrice"
                                   label="单价">
                    <template #default="scope">¥{{ Number(scope.row.unitPrice ?? 0).toFixed(2) }}</template>
                  </el-table-column>
                </el-table>
              </div>
              <div v-if="currentQuotation.remark" style="margin-top: 20px;">
              <div v-if="currentQuotation.remark"
                   style="margin-top: 20px;">
                <h4>备注</h4>
                <p>{{ currentQuotation.remark }}</p>
              </div>
@@ -120,20 +137,26 @@
          </template>
        </el-skeleton>
      </div>
      <!-- 采购审批:展示采购详情 -->
      <div v-if="isPurchaseApproval" style="margin: 10px 0 18px;">
      <div v-if="isPurchaseApproval"
           style="margin: 10px 0 18px;">
        <el-divider content-position="left">采购详情</el-divider>
        <el-skeleton :loading="purchaseLoading" animated>
        <el-skeleton :loading="purchaseLoading"
                     animated>
          <template #template>
            <el-skeleton-item variant="h3" style="width: 30%" />
            <el-skeleton-item variant="text" style="width: 100%" />
            <el-skeleton-item variant="text" style="width: 100%" />
            <el-skeleton-item variant="h3"
                              style="width: 30%" />
            <el-skeleton-item variant="text"
                              style="width: 100%" />
            <el-skeleton-item variant="text"
                              style="width: 100%" />
          </template>
          <template #default>
            <el-empty v-if="!currentPurchase || !currentPurchase.purchaseContractNumber" description="未查询到对应采购详情" />
            <el-empty v-if="!currentPurchase || !currentPurchase.purchaseContractNumber"
                      description="未查询到对应采购详情" />
            <template v-else>
              <el-descriptions :column="2" border>
              <el-descriptions :column="2"
                               border>
                <el-descriptions-item label="采购合同号">{{ currentPurchase.purchaseContractNumber }}</el-descriptions-item>
                <el-descriptions-item label="供应商名称">{{ currentPurchase.supplierName }}</el-descriptions-item>
                <el-descriptions-item label="项目名称">{{ currentPurchase.projectName }}</el-descriptions-item>
@@ -141,24 +164,32 @@
                <el-descriptions-item label="签订日期">{{ currentPurchase.executionDate }}</el-descriptions-item>
                <el-descriptions-item label="录入日期">{{ currentPurchase.entryDate }}</el-descriptions-item>
                <el-descriptions-item label="付款方式">{{ currentPurchase.paymentMethod }}</el-descriptions-item>
                <el-descriptions-item label="合同金额" :span="2">
                <el-descriptions-item label="合同金额"
                                      :span="2">
                  <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
                    ¥{{ Number(currentPurchase.contractAmount ?? 0).toFixed(2) }}
                  </span>
                </el-descriptions-item>
              </el-descriptions>
              <div style="margin-top: 20px;">
                <h4>产品明细</h4>
                <el-table :data="currentPurchase.productData || []" border style="width: 100%">
                  <el-table-column prop="productCategory" label="产品名称" />
                  <el-table-column prop="specificationModel" label="规格型号" />
                  <el-table-column prop="unit" label="单位" />
                  <el-table-column prop="quantity" label="数量" />
                  <el-table-column prop="taxInclusiveUnitPrice" label="含税单价">
                <el-table :data="currentPurchase.productData || []"
                          border
                          style="width: 100%">
                  <el-table-column prop="productCategory"
                                   label="产品名称" />
                  <el-table-column prop="specificationModel"
                                   label="规格型号" />
                  <el-table-column prop="unit"
                                   label="单位" />
                  <el-table-column prop="quantity"
                                   label="数量" />
                  <el-table-column prop="taxInclusiveUnitPrice"
                                   label="含税单价">
                    <template #default="scope">¥{{ Number(scope.row.taxInclusiveUnitPrice ?? 0).toFixed(2) }}</template>
                  </el-table-column>
                  <el-table-column prop="taxInclusiveTotalPrice" label="含税总价">
                  <el-table-column prop="taxInclusiveTotalPrice"
                                   label="含税总价">
                    <template #default="scope">¥{{ Number(scope.row.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</template>
                  </el-table-column>
                </el-table>
@@ -167,22 +198,41 @@
          </template>
        </el-skeleton>
      </div>
      <el-form :model="{ activities }" ref="formRef" label-position="top">
        <el-steps :active="getActiveStep()" finish-status="success" process-status="process" align-center direction="vertical">
          <el-step
            v-for="(activity, index) in activities"
      <el-form :model="{ activities }"
               ref="formRef"
               label-position="top">
        <el-steps :active="getActiveStep()"
                  finish-status="success"
                  process-status="process"
                  align-center
                  direction="vertical">
          <el-step v-for="(activity, index) in activities"
            :key="index"
                        finish-status="success"
            :title="getNodeTitle(index, activities.length)"
            :description="activity.approveNodeUser"
            :icon="getNodeIcon(activity, index)"
          >
                   :icon="getNodeIcon(activity, index)">
                        <template #icon>
                            <el-icon v-if="activity.approveNodeStatus === 2" color="red" :size="22"><WarningFilled /></el-icon>
                            <el-icon v-else-if="activity.isShen" color="#1890ff" :size="22"><Edit /></el-icon>
                            <el-icon v-else-if="activity.approveNodeStatus === 1" color="#67C23A" :size="26"><Check /></el-icon>
                            <el-icon v-else color="#C0C4CC" :size="22"><MoreFilled /></el-icon>
              <el-icon v-if="activity.approveNodeStatus === 2"
                       color="red"
                       :size="22">
                <WarningFilled />
              </el-icon>
              <el-icon v-else-if="activity.isShen"
                       color="#1890ff"
                       :size="22">
                <Edit />
              </el-icon>
              <el-icon v-else-if="activity.approveNodeStatus === 1"
                       color="#67C23A"
                       :size="26">
                <Check />
              </el-icon>
              <el-icon v-else
                       color="#C0C4CC"
                       :size="22">
                <MoreFilled />
              </el-icon>
                        </template>
            <template #title>
              <span style="color: #000000">{{ getNodeTitle(index, activities.length) }}</span>
@@ -190,29 +240,36 @@
            <template #description>
              <div class="node-user">
                <div class="avatar-wrapper">
                  <img :src="userStore.avatar" class="user-avatar" alt=""/>
                  <img :src="userStore.avatar"
                       class="user-avatar"
                       alt="" />
                </div>
                <span style="color: #000000">{{ activity.approveNodeUser }}-{{activity.isApproval}}</span>
              </div>
              <div v-if="!activity.isShen" class="node-reason">
              <div v-if="!activity.isShen"
                   class="node-reason">
                <span>审批意见:</span>{{ activity.approveNodeReason }}
              </div>
              <div v-else-if="activity.isShen">
                <el-form-item
                  :prop="'activities.' + index + '.approveNodeReason'"
                  :rules="[{ required: true, message: '审批意见不能为空', trigger: 'blur' }]"
                >
                  <el-input v-model="activity.approveNodeReason" clearable type="textarea" :disabled="operationType === 'view'"></el-input>
                <el-form-item :prop="'activities.' + index + '.approveNodeReason'"
                              :rules="[{ required: true, message: '审批意见不能为空', trigger: 'blur' }]">
                  <el-input v-model="activity.approveNodeReason"
                            clearable
                            type="textarea"
                            :disabled="operationType === 'view'"></el-input>
                </el-form-item>
              </div>
            </template>
          </el-step>
        </el-steps>
      </el-form>
      <template #footer v-if="operationType === 'approval'">
      <template #footer
                v-if="operationType === 'approval'">
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm(2)">不通过</el-button>
          <el-button type="primary" @click="submitForm(1)">通过</el-button>
          <el-button type="primary"
                     @click="submitForm(2)">不通过</el-button>
          <el-button type="primary"
                     @click="submitForm(1)">通过</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
@@ -221,40 +278,52 @@
</template>
<script setup>
import { computed, getCurrentInstance, nextTick, reactive, ref, toRefs } from "vue";
  import {
    computed,
    getCurrentInstance,
    nextTick,
    reactive,
    ref,
    toRefs,
  } from "vue";
import {
    approveProcessDetails,
    getDept,
    updateApproveNode
    updateApproveNode,
} from "@/api/collaborativeApproval/approvalProcess.js";
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
  import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js";
  import {
    WarningFilled,
    Edit,
    Check,
    MoreFilled,
  } from "@element-plus/icons-vue";
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
const emit = defineEmits(['close'])
const { proxy } = getCurrentInstance()
  const emit = defineEmits(["close"]);
  const { proxy } = getCurrentInstance();
const props = defineProps({
  approveType: {
    type: [Number, String],
    default: 0
  }
})
      default: 0,
    },
  });
const dialogFormVisible = ref(false);
const operationType = ref('')
const activities = ref([])
  const operationType = ref("");
  const activities = ref([]);
const formRef = ref(null);
const userStore = useUserStore()
  const userStore = useUserStore();
const productOptions = ref([]);
const userList = ref([])
const quotationLoading = ref(false)
const currentQuotation = ref({})
const purchaseLoading = ref(false)
const currentPurchase = ref({})
const isQuotationApproval = computed(() => Number(props.approveType) === 6)
const isPurchaseApproval = computed(() => Number(props.approveType) === 5)
  const userList = ref([]);
  const quotationLoading = ref(false);
  const currentQuotation = ref({});
  const purchaseLoading = ref(false);
  const currentPurchase = ref({});
  const isQuotationApproval = computed(() => Number(props.approveType) === 6);
  const isPurchaseApproval = computed(() => Number(props.approveType) === 5);
const data = reactive({
    form: {
@@ -270,8 +339,8 @@
// 节点标题
const getNodeTitle = (index, len) => {
  if (index === len - 1) return '结束';
  return '审批';
    if (index === len - 1) return "结束";
    return "审批";
};
// 获取当前激活步骤
@@ -284,21 +353,21 @@
};
// 步骤icon
const getNodeIcon = (activity, index) => {
  if (activity.approveNodeStatus === 2) return 'el-icon-warning'; // 不通过
  if (activity.isShen) return 'Edit';
  return '';
    if (activity.approveNodeStatus === 2) return "el-icon-warning"; // 不通过
    if (activity.isShen) return "Edit";
    return "";
};
// 打开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  currentQuotation.value = {}
  currentPurchase.value = {}
    userListNoPageByTenantId().then((res) => {
    currentQuotation.value = {};
    currentPurchase.value = {};
    approveUserList({ approveType: props.approveType }).then(res => {
        userList.value = res.data;
    });
    form.value = {...row}
    form.value = { ...row };
    // 立即清除表单验证状态(因为字段是disabled的,不需要验证)
    nextTick(() => {
        if (formRef.value) {
@@ -309,7 +378,8 @@
    getProductOptions().then(() => {
        // 确保值类型匹配(如果选项已加载)
        if (productOptions.value.length > 0 && form.value.approveDeptId) {
            const matchedOption = productOptions.value.find(opt =>
        const matchedOption = productOptions.value.find(
          opt =>
                opt.deptId == form.value.approveDeptId || 
                String(opt.deptId) === String(form.value.approveDeptId)
            );
@@ -329,13 +399,15 @@
  if (isQuotationApproval.value) {
    const quotationNo = row?.approveReason;
    if (quotationNo) {
      quotationLoading.value = true
      getQuotationList({ quotationNo }).then((res) => {
        const records = res?.data?.records || []
        currentQuotation.value = records[0] || {}
      }).finally(() => {
        quotationLoading.value = false
        quotationLoading.value = true;
        getQuotationList({ quotationNo })
          .then(res => {
            const records = res?.data?.records || [];
            currentQuotation.value = records[0] || {};
      })
          .finally(() => {
            quotationLoading.value = false;
          });
    }
  }
@@ -343,45 +415,50 @@
  if (isPurchaseApproval.value) {
    const purchaseContractNumber = row?.approveReason;
    if (purchaseContractNumber) {
      purchaseLoading.value = true
      getPurchaseByCode({ purchaseContractNumber }).then((res) => {
        currentPurchase.value = res
      }).catch((err) => {
        console.error('查询采购详情失败:', err)
        proxy.$modal.msgError('查询采购详情失败')
      }).finally(() => {
        purchaseLoading.value = false
        purchaseLoading.value = true;
        getPurchaseByCode({ purchaseContractNumber })
          .then(res => {
            currentPurchase.value = res;
      })
          .catch(err => {
            console.error("查询采购详情失败:", err);
            proxy.$modal.msgError("查询采购详情失败");
          })
          .finally(() => {
            purchaseLoading.value = false;
          });
    }
  }
  approveProcessDetails(row.approveId).then((res) => {
    activities.value = res.data
    approveProcessDetails(row.approveId).then(res => {
      activities.value = res.data;
    // 增加isApproval字段
    activities.value.forEach(item => {
            if (item.url && item.url.includes('word')) {
                item.urlTem = item.url.replaceAll('word', 'img')
        if (item.url && item.url.includes("word")) {
          item.urlTem = item.url.replaceAll("word", "img");
            } else {
                item.urlTem = item.url
          item.urlTem = item.url;
            }
      if (item.approveNodeStatus === 2) {
        item.isApproval = '已驳回';
          item.isApproval = "已驳回";
      } else if (item.approveNodeStatus === 1) {
        item.isApproval = '已同意';
          item.isApproval = "已同意";
      } else {
        item.isApproval = '未审批';
          item.isApproval = "未审批";
      }
    })
  })
}
      });
    });
  };
const getProductOptions = () => {
    return getDept().then((res) => {
    return getDept().then(res => {
        productOptions.value = res.data;
    });
};
// 提交审批
const submitForm = (status) => {
  const filteredActivities = activities.value.filter(activity => activity.isShen);
  const submitForm = status => {
    const filteredActivities = activities.value.filter(
      activity => activity.isShen
    );
  if (!filteredActivities || filteredActivities.length === 0) {
    proxy.$modal.msgError("未找到待审批的节点");
    return;
@@ -393,7 +470,8 @@
  }
  currentActivity.approveNodeStatus = status;
  // 判断是否为最后一步
  const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length-1;
    const isLast =
      activities.value.findIndex(a => a.isShen) === activities.value.length - 1;
  updateApproveNode({ ...currentActivity, isLast }).then(() => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
@@ -403,11 +481,11 @@
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  quotationLoading.value = false
  currentQuotation.value = {}
  purchaseLoading.value = false
  currentPurchase.value = {}
  emit('close')
    quotationLoading.value = false;
    currentQuotation.value = {};
    purchaseLoading.value = false;
    currentPurchase.value = {};
    emit("close");
};
defineExpose({
  openDialog,
@@ -415,7 +493,6 @@
</script>
<style scoped>
.node-user {
  margin: 10px 0;
  font-size: 16px;
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -1,99 +1,103 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
    <el-dialog v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增审批流程' : '编辑审批流程'"
        width="50%"
        @close="closeDia"
    >
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
               @close="closeDia">
      <el-form :model="form"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        <el-row>
          <el-col :span="24">
            <el-form-item label="流程编号:" prop="approveId">
              <el-input v-model="form.approveId" placeholder="自动编号" clearable disabled/>
            <el-form-item label="流程编号:"
                          prop="approveId">
              <el-input v-model="form.approveId"
                        placeholder="自动编号"
                        clearable
                        disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="申请部门:" prop="approveDeptName">
            <el-form-item label="申请部门:"
                          prop="approveDeptName">
<!--              <el-input v-model="form.approveDeptName" placeholder="请输入" clearable/>-->
                            <el-select
                                v-model="form.approveDeptId"
              <el-select v-model="form.approveDeptId"
                                placeholder="选择部门"
                @change="handleDeptChange"
                            >
                                <el-option
                                    v-for="user in productOptions"
                         @change="handleDeptChange">
                <el-option v-for="user in productOptions"
                                    :key="user.deptId"
                                    :label="user.deptName"
                                    :value="user.deptId"
                                />
                           :value="user.deptId" />
                            </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item :label="props.approveType == 5 ? '采购合同号:' : '审批事由:'" prop="approveReason">
              <el-input v-model="form.approveReason" placeholder="请输入" clearable type="textarea" />
            <el-form-item :label="props.approveType == 5 ? '采购合同号:' : '审批事由:'"
                          prop="approveReason">
              <el-input v-model="form.approveReason"
                        placeholder="请输入"
                        clearable
                        type="textarea" />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- 请假时间(仅当 approveType 为 2 时显示) -->
        <el-row :gutter="30" v-if="props.approveType == 2">
        <el-row :gutter="30"
                v-if="props.approveType == 2">
          <el-col :span="12">
            <el-form-item label="请假开始时间:" prop="startDate">
              <el-date-picker
                  v-model="form.startDate"
            <el-form-item label="请假开始时间:"
                          prop="startDate">
              <el-date-picker v-model="form.startDate"
                  type="date"
                  placeholder="请选择开始日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
                              style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="请假结束时间:" prop="endDate">
              <el-date-picker
                  v-model="form.endDate"
            <el-form-item label="请假结束时间:"
                          prop="endDate">
              <el-date-picker v-model="form.endDate"
                  type="date"
                  placeholder="请选择结束日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- 报销金额(仅当 approveType 为 4 时显示) -->
        <el-row v-if="props.approveType == 4">
          <el-col :span="24">
            <el-form-item label="报销金额:" prop="price">
              <el-input-number
                  v-model="form.price"
            <el-form-item label="报销金额:"
                          prop="price">
              <el-input-number v-model="form.price"
                  placeholder="请输入报销金额"
                  :min="0"
                  :precision="2"
                  :step="0.01"
                  style="width: 100%"
                  clearable
              />
                               clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- 出差地点(仅当 approveType 为 3 时显示) -->
        <el-row v-if="props.approveType == 3">
          <el-col :span="24">
            <el-form-item label="出差地点:" prop="location">
              <el-input
                  v-model="form.location"
            <el-form-item label="出差地点:"
                          prop="location">
              <el-input v-model="form.location"
                  placeholder="请输入出差地点"
                  clearable
              />
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
@@ -103,37 +107,31 @@
            <el-form-item>
              <template #label>
                <span>审批人选择:</span>
                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button>
                <el-button type="primary"
                           @click="addApproverNode"
                           style="margin-left: 8px;">新增节点</el-button>
              </template>
              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
                <div
                  v-for="(node, index) in approverNodes"
                <div v-for="(node, index) in approverNodes"
                  :key="node.id"
                  style="margin-right: 30px; text-align: center; margin-bottom: 10px;"
                >
                     style="margin-right: 30px; text-align: center; margin-bottom: 10px;">
                  <div>
                    <span>审批人</span>
                    →
                  </div>
                  <el-select
                    v-model="node.userId"
                  <el-select v-model="node.userId"
                    placeholder="选择人员"
                    style="width: 120px; margin-bottom: 8px;"
                  >
                    <el-option
                      v-for="user in userList"
                             style="width: 120px; margin-bottom: 8px;">
                    <el-option v-for="user in userListApproval"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                               :label="user.userName"
                               :value="user.userId" />
                  </el-select>
                  <div>
                    <el-button
                      type="danger"
                    <el-button type="danger"
                      size="small"
                      @click="removeApproverNode(index)"
                      v-if="approverNodes.length > 1"
                    >删除</el-button>
                               v-if="approverNodes.length > 1">删除</el-button>
                  </div>
                </div>
              </div>
@@ -142,45 +140,51 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="申请人:" prop="approveUser">
                            <el-select
                                v-model="form.approveUser"
            <el-form-item label="申请人:"
                          prop="approveUser">
              <el-select v-model="form.approveUser"
                                placeholder="选择人员"
                filterable
                default-first-option
                :reserve-keyword="false"
                            >
                                <el-option
                                    v-for="user in userList"
                         :reserve-keyword="false">
                <el-option v-for="user in userList"
                                    :key="user.userId"
                                    :label="user.nickName"
                                    :value="user.userId"
                                />
                           :value="user.userId" />
                            </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="申请日期:" prop="approveTime">
              <el-date-picker
                  v-model="form.approveTime"
            <el-form-item label="申请日期:"
                          prop="approveTime">
              <el-date-picker v-model="form.approveTime"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="附件材料:" prop="remark">
              <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                         :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                         :on-success="handleUploadSuccess" :on-remove="handleRemove">
                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                <template #tip v-if="operationType !== 'view'">
            <el-form-item label="附件材料:"
                          prop="remark">
              <el-upload v-model:file-list="fileList"
                         :action="upload.url"
                         multiple
                         ref="fileUpload"
                         auto-upload
                         :headers="upload.headers"
                         :before-upload="handleBeforeUpload"
                         :on-error="handleUploadError"
                         :on-success="handleUploadSuccess"
                         :on-remove="handleRemove">
                <el-button type="primary"
                           v-if="operationType !== 'view'">上传</el-button>
                <template #tip
                          v-if="operationType !== 'view'">
                  <div class="el-upload__tip">
                    文件格式支持
                    doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
@@ -193,7 +197,8 @@
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
@@ -204,24 +209,24 @@
<script setup>
import {ref, reactive, toRefs, getCurrentInstance} from "vue";
import {
  approveProcessAdd, approveProcessGetInfo,
    approveProcessAdd,
    approveProcessGetInfo,
  approveProcessUpdate,
  getDept
    getDept,
} from "@/api/collaborativeApproval/approvalProcess.js";
import {
  delLedgerFile,
} from "@/api/salesManagement/salesLedger.js";
  import { delLedgerFile } from "@/api/salesManagement/salesLedger.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
  import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js";
import { getToken } from "@/utils/auth";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
  const { proxy } = getCurrentInstance();
  const emit = defineEmits(["close"]);
import useUserStore from "@/store/modules/user";
import { getCurrentDate } from "@/utils/index.js";
import log from "@/views/monitor/job/log.vue";
const userStore = useUserStore();
const dialogFormVisible = ref(false);
const operationType = ref('')
  const operationType = ref("");
const fileList = ref([]);
const upload = reactive({
  // 上传的地址
@@ -243,100 +248,110 @@
    startDate: "", // 请假开始时间
    endDate: "", // 请假结束时间
    price: null, // 报销金额
    location: "" // 出差地点
      location: "", // 出差地点
  },
  rules: {
    approveTime: [{ required: false, message: "请输入", trigger: "change" },],
      approveTime: [{ required: false, message: "请输入", trigger: "change" }],
    approveId: [{ required: false, message: "请输入", trigger: "blur" }],
    approveUser: [{ required: false, message: "请输入", trigger: "blur" }],
    approveDeptName: [{ required: true, message: "请输入", trigger: "blur" }],
    approveReason: [{ required: true, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
    startDate: [{ required: true, message: "请选择请假开始时间", trigger: "change" }],
    endDate: [{ required: true, message: "请选择请假结束时间", trigger: "change" }],
      startDate: [
        { required: true, message: "请选择请假开始时间", trigger: "change" },
      ],
      endDate: [
        { required: true, message: "请选择请假结束时间", trigger: "change" },
      ],
    price: [{ required: true, message: "请输入报销金额", trigger: "blur" }],
    location: [{ required: true, message: "请输入出差地点", trigger: "blur" }],
  },
});
const { form, rules } = toRefs(data);
const productOptions = ref([]);
const currentApproveStatus = ref(0)
  const currentApproveStatus = ref(0);
const props = defineProps({
  approveType: {
    type: [Number, String],
    default: 0
  }
})
      default: 0,
    },
  });
// 审批人节点相关
const approverNodes = ref([
  { id: 1, userId: null }
])
let nextApproverId = 2
const userList = ref([])
  const approverNodes = ref([{ id: 1, userId: null }]);
  let nextApproverId = 2;
  const userList = ref([]);
  const userListApproval = ref([]);
function addApproverNode() {
  approverNodes.value.push({ id: nextApproverId++, userId: null })
    approverNodes.value.push({ id: nextApproverId++, userId: null });
}
function removeApproverNode(index) {
  approverNodes.value.splice(index, 1)
    approverNodes.value.splice(index, 1);
}
// 处理部门选择变化
const handleDeptChange = (deptId) => {
  const handleDeptChange = deptId => {
  if (deptId) {
    const selectedDept = productOptions.value.find(dept => dept.deptId === deptId);
      const selectedDept = productOptions.value.find(
        dept => dept.deptId === deptId
      );
    if (selectedDept) {
      form.value.approveDeptName = selectedDept.deptName;
    }
  } else {
    form.value.approveDeptName = '';
      form.value.approveDeptName = "";
  }
};
// 打开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
    userListNoPageByTenantId().then((res) => {
    userListNoPageByTenantId().then(res => {
    userList.value = res.data;
  });
    form.value = {}
    approverNodes.value = [
        { id: 1, userId: null }
    ]
    approveUserList({ approveType: props.approveType }).then(res => {
      userListApproval.value = res.data;
    });
    form.value = {};
    approverNodes.value = [{ id: 1, userId: null }];
  form.value.approveUser = userStore.id;
  form.value.approveTime = getCurrentDate();
  
  // 获取当前用户信息并设置部门ID
  form.value.approveDeptId = userStore.currentDeptId
    form.value.approveDeptId = userStore.currentDeptId;
  
  // 加载部门选项,并在加载完成后设置部门名称
  getProductOptions();
  if (operationType.value === 'edit') {
    fileList.value = row.commonFileList
    form.value.tempFileIds = fileList.value.map(file => file.id)
        currentApproveStatus.value = row.approveStatus
    approveProcessGetInfo({id: row.approveId,approveReason: '1'}).then(res => {
            form.value = {...res.data}
    if (operationType.value === "edit") {
      fileList.value = row.commonFileList;
      form.value.tempFileIds = fileList.value.map(file => file.id);
      currentApproveStatus.value = row.approveStatus;
      approveProcessGetInfo({ id: row.approveId, approveReason: "1" }).then(
        res => {
          form.value = { ...res.data };
      // 反显审批人
      if (res.data && res.data.approveUserIds) {
        const userIds = res.data.approveUserIds.split(',')
            const userIds = res.data.approveUserIds.split(",");
        approverNodes.value = userIds.map((userId, idx) => ({
          id: idx + 1, 
          userId: parseInt(userId.trim())
        }))
        nextApproverId = userIds.length + 1
              userId: parseInt(userId.trim()),
            }));
            nextApproverId = userIds.length + 1;
      } else {
        approverNodes.value = [{ id: 1, userId: null }]
        nextApproverId = 2
      }
    })
            approverNodes.value = [{ id: 1, userId: null }];
            nextApproverId = 2;
  }
}
      );
    }
  };
const getProductOptions = () => {
  return getDept().then((res) => {
    return getDept().then(res => {
    productOptions.value = res.data;
    // 如果已有部门ID,自动设置部门名称(用于验证)
    if (form.value.approveDeptId && productOptions.value.length > 0) {
      const matchedDept = productOptions.value.find(dept =>
        const matchedDept = productOptions.value.find(
          dept =>
        dept.deptId == form.value.approveDeptId || 
        String(dept.deptId) === String(form.value.approveDeptId)
      );
@@ -347,7 +362,7 @@
  });
};
function convertIdToValue(data) {
  return data.map((item) => {
    return data.map(item => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
@@ -363,42 +378,44 @@
// 提交产品表单
const submitForm = () => {
  // 收集所有节点的审批人id
  form.value.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
  form.value.approveType = props.approveType
    form.value.approveUserIds = approverNodes.value
      .map(node => node.userId)
      .join(",");
    form.value.approveType = props.approveType;
  // 审批人必填校验
  const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
    const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
  if (hasEmptyApprover) {
    proxy.$modal.msgError("请为所有审批节点选择审批人!")
    return
      proxy.$modal.msgError("请为所有审批节点选择审批人!");
      return;
  }
  // 当 approveType 为 2 时,校验请假时间
  if (props.approveType == 2) {
    if (!form.value.startDate) {
      proxy.$modal.msgError("请选择请假开始时间!")
      return
        proxy.$modal.msgError("请选择请假开始时间!");
        return;
    }
    if (!form.value.endDate) {
      proxy.$modal.msgError("请选择请假结束时间!")
      return
        proxy.$modal.msgError("请选择请假结束时间!");
        return;
    }
    // 校验结束时间不能早于开始时间
    if (new Date(form.value.endDate) < new Date(form.value.startDate)) {
      proxy.$modal.msgError("请假结束时间不能早于开始时间!")
      return
        proxy.$modal.msgError("请假结束时间不能早于开始时间!");
        return;
    }
  }
  // 当 approveType 为 3 时,校验出差地点
  if (props.approveType == 3) {
    if (!form.value.location || form.value.location.trim() === '') {
      proxy.$modal.msgError("请输入出差地点!")
      return
      if (!form.value.location || form.value.location.trim() === "") {
        proxy.$modal.msgError("请输入出差地点!");
        return;
    }
  }
  // 当 approveType 为 4 时,校验报销金额
  if (props.approveType == 4) {
    if (!form.value.price || form.value.price <= 0) {
      proxy.$modal.msgError("请输入有效的报销金额!")
      return
        proxy.$modal.msgError("请输入有效的报销金额!");
        return;
    }
  }
  proxy.$refs.formRef.validate(valid => {
@@ -407,22 +424,22 @@
        approveProcessAdd(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
          });
      } else {
        approveProcessUpdate(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
          });
      }
    }
  })
}
    });
  };
// 关闭弹框
const closeDia = () => {
  fileList.value = []
    fileList.value = [];
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  emit('close')
    emit("close");
};
// 上传前校检
@@ -460,7 +477,7 @@
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.id);
    delLedgerFile(ids).then((res) => {
      delLedgerFile(ids).then(res => {
      proxy.$modal.msgSuccess("删除成功");
    });
  }
@@ -472,5 +489,4 @@
</script>
<style scoped>
</style>
src/views/collaborativeApproval/approvalProcess/index.vue
@@ -1,57 +1,67 @@
<template>
  <div class="app-container">
    <!-- 标签页切换不同的审批类型 -->
    <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="approval-tabs">
      <el-tab-pane label="公出管理" name="1"></el-tab-pane>
      <el-tab-pane label="请假管理" name="2"></el-tab-pane>
      <el-tab-pane label="出差管理" name="3"></el-tab-pane>
      <el-tab-pane label="报销管理" name="4"></el-tab-pane>
      <el-tab-pane label="采购审批" name="5"></el-tab-pane>
      <el-tab-pane label="报价审批" name="6"></el-tab-pane>
      <el-tab-pane label="发货审批" name="7"></el-tab-pane>
    <el-tabs v-model="activeTab"
             @tab-change="handleTabChange"
             class="approval-tabs">
      <el-tab-pane label="公出管理"
                   name="1"></el-tab-pane>
      <el-tab-pane label="请假管理"
                   name="2"></el-tab-pane>
      <el-tab-pane label="出差管理"
                   name="3"></el-tab-pane>
      <el-tab-pane label="报销管理"
                   name="4"></el-tab-pane>
      <el-tab-pane label="采购审批"
                   name="5"></el-tab-pane>
      <el-tab-pane label="报价审批"
                   name="6"></el-tab-pane>
      <el-tab-pane label="发货审批"
                   name="7"></el-tab-pane>
    </el-tabs>
    <div class="search_form">
      <div>
        <span class="search_title">流程编号:</span>
        <el-input
            v-model="searchForm.approveId"
        <el-input v-model="searchForm.approveId"
            style="width: 240px"
            placeholder="请输入流程编号搜索"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
        />
                  :prefix-icon="Search" />
        <span class="search_title ml10">审批状态:</span>
                <el-select v-model="searchForm.approveStatus" clearable @change="handleQuery" style="width: 240px">
                    <el-option label="待审核" :value="0" />
                    <el-option label="审核中" :value="1" />
                    <el-option label="审核完成" :value="2" />
                    <el-option label="审核未通过" :value="3" />
                    <el-option label="已重新提交" :value="4" />
        <el-select v-model="searchForm.approveStatus"
                   clearable
                   @change="handleQuery"
                   style="width: 240px">
          <el-option label="待审核"
                     :value="0" />
          <el-option label="审核中"
                     :value="1" />
          <el-option label="审核完成"
                     :value="2" />
          <el-option label="审核未通过"
                     :value="3" />
          <el-option label="已重新提交"
                     :value="4" />
                </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索</el-button
        >
        <el-button type="primary"
                   @click="handleQuery"
                   style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <el-button
          type="primary"
        <el-button @click="handleOut">审批人维护</el-button>
        <el-button type="primary"
          @click="openForm('add')"
          v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
        >新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button
          type="danger"
                   v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7">新增</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">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable
          rowKey="id"
      <PIMTable rowKey="id"
          :column="tableColumnCopy"
          :tableData="tableData"
          :page="page"
@@ -59,31 +69,95 @@
          @selection-change="handleSelectionChange"
          :tableLoading="tableLoading"
          @pagination="pagination"
          :total="page.total"
      ></PIMTable>
                :total="page.total"></PIMTable>
    </div>
    <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="currentApproveType"></info-form-dia>
    <approval-dia ref="approvalDia" @close="handleQuery" :approveType="currentApproveType"></approval-dia>
    <info-form-dia ref="infoFormDia"
                   @close="handleQuery"
                   :approveType="currentApproveType"></info-form-dia>
    <approval-dia ref="approvalDia"
                  @close="handleQuery"
                  :approveType="currentApproveType"></approval-dia>
    <FileList ref="fileListRef" />
    <!-- 审批人维护对话框 -->
    <el-dialog v-model="approverDialogVisible"
               title="审批人维护"
               width="800px">
      <div class="approver-dialog">
        <div class="selected-info"
             v-if="selectedApprovers.length > 0">
          <div class="info-title">已选择的审批人:</div>
          <div class="selected-list">
            <el-tag v-for="approver in selectedApprovers"
                    :key="approver.id"
                    class="approver-tag">
              {{ approver.userName }}
              <el-icon class="el-tag__close el-icon--close"
                       @click="removeApprover(approver)">
                <CircleClose />
              </el-icon>
            </el-tag>
          </div>
        </div>
        <el-table ref="approverTable"
                  :data="approverList"
                  style="width: 100%"
                  @selection-change="handleApproverSelectionChange"
                  v-loading="approverLoading">
          <el-table-column type="selection"
                           width="55"></el-table-column>
          <el-table-column prop="userId"
                           label="ID"></el-table-column>
          <el-table-column prop="userName"
                           label="姓名"></el-table-column>
          <el-table-column prop="createTime"
                           label="创建时间"></el-table-column>
        </el-table>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="approverDialogVisible = false">取消</el-button>
          <el-button type="primary"
                     @click="submitApprovers"
                     :disabled="selectedApprovers.length === 0">
            提交
          </el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import FileList from "./fileList.vue";
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref, computed, reactive, toRefs, nextTick, getCurrentInstance} from "vue";
  import {
    onMounted,
    ref,
    computed,
    reactive,
    toRefs,
    nextTick,
    getCurrentInstance,
  } from "vue";
import {ElMessageBox} from "element-plus";
import { useRoute } from 'vue-router';
  import { useRoute } from "vue-router";
import InfoFormDia from "@/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue";
import ApprovalDia from "@/views/collaborativeApproval/approvalProcess/components/approvalDia.vue";
import {approveProcessDelete, approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess.js";
  import {
    approveProcessDelete,
    approveProcessListPage,
    approveUserList,
    addApproveUser,
    deleteApproveUser,
  } from "@/api/collaborativeApproval/approvalProcess.js";
  import { userListNoPageByTenantId } from "@/api/system/user.js";
import useUserStore from "@/store/modules/user";
const userStore = useUserStore();
const route = useRoute();
// 当前选中的标签页,默认为公出管理
const activeTab = ref('1');
  const activeTab = ref("1");
// 当前审批类型,根据选中的标签页计算
const currentApproveType = computed(() => {
@@ -91,14 +165,13 @@
});
// 标签页切换处理
const handleTabChange = (tabName) => {
  const handleTabChange = tabName => {
  // 切换标签页时重置搜索条件和分页,并重新加载数据
  searchForm.value.approveId = '';
  searchForm.value.approveStatus = '';
    searchForm.value.approveId = "";
    searchForm.value.approveStatus = "";
  page.current = 1;
  getList();
};
const data = reactive({
  searchForm: {
@@ -122,7 +195,7 @@
      prop: "approveStatus",
      dataType: "tag",
      width: 100,
      formatData: (params) => {
        formatData: params => {
        if (params == 0) {
          return "待审核";
        } else if (params == 1) {
@@ -132,10 +205,10 @@
        } else if (params == 4) {
          return "已重新提交";
        } else {
          return '不通过';
            return "不通过";
        }
      },
      formatType: (params) => {
        formatType: params => {
        if (params == 0) {
          return "warning";
        } else if (params == 1) {
@@ -145,29 +218,33 @@
        } else if (params == 4) {
          return "info";
        } else {
          return 'danger';
            return "danger";
        }
      },
    },
    {
      label: "流程编号",
      prop: "approveId",
      width: 170
        width: 170,
    },
    {
      label: "申请部门",
      prop: "approveDeptName",
      width: 220
        width: 220,
    },
    {
      label: isQuotationType ? "报价单号" : isPurchaseType ? "采购合同号" : "审批事由",
        label: isQuotationType
          ? "报价单号"
          : isPurchaseType
          ? "采购合同号"
          : "审批事由",
      prop: "approveReason",
    },
    {
      label: "申请人",
      prop: "approveUserName",
      width: 120
    }
        width: 120,
      },
  ];
  
  // 金额列(仅报销管理显示)
@@ -175,7 +252,7 @@
    baseColumns.push({
      label: "金额(元)",
      prop: "price",
      width: 120
        width: 120,
    });
  }
  
@@ -184,12 +261,12 @@
    {
      label: isLeaveType ? "开始日期" : "申请日期",
      prop: isLeaveType ? "startDate" : "approveTime",
      width: 200
        width: 200,
    },
    {
      label: "结束日期",
      prop: isLeaveType ? "endDate" : "approveOverTime",
      width: 120
        width: 120,
    }
  );
  
@@ -197,7 +274,7 @@
  baseColumns.push({
    label: "当前审批人",
    prop: "approveUserCurrentName",
    width: 120
      width: 120,
  });
  
  // 操作列
@@ -205,34 +282,34 @@
    {
      name: "编辑",
      type: "text",
      clickFun: (row) => {
        clickFun: row => {
        openForm("edit", row);
      },
      disabled: (row) =>
        disabled: row =>
        currentApproveType.value === 5 ||
        currentApproveType.value === 6 ||
        currentApproveType.value === 7 ||
        row.approveStatus == 2 ||
        row.approveStatus == 1 ||
        row.approveStatus == 4
          row.approveStatus == 4,
    },
    {
      name: "审核",
      type: "text",
      clickFun: (row) => {
        clickFun: row => {
        openApprovalDia("approval", row);
      },
      disabled: (row) =>
        disabled: row =>
        row.approveUserCurrentId == null ||
        row.approveStatus == 2 ||
        row.approveStatus == 3 ||
        row.approveStatus == 4 ||
        row.approveUserCurrentId !== userStore.id
          row.approveUserCurrentId !== userStore.id,
    },
    {
      name: "详情",
      type: "text",
      clickFun: (row) => {
        clickFun: row => {
        openApprovalDia("view", row);
      },
    },
@@ -243,7 +320,7 @@
    actionOperations.push({
      name: "附件",
      type: "text",
      clickFun: (row) => {
        clickFun: row => {
        downLoadFile(row);
      },
    });
@@ -266,11 +343,21 @@
const page = reactive({
  current: 1,
  size: 100,
  total: 0
    total: 0,
});
const infoFormDia = ref()
const approvalDia = ref()
const { proxy } = getCurrentInstance()
  const infoFormDia = ref();
  const approvalDia = ref();
  const { proxy } = getCurrentInstance();
  // 审批人维护对话框
  const approverDialogVisible = ref(false);
  const selectedApprovers = ref([]);
  const existingApprovers = ref([]); // 已有的审批人列表
  const approverLoading = ref(false); // 加载状态
  // 审批人列表数据
  const approverList = ref([]);
  const approverTable = ref(null);
// 查询列表
/** 搜索按钮操作 */
@@ -278,29 +365,34 @@
  page.current = 1;
  getList();
};
const fileListRef = ref(null)
const downLoadFile = (row) => {
  fileListRef.value.open(row.commonFileList)
}
const pagination = (obj) => {
  const fileListRef = ref(null);
  const downLoadFile = row => {
    fileListRef.value.open(row.commonFileList);
  };
  const pagination = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  approveProcessListPage({...page, ...searchForm.value, approveType: currentApproveType.value}).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
    approveProcessListPage({
      ...page,
      ...searchForm.value,
      approveType: currentApproveType.value,
  })
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(err => {
        tableLoading.value = false;
      });
};
// 导出
const handleOut = () => {
  const type = currentApproveType.value
  const handleExport = () => {
    const type = currentApproveType.value;
  const urlMap = {
    0: "/approveProcess/exportZero",
    1: "/approveProcess/exportOne",
@@ -310,8 +402,8 @@
    5: "/approveProcess/exportFive",
    6: "/approveProcess/exportSix",
    7: "/approveProcess/exportSeven",
  }
  const url = urlMap[type] || urlMap[0]
    };
    const url = urlMap[type] || urlMap[0];
  const nameMap = {
    0: "协同审批管理表",
    1: "公出管理审批表",
@@ -321,33 +413,166 @@
    5: "采购申请审批表",
    6: "报价审批表",
    7: "发货审批表",
    };
    const fileName = nameMap[type] || nameMap[0];
    proxy.download(url, {}, `${fileName}.xlsx`);
  };
  // 审批人维护
  const handleOut = () => {
    approverLoading.value = true;
    // 从 API 获取所有用户列表
    userListNoPageByTenantId()
      .then(res => {
        // 转换 API 返回的数据结构为表格需要的格式
        approverList.value = res.data.map(user => ({
          userId: user.userId,
          userName: user.nickName,
          createTime: user.createTime || "",
        }));
        // 获取当前审批类型已有的审批人列表
        const currentType = currentApproveType.value;
        approveUserList({ approveType: currentType })
          .then(approversRes => {
            existingApprovers.value = approversRes.data || [];
            // approverList.value = approversRes.data;
            selectedApprovers.value = existingApprovers.value;
            // approverList.value = ;
            // 标记已有的审批人
            // approverList.value = allUsers.map(user => ({
            //   ...user,
            //   id:
            //     existingApprovers.value.find(
            //       approver => approver.userId === user.userId
            //     )?.id || 0,
            //   isExisting: existingApprovers.value.some(
            //     approver => approver.userId === user.userId
            //   ),
            // }));
            console.log(approverList.value, "==approverList.value==");
            approverDialogVisible.value = true;
            approverLoading.value = false;
            // 更新表格勾选状态
            nextTick(() => {
              if (approverTable.value) {
                // 先清空所有勾选
                approverList.value.forEach(row => {
                  approverTable.value.toggleRowSelection(row, false);
                });
                // 再勾选已有的审批人
                existingApprovers.value.forEach(existingApprover => {
                  const row = approverList.value.find(
                    user => user.userId === existingApprover.userId
                  );
                  if (row) {
                    approverTable.value.toggleRowSelection(row, true);
  }
  const fileName = nameMap[type] || nameMap[0]
  proxy.download(url, {}, `${fileName}.xlsx`)
                });
}
            });
          })
          .catch(err => {
            console.error("获取已有审批人列表失败:", err);
            proxy.$modal.msgError("获取已有审批人列表失败");
            approverLoading.value = false;
          });
      })
      .catch(err => {
        console.error("获取用户列表失败:", err);
        proxy.$modal.msgError("获取用户列表失败");
        approverLoading.value = false;
      });
  };
  // 处理审批人选择
  const handleApproverSelectionChange = selection => {
    selectedApprovers.value = selection;
  };
  // 移除审批人
  const removeApprover = approver => {
    selectedApprovers.value = selectedApprovers.value.filter(
      item => item.id !== approver.id
    );
    approverTable.value.toggleRowSelection(approver, false);
  };
  // 提交审批人
  const submitApprovers = () => {
    if (selectedApprovers.value.length === 0) {
      proxy.$modal.msgWarning("请选择审批人");
      return;
    }
    const currentType = currentApproveType.value;
    const selectedIds = selectedApprovers.value.map(approver => approver.userId);
    const existingIds = existingApprovers.value.map(approver => approver.userId);
    // 需要删除的审批人(原有的但未被选择的)
    const toDelete = existingApprovers.value
      .filter(approver => !selectedIds.includes(approver.userId))
      .map(approver => approver.id);
    // 需要添加的审批人(新选择的但不在现有列表中的)
    const toAdd = selectedApprovers.value
      .filter(approver => !existingIds.includes(approver.userId))
      .map(approver => ({
        approveType: currentType,
        id: 0,
        userId: approver.userId,
        userName: approver.userName,
      }));
    console.log(toDelete, "==删除==");
    console.log(toAdd, "==添加==");
    // 先删除不需要的审批人
    const deletePromise =
      toDelete.length > 0 ? deleteApproveUser(toDelete) : Promise.resolve();
    deletePromise
      .then(() => {
        // 然后添加新的审批人
        if (toAdd.length === 0) {
          return Promise.resolve();
        }
        // 逐个添加审批人
        return Promise.all(toAdd.map(user => addApproveUser(user)));
      })
      .then(() => {
        proxy.$modal.msgSuccess("审批人维护成功");
        approverDialogVisible.value = false;
        selectedApprovers.value = [];
      })
      .catch(err => {
        console.error("审批人维护失败:", err);
        proxy.$modal.msgError("审批人维护失败");
      });
  };
// 表格选择数据
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  selectedRows.value = selection;
};
// 打开新增、编辑弹框
const openForm = (type, row) => {
  nextTick(() => {
    infoFormDia.value?.openDialog(type, row)
  })
      infoFormDia.value?.openDialog(type, row);
    });
};
// 打开新增检验弹框
const openApprovalDia = (type, row) => {
  nextTick(() => {
    approvalDia.value?.openDialog(type, row)
  })
      approvalDia.value?.openDialog(type, row);
    });
};
// 删除
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.approveId);
      ids = selectedRows.value.map(item => item.approveId);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
@@ -358,7 +583,7 @@
    type: "warning",
  })
      .then(() => {
        approveProcessDelete(ids).then((res) => {
        approveProcessDelete(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
@@ -391,4 +616,40 @@
.approval-tabs {
  margin-bottom: 10px;
}
  /* 审批人维护对话框样式 */
  .approver-dialog {
    display: flex;
    flex-direction: column;
    gap: 20px;
  }
  .selected-info {
    /* margin-top: 20px; */
    padding: 15px;
    background: #f5f7fa;
    border-radius: 4px;
  }
  .info-title {
    font-weight: 600;
    margin-bottom: 10px;
    color: #303133;
  }
  .selected-list {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
  }
  .approver-tag {
    margin-right: 10px;
  }
  .dialog-footer {
    width: 100%;
    display: flex;
    justify-content: flex-end;
  }
</style>
src/views/procurementManagement/procurementLedger/index.vue
@@ -53,7 +53,9 @@
      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
        <el-button type="primary"
                   @click="openForm('add')">新增台账</el-button>
        <el-button type="primary" plain @click="handleImport">导入</el-button>
        <el-button type="primary"
                   plain
                   @click="handleImport">导入</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger"
                   plain
@@ -94,7 +96,6 @@
                               prop="availableQuality" />
              <el-table-column label="退货数量"
                               prop="returnQuality" />
              <el-table-column label="税率(%)"
                               prop="taxRate" />
              <el-table-column label="含税单价(元)"
@@ -134,8 +135,7 @@
                         width="100"
                         show-overflow-tooltip>
          <template #default="scope">
            <el-tag
              :type="getApprovalStatusType(scope.row.approvalStatus)"
            <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)"
              size="small">
              {{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }}
            </el-tag>
@@ -304,38 +304,33 @@
              <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>
                  <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"
                <div v-for="(node, index) in approverNodes"
                  :key="node.id"
                  class="approver-node-item"
                >
                     class="approver-node-item">
                  <div class="approver-node-header">
                    <span class="approver-node-label">审批节点 {{ index + 1 }}</span>
                    <el-button
                      v-if="approverNodes.length > 1"
                    <el-button v-if="approverNodes.length > 1"
                      type="danger"
                      size="small"
                      text
                      @click="removeApproverNode(index)"
                      icon="Delete"
                    >删除</el-button>
                               icon="Delete">删除</el-button>
                  </div>
                  <el-select
                    v-model="node.userId"
                  <el-select v-model="node.userId"
                    placeholder="请选择审批人"
                    filterable
                    style="width: 100%;"
                  >
                    <el-option
                      v-for="user in userList"
                             style="width: 100%;">
                    <el-option v-for="user in userListApprove"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                               :label="user.userName"
                               :value="user.userId" />
                  </el-select>
                </div>
              </div>
@@ -373,8 +368,7 @@
                         :value="item.templateName">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                  <span>{{ item.templateName }}</span>
                  <el-icon
                    v-if="item.id"
                  <el-icon v-if="item.id"
                    class="delete-icon"
                    @click.stop="handleDeleteTemplate(item)"
                    style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;">
@@ -493,16 +487,13 @@
      </el-form>
    </FormDialog>
    <!-- 导入弹窗 -->
    <FormDialog
      v-model="importUpload.open"
    <FormDialog v-model="importUpload.open"
      :title="importUpload.title"
      :width="'600px'"
      @close="importUpload.open = false"
      @confirm="submitImportFile"
      @cancel="importUpload.open = false"
    >
      <el-upload
        ref="importUploadRef"
                @cancel="importUpload.open = false">
      <el-upload ref="importUploadRef"
        :limit="1"
        accept=".xlsx,.xls"
        :action="importUpload.url"
@@ -513,8 +504,7 @@
        :on-progress="importUpload.onProgress"
        :on-change="importUpload.onChange"
        :auto-upload="false"
        drag
      >
                 drag>
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          将文件拖到此处,或<em>点击上传</em>
@@ -522,7 +512,9 @@
        <template #tip>
          <div class="el-upload__tip">
            仅支持 xls/xlsx,大小不超过 10MB。
            <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button>
            <el-button link
                       type="primary"
                       @click="downloadTemplate">下载导入模板</el-button>
          </div>
        </template>
      </el-upload>
@@ -694,11 +686,9 @@
        </el-row>
      </el-form>
    </FormDialog>
    <FileListDialog
      ref="fileListRef"
    <FileListDialog ref="fileListRef"
      v-model="fileListDialogVisible"
      title="附件列表"
    />
                    title="附件列表" />
  </div>
</template>
@@ -716,8 +706,10 @@
  import { Search, Delete } from "@element-plus/icons-vue";
  import { ElMessageBox, ElMessage } from "element-plus";
  import { userListNoPage } from "@/api/system/user.js";
  import FormDialog from '@/components/Dialog/FormDialog.vue';
  import FileListDialog from '@/components/Dialog/FileListDialog.vue';
  import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js";
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
  import {
    getSalesLedgerWithProducts,
    addOrUpdateSalesLedgerProduct,
@@ -748,6 +740,7 @@
  const productSelectedRows = ref([]);
  const modelOptions = ref([]);
  const userList = ref([]);
  const userListApprove = ref([]);
  const productOptions = ref([]);
  const salesContractList = ref([]);
  const supplierList = ref([]);
@@ -770,7 +763,7 @@
  const addApproverNode = () => {
    approverNodes.value.push({ id: nextApproverId++, userId: null });
  };
  const removeApproverNode = (index) => {
  const removeApproverNode = index => {
    approverNodes.value.splice(index, 1);
  };
@@ -783,7 +776,7 @@
  };
  // 获取审批状态标签类型
  const getApprovalStatusType = (status) => {
  const getApprovalStatusType = status => {
    const typeMap = {
      1: "info",      // 待审核 - 灰色
      2: "warning",   // 审批中 - 橙色
@@ -870,7 +863,8 @@
        form.value.paymentMethod = matchedTemplate.paymentMethod;
      }
      // 模板数据中的产品字段是 productList,需要转换为 productData
      productData.value = matchedTemplate.productList || matchedTemplate.productData || [];
      productData.value =
        matchedTemplate.productList || matchedTemplate.productData || [];
    } else {
      // 未匹配到已有模板,视为新模板
      currentTemplateId.value = null;
@@ -1004,7 +998,7 @@
    url: import.meta.env.VITE_APP_BASE_API + "/purchase/ledger/import",
    headers: { Authorization: "Bearer " + getToken() },
    isUploading: false,
    beforeUpload: (file) => {
    beforeUpload: file => {
      const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
      const isLt10M = file.size / 1024 / 1024 < 10;
      if (!isExcel) {
@@ -1053,7 +1047,11 @@
  // 下载导入模板(如后端路径不同,可在此处调整)
  const downloadTemplate = () => {
    proxy.download("/purchase/ledger/exportTemplate", {}, "采购台账导入模板.xlsx");
    proxy.download(
      "/purchase/ledger/exportTemplate",
      {},
      "采购台账导入模板.xlsx"
    );
  };
  const submitImportFile = () => {
@@ -1118,8 +1116,8 @@
    // 检查是否有产品数据
    if (!productData.value || productData.value.length === 0) {
      ElMessage({
        message: '请先添加产品信息',
        type: 'warning',
        message: "请先添加产品信息",
        type: "warning",
      });
      return;
    }
@@ -1140,7 +1138,12 @@
        approveUserIds: approveUserIds,
        templateName: templateName.value.trim(),
      };
      console.log("template params ===>", params, "currentTemplateId:", currentTemplateId.value);
      console.log(
        "template params ===>",
        params,
        "currentTemplateId:",
        currentTemplateId.value
      );
      // 如果 currentTemplateId 有值,说明当前是“编辑已有模板” → 调用更新接口
      // 否则为“新建模板” → 调用新增接口
@@ -1298,7 +1301,9 @@
        getSalesNo(),
        getOptions(),
      ]);
      approveUserList({ approveType: 5 }).then(res => {
        userListApprove.value = res.data;
      });
      userList.value = userRes.data || [];
      salesContractList.value = salesRes || [];
      // 供应商过滤出isWhite=0 的数据
@@ -1334,7 +1339,7 @@
            const approverIds = purchaseRes.approveUserIds.split(",");
            approverNodes.value = approverIds.map((id, index) => ({
              id: index + 1,
              userId: Number(id)
              userId: Number(id),
            }));
            nextApproverId = approverIds.length + 1;
          }
@@ -1412,7 +1417,9 @@
          proxy.$modal.msgError("请为所有审批节点选择审批人!");
          return;
        }
        const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
        const approveUserIds = approverNodes.value
          .map(node => node.userId)
          .join(",");
        
        if (productData.value.length > 0) {
          // 新增时,需要从每个产品对象中删除 id 字段
@@ -1491,14 +1498,20 @@
              return nodes[i].value;
            }
            if (nodes[i].children && nodes[i].children.length > 0) {
              const found = findProductIdByCategory(nodes[i].children, categoryName);
              const found = findProductIdByCategory(
                nodes[i].children,
                categoryName
              );
              if (found) return found;
            }
          }
          return null;
        };
        
        const productId = findProductIdByCategory(productOptions.value, productForm.value.productCategory);
        const productId = findProductIdByCategory(
          productOptions.value,
          productForm.value.productCategory
        );
        if (productId) {
          productForm.value.productId = productId;
          // 获取型号列表并等待完成
@@ -1509,7 +1522,10 @@
          await nextTick();
          
          // 根据 specificationModel 查找 productModelId
          if (productForm.value.specificationModel && modelOptions.value.length > 0) {
          if (
            productForm.value.specificationModel &&
            modelOptions.value.length > 0
          ) {
            const modelItem = modelOptions.value.find(
              item => item.model === productForm.value.specificationModel
            );
@@ -1654,11 +1670,9 @@
          delProduct(ids).then(res => {
            proxy.$modal.msgSuccess("删除成功");
            closeProductDia();
            getPurchaseById({ id: currentId.value, type: 2 }).then(
              res => {
            getPurchaseById({ id: currentId.value, type: 2 }).then(res => {
                productData.value = res.productData;
              }
            );
            });
          });
        })
        .catch(() => {
@@ -1866,7 +1880,7 @@
  };
  // 删除模板
  const handleDeleteTemplate = async (item) => {
  const handleDeleteTemplate = async item => {
    if (!item.id) {
      proxy.$modal.msgWarning("无法删除该模板");
      return;
src/views/salesManagement/salesLedger/index.vue
@@ -1,54 +1,75 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm" :inline="true">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="客户名称:">
          <el-select
            v-model="searchForm.customerId"
          <el-select v-model="searchForm.customerId"
            filterable
            placeholder="请选择客户名称"
            clearable
            style="width: 220px"
            @change="handleQuery"
          >
            <el-option
              v-for="item in customerOption"
                     @change="handleQuery">
            <el-option v-for="item in customerOption"
              :key="item.id"
              :label="item.customerName"
              :value="item.id"
            >
                       :value="item.id">
              {{ item.customerName + "——" + item.taxpayerIdentificationNumber }}
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="销售合同号:">
          <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
          <el-input v-model="searchForm.salesContractNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="项目名称:">
          <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search"
          <el-input v-model="searchForm.projectName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="录入日期:">
          <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
            placeholder="请选择" clearable @change="changeDaterange" />
          <el-date-picker v-model="searchForm.entryDate"
                          value-format="YYYY-MM-DD"
                          format="YYYY-MM-DD"
                          type="daterange"
                          placeholder="请选择"
                          clearable
                          @change="changeDaterange" />
        </el-form-item>
        <el-form-item label="发货状态:">
          <el-select v-model="searchForm.deliveryStatus" placeholder="请选择" clearable style="width: 140px">
            <el-option label="未发货" :value="1" />
            <el-option label="审批中" :value="2" />
            <el-option label="审批失败" :value="3" />
            <el-option label="已发货" :value="4" />
          <el-select v-model="searchForm.deliveryStatus"
                     placeholder="请选择"
                     clearable
                     style="width: 140px">
            <el-option label="未发货"
                       :value="1" />
            <el-option label="审批中"
                       :value="2" />
            <el-option label="审批失败"
                       :value="3" />
            <el-option label="已发货"
                       :value="4" />
          </el-select>
        </el-form-item>
        <el-form-item label="入库状态:">
          <el-select v-model="searchForm.stockStatus" placeholder="请选择" clearable style="width: 140px">
            <el-option label="未入库" :value="0" />
            <el-option label="已入库" :value="1" />
          <el-select v-model="searchForm.stockStatus"
                     placeholder="请选择"
                     clearable
                     style="width: 140px">
            <el-option label="未入库"
                       :value="0" />
            <el-option label="已入库"
                       :value="1" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery"> 搜索 </el-button>
          <el-button type="primary"
                     @click="handleQuery"> 搜索 </el-button>
        </el-form-item>
      </el-form>
    </div>
@@ -58,23 +79,27 @@
          <OtherAmountMaintenanceButton />
          <ProcessFlowMaintenanceButton />
        </div>
        <ProcessFlowConfigSelectDialog
          v-model:visible="processFlowSelectDialogVisible"
        <ProcessFlowConfigSelectDialog v-model:visible="processFlowSelectDialogVisible"
          :default-route-id="processFlowSelectDefaultRouteId"
          :bound-route-name="processFlowSelectBoundRouteName"
          @confirm="handleProcessFlowSelectConfirm"
        />
                                       @confirm="handleProcessFlowSelectConfirm" />
            <el-space wrap>
                    <el-button type="primary" @click="handleSalesStock">入库</el-button>
                    <el-button type="primary" @click="openForm('add')">新增台账</el-button>
                    <el-button type="primary" @click="handleBulkDelivery">发货</el-button>
                    <el-button type="primary" plain @click="handleImport">导入</el-button>
          <el-button type="primary"
                     @click="handleSalesStock">入库</el-button>
          <el-button type="primary"
                     @click="openForm('add')">新增台账</el-button>
          <el-button type="primary"
                     @click="handleBulkDelivery">发货</el-button>
          <el-button type="primary"
                     plain
                     @click="handleImport">导入</el-button>
                    <el-button @click="handleOut">导出</el-button>
                    <el-button type="danger" plain @click="handleDelete">删除</el-button>
          <el-button type="danger"
                     plain
                     @click="handleDelete">删除</el-button>
                    <el-dropdown @command="handlePrintCommand">
                        <el-button type="primary" plain>
            <el-button type="primary"
                       plain>
                            打印单据<el-icon class="el-icon--right">
                                <ArrowDown />
                            </el-icon>
@@ -87,49 +112,92 @@
                            </el-dropdown-menu>
                        </template>
                    </el-dropdown>
                    <el-button type="primary" plain @click="handlePrintLabel">打印标签</el-button>
          <el-button type="primary"
                     plain
                     @click="handlePrintLabel">打印标签</el-button>
                </el-space>
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" fixed="left"/>
        <el-table-column type="expand" width="60" fixed="left">
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange"
                :expand-row-keys="expandedRowKeys"
                :row-key="(row) => row.id"
                :row-class-name="tableRowClassName"
                show-summary
                style="width: 100%"
                :summary-method="summarizeMainTable"
                @expand-change="expandChange"
                height="calc(100vh - 18.5em)">
        <el-table-column align="center"
                         type="selection"
                         width="55"
                         fixed="left" />
        <el-table-column type="expand"
                         width="60"
                         fixed="left">
          <template #default="props">
            <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
              <el-table-column align="center" label="序号" type="index"/>
            <el-table-column label="楼层编号" prop="floorCode" min-width="100" show-overflow-tooltip />
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="厚度" prop="thickness" min-width="90">
            <el-table :data="props.row.children"
                      border
                      show-summary
                      :summary-method="summarizeChildrenTable">
              <el-table-column align="center"
                               label="序号"
                               type="index" />
              <el-table-column label="楼层编号"
                               prop="floorCode"
                               min-width="100"
                               show-overflow-tooltip />
              <el-table-column label="产品大类"
                               prop="productCategory" />
              <el-table-column label="规格型号"
                               prop="specificationModel" />
              <el-table-column label="厚度"
                               prop="thickness"
                               min-width="90">
                <template #default="scope">
                  {{ scope.row.thickness ?? "" }}
                </template>
              </el-table-column>
                            <el-table-column label="宽(mm)" prop="width" min-width="80">
              <el-table-column label="宽(mm)"
                               prop="width"
                               min-width="80">
                                <template #default="scope">
                                    {{ scope.row.width ?? "" }}
                                </template>
                            </el-table-column>
                            <el-table-column label="高(mm)" prop="height" min-width="80">
              <el-table-column label="高(mm)"
                               prop="height"
                               min-width="80">
                                <template #default="scope">
                                    {{ scope.row.height ?? "" }}
                                </template>
                            </el-table-column>
                            <el-table-column label="周长(cm)" prop="perimeter" min-width="90">
              <el-table-column label="周长(cm)"
                               prop="perimeter"
                               min-width="90">
                                <template #default="scope">
                                    {{ scope.row.perimeter ?? "" }}
                                </template>
                            </el-table-column>
                            <el-table-column label="总面积(cm²)" prop="actualTotalArea" min-width="100">
              <el-table-column label="总面积(cm²)"
                               prop="actualTotalArea"
                               min-width="100">
                                <template #default="scope">
                                    {{ scope.row.actualTotalArea ?? "" }}
                                </template>
                            </el-table-column>
                            <el-table-column label="加工要求" prop="processRequirement" min-width="120"
              <el-table-column label="加工要求"
                               prop="processRequirement"
                               min-width="120"
                                show-overflow-tooltip />
                            <el-table-column label="备注" prop="remark" min-width="120" show-overflow-tooltip />
                            <el-table-column label="重箱" prop="heavyBox" min-width="80">
              <el-table-column label="备注"
                               prop="remark"
                               min-width="120"
                               show-overflow-tooltip />
              <el-table-column label="重箱"
                               prop="heavyBox"
                               min-width="80">
                                <template #default="scope">
                                    {{ scope.row.heavyBox ?? "" }}
                                </template>
@@ -138,12 +206,12 @@
                                                             width="100px"
                                                             align="center">
                <template #default="scope">
                                    <el-tag v-if="scope.row.approveStatus === 1 && (!scope.row.shippingDate || !scope.row.shippingCarNumber)"
                                                    type="success">充足</el-tag>
                                    <el-tag v-else-if="scope.row.approveStatus === 0 && (scope.row.shippingDate || scope.row.shippingCarNumber)"
                                                    type="success">已出库</el-tag>
                                    <el-tag v-else type="danger">不足</el-tag>
                  <el-tag v-else
                          type="danger">不足</el-tag>
                </template>
              </el-table-column>
                            <!-- <el-table-column label="发货状态" width="140" align="center">
@@ -153,13 +221,21 @@
                                    </el-tag>
                                </template>
                            </el-table-column> -->
                            <el-table-column label="快递公司" prop="expressCompany" show-overflow-tooltip />
                            <el-table-column label="快递单号" prop="expressNumber" show-overflow-tooltip />
              <el-table-column label="发货车牌" minWidth="100px" align="center">
              <el-table-column label="快递公司"
                               prop="expressCompany"
                               show-overflow-tooltip />
              <el-table-column label="快递单号"
                               prop="expressNumber"
                               show-overflow-tooltip />
              <el-table-column label="发货车牌"
                               minWidth="100px"
                               align="center">
                <template #default="scope">
                  <div>
                    <el-tag type="success" v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag>
                    <el-tag v-else type="info">-</el-tag>
                    <el-tag type="success"
                            v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag>
                    <el-tag v-else
                            type="info">-</el-tag>
                  </div>
                </template>
              </el-table-column>
@@ -174,11 +250,19 @@
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
              <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
              <el-table-column label="数量"
                               prop="quantity" />
              <el-table-column label="税率(%)"
                               prop="taxRate" />
              <el-table-column label="含税单价(元)"
                               prop="taxInclusiveUnitPrice"
                               :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)"
                               prop="taxInclusiveTotalPrice"
                               :formatter="formattedNumber" />
              <el-table-column label="不含税总价(元)"
                               prop="taxExclusiveTotalPrice"
                               :formatter="formattedNumber" />
            <!--操作-->
              <!-- <el-table-column Width="60px" label="操作" align="center">
                <template #default="scope">
@@ -194,67 +278,151 @@
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip />
        <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip />
        <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip />
        <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="销售合同号"
                         prop="salesContractNo"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column label="客户名称"
                         prop="customerName"
                         width="300"
                         show-overflow-tooltip />
        <el-table-column label="业务员"
                         prop="salesman"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="项目名称"
                         prop="projectName"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column label="合同金额(元)"
                         prop="contractAmount"
                         width="220"
                         show-overflow-tooltip
          :formatter="formattedNumber" />
          <el-table-column label="发货状态" width="140" align="center">
        <el-table-column label="发货状态"
                         width="140"
                         align="center">
                <template #default="scope">
                        <el-tag v-if="Number(scope.row.deliveryStatus) === 1" type="info">未发货</el-tag>
                        <el-tag v-else-if="Number(scope.row.deliveryStatus) === 2" type="warning">审批中</el-tag>
                        <el-tag v-else-if="Number(scope.row.deliveryStatus) === 3" type="danger">审批不通过</el-tag>
                        <el-tag v-else-if="Number(scope.row.deliveryStatus) === 4" type="primary">审批通过</el-tag>
                        <el-tag v-else-if="Number(scope.row.deliveryStatus) === 5" type="success">已发货</el-tag>
                        <el-tag v-else type="info">-</el-tag>
            <el-tag v-if="Number(scope.row.deliveryStatus) === 1"
                    type="info">未发货</el-tag>
            <el-tag v-else-if="Number(scope.row.deliveryStatus) === 2"
                    type="warning">审批中</el-tag>
            <el-tag v-else-if="Number(scope.row.deliveryStatus) === 3"
                    type="danger">审批不通过</el-tag>
            <el-tag v-else-if="Number(scope.row.deliveryStatus) === 4"
                    type="primary">审批通过</el-tag>
            <el-tag v-else-if="Number(scope.row.deliveryStatus) === 5"
                    type="success">已发货</el-tag>
            <el-tag v-else
                    type="info">-</el-tag>
                    </template>
          </el-table-column>
          <el-table-column label="入库状态" width="120" align="center">
        <el-table-column label="入库状态"
                         width="120"
                         align="center">
                <template #default="scope">
                        <el-tag v-if="Number(scope.row.stockStatus) === 0" type="info">未入库</el-tag>
                        <el-tag v-else-if="Number(scope.row.stockStatus) === 1" type="success">已入库</el-tag>
                        <el-tag v-else type="info">-</el-tag>
            <el-tag v-if="Number(scope.row.stockStatus) === 0"
                    type="info">未入库</el-tag>
            <el-tag v-else-if="Number(scope.row.stockStatus) === 1"
                    type="success">已入库</el-tag>
            <el-tag v-else
                    type="info">-</el-tag>
                    </template>
          </el-table-column>
        <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip />
        <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
        <el-table-column label="交付日期" prop="deliveryDate" width="120" show-overflow-tooltip />
        <el-table-column label="备注" prop="remarks" width="200" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" width="200" align="center">
        <el-table-column label="录入人"
                         prop="entryPersonName"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="录入日期"
                         prop="entryDate"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="签订日期"
                         prop="executionDate"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="交付日期"
                         prop="deliveryDate"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="备注"
                         prop="remarks"
                         width="200"
                         show-overflow-tooltip />
        <el-table-column fixed="right"
                         label="操作"
                         width="200"
                         align="center">
          <template #default="scope">
            <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">编辑</el-button>
            <el-button link type="primary" @click="openProcessFlowSelect(scope.row)" :disabled="!scope.row.isEdit">工艺路线</el-button>
            <el-button link type="primary" @click="downLoadFile(scope.row)">附件</el-button>
            <el-button link
                       type="primary"
                       @click="openForm('edit', scope.row)"
                       :disabled="!scope.row.isEdit">编辑</el-button>
            <el-button link
                       type="primary"
                       @click="openProcessFlowSelect(scope.row)"
                       :disabled="!scope.row.isEdit">工艺路线</el-button>
            <el-button link
                       type="primary"
                       @click="downLoadFile(scope.row)">附件</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
      <pagination v-show="total > 0"
                  :total="total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange" />
    </div>
    <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" :width="'70%'"
      :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
    <FormDialog v-model="dialogFormVisible"
                :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'"
                :width="'70%'"
                :operation-type="operationType"
                @close="closeDia"
                @confirm="submitForm"
                @cancel="closeDia">
      <el-form :model="form"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
                <!-- 报价单导入入口:放在表单顶部,选择后反显客户/业务员等 -->
                <el-row v-if="operationType === 'add'" style="margin-bottom: 10px;">
                    <el-col :span="24" style="text-align: right;">
                        <el-button type="primary" plain @click="openQuotationDialog">
        <el-row v-if="operationType === 'add'"
                style="margin-bottom: 10px;">
          <el-col :span="24"
                  style="text-align: right;">
            <el-button type="primary"
                       plain
                       @click="openQuotationDialog">
                            从销售报价导入
                        </el-button>
                    </el-col>
                </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="销售合同号:" prop="salesContractNo">
              <el-input v-model="form.salesContractNo" placeholder="自动生成" clearable disabled />
            <el-form-item label="销售合同号:"
                          prop="salesContractNo">
              <el-input v-model="form.salesContractNo"
                        placeholder="自动生成"
                        clearable
                        disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="业务员:" prop="salesman">
              <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
            <el-form-item label="业务员:"
                          prop="salesman">
              <el-select v-model="form.salesman"
                         placeholder="请选择"
                         clearable
                         :disabled="operationType === 'view'">
                <el-option v-for="item in userList"
                           :key="item.nickName"
                           :label="item.nickName"
                  :value="item.nickName" />
              </el-select>
            </el-form-item>
@@ -262,9 +430,17 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户名称:" prop="customerId">
              <el-select v-model="form.customerId" filterable placeholder="请选择" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
            <el-form-item label="客户名称:"
                          prop="customerId">
              <el-select v-model="form.customerId"
                         filterable
                         placeholder="请选择"
                         clearable
                         :disabled="operationType === 'view'">
                <el-option v-for="item in customerOption"
                           :key="item.id"
                           :label="item.customerName"
                           :value="item.id">
                  {{
                    item.customerName + "——" + item.taxpayerIdentificationNumber
                  }}
@@ -273,65 +449,107 @@
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="项目名称:" prop="projectName">
                            <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            <el-form-item label="项目名称:"
                          prop="projectName">
              <el-input v-model="form.projectName"
                        placeholder="请输入"
                        clearable
                        :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="签订日期:" prop="executionDate">
                            <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD"
                                                            format="YYYY-MM-DD" type="date" placeholder="请选择" clearable :disabled="operationType === 'view'" />
            <el-form-item label="签订日期:"
                          prop="executionDate">
              <el-date-picker style="width: 100%"
                              v-model="form.executionDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable
                              :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
          <el-col :span="12">
            <el-form-item label="交货日期:" prop="deliveryDate">
              <el-date-picker style="width: 100%" v-model="form.deliveryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                              type="date" placeholder="请选择" clearable />
            <el-form-item label="交货日期:"
                          prop="deliveryDate">
              <el-date-picker style="width: 100%"
                              v-model="form.deliveryDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="录入人:" prop="entryPerson">
            <el-form-item label="录入人:"
                          prop="entryPerson">
                            <el-select v-model="form.entryPerson"
                                                 filterable
                                                 default-first-option
                                                 :reserve-keyword="false" placeholder="请选择" clearable @change="changs">
                                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
                         :reserve-keyword="false"
                         placeholder="请选择"
                         clearable
                         @change="changs">
                <el-option v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
                           :value="item.userId" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="录入日期:" prop="entryDate">
                            <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                                                            type="date" placeholder="请选择" clearable />
            <el-form-item label="录入日期:"
                          prop="entryDate">
              <el-date-picker style="width: 100%"
                              v-model="form.entryDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-form-item label="产品信息:" prop="entryDate">
                        <el-button
                            v-if="operationType !== 'view'"
          <el-form-item label="产品信息:"
                        prop="entryDate">
            <el-button v-if="operationType !== 'view'"
                            type="primary"
                            :disabled="hasEditingProductRow()"
                            @click="addProductInline"
                        >
                       @click="addProductInline">
                            添加
                        </el-button>
                        <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >删除</el-button>
            <el-button v-if="operationType !== 'view'"
                       plain
                       type="danger"
                       @click="deleteProduct">删除</el-button>
                    </el-form-item>
                </el-row>
                <el-table :data="productData" border @selection-change="productSelected" show-summary
        <el-table :data="productData"
                  border
                  @selection-change="productSelected"
                  show-summary
                                    :summary-method="summarizeMainTable">
                    <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'"
          <el-table-column align="center"
                           type="selection"
                           width="55"
                           v-if="operationType !== 'view'"
                        :selectable="(row) => !isProductShipped(row)" />
                    <el-table-column align="center" label="序号" type="index" width="60" />
                    <el-table-column label="产品大类" prop="productCategory" min-width="160">
          <el-table-column align="center"
                           label="序号"
                           type="index"
                           width="60" />
          <el-table-column label="产品大类"
                           prop="productCategory"
                           min-width="160">
                        <template #default="scope">
                            <el-tree-select
                                v-if="scope.row.__editing"
              <el-tree-select v-if="scope.row.__editing"
                                v-model="scope.row.__productCategoryId"
                                placeholder="请选择"
                                clearable
@@ -341,46 +559,49 @@
                                :render-after-expand="false"
                                style="width: 100%"
                                :filter-node-method="filterProductCategoryNode"
                                @change="(val) => handleInlineProductCategoryChange(scope.row, val)"
                            />
                              @change="(val) => handleInlineProductCategoryChange(scope.row, val)" />
                            <span v-else>{{ scope.row.productCategory ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="规格型号" prop="specificationModel" min-width="160">
          <el-table-column label="规格型号"
                           prop="specificationModel"
                           min-width="160">
                        <template #default="scope">
                            <el-select
                                v-if="scope.row.__editing"
              <el-select v-if="scope.row.__editing"
                                v-model="scope.row.productModelId"
                                placeholder="请选择"
                                clearable
                                filterable
                                style="width: 100%"
                                @change="(val) => handleInlineProductModelChange(scope.row, val)"
                            >
                                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
                         @change="(val) => handleInlineProductModelChange(scope.row, val)">
                <el-option v-for="item in modelOptions"
                           :key="item.id"
                           :label="item.model"
                           :value="item.id" />
                            </el-select>
                            <span v-else>{{ scope.row.specificationModel ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="厚度(mm)" prop="thickness" min-width="160">
          <el-table-column label="厚度(mm)"
                           prop="thickness"
                           min-width="160">
                        <template #default="scope">
                            <el-input-number
                                v-if="scope.row.__editing"
              <el-input-number v-if="scope.row.__editing"
                                v-model="scope.row.thickness"
                                :min="0"
                                :step="0.000000000000001"
                                :precision="15"
                                style="width: 100%"
                                placeholder="请输入"
                                clearable
                            />
                               clearable />
                            <span v-else>{{ scope.row.thickness ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="宽(mm)" prop="width" min-width="160">
          <el-table-column label="宽(mm)"
                           prop="width"
                           min-width="160">
                        <template #default="scope">
                            <el-input-number
                                v-if="scope.row.__editing"
              <el-input-number v-if="scope.row.__editing"
                                v-model="scope.row.width"
                                :min="0"
                                :step="1"
@@ -389,15 +610,15 @@
                                placeholder="请输入"
                                clearable
                                @change="() => handleInlineSizeChange(scope.row)"
                                @input="() => handleInlineSizeChange(scope.row)"
                            />
                               @input="() => handleInlineSizeChange(scope.row)" />
                            <span v-else>{{ scope.row.width ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="高(mm)" prop="height" min-width="160">
          <el-table-column label="高(mm)"
                           prop="height"
                           min-width="160">
                        <template #default="scope">
                            <el-input-number
                                v-if="scope.row.__editing"
              <el-input-number v-if="scope.row.__editing"
                                v-model="scope.row.height"
                                :min="0"
                                :step="1"
@@ -406,15 +627,15 @@
                                placeholder="请输入"
                                clearable
                                @change="() => handleInlineSizeChange(scope.row)"
                                @input="() => handleInlineSizeChange(scope.row)"
                            />
                               @input="() => handleInlineSizeChange(scope.row)" />
                            <span v-else>{{ scope.row.height ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="结算单片面积(㎡)" prop="settlePieceArea" min-width="160">
          <el-table-column label="结算单片面积(㎡)"
                           prop="settlePieceArea"
                           min-width="160">
                        <template #default="scope">
                            <el-input-number
                                v-if="scope.row.__editing"
              <el-input-number v-if="scope.row.__editing"
                                v-model="scope.row.settlePieceArea"
                                :min="0"
                                :step="0.00001"
@@ -422,15 +643,15 @@
                                style="width: 100%"
                                placeholder="请输入"
                                clearable
                                @change="() => handleInlineSettleAreaChange(scope.row)"
                            />
                               @change="() => handleInlineSettleAreaChange(scope.row)" />
                            <span v-else>{{ scope.row.settlePieceArea ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="数量" prop="quantity" min-width="150">
          <el-table-column label="数量"
                           prop="quantity"
                           min-width="150">
                        <template #default="scope">
                            <el-input-number
                                v-if="scope.row.__editing"
              <el-input-number v-if="scope.row.__editing"
                                v-model="scope.row.quantity"
                                :step="0.1"
                                :min="0"
@@ -439,29 +660,29 @@
                                placeholder="请输入"
                                clearable
                                @change="() => handleInlineQuantityChange(scope.row)"
                                @input="() => handleInlineQuantityChange(scope.row)"
                            />
                               @input="() => handleInlineQuantityChange(scope.row)" />
                            <span v-else>{{ scope.row.quantity ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="面积(m²)" prop="actualTotalArea" min-width="160">
          <el-table-column label="面积(m²)"
                           prop="actualTotalArea"
                           min-width="160">
                        <template #default="scope">
                            <el-input-number
                                v-if="scope.row.__editing"
              <el-input-number v-if="scope.row.__editing"
                                v-model="scope.row.actualTotalArea"
                                :min="0"
                                :step="0.00001"
                                :precision="5"
                                style="width: 100%"
                                placeholder="自动计算"
                            />
                               placeholder="自动计算" />
                            <span v-else>{{ scope.row.actualTotalArea ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" min-width="140">
          <el-table-column label="含税单价(元)"
                           prop="taxInclusiveUnitPrice"
                           min-width="140">
                        <template #default="scope">
                            <el-input-number
                                v-if="scope.row.__editing"
              <el-input-number v-if="scope.row.__editing"
                                :step="0.01"
                                :min="0"
                                :precision="2"
@@ -470,310 +691,296 @@
                                placeholder="请输入"
                                clearable
                                @change="() => handleInlineUnitPriceChange(scope.row)"
                                @input="() => handleInlineUnitPriceChange(scope.row)"
                            />
                               @input="() => handleInlineUnitPriceChange(scope.row)" />
                            <span v-else>{{ formattedNumber(null, null, scope.row.taxInclusiveUnitPrice ?? 0) }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="税率(%)" prop="taxRate" min-width="120">
          <el-table-column label="税率(%)"
                           prop="taxRate"
                           min-width="120">
                        <template #default="scope">
                            <el-select
                                v-if="scope.row.__editing"
              <el-select v-if="scope.row.__editing"
                                v-model="scope.row.taxRate"
                                placeholder="请选择"
                                clearable
                                style="width: 100%"
                                @change="() => handleInlineTaxRateChange(scope.row)"
                            >
                                <el-option label="1" value="1" />
                                <el-option label="3" value="3" />
                                <el-option label="6" value="6" />
                                <el-option label="9" value="9" />
                                <el-option label="13" value="13" />
                         @change="() => handleInlineTaxRateChange(scope.row)">
                <el-option label="1"
                           value="1" />
                <el-option label="3"
                           value="3" />
                <el-option label="6"
                           value="6" />
                <el-option label="9"
                           value="9" />
                <el-option label="13"
                           value="13" />
                            </el-select>
                            <span v-else>{{ scope.row.taxRate ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber"  min-width="120"/>
                    <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber"  min-width="120"/>
                    <el-table-column label="发票类型" prop="invoiceType" min-width="120">
          <el-table-column label="含税总价(元)"
                           prop="taxInclusiveTotalPrice"
                           :formatter="formattedNumber"
                           min-width="120" />
          <el-table-column label="不含税总价(元)"
                           prop="taxExclusiveTotalPrice"
                           :formatter="formattedNumber"
                           min-width="120" />
          <el-table-column label="发票类型"
                           prop="invoiceType"
                           min-width="120">
                        <template #default="scope">
                            <el-select
                                v-if="scope.row.__editing"
              <el-select v-if="scope.row.__editing"
                                v-model="scope.row.invoiceType"
                                placeholder="请选择"
                                clearable
                                style="width: 100%"
                            >
                                <el-option label="增普票" value="增普票" />
                                <el-option label="增专票" value="增专票" />
                         style="width: 100%">
                <el-option label="增普票"
                           value="增普票" />
                <el-option label="增专票"
                           value="增专票" />
                            </el-select>
                            <span v-else>{{ scope.row.invoiceType ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="加工要求" prop="processRequirement" min-width="160" show-overflow-tooltip>
          <el-table-column label="加工要求"
                           prop="processRequirement"
                           min-width="160"
                           show-overflow-tooltip>
                        <template #default="scope">
                            <el-input
                                v-if="scope.row.__editing"
              <el-input v-if="scope.row.__editing"
                                v-model="scope.row.processRequirement"
                                placeholder="请输入"
                                clearable
                                style="width: 100%"
                            />
                        style="width: 100%" />
                            <span v-else>{{ scope.row.processRequirement ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="备注" prop="remark" min-width="140" show-overflow-tooltip>
          <el-table-column label="备注"
                           prop="remark"
                           min-width="140"
                           show-overflow-tooltip>
                        <template #default="scope">
                            <el-input
                                v-if="scope.row.__editing"
              <el-input v-if="scope.row.__editing"
                                v-model="scope.row.remark"
                                placeholder="请输入"
                                clearable
                                style="width: 100%"
                            />
                        style="width: 100%" />
                            <span v-else>{{ scope.row.remark ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="楼层编号" prop="floorCode" min-width="140" show-overflow-tooltip>
          <el-table-column label="楼层编号"
                           prop="floorCode"
                           min-width="140"
                           show-overflow-tooltip>
                        <template #default="scope">
                            <el-input
                                v-if="scope.row.__editing"
              <el-input v-if="scope.row.__editing"
                                v-model="scope.row.floorCode"
                                placeholder="请输入"
                                clearable
                                style="width: 100%"
                            />
                        style="width: 100%" />
                            <span v-else>{{ scope.row.floorCode ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="重箱" prop="heavyBox" min-width="100">
          <el-table-column label="重箱"
                           prop="heavyBox"
                           min-width="100">
                        <template #default="scope">
                            <el-input
                                v-if="scope.row.__editing"
              <el-input v-if="scope.row.__editing"
                                v-model="scope.row.heavyBox"
                                placeholder="请输入"
                                clearable
                                style="width: 100%"
                            />
                        style="width: 100%" />
                            <span v-else>{{ scope.row.heavyBox ?? "" }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column fixed="right" label="操作" min-width="220" align="center" v-if="operationType !== 'view'">
          <el-table-column fixed="right"
                           label="操作"
                           min-width="220"
                           align="center"
                           v-if="operationType !== 'view'">
                        <template #default="scope">
                            <template v-if="scope.row.__editing">
                                <el-button link type="primary" size="small" @click="saveProductInline(scope.row, scope.$index)">保存</el-button>
                                <el-button link type="danger" size="small" @click="cancelProductInline(scope.row, scope.$index)">取消</el-button>
                                <el-popover
                                    :width="560"
                <el-button link
                           type="primary"
                           size="small"
                           @click="saveProductInline(scope.row, scope.$index)">保存</el-button>
                <el-button link
                           type="danger"
                           size="small"
                           @click="cancelProductInline(scope.row, scope.$index)">取消</el-button>
                <el-popover :width="560"
                                    trigger="click"
                                    :hide-after="0"
                                    :visible="scope.row.__otherAmountPopoverVisible"
                                    @update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)"
                                >
                            @update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)">
                                    <template #reference>
                                        <el-button
                                            link
                    <el-button link
                                            type="primary"
                                            size="small"
                                            @click="openOtherAmountInline(scope.row)"
                                        >
                               @click="openOtherAmountInline(scope.row)">
                                            其他金额({{ (scope.row.salesProductProcessList || []).length || 0 }})
                                        </el-button>
                                    </template>
                                    <div style="display:flex; align-items:center; justify-content:space-between; gap: 10px; margin-bottom: 8px;">
                                        <div style="font-weight: 600; color:#303133;">
                                            其他金额
                                        </div>
                                        <el-button type="primary" plain size="small" @click="startAddOtherAmountForRow(scope.row)">
                    <el-button type="primary"
                               plain
                               size="small"
                               @click="startAddOtherAmountForRow(scope.row)">
                                            新增
                                        </el-button>
                                    </div>
                                    <div
                                        v-if="scope.row.__inlineOtherAmountAdding"
                  <div v-if="scope.row.__inlineOtherAmountAdding"
                                        style="display:flex; flex-direction:column; gap: 8px; margin-bottom: 10px;"
                                        @click.stop
                                    >
                                        <el-select
                                            v-model="scope.row.__inlineOtherAmountAddId"
                       @click.stop>
                    <el-select v-model="scope.row.__inlineOtherAmountAddId"
                                            filterable
                                            clearable
                                            placeholder="请选择其他金额项目"
                                            style="width: 100%;"
                                        >
                                            <el-option
                                                v-for="item in otherAmountSelectOptions"
                               style="width: 100%;">
                      <el-option v-for="item in otherAmountSelectOptions"
                                                :key="item.id"
                                                :label="item.processName"
                                                :value="item.id"
                                            />
                                 :value="item.id" />
                                        </el-select>
                                        <div style="display:flex; justify-content:flex-end; gap: 8px;">
                                            <el-button
                                                size="small"
                                                @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null"
                                            >
                      <el-button size="small"
                                 @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null">
                                                取消
                                            </el-button>
                                            <el-button
                                                type="primary"
                      <el-button type="primary"
                                                size="small"
                                                :disabled="scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''"
                                                @click="confirmAddOtherAmountForRow(scope.row)"
                                            >
                                 @click="confirmAddOtherAmountForRow(scope.row)">
                                                确认添加
                                            </el-button>
                                        </div>
                                    </div>
                                    <div v-if="Array.isArray(scope.row.salesProductProcessList) && scope.row.salesProductProcessList.length > 0"
                                        style="display:flex; flex-wrap:wrap; gap: 8px;"
                                    >
                                        <div
                                            v-for="(item, idx) in scope.row.salesProductProcessList"
                       style="display:flex; flex-wrap:wrap; gap: 8px;">
                    <div v-for="(item, idx) in scope.row.salesProductProcessList"
                                            :key="String(item.id) + '_' + idx"
                                            style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);"
                                        >
                                            <el-tag type="info" style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
                         style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);">
                      <el-tag type="info"
                              style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
                                                {{ item.processName }}
                                            </el-tag>
                                            <el-input-number
                                                v-model="item.quantity"
                      <el-input-number v-model="item.quantity"
                                                :min="0"
                                                :step="1"
                                                :precision="0"
                                                style="width: 120px;"
                                                placeholder="数量"
                                                :disabled="operationType === 'view'"
                                                @change="handleOtherAmountQuantityChange(scope.row)"
                                            />
                                            <el-button type="danger" link size="small" @click="removeOtherAmountAtForRow(scope.row, idx)">
                                       @change="handleOtherAmountQuantityChange(scope.row)" />
                      <el-button type="danger"
                                 link
                                 size="small"
                                 @click="removeOtherAmountAtForRow(scope.row, idx)">
                                                删除
                                            </el-button>
                                        </div>
                                    </div>
                                    <div v-else style="color:#909399; font-size: 13px;">
                  <div v-else
                       style="color:#909399; font-size: 13px;">
                                        暂无其他金额
                                    </div>
                                </el-popover>
                            </template>
                            <template v-else>
                                <el-button
                                    link
                <el-button link
                                    type="primary"
                                    size="small"
                                    :disabled="isProductShipped(scope.row)"
                                    @click="editProductInline(scope.row, scope.$index)"
                                >
                           @click="editProductInline(scope.row, scope.$index)">
                                    编辑
                                </el-button>
                                <el-popover
                                    :width="560"
                <el-popover :width="560"
                                    trigger="click"
                                    :hide-after="0"
                                    :visible="scope.row.__otherAmountPopoverVisible"
                                    @update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)"
                                >
                            @update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)">
                                    <template #reference>
                                        <el-button
                                            link
                    <el-button link
                                            type="primary"
                                            size="small"
                                            :disabled="isProductShipped(scope.row)"
                                            @click="openOtherAmountInline(scope.row)"
                                        >
                               @click="openOtherAmountInline(scope.row)">
                                            其他金额({{ (scope.row.salesProductProcessList || []).length || 0 }})
                                        </el-button>
                                    </template>
                                    <div style="display:flex; align-items:center; justify-content:space-between; gap: 10px; margin-bottom: 8px;">
                                        <div style="font-weight: 600; color:#303133;">
                                            其他金额
                                        </div>
                                        <el-button
                                            type="primary"
                    <el-button type="primary"
                                            plain
                                            size="small"
                                            :disabled="isProductShipped(scope.row)"
                                            @click="startAddOtherAmountForRow(scope.row)"
                                        >
                               @click="startAddOtherAmountForRow(scope.row)">
                                            新增
                                        </el-button>
                                    </div>
                                    <div
                                        v-if="scope.row.__inlineOtherAmountAdding"
                  <div v-if="scope.row.__inlineOtherAmountAdding"
                                        style="display:flex; flex-direction:column; gap: 8px; margin-bottom: 10px;"
                                        @click.stop
                                    >
                                        <el-select
                                            v-model="scope.row.__inlineOtherAmountAddId"
                       @click.stop>
                    <el-select v-model="scope.row.__inlineOtherAmountAddId"
                                            filterable
                                            clearable
                                            placeholder="请选择其他金额项目"
                                            style="width: 100%;"
                                            :disabled="isProductShipped(scope.row)"
                                        >
                                            <el-option
                                                v-for="item in otherAmountSelectOptions"
                               :disabled="isProductShipped(scope.row)">
                      <el-option v-for="item in otherAmountSelectOptions"
                                                :key="item.id"
                                                :label="item.processName"
                                                :value="item.id"
                                            />
                                 :value="item.id" />
                                        </el-select>
                                        <div style="display:flex; justify-content:flex-end; gap: 8px;">
                                            <el-button
                                                size="small"
                      <el-button size="small"
                                                :disabled="isProductShipped(scope.row)"
                                                @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null"
                                            >
                                 @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null">
                                                取消
                                            </el-button>
                                            <el-button
                                                type="primary"
                      <el-button type="primary"
                                                size="small"
                                                :disabled="isProductShipped(scope.row) || scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''"
                                                @click="confirmAddOtherAmountForRow(scope.row)"
                                            >
                                 @click="confirmAddOtherAmountForRow(scope.row)">
                                                确认添加
                                            </el-button>
                                        </div>
                                    </div>
                                    <div v-if="Array.isArray(scope.row.salesProductProcessList) && scope.row.salesProductProcessList.length > 0"
                                        style="display:flex; flex-wrap:wrap; gap: 8px;"
                                    >
                                        <div
                                            v-for="(item, idx) in scope.row.salesProductProcessList"
                       style="display:flex; flex-wrap:wrap; gap: 8px;">
                    <div v-for="(item, idx) in scope.row.salesProductProcessList"
                                            :key="String(item.id) + '_' + idx"
                                            style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);"
                                        >
                                            <el-tag type="info" style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
                         style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);">
                      <el-tag type="info"
                              style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
                                                {{ item.processName }}
                                            </el-tag>
                                            <el-input-number
                                                v-model="item.quantity"
                      <el-input-number v-model="item.quantity"
                                                :min="0"
                                                :step="1"
                                                :precision="0"
                                                style="width: 120px;"
                                                placeholder="数量"
                                                :disabled="operationType === 'view' || isProductShipped(scope.row)"
                                                @change="handleOtherAmountQuantityChange(scope.row)"
                                            />
                                            <el-button
                                                type="danger"
                                       @change="handleOtherAmountQuantityChange(scope.row)" />
                      <el-button type="danger"
                                                link
                                                size="small"
                                                :disabled="isProductShipped(scope.row)"
                                                @click="removeOtherAmountAtForRow(scope.row, idx)"
                                            >
                                 @click="removeOtherAmountAtForRow(scope.row, idx)">
                                                删除
                                            </el-button>
                                        </div>
                                    </div>
                                    <div v-else style="color:#909399; font-size: 13px;">
                  <div v-else
                       style="color:#909399; font-size: 13px;">
                                        暂无其他金额
                                    </div>
                                </el-popover>
@@ -783,33 +990,48 @@
                </el-table>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="备注:" prop="remarks">
                            <el-input v-model="form.remarks" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="客户备注:" prop="customerRemarks">
                            <el-input
                                v-model="form.customerRemarks"
            <el-form-item label="备注:"
                          prop="remarks">
              <el-input v-model="form.remarks"
                                placeholder="请输入"
                                clearable
                                type="textarea"
                                :rows="2"
                                :disabled="operationType === 'view'"
                            />
                        :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="附件材料:" prop="salesLedgerFiles">
                            <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                                                 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                                                 :on-success="handleUploadSuccess" :on-remove="handleRemove">
                                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                                <template #tip v-if="operationType !== 'view'">
            <el-form-item label="客户备注:"
                          prop="customerRemarks">
              <el-input v-model="form.customerRemarks"
                        placeholder="请输入"
                        clearable
                        type="textarea"
                        :rows="2"
                        :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="附件材料:"
                          prop="salesLedgerFiles">
              <el-upload v-model:file-list="fileList"
                         :action="upload.url"
                         multiple
                         ref="fileUpload"
                         auto-upload
                         :headers="upload.headers"
                         :before-upload="handleBeforeUpload"
                         :on-error="handleUploadError"
                         :on-success="handleUploadSuccess"
                         :on-remove="handleRemove">
                <el-button type="primary"
                           v-if="operationType !== 'view'">上传</el-button>
                <template #tip
                          v-if="operationType !== 'view'">
                                    <div class="el-upload__tip">
                                        文件格式支持
                                        doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
@@ -821,86 +1043,101 @@
                </el-row>
            </el-form>
        </FormDialog>
        <!-- 从报价单导入(仅审批通过) -->
        <el-dialog
            v-model="quotationDialogVisible"
    <el-dialog v-model="quotationDialogVisible"
            title="选择审批通过的销售报价单"
            width="80%"
            :close-on-click-modal="false"
        >
               :close-on-click-modal="false">
            <div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;">
                <el-input
                    v-model="quotationSearchForm.quotationNo"
        <el-input v-model="quotationSearchForm.quotationNo"
                    placeholder="请输入报价单号"
                    clearable
                    style="max-width: 260px;"
                    @change="fetchQuotationList"
                />
                <el-input
                    v-model="quotationSearchForm.customer"
                  @change="fetchQuotationList" />
        <el-input v-model="quotationSearchForm.customer"
                    placeholder="请输入客户名称"
                    clearable
                    style="max-width: 260px;"
                    @change="fetchQuotationList"
                />
                <el-button type="primary" @click="fetchQuotationList">搜索</el-button>
                  @change="fetchQuotationList" />
        <el-button type="primary"
                   @click="fetchQuotationList">搜索</el-button>
                <el-button @click="resetQuotationSearch">重置</el-button>
            </div>
            <el-table
                :data="quotationList"
      <el-table :data="quotationList"
                border
                stripe
                v-loading="quotationLoading"
                height="420px"
            >
                <el-table-column align="center" label="序号" type="index" width="60" />
                <el-table-column prop="quotationNo" label="报价单号" width="180" show-overflow-tooltip />
                <el-table-column prop="customer" label="客户名称" min-width="220" show-overflow-tooltip />
                <el-table-column prop="salesperson" label="业务员" width="120" show-overflow-tooltip />
                <el-table-column prop="quotationDate" label="报价日期" width="140" />
                <el-table-column prop="status" label="审批状态" width="120" align="center" />
                <el-table-column prop="totalAmount" label="报价金额(元)" width="160" align="right">
                height="420px">
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column prop="quotationNo"
                         label="报价单号"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column prop="customer"
                         label="客户名称"
                         min-width="220"
                         show-overflow-tooltip />
        <el-table-column prop="salesperson"
                         label="业务员"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column prop="quotationDate"
                         label="报价日期"
                         width="140" />
        <el-table-column prop="status"
                         label="审批状态"
                         width="120"
                         align="center" />
        <el-table-column prop="totalAmount"
                         label="报价金额(元)"
                         width="160"
                         align="right">
                    <template #default="scope">
                        {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
                    </template>
                </el-table-column>
                <el-table-column fixed="right" label="操作" width="120" align="center">
        <el-table-column fixed="right"
                         label="操作"
                         width="120"
                         align="center">
                    <template #default="scope">
                        <el-button type="primary" link @click="applyQuotation(scope.row)">选择</el-button>
            <el-button type="primary"
                       link
                       @click="applyQuotation(scope.row)">选择</el-button>
                    </template>
                </el-table-column>
            </el-table>
            <pagination
                v-show="quotationPage.total > 0"
      <pagination v-show="quotationPage.total > 0"
                :total="quotationPage.total"
                layout="total, sizes, prev, pager, next, jumper"
                :page="quotationPage.current"
                :limit="quotationPage.size"
                @pagination="quotationPaginationChange"
            />
                  @pagination="quotationPaginationChange" />
            <template #footer>
                <el-button @click="quotationDialogVisible = false">关闭</el-button>
            </template>
        </el-dialog>
        <FormDialog
            v-model="productFormVisible"
    <FormDialog v-model="productFormVisible"
            :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
            :width="'60%'"
            :operation-type="productOperationType"
            @close="closeProductDia"
            @confirm="submitProduct"
            @cancel="closeProductDia">
            <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
      <el-form :model="productForm"
               label-width="140px"
               label-position="top"
               :rules="productRules"
               ref="productFormRef">
                <!-- 每行三个:产品大类/规格型号/单位 -->
                <el-row :gutter="30">
                    <el-col :span="8">
                        <el-form-item label="产品大类:" prop="productCategory">
                            <el-tree-select
                                v-model="productForm.productCategory"
            <el-form-item label="产品大类:"
                          prop="productCategory">
              <el-tree-select v-model="productForm.productCategory"
                                placeholder="请选择"
                                clearable
                                filterable
@@ -909,246 +1146,271 @@
                                @change="getModels"
                                :data="productOptions"
                                :render-after-expand="false"
                                style="width: 100%"
                            />
                              style="width: 100%" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="规格型号:" prop="productModelId">
                            <el-select
                                v-model="productForm.productModelId"
            <el-form-item label="规格型号:"
                          prop="productModelId">
              <el-select v-model="productForm.productModelId"
                                placeholder="请选择"
                                clearable
                                @change="getProductModel"
                                filterable
                                style="width: 100%"
                            >
                                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
                         style="width: 100%">
                <el-option v-for="item in modelOptions"
                           :key="item.id"
                           :label="item.model"
                           :value="item.id" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="厚度:" prop="thickness">
                            <el-input-number
                                v-model="productForm.thickness"
            <el-form-item label="厚度:"
                          prop="thickness">
              <el-input-number v-model="productForm.thickness"
                                :min="0"
                                :step="0.000000000000001"
                                :precision="15"
                                style="width: 100%;"
                                placeholder="请输入"
                                clearable
                            />
                               clearable />
                        </el-form-item>
                    </el-col>
                </el-row>
                <!-- 每行三个:税率/含税单价/数量 -->
                <el-row :gutter="30">
                    <el-col :span="8">
                        <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
                            <el-input-number
                                :step="0.01"
            <el-form-item label="含税单价(元):"
                          prop="taxInclusiveUnitPrice">
              <el-input-number :step="0.01"
                                :min="0"
                                v-model="productForm.taxInclusiveUnitPrice"
                                style="width: 100%"
                                :precision="2"
                                placeholder="请输入"
                                clearable
                                @change="calculateFromUnitPrice"
                            />
                               @change="calculateFromUnitPrice" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="税率(%):" prop="taxRate">
                            <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate" style="width: 100%">
                                <el-option label="1" value="1" />
                                <el-option label="3" value="3" />
                                <el-option label="6" value="6" />
                                <el-option label="9" value="9" />
                                <el-option label="13" value="13" />
            <el-form-item label="税率(%):"
                          prop="taxRate">
              <el-select v-model="productForm.taxRate"
                         placeholder="请选择"
                         clearable
                         @change="calculateFromTaxRate"
                         style="width: 100%">
                <el-option label="1"
                           value="1" />
                <el-option label="3"
                           value="3" />
                <el-option label="6"
                           value="6" />
                <el-option label="9"
                           value="9" />
                <el-option label="13"
                           value="13" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="数量:" prop="quantity">
                            <el-input-number
                                :step="0.1"
            <el-form-item label="数量:"
                          prop="quantity">
              <el-input-number :step="0.1"
                                :min="0"
                                v-model="productForm.quantity"
                                placeholder="请输入"
                                clearable
                                :precision="2"
                                @change="() => { calculateFromQuantity(); recalcAreaTotals(); }"
                                style="width: 100%"
                            />
                               style="width: 100%" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <!-- 每行三个:含税总价/不含税总价/发票类型 -->
                <el-row :gutter="30">
                    <el-col :span="8">
                        <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
                            <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" />
            <el-form-item label="含税总价(元):"
                          prop="taxInclusiveTotalPrice">
              <el-input v-model="productForm.taxInclusiveTotalPrice"
                        placeholder="请输入"
                        clearable
                        @change="calculateFromTotalPrice" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice">
                            <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" />
            <el-form-item label="不含税总价(元):"
                          prop="taxExclusiveTotalPrice">
              <el-input v-model="productForm.taxExclusiveTotalPrice"
                        placeholder="请输入"
                        clearable
                        @change="calculateFromExclusiveTotalPrice" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="发票类型:" prop="invoiceType">
                            <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable style="width: 100%">
                                <el-option label="增普票" value="增普票" />
                                <el-option label="增专票" value="增专票" />
            <el-form-item label="发票类型:"
                          prop="invoiceType">
              <el-select v-model="productForm.invoiceType"
                         placeholder="请选择"
                         clearable
                         style="width: 100%">
                <el-option label="增普票"
                           value="增普票" />
                <el-option label="增专票"
                           value="增专票" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <!-- 每行三个:宽/高/实际单片面积 -->
                <el-row :gutter="30">
                    <el-col :span="8">
                        <el-form-item label="宽(mm):" prop="width">
                            <el-input-number
                                v-model="productForm.width"
            <el-form-item label="宽(mm):"
                          prop="width">
              <el-input-number v-model="productForm.width"
                                :min="0"
                                :step="1"
                                :precision="2"
                                style="width: 100%"
                                placeholder="请输入宽(mm)"
                                clearable
                                @change="recalcAreaFromWidthHeight"
                            />
                               @change="recalcAreaFromWidthHeight" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="高(mm):" prop="height">
                            <el-input-number
                                v-model="productForm.height"
            <el-form-item label="高(mm):"
                          prop="height">
              <el-input-number v-model="productForm.height"
                                :min="0"
                                :step="1"
                                :precision="2"
                                style="width: 100%"
                                placeholder="请输入高(mm)"
                                clearable
                                @change="recalcAreaFromWidthHeight"
                            />
                               @change="recalcAreaFromWidthHeight" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="周长(cm):" prop="perimeter">
                            <el-input-number
                                v-model="productForm.perimeter"
            <el-form-item label="周长(cm):"
                          prop="perimeter">
              <el-input-number v-model="productForm.perimeter"
                                :min="0"
                                :step="0.01"
                                :precision="2"
                                style="width: 100%"
                                placeholder="请输入"
                                clearable
                                :disabled="true"
                            />
                               :disabled="true" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <!-- 每行三个:实际单片面积/实际总面积/结算单片面积 -->
                <el-row :gutter="30">
                    <el-col :span="8">
                        <el-form-item label="实际单片面积(㎡):" prop="actualPieceArea">
                            <el-input-number
                                v-model="productForm.actualPieceArea"
            <el-form-item label="实际单片面积(㎡):"
                          prop="actualPieceArea">
              <el-input-number v-model="productForm.actualPieceArea"
                                :min="0"
                                :step="0.00001"
                                :precision="5"
                                style="width: 100%"
                                placeholder="请输入"
                                clearable
                                @change="recalcAreaTotals"
                            />
                               @change="recalcAreaTotals" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="实际总面积(㎡):" prop="actualTotalArea">
                            <el-input-number
                                v-model="productForm.actualTotalArea"
            <el-form-item label="实际总面积(㎡):"
                          prop="actualTotalArea">
              <el-input-number v-model="productForm.actualTotalArea"
                               :min="0"
                               :step="0.00001"
                               :precision="5"
                               style="width: 100%"
                               placeholder="请输入"
                               clearable />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="结算单片面积(㎡):"
                          prop="settlePieceArea">
              <el-input-number v-model="productForm.settlePieceArea"
                                :min="0"
                                :step="0.00001"
                                :precision="5"
                                style="width: 100%"
                                placeholder="请输入"
                                clearable
                            />
                               @change="() => { recalcAreaTotals(); calculateFromUnitPrice(true); }" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="结算单片面积(㎡):" prop="settlePieceArea">
                            <el-input-number
                                v-model="productForm.settlePieceArea"
            <el-form-item label="结算总面积(㎡):"
                          prop="settleTotalArea">
              <el-input-number v-model="productForm.settleTotalArea"
                                :min="0"
                                :step="0.00001"
                                :precision="5"
                                style="width: 100%"
                                placeholder="请输入"
                                clearable
                                @change="() => { recalcAreaTotals(); calculateFromUnitPrice(true); }"
                            />
                               clearable />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="结算总面积(㎡):" prop="settleTotalArea">
                            <el-input-number
                                v-model="productForm.settleTotalArea"
            <el-form-item label="结算总面积(㎡):"
                          prop="settleTotalArea">
              <el-input-number v-model="productForm.settleTotalArea"
                                :min="0"
                                :step="0.00001"
                                :precision="5"
                                style="width: 100%"
                                placeholder="请输入"
                                clearable
                            />
                               clearable />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="结算总面积(㎡):" prop="settleTotalArea">
                            <el-input-number
                                v-model="productForm.settleTotalArea"
                                :min="0"
                                :step="0.00001"
                                :precision="5"
                                style="width: 100%"
            <el-form-item label="重箱:"
                          prop="heavyBox">
              <el-input v-model="productForm.heavyBox"
                                placeholder="请输入"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="重箱:" prop="heavyBox">
                            <el-input v-model="productForm.heavyBox" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" />
                        @change="calculateFromExclusiveTotalPrice" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="楼层编号:" prop="floorCode">
                            <el-input v-model="productForm.floorCode" placeholder="请输入楼层编号" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
            <el-form-item label="楼层编号:"
                          prop="floorCode">
              <el-input v-model="productForm.floorCode"
                        placeholder="请输入楼层编号"
                        clearable
                        type="textarea"
                        :rows="2"
                        :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <!-- 其他金额(占满一行:等同于 3 列网格的整行) -->
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="加工要求:" prop="processRequirement">
                            <el-input v-model="productForm.processRequirement" placeholder="请输入加工要求" clearable />
            <el-form-item label="加工要求:"
                          prop="processRequirement">
              <el-input v-model="productForm.processRequirement"
                        placeholder="请输入加工要求"
                        clearable />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="备注:" prop="remark">
                            <el-input v-model="productForm.remark" placeholder="请输入备注" clearable />
            <el-form-item label="备注:"
                          prop="remark">
              <el-input v-model="productForm.remark"
                        placeholder="请输入备注"
                        clearable />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item>
@@ -1158,58 +1420,49 @@
                                    <div style="color:#909399; font-size: 13px; flex: 1;">
                                        已选择 {{ productForm?.salesProductProcessList?.length || 0 }} 项
                                    </div>
                                    <el-button
                                        v-if="operationType !== 'view'"
                  <el-button v-if="operationType !== 'view'"
                                        type="primary"
                                        plain
                                        size="small"
                                        @click="startAddOtherAmount"
                                    >
                             @click="startAddOtherAmount">
                                        新增
                                    </el-button>
                                </div>
                            </template>
                            <div style="display:flex; flex-direction:column; gap: 12px;">
                                <div v-if="Array.isArray(productForm?.salesProductProcessList) && productForm.salesProductProcessList.length > 0"
                                    style="display:flex; flex-wrap:wrap; gap: 12px; align-items:flex-start;"
                                >
                                    <div
                                        v-for="(item, index) in productForm.salesProductProcessList"
                     style="display:flex; flex-wrap:wrap; gap: 12px; align-items:flex-start;">
                  <div v-for="(item, index) in productForm.salesProductProcessList"
                                        :key="String(item.id) + '_' + index"
                                        style="display:flex; gap: 10px; align-items:center; padding: 10px 12px; border: 1px solid #ebeef5; border-radius: 8px; box-sizing:border-box; min-width: 0;"
                                        :style="getOtherAmountCardFlexStyle()"
                                    >
                       :style="getOtherAmountCardFlexStyle()">
                                        <div style="flex: 1; min-width: 0;">
                                            <el-tag type="info" style="width: 100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
                      <el-tag type="info"
                              style="width: 100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
                                                {{ item.processName }}
                                            </el-tag>
                                        </div>
                                        <div style="flex: 1;">
                                            <el-input-number
                                                v-model="item.quantity"
                      <el-input-number v-model="item.quantity"
                                                :min="0"
                                                :step="1"
                                                :precision="0"
                                                style="width: 100%;"
                                                placeholder="请输入数量"
                                                :disabled="operationType === 'view'"
                                                @change="calculateFromUnitPrice(true)"
                                            />
                                       @change="calculateFromUnitPrice(true)" />
                                        </div>
                                        <el-button
                                            v-if="operationType !== 'view'"
                    <el-button v-if="operationType !== 'view'"
                                            type="danger"
                                            link
                                            size="small"
                                            @click="removeOtherAmountAt(index)"
                                        >
                               @click="removeOtherAmountAt(index)">
                                            删除
                                        </el-button>
                                    </div>
                                </div>
                                <div v-else style="color:#909399; font-size: 13px;">
                <div v-else
                     style="color:#909399; font-size: 13px;">
                                    暂无其他金额
                                </div>
                            </div>
@@ -1219,55 +1472,43 @@
            </el-form>
        </FormDialog>
        <!-- 其他金额:新增弹框 -->
        <el-dialog
            v-model="otherAmountAddDialogVisible"
    <el-dialog v-model="otherAmountAddDialogVisible"
            title="新增其他金额"
            width="520px"
            :close-on-click-modal="false"
        >
               :close-on-click-modal="false">
            <div style="padding: 4px 0 10px;">
                <div style="font-size: 14px; color: #606266; margin-bottom: 10px;">
                    请选择要新增的其他金额项目
                </div>
                <el-select
                    v-model="otherAmountAddId"
        <el-select v-model="otherAmountAddId"
                    filterable
                    clearable
                    placeholder="请选择其他金额项目"
                    style="width: 100%;"
                    :disabled="operationType === 'view'"
                >
                    <el-option
                        v-for="item in otherAmountSelectOptions"
                   :disabled="operationType === 'view'">
          <el-option v-for="item in otherAmountSelectOptions"
                        :key="item.id"
                        :label="item.processName"
                        :value="item.id"
                    />
                     :value="item.id" />
                </el-select>
            </div>
            <template #footer>
                <el-button @click="cancelAddOtherAmount">取消</el-button>
                <el-button
                    type="primary"
        <el-button type="primary"
                    @click="confirmAddOtherAmount"
                    :disabled="operationType === 'view' || otherAmountAddId === null || otherAmountAddId === undefined || otherAmountAddId === ''"
                >
                   :disabled="operationType === 'view' || otherAmountAddId === null || otherAmountAddId === undefined || otherAmountAddId === ''">
                    确认添加
                </el-button>
            </template>
        </el-dialog>
        <!-- 导入弹窗 -->
        <FormDialog
            v-model="importUpload.open"
    <FormDialog v-model="importUpload.open"
            :title="importUpload.title"
            :width="'600px'"
            @close="importUpload.open = false"
            @confirm="submitImportFile"
            @cancel="importUpload.open = false"
        >
            <el-upload
                ref="importUploadRef"
                @cancel="importUpload.open = false">
      <el-upload ref="importUploadRef"
                :limit="1"
                accept=".xlsx,.xls"
                :action="importUpload.url"
@@ -1278,8 +1519,7 @@
                :on-progress="importUpload.onProgress"
                :on-change="importUpload.onChange"
                :auto-upload="false"
                drag
            >
                 drag>
                <i class="el-icon-upload"></i>
                <div class="el-upload__text">
                    将文件拖到此处,或<em>点击上传</em>
@@ -1287,77 +1527,73 @@
                <template #tip>
                    <div class="el-upload__tip">
                        仅支持 xls/xlsx,大小不超过 10MB。
                        <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button>
            <el-button link
                       type="primary"
                       @click="downloadTemplate">下载导入模板</el-button>
                    </div>
                </template>
            </el-upload>
        </FormDialog>
        <!-- 附件列表弹窗 -->
        <FileListDialog
            ref="fileListRef"
    <FileListDialog ref="fileListRef"
            v-model="fileListDialogVisible"
            title="附件列表"
        />
                    title="附件列表" />
        <!-- 发货弹框 -->
        <el-dialog
            v-model="deliveryFormVisible"
    <el-dialog v-model="deliveryFormVisible"
            title="发货信息"
        width="40%"
            @close="closeDeliveryDia"
        >
            <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef">
               @close="closeDeliveryDia">
      <el-form :model="deliveryForm"
               label-width="120px"
               label-position="top"
               :rules="deliveryRules"
               ref="deliveryFormRef">
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="发货类型:" prop="type">
                            <el-select
                                v-model="deliveryForm.type"
            <el-form-item label="发货类型:"
                          prop="type">
              <el-select v-model="deliveryForm.type"
                                placeholder="请选择发货类型"
                                style="width: 100%"
                            >
                                <el-option label="货车" value="货车" />
                                <el-option label="快递" value="快递" />
                         style="width: 100%">
                <el-option label="货车"
                           value="货车" />
                <el-option label="快递"
                           value="快递" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
        <!-- 审批人选择(仿协同审批里的审批人节点选择) -->
        <el-row>
          <el-col :span="24">
            <el-form-item>
              <template #label>
                <span>审批人选择:</span>
                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button>
                <el-button type="primary"
                           @click="addApproverNode"
                           style="margin-left: 8px;">新增节点</el-button>
              </template>
              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
                <div
                  v-for="(node, index) in approverNodes"
                <div v-for="(node, index) in approverNodes"
                  :key="node.id"
                  style="margin-right: 20px; text-align: center; margin-bottom: 10px;"
                >
                     style="margin-right: 20px; text-align: center; margin-bottom: 10px;">
                  <div>
                    <span>审批人</span>
                    →
                  </div>
                  <el-select
                    v-model="node.userId"
                  <el-select v-model="node.userId"
                    placeholder="选择人员"
                    filterable
                    style="width: 140px; margin-bottom: 8px;"
                  >
                    <el-option
                      v-for="user in userList"
                             style="width: 140px; margin-bottom: 8px;">
                    <el-option v-for="user in userListApprove"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                               :label="user.userName"
                               :value="user.userId" />
                  </el-select>
                  <div>
                    <el-button
                      type="danger"
                    <el-button type="danger"
                      @click="removeApproverNode(index)"
                      v-if="approverNodes.length > 1"
                    >删除</el-button>
                               v-if="approverNodes.length > 1">删除</el-button>
                  </div>
                </div>
              </div>
@@ -1367,12 +1603,12 @@
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitDelivery">确认发货</el-button>
          <el-button type="primary"
                     @click="submitDelivery">确认发货</el-button>
                    <el-button @click="closeDeliveryDia">取消</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>
@@ -1384,9 +1620,10 @@
import { ElMessageBox, ElMessage } from "element-plus";
import { ArrowDown } from "@element-plus/icons-vue";
import useUserStore from "@/store/modules/user";
  import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js";
import { userListNoPage } from "@/api/system/user.js";
import FileListDialog from '@/components/Dialog/FileListDialog.vue';
import FormDialog from '@/components/Dialog/FormDialog.vue';
  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
  import FormDialog from "@/components/Dialog/FormDialog.vue";
import OtherAmountMaintenanceButton from "./components/OtherAmountMaintenanceButton.vue";
import ProcessFlowMaintenanceButton from "./components/ProcessFlowMaintenanceButton.vue";
import ProcessFlowConfigSelectDialog from "./components/ProcessFlowConfigSelectDialog.vue";
@@ -1428,6 +1665,7 @@
const selectedRows = ref([]);
const productSelectedRows = ref([]);
const userList = ref([]);
  const userListApprove = ref([]);
const customerOption = ref([]);
const productOptions = ref([]);
const modelOptions = ref([]);
@@ -1535,39 +1773,67 @@
// 产品行内编辑:只允许同时编辑一行
const editingProductRow = ref(null);
const ensureProductRowDefaults = (row) => {
  const ensureProductRowDefaults = row => {
    if (!row || typeof row !== "object") return;
    if (!Array.isArray(row.salesProductProcessList)) row.salesProductProcessList = [];
    if (row.__otherAmountPopoverVisible === undefined || row.__otherAmountPopoverVisible === null) row.__otherAmountPopoverVisible = false;
    if (row.__inlineOtherAmountAdding === undefined || row.__inlineOtherAmountAdding === null) row.__inlineOtherAmountAdding = false;
    if (row.__inlineOtherAmountAddId === undefined) row.__inlineOtherAmountAddId = null;
    if (!Array.isArray(row.salesProductProcessList))
      row.salesProductProcessList = [];
    if (
      row.__otherAmountPopoverVisible === undefined ||
      row.__otherAmountPopoverVisible === null
    )
      row.__otherAmountPopoverVisible = false;
    if (
      row.__inlineOtherAmountAdding === undefined ||
      row.__inlineOtherAmountAdding === null
    )
      row.__inlineOtherAmountAdding = false;
    if (row.__inlineOtherAmountAddId === undefined)
      row.__inlineOtherAmountAddId = null;
    if (row.width === undefined || row.width === null) row.width = 0;
    if (row.height === undefined || row.height === null) row.height = 0;
    if (row.perimeter === undefined || row.perimeter === null) row.perimeter = 0;
    if (row.actualPieceArea === undefined || row.actualPieceArea === null) row.actualPieceArea = 0;
    if (row.actualTotalArea === undefined || row.actualTotalArea === null) row.actualTotalArea = 0;
    if (row.settlePieceArea === undefined || row.settlePieceArea === null) row.settlePieceArea = 0;
    if (row.settleTotalArea === undefined || row.settleTotalArea === null) row.settleTotalArea = 0;
    if (row.processRequirement === undefined || row.processRequirement === null) row.processRequirement = "";
    if (row.actualPieceArea === undefined || row.actualPieceArea === null)
      row.actualPieceArea = 0;
    if (row.actualTotalArea === undefined || row.actualTotalArea === null)
      row.actualTotalArea = 0;
    if (row.settlePieceArea === undefined || row.settlePieceArea === null)
      row.settlePieceArea = 0;
    if (row.settleTotalArea === undefined || row.settleTotalArea === null)
      row.settleTotalArea = 0;
    if (row.processRequirement === undefined || row.processRequirement === null)
      row.processRequirement = "";
    if (row.remark === undefined || row.remark === null) row.remark = "";
    if (row.floorCode === undefined || row.floorCode === null) row.floorCode = "";
    if (row.invoiceType === undefined || row.invoiceType === null) row.invoiceType = "";
    if (row.invoiceType === undefined || row.invoiceType === null)
      row.invoiceType = "";
    if (row.taxRate === undefined || row.taxRate === null) row.taxRate = "";
    if (row.quantity === undefined || row.quantity === null) row.quantity = 0;
    if (row.taxInclusiveUnitPrice === undefined || row.taxInclusiveUnitPrice === null) row.taxInclusiveUnitPrice = 0;
    if (row.taxInclusiveTotalPrice === undefined || row.taxInclusiveTotalPrice === null) row.taxInclusiveTotalPrice = 0;
    if (row.taxExclusiveTotalPrice === undefined || row.taxExclusiveTotalPrice === null) row.taxExclusiveTotalPrice = 0;
    if (
      row.taxInclusiveUnitPrice === undefined ||
      row.taxInclusiveUnitPrice === null
    )
      row.taxInclusiveUnitPrice = 0;
    if (
      row.taxInclusiveTotalPrice === undefined ||
      row.taxInclusiveTotalPrice === null
    )
      row.taxInclusiveTotalPrice = 0;
    if (
      row.taxExclusiveTotalPrice === undefined ||
      row.taxExclusiveTotalPrice === null
    )
      row.taxExclusiveTotalPrice = 0;
};
const stopOtherEditingRows = () => {
    (productData.value || []).forEach((r) => {
    (productData.value || []).forEach(r => {
        if (r && r.__editing) r.__editing = false;
    });
    editingProductRow.value = null;
};
const hasEditingProductRow = () => {
    return (productData.value || []).some((r) => r && r.__editing);
    return (productData.value || []).some(r => r && r.__editing);
};
const addProductInline = async () => {
@@ -1626,23 +1892,31 @@
    await fetchOtherAmountSelectOptions(true);
    ensureProductRowDefaults(row);
    // 产品大类 tree-select 回显:名称 -> id
    row.__productCategoryId = findNodeIdByLabel(productOptions.value, row.productCategory);
    row.__productCategoryId = findNodeIdByLabel(
      productOptions.value,
      row.productCategory
    );
    // 兼容后端字段命名(保持原逻辑)
    row.actualPieceArea = row?.actualPieceArea ?? row?.actual_piece_area ?? 0;
    row.actualTotalArea = row?.actualTotalArea ?? row?.actual_total_area ?? 0;
    row.settlePieceArea = row?.settlePieceArea ?? row?.settle_piece_area ?? 0;
    row.settleTotalArea = row?.settleTotalArea ?? row?.settle_total_area ?? 0;
    row.processRequirement = row?.processRequirement ?? row?.process_requirement ?? "";
    row.processRequirement =
      row?.processRequirement ?? row?.process_requirement ?? "";
    row.remark = row?.remark ?? row?.remarks ?? "";
    row.floorCode = row?.floorCode ?? row?.floor_code ?? "";
    row.processFlowConfigId = row?.processFlowConfigId ?? row?.process_flow_config_id ?? null;
    row.perimeter = row?.perimeter ?? row?.heavyBoxPerimeter ?? row?.heavyboxPerimeter ?? 0;
    row.processFlowConfigId =
      row?.processFlowConfigId ?? row?.process_flow_config_id ?? null;
    row.perimeter =
      row?.perimeter ?? row?.heavyBoxPerimeter ?? row?.heavyboxPerimeter ?? 0;
    row.thickness = row?.thickness;
    row.salesProductProcessList = normalizeOtherAmountsFromRow(row);
    mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
    row.salesProductProcessList = fillOtherAmountProcessName(row.salesProductProcessList);
    row.salesProductProcessList = fillOtherAmountProcessName(
      row.salesProductProcessList
    );
    // 备份用于取消
    row.__backup = JSON.parse(JSON.stringify(row));
@@ -1652,12 +1926,17 @@
    // 根据产品大类名称反查 tree 节点 id,并加载规格型号列表
    try {
        const options = productOptions.value && productOptions.value.length > 0 ? productOptions.value : await getProductOptions();
      const options =
        productOptions.value && productOptions.value.length > 0
          ? productOptions.value
          : await getProductOptions();
        const categoryId = findNodeIdByLabel(options, row.productCategory);
        if (categoryId) {
            const models = await modelList({ id: categoryId });
            modelOptions.value = models || [];
            const currentModel = (modelOptions.value || []).find((m) => m.model === row.specificationModel);
        const currentModel = (modelOptions.value || []).find(
          m => m.model === row.specificationModel
        );
            if (currentModel) row.productModelId = currentModel.id;
        }
    } catch (e) {
@@ -1669,7 +1948,7 @@
    recalcAreaFromWidthHeight();
};
const validateInlineProductRow = (row) => {
  const validateInlineProductRow = row => {
    if (!row) return false;
    if (!row.productCategory) {
        proxy.$modal.msgWarning("请选择产品大类");
@@ -1696,7 +1975,11 @@
    if (!validateInlineProductRow(row)) return;
    // 厚度精度处理
    if (row.thickness !== null && row.thickness !== undefined && row.thickness !== "") {
    if (
      row.thickness !== null &&
      row.thickness !== undefined &&
      row.thickness !== ""
    ) {
        row.thickness = Number(Number(row.thickness).toFixed(15));
    }
@@ -1707,17 +1990,23 @@
    row.quantity = Number(row.quantity ?? 0) || 0;
    // 规范化其他金额提交结构
    row.salesProductProcessList = (Array.isArray(row.salesProductProcessList) ? row.salesProductProcessList : [])
        .map((it) => ({
    row.salesProductProcessList = (
      Array.isArray(row.salesProductProcessList)
        ? row.salesProductProcessList
        : []
    )
      .map(it => ({
            id: it?.id,
            processName: it?.processName ?? "",
            unitPrice: Number(it?.unitPrice ?? 0) || 0,
            quantity: Number(it?.quantity ?? 0) || 0,
        }))
        .filter((it) => it.id !== null && it.id !== undefined && it.id !== "");
      .filter(it => it.id !== null && it.id !== undefined && it.id !== "");
    // 规格型号:根据 productModelId 回填名称
    const model = (modelOptions.value || []).find((m) => String(m.id) === String(row.productModelId));
    const model = (modelOptions.value || []).find(
      m => String(m.id) === String(row.productModelId)
    );
    if (model?.model) row.specificationModel = model.model;
    if (operationType.value === "edit") {
@@ -1730,9 +2019,11 @@
        delete payload.__tempKey;
        await addOrUpdateSalesLedgerProduct(payload);
        proxy.$modal.msgSuccess("提交成功");
        await getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
      await getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
        res => {
            productData.value = res.productData;
        });
        }
      );
    } else {
        // 新增台账:仅在本地 productData 生效,最终随台账一起提交
        row.__isNew = false;
@@ -1751,7 +2042,7 @@
        const restored = JSON.parse(JSON.stringify(row.__backup));
        // 保留 id 与状态字段
        const keepId = row.id;
        Object.keys(row).forEach((k) => delete row[k]);
      Object.keys(row).forEach(k => delete row[k]);
        Object.assign(row, restored);
        row.id = keepId;
        row.__editing = false;
@@ -1760,7 +2051,7 @@
    stopOtherEditingRows();
};
const openOtherAmountInline = async (row) => {
  const openOtherAmountInline = async row => {
    if (!row) return;
    if (operationType.value === "view") return;
    if (isProductShipped(row)) {
@@ -1772,7 +2063,9 @@
    otherAmountAddTargetRow.value = row;
    await fetchOtherAmountSelectOptions(true);
    mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
    row.salesProductProcessList = fillOtherAmountProcessName(row.salesProductProcessList);
    row.salesProductProcessList = fillOtherAmountProcessName(
      row.salesProductProcessList
    );
    // 只做数据准备与打开浮层(新增由浮层内按钮触发)
    row.__otherAmountPopoverVisible = true;
};
@@ -1780,7 +2073,7 @@
const keepOtherAmountPopoverOpenKey = ref(null);
const keepOtherAmountPopoverOpenUntil = ref(0);
const getOtherAmountRowKey = (row) => String(row?.__tempKey ?? row?.id ?? "");
  const getOtherAmountRowKey = row => String(row?.__tempKey ?? row?.id ?? "");
const lockOtherAmountPopoverOpen = (row, durationMs = 1200) => {
    const key = getOtherAmountRowKey(row);
@@ -1808,7 +2101,7 @@
    row.__otherAmountPopoverVisible = shouldKeepOpen;
};
const startAddOtherAmountForRow = async (row) => {
  const startAddOtherAmountForRow = async row => {
    if (!row) return;
    if (operationType.value === "view") return;
    if (isProductShipped(row)) {
@@ -1819,22 +2112,27 @@
    productForm.value = row;
    await fetchOtherAmountSelectOptions(true);
    mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
    row.salesProductProcessList = fillOtherAmountProcessName(row.salesProductProcessList);
    row.salesProductProcessList = fillOtherAmountProcessName(
      row.salesProductProcessList
    );
    row.__inlineOtherAmountAddId = null;
    row.__inlineOtherAmountAdding = true;
    row.__otherAmountPopoverVisible = true;
};
const confirmAddOtherAmountForRow = (row) => {
  const confirmAddOtherAmountForRow = row => {
    if (!row) return;
    ensureProductRowDefaults(row);
    productForm.value = row;
    const selectedId = row.__inlineOtherAmountAddId;
    if (selectedId === null || selectedId === undefined || selectedId === "") return;
    const opt = otherAmountSelectOptions.value.find((o) => String(o.id) === String(selectedId));
    if (selectedId === null || selectedId === undefined || selectedId === "")
      return;
    const opt = otherAmountSelectOptions.value.find(
      o => String(o.id) === String(selectedId)
    );
    if (!opt) return;
    const exists = (row.salesProductProcessList ?? []).some(
        (it) => String(it?.id) === String(opt.id)
      it => String(it?.id) === String(opt.id)
    );
    if (exists) {
        proxy.$modal.msgWarning("该其他金额项目已添加");
@@ -1860,7 +2158,7 @@
    removeOtherAmountAt(index);
};
const handleOtherAmountQuantityChange = (row) => {
  const handleOtherAmountQuantityChange = row => {
    if (!row) return;
    productForm.value = row;
    calculateFromUnitPrice(true);
@@ -1882,7 +2180,7 @@
    getProductModel(val);
};
const handleInlineSizeChange = (row) => {
  const handleInlineSizeChange = row => {
    if (!row) return;
    productForm.value = row;
    recalcPerimeterFromWidthHeight();
@@ -1890,27 +2188,27 @@
    recalcAreaTotals();
};
const handleInlineUnitPriceChange = (row) => {
  const handleInlineUnitPriceChange = row => {
    if (!row) return;
    productForm.value = row;
    calculateFromUnitPrice();
    recalcAreaTotals();
};
const handleInlineQuantityChange = (row) => {
  const handleInlineQuantityChange = row => {
    if (!row) return;
    productForm.value = row;
    calculateFromQuantity();
    recalcAreaTotals();
};
const handleInlineTaxRateChange = (row) => {
  const handleInlineTaxRateChange = row => {
    if (!row) return;
    productForm.value = row;
    calculateFromTaxRate();
};
const handleInlineSettleAreaChange = (row) => {
  const handleInlineSettleAreaChange = row => {
    if (!row) return;
    productForm.value = row;
    recalcAreaTotals();
@@ -1946,9 +2244,7 @@
    type: "货车", // 货车, 快递
  },
  deliveryRules: {
    type: [
      { required: true, message: "请选择发货类型", trigger: "change" }
    ]
      type: [{ required: true, message: "请选择发货类型", trigger: "change" }],
  },
});
const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
@@ -1969,7 +2265,7 @@
        const res = await salesLedgerProductProcessList(params);
        const records = res?.records ?? res?.data?.records ?? [];
        otherAmountSelectOptions.value = records.map((item) => ({
      otherAmountSelectOptions.value = records.map(item => ({
            id: item.id,
            processName: item.processName ?? "",
            unitPrice: item.unitPrice ?? 0,
@@ -1979,7 +2275,7 @@
    }
};
const normalizeOtherAmountsFromRow = (row) => {
  const normalizeOtherAmountsFromRow = row => {
    if (!row) return [];
    const raw =
        row.other_amounts ??
@@ -1995,7 +2291,7 @@
    // 情况1:后端直接返回 [{id, processName}...]
    if (typeof raw[0] === "object") {
        return raw
            .map((it) => {
        .map(it => {
                const id = it?.id ?? it?.processId ?? it?.otherAmountId ?? null;
                const quantity =
                    Number(
@@ -2011,23 +2307,25 @@
                    quantity,
                };
            })
            .filter((it) => it.id !== null && it.id !== undefined && it.id !== "");
        .filter(it => it.id !== null && it.id !== undefined && it.id !== "");
    }
    // 情况2:后端只返回 ids: [1,2,3]
    return raw
        .map((id) => ({
      .map(id => ({
            id,
            processName: "",
            quantity: 0,
        }))
        .filter((it) => it.id !== null && it.id !== undefined && it.id !== "");
      .filter(it => it.id !== null && it.id !== undefined && it.id !== "");
};
const mergeOtherAmountOptionsBySelection = (selected) => {
  const mergeOtherAmountOptionsBySelection = selected => {
    const list = Array.isArray(selected) ? selected : [];
    for (const s of list) {
        const exists = otherAmountSelectOptions.value.some((o) => String(o.id) === String(s.id));
      const exists = otherAmountSelectOptions.value.some(
        o => String(o.id) === String(s.id)
      );
        if (!exists) {
            otherAmountSelectOptions.value.push({
                id: s.id,
@@ -2037,10 +2335,12 @@
    }
};
const fillOtherAmountProcessName = (selected) => {
  const fillOtherAmountProcessName = selected => {
    const list = Array.isArray(selected) ? selected : [];
    return list.map((s) => {
        const opt = otherAmountSelectOptions.value.find((o) => String(o.id) === String(s.id));
    return list.map(s => {
      const opt = otherAmountSelectOptions.value.find(
        o => String(o.id) === String(s.id)
      );
        return {
            id: s.id,
            processName: opt?.processName ?? s.processName ?? "",
@@ -2089,14 +2389,17 @@
    keepOtherAmountPopoverOpenUntil.value = 0;
};
const handleOtherAmountSelected = (id) => {
  const handleOtherAmountSelected = id => {
    const selectedId = id ?? otherAmountAddId.value;
    if (selectedId === null || selectedId === undefined || selectedId === "") return;
    const opt = otherAmountSelectOptions.value.find((o) => String(o.id) === String(selectedId));
    if (selectedId === null || selectedId === undefined || selectedId === "")
      return;
    const opt = otherAmountSelectOptions.value.find(
      o => String(o.id) === String(selectedId)
    );
    if (!opt) return;
    const exists = (productForm.value?.salesProductProcessList ?? []).some(
        (it) => String(it?.id) === String(opt.id)
      it => String(it?.id) === String(opt.id)
    );
    if (exists) {
        proxy.$modal.msgWarning("该其他金额项目已添加");
@@ -2119,7 +2422,7 @@
        const rowKey = otherAmountAddTargetRowKey.value;
        if (rowKey) {
            const matchedRow = (productData.value || []).find(
                (it) => String(it?.__tempKey ?? it?.id ?? "") === rowKey
          it => String(it?.__tempKey ?? it?.id ?? "") === rowKey
            );
            if (matchedRow) targetRow = matchedRow;
        }
@@ -2141,7 +2444,7 @@
    handleOtherAmountSelected(otherAmountAddId.value);
};
const removeOtherAmountAt = (index) => {
  const removeOtherAmountAt = index => {
    if (operationType.value === "view") return;
    if (!Array.isArray(productForm.value?.salesProductProcessList)) return;
    productForm.value.salesProductProcessList.splice(index, 1);
@@ -2154,7 +2457,7 @@
const addApproverNode = () => {
  approverNodes.value.push({ id: nextApproverId++, userId: null });
};
const removeApproverNode = (index) => {
  const removeApproverNode = index => {
  approverNodes.value.splice(index, 1);
};
@@ -2166,8 +2469,8 @@
    url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
    headers: { Authorization: "Bearer " + getToken() },
    isUploading: false,
    beforeUpload: (file) => {
        const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
    beforeUpload: file => {
      const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
        const isLt10M = file.size / 1024 / 1024 < 10;
        if (!isExcel) {
            proxy.$modal.msgError("上传文件只能是 xlsx/xls 格式!");
@@ -2180,13 +2483,13 @@
        return true;
    },
    onChange: (file, fileList) => {
        console.log('文件状态改变', file, fileList);
      console.log("文件状态改变", file, fileList);
    },
    onProgress: (event, file, fileList) => {
        console.log('上传中...', event.percent);
      console.log("上传中...", event.percent);
    },
    onSuccess: (response, file, fileList) => {
        console.log('上传成功', response, file, fileList);
      console.log("上传成功", response, file, fileList);
        importUpload.isUploading = false;
        if (response.code === 200) {
            proxy.$modal.msgSuccess("导入成功");
@@ -2200,13 +2503,13 @@
        }
    },
    onError: (error, file, fileList) => {
        console.error('上传失败', error, file, fileList);
      console.error("上传失败", error, file, fileList);
        importUpload.isUploading = false;
        proxy.$modal.msgError("导入失败,请重试");
    },
});
const changeDaterange = (value) => {
  const changeDaterange = value => {
    if (value) {
        searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
        searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
@@ -2228,7 +2531,7 @@
    expandedRowKeys.value = [];
    getList();
};
const paginationChange = (obj) => {
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
@@ -2242,12 +2545,13 @@
    delete params.entryDate;
    // 查询客户名称与新增保持一致:先选 customerId,再映射为 customerName 查询
    const selectedCustomer = (customerOption.value || []).find(
        (item) => String(item?.id ?? "") === String(params.customerId ?? "")
      item => String(item?.id ?? "") === String(params.customerId ?? "")
    );
    if (selectedCustomer?.customerName) {
        params.customerName = String(selectedCustomer.customerName).trim();
    } else {
        const cn = params.customerName != null ? String(params.customerName).trim() : "";
      const cn =
        params.customerName != null ? String(params.customerName).trim() : "";
        if (cn) {
            params.customerName = cn;
        } else {
@@ -2256,10 +2560,10 @@
    }
    delete params.customerId;
    return ledgerListPage(params)
        .then((res) => {
      .then(res => {
            tableLoading.value = false;
            tableData.value = res.records;
            tableData.value.map((item) => {
        tableData.value.map(item => {
                item.children = [];
            });
            total.value = res.total;
@@ -2308,7 +2612,7 @@
};
// 打开“工艺路线配置”选择弹窗(必须显式选择)
const openProcessFlowSelect = async (ledgerRow) => {
  const openProcessFlowSelect = async ledgerRow => {
    if (!ledgerRow) return;
    if (!ledgerRow.isEdit) return;
@@ -2320,16 +2624,9 @@
    try {
        const res = await getSaleProcessBindInfo(ledgerRow.id);
        const info = res?.data ?? res ?? {};
        const boundId =
            info?.processRouteId ??
            info?.routeId ??
            info?.id ??
            null;
      const boundId = info?.processRouteId ?? info?.routeId ?? info?.id ?? null;
        const boundName =
            info?.processRouteName ??
            info?.routeName ??
            info?.name ??
            "";
        info?.processRouteName ?? info?.routeName ?? info?.name ?? "";
        processFlowSelectBoundRouteId.value = boundId;
        processFlowSelectBoundRouteName.value = boundName;
        processFlowSelectDefaultRouteId.value = boundId;
@@ -2344,7 +2641,7 @@
};
// 绑定工艺路线到当前台账数据
const handleProcessFlowSelectConfirm = async (routeId) => {
  const handleProcessFlowSelectConfirm = async routeId => {
    const ledgerRow = processFlowSelectLedgerRow.value;
    if (!ledgerRow?.id) return;
@@ -2352,7 +2649,12 @@
    if (!finalRouteId) return;
    const oldRouteId = processFlowSelectBoundRouteId.value;
    if (oldRouteId !== null && oldRouteId !== undefined && oldRouteId !== "" && String(oldRouteId) !== String(finalRouteId)) {
    if (
      oldRouteId !== null &&
      oldRouteId !== undefined &&
      oldRouteId !== "" &&
      String(oldRouteId) !== String(finalRouteId)
    ) {
        try {
            await ElMessageBox.confirm(
                "该订单已绑定工艺路线,是否确定更换?",
@@ -2389,7 +2691,7 @@
// 获取产品大类tree数据
const getProductOptions = () => {
    // 返回 Promise,便于在编辑产品时等待加载完成
    return productTreeList().then((res) => {
    return productTreeList().then(res => {
        productOptions.value = convertIdToValue(res);
        return productOptions.value;
    });
@@ -2398,7 +2700,7 @@
    return parseFloat(cellValue).toFixed(2);
};
// 获取tree子数据
const getModels = (value) => {
  const getModels = value => {
    // 产品大类变化时,重置规格型号与厚度,避免旧值残留
    productForm.value.productModelId = null;
    productForm.value.specificationModel = "";
@@ -2411,12 +2713,12 @@
    }
    productForm.value.productCategory = findNodeById(productOptions.value, value);
    modelList({ id: value }).then((res) => {
    modelList({ id: value }).then(res => {
        modelOptions.value = res || [];
    });
};
const getProductModel = (value) => {
    const index = modelOptions.value.findIndex((item) => item.id === value);
  const getProductModel = value => {
    const index = modelOptions.value.findIndex(item => item.id === value);
    if (index !== -1) {
        productForm.value.specificationModel = modelOptions.value[index].model;
        const selectedModel = modelOptions.value[index];
@@ -2426,7 +2728,9 @@
            selectedModel?.thick ??
            null;
        productForm.value.thickness =
            modelThickness === null || modelThickness === undefined || modelThickness === ""
        modelThickness === null ||
        modelThickness === undefined ||
        modelThickness === ""
                ? null
                : Number(modelThickness);
    } else {
@@ -2436,7 +2740,9 @@
};
const filterProductCategoryNode = (value, data) => {
    if (!value) return true;
    return String(data?.label || "").toLowerCase().includes(String(value).toLowerCase());
    return String(data?.label || "")
      .toLowerCase()
      .includes(String(value).toLowerCase());
};
const findNodeById = (nodes, productId) => {
    for (let i = 0; i < nodes.length; i++) {
@@ -2453,7 +2759,7 @@
    return null; // 没有找到节点,返回null
};
function convertIdToValue(data, level = 0) {
    return data.map((item) => {
    return data.map(item => {
        const { id, children, ...rest } = item;
        const hasChildren = Array.isArray(children) && children.length > 0;
        const newItem = {
@@ -2483,12 +2789,12 @@
    return null;
}
// 表格选择数据
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
    // 过滤掉子数据
    selectedRows.value = selection.filter((item) => item.children !== undefined);
    selectedRows.value = selection.filter(item => item.children !== undefined);
    console.log("selection", selectedRows.value);
};
const productSelected = (selectedRows) => {
  const productSelected = selectedRows => {
    productSelectedRows.value = selectedRows;
};
const expandedRowKeys = ref([]);
@@ -2497,8 +2803,8 @@
    if (expandedRows.length > 0) {
        expandedRowKeys.value = [];
        try {
            productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
                const index = tableData.value.findIndex((item) => item.id === row.id);
        productList({ salesLedgerId: row.id, type: 1 }).then(res => {
          const index = tableData.value.findIndex(item => item.id === row.id);
                if (index > -1) {
                    tableData.value[index].children = res.data;
                }
@@ -2514,22 +2820,22 @@
// 添加表行类名方法
const tableRowClassName = ({ row }) => {
  if (!row.deliveryDate) return '';
  if (row.isFh) return '';
    if (!row.deliveryDate) return "";
    if (row.isFh) return "";
  const diff = row.deliveryDaysDiff;
  if (diff === 15) {
    return 'yellow';
      return "yellow";
  } else if (diff === 10) {
    return 'pink';
      return "pink";
  } else if (diff === 2) {
    return 'purple';
      return "purple";
  } else if (diff < 2) {
    return 'red';
      return "red";
  }
};
// 主表合计方法
const summarizeMainTable = (param) => {
  const summarizeMainTable = param => {
    return proxy.summarizeTable(param, [
        "contractAmount",
        "taxInclusiveTotalPrice",
@@ -2537,7 +2843,7 @@
    ]);
};
// 子表合计方法
const summarizeChildrenTable = (param) => {
  const summarizeChildrenTable = param => {
    return proxy.summarizeTable(param, [
        "taxInclusiveUnitPrice",
        "taxInclusiveTotalPrice",
@@ -2552,7 +2858,7 @@
    selectedQuotation.value = null;
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    customerList().then((res) => {
    customerList().then(res => {
        customerOption.value = res;
    });
    form.value.entryPerson = userStore.id;
@@ -2564,11 +2870,12 @@
        form.value.customerRemarks = "";
    } else {
        currentId.value = row.id;
        getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
      getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => {
            form.value = { ...res };
            form.value.entryPerson = Number(res.entryPerson);
            // 字段名兼容:后端可能返回 customer_remarks
            form.value.customerRemarks = res?.customerRemarks ?? res?.customer_remarks ?? "";
        form.value.customerRemarks =
          res?.customerRemarks ?? res?.customer_remarks ?? "";
            productData.value = form.value.productData;
            fileList.value = form.value.salesLedgerFiles;
        });
@@ -2581,7 +2888,9 @@
    // });
    form.value.entryDate = getCurrentDate(); // 设置默认录入日期为当前日期
    if (type === "add") {
        form.value.deliveryDate = dayjs(form.value.entryDate).add(7, "day").format("YYYY-MM-DD");
      form.value.deliveryDate = dayjs(form.value.entryDate)
        .add(7, "day")
        .format("YYYY-MM-DD");
    }
    dialogFormVisible.value = true;
};
@@ -2630,14 +2939,14 @@
};
// 报价单弹框分页切换
const quotationPaginationChange = (obj) => {
  const quotationPaginationChange = obj => {
    quotationPage.current = obj.page;
    quotationPage.size = obj.limit;
    fetchQuotationList();
};
// 选中报价单后回填到台账表单
const applyQuotation = (row) => {
  const applyQuotation = row => {
    if (!row) return;
    selectedQuotation.value = row;
    
@@ -2646,9 +2955,13 @@
    
    // 客户名称 -> customerId
    const qCustomerName = String(row.customer || "").trim();
    const customer = (customerOption.value || []).find((c) => {
    const customer = (customerOption.value || []).find(c => {
        const name = String(c.customerName || "").trim();
        return name === qCustomerName || name.includes(qCustomerName) || qCustomerName.includes(name);
      return (
        name === qCustomerName ||
        name.includes(qCustomerName) ||
        qCustomerName.includes(name)
      );
    });
    if (customer?.id) {
        form.value.customerId = customer.id;
@@ -2659,13 +2972,20 @@
    
    // 产品信息映射:报价 products -> 台账 productData
    const products = Array.isArray(row.products) ? row.products : [];
    productData.value = products.map((p) => {
    productData.value = products.map(p => {
        const quantity = Number(p.quantity ?? 0) || 0;
        const unitPrice = Number(p.unitPrice ?? 0) || 0;
        const settlePieceArea = Number(p.settlePieceArea ?? 0) || 1;
        const taxRate = "13"; // 默认 13%,便于直接提交(如需可在产品中自行修改)
        const taxInclusiveTotalPrice = (unitPrice * settlePieceArea * quantity).toFixed(2);
        const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate);
      const taxInclusiveTotalPrice = (
        unitPrice *
        settlePieceArea *
        quantity
      ).toFixed(2);
      const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
        taxInclusiveTotalPrice,
        taxRate
      );
        return {
            // 台账字段
            productCategory: p.product || p.productName || "",
@@ -2727,26 +3047,36 @@
    if (operationType.value === "edit") {
        let ids = [];
        ids.push(file.id);
        delLedgerFile(ids).then((res) => {
      delLedgerFile(ids).then(res => {
            proxy.$modal.msgSuccess("删除成功");
        });
    }
}
// 提交表单
const submitForm = () => {
    proxy.$refs["formRef"].validate((valid) => {
    proxy.$refs["formRef"].validate(valid => {
        if (valid) {
            console.log('productData.value--', productData.value)
        console.log("productData.value--", productData.value);
            // 行内编辑未保存时不允许提交,避免脏数据/临时字段进入后端
            const hasEditingRow = (productData.value || []).some((r) => r && r.__editing);
        const hasEditingRow = (productData.value || []).some(
          r => r && r.__editing
        );
            if (hasEditingRow) {
                proxy.$modal.msgWarning("产品信息存在未保存的编辑行,请先保存或取消");
                return;
            }
            if (productData.value !== null && productData.value.length > 0) {
                const cleanedProducts = (productData.value || []).map((p) => {
          const cleanedProducts = (productData.value || []).map(p => {
                    if (!p || typeof p !== "object") return p;
                    const { __editing, __isNew, __backup, __productCategoryId, __tempKey, __otherAmountPopoverVisible, ...rest } = p;
            const {
              __editing,
              __isNew,
              __backup,
              __productCategoryId,
              __tempKey,
              __otherAmountPopoverVisible,
              ...rest
            } = p;
                    rest.taxRate = Number(rest.taxRate ?? 0) || 0;
                    rest.quantity = Number(rest.quantity ?? 0) || 0;
                    return rest;
@@ -2758,13 +3088,13 @@
            }
            let tempFileIds = [];
            if (fileList.value !== null && fileList.value.length > 0) {
                tempFileIds = fileList.value.map((item) => item.tempId);
          tempFileIds = fileList.value.map(item => item.tempId);
            }
            form.value.tempFileIds = tempFileIds;
            form.value.type = 1;
            const submitPayload = { ...form.value };
            delete submitPayload.paymentMethod;
            addOrUpdateSalesLedger(submitPayload).then((res) => {
        addOrUpdateSalesLedger(submitPayload).then(res => {
                proxy.$modal.msgSuccess("提交成功");
                closeDia();
                getList();
@@ -2798,10 +3128,14 @@
        productForm.value = { ...row };
        // 字段命名兼容:优先驼峰(如 actualPieceArea),兼容后端可能返回下划线(如 actual_piece_area)
        productForm.value.actualPieceArea = row?.actualPieceArea ?? row?.actual_piece_area ?? 0;
        productForm.value.actualTotalArea = row?.actualTotalArea ?? row?.actual_total_area ?? 0;
        productForm.value.settlePieceArea = row?.settlePieceArea ?? row?.settle_piece_area ?? 0;
        productForm.value.settleTotalArea = row?.settleTotalArea ?? row?.settle_total_area ?? 0;
      productForm.value.actualPieceArea =
        row?.actualPieceArea ?? row?.actual_piece_area ?? 0;
      productForm.value.actualTotalArea =
        row?.actualTotalArea ?? row?.actual_total_area ?? 0;
      productForm.value.settlePieceArea =
        row?.settlePieceArea ?? row?.settle_piece_area ?? 0;
      productForm.value.settleTotalArea =
        row?.settleTotalArea ?? row?.settle_total_area ?? 0;
        // 加工要求/备注兼容:后端可能使用其它命名
        productForm.value.processRequirement =
@@ -2819,20 +3153,25 @@
        // 后端直接返回 thickness
        productForm.value.thickness = row?.thickness;
        productForm.value.salesProductProcessList = normalizeOtherAmountsFromRow(row);
      productForm.value.salesProductProcessList =
        normalizeOtherAmountsFromRow(row);
        productIndex.value = index;
        // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表
        try {
            const options = productOptions.value && productOptions.value.length > 0
        const options =
          productOptions.value && productOptions.value.length > 0
                ? productOptions.value
                : await getProductOptions();
            const categoryId = findNodeIdByLabel(options, productForm.value.productCategory);
        const categoryId = findNodeIdByLabel(
          options,
          productForm.value.productCategory
        );
            if (categoryId) {
                const models = await modelList({ id: categoryId });
                modelOptions.value = models || [];
                // 根据当前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
                const currentModel = (modelOptions.value || []).find(
                    (m) => m.model === productForm.value.specificationModel
            m => m.model === productForm.value.specificationModel
                );
                if (currentModel) {
                    productForm.value.productModelId = currentModel.id;
@@ -2849,10 +3188,14 @@
        // 回显“其他金额”多选:先拉取下拉选项,再补齐 processName
        await fetchOtherAmountSelectOptions(true);
        mergeOtherAmountOptionsBySelection(productForm.value.salesProductProcessList);
        productForm.value.salesProductProcessList = fillOtherAmountProcessName(productForm.value.salesProductProcessList);
      mergeOtherAmountOptionsBySelection(
        productForm.value.salesProductProcessList
      );
      productForm.value.salesProductProcessList = fillOtherAmountProcessName(
        productForm.value.salesProductProcessList
      );
    } else {
        getProductOptions()
      getProductOptions();
        // 新增时下拉选项加载一次即可
        fetchOtherAmountSelectOptions(true);
    }
@@ -2860,11 +3203,16 @@
};
// 提交产品表单
const submitProduct = () => {
    proxy.$refs["productFormRef"].validate((valid) => {
    proxy.$refs["productFormRef"].validate(valid => {
        if (valid) {
            // 厚度保留 15 位小数,避免由于浮点计算/输入导致精度偏差
            if (productForm.value.thickness !== null && productForm.value.thickness !== undefined) {
                productForm.value.thickness = Number(Number(productForm.value.thickness).toFixed(15));
        if (
          productForm.value.thickness !== null &&
          productForm.value.thickness !== undefined
        ) {
          productForm.value.thickness = Number(
            Number(productForm.value.thickness).toFixed(15)
          );
            }
            // 面积/总计字段在提交前兜底计算一次
@@ -2873,17 +3221,18 @@
            productForm.value.taxRate = Number(productForm.value.taxRate ?? 0) || 0;
            productForm.value.quantity = Number(productForm.value.quantity ?? 0) || 0;
            // 其他金额只提交 {id, processName, quantity}(后端字段:salesProductProcessList)
            productForm.value.salesProductProcessList = (Array.isArray(productForm.value.salesProductProcessList)
        productForm.value.salesProductProcessList = (
          Array.isArray(productForm.value.salesProductProcessList)
            ? productForm.value.salesProductProcessList
            : []
        )
            .map((it) => ({
          .map(it => ({
                id: it?.id,
                processName: it?.processName ?? "",
                unitPrice: Number(it?.unitPrice ?? 0) || 0,
                quantity: Number(it?.quantity ?? 0) || 0,
            }))
            .filter((it) => it.id !== null && it.id !== undefined && it.id !== "");
          .filter(it => it.id !== null && it.id !== undefined && it.id !== "");
            if (operationType.value === "edit") {
                submitProductEdit();
@@ -2891,7 +3240,7 @@
                if(productOperationType.value === "add"){
                    productData.value.push({ ...productForm.value });
                }else{
                    productData.value[productIndex.value] = { ...productForm.value }
            productData.value[productIndex.value] = { ...productForm.value };
                }
                closeProductDia();
            }
@@ -2900,11 +3249,11 @@
};
const submitProductEdit = () => {
    productForm.value.salesLedgerId = currentId.value;
    productForm.value.type = 1
    addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
    productForm.value.type = 1;
    addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
        proxy.$modal.msgSuccess("提交成功");
        closeProductDia();
        getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
      getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(res => {
            productData.value = res.productData;
        });
    });
@@ -2917,15 +3266,17 @@
    }
    
    // 检查是否有已发货或审核通过的产品
    const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row));
    const shippedProducts = productSelectedRows.value.filter(row =>
      isProductShipped(row)
    );
    if (shippedProducts.length > 0) {
        proxy.$modal.msgWarning("已发货或审核通过的产品不能删除");
        return;
    }
    
    if (operationType.value === "add") {
        productSelectedRows.value.forEach((selectedRow) => {
            const index = productData.value.findIndex((product) => {
      productSelectedRows.value.forEach(selectedRow => {
        const index = productData.value.findIndex(product => {
                if (!product || !selectedRow) return false;
                // 新增行 id 为空时,用临时 key 定位
                if (product.id != null && selectedRow.id != null) {
@@ -2944,7 +3295,7 @@
    } else {
        let ids = [];
        if (productSelectedRows.value.length > 0) {
            ids = productSelectedRows.value.map((item) => item.id);
        ids = productSelectedRows.value.map(item => item.id);
        }
        ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
            confirmButtonText: "确认",
@@ -2952,11 +3303,11 @@
            type: "warning",
        })
            .then(() => {
                delProduct(ids).then((res) => {
          delProduct(ids).then(res => {
                    proxy.$modal.msgSuccess("删除成功");
                    closeProductDia();
                    getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
                        (res) => {
              res => {
                            productData.value = res.productData;
                        }
                    );
@@ -3009,7 +3360,7 @@
        });
};
/** 判断单个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */
const isProductShipped = (product) => {
  const isProductShipped = product => {
    if (!product) return false;
    const status = String(product.shippingStatus || "").trim();
    // 如果发货状态是"已发货"或"审核通过",则不可编辑和删除
@@ -3017,14 +3368,16 @@
};
/** 判断销售订单下是否存在已发货/发货完成的产品(不可删除) */
const hasShippedProducts = (products) => {
  const hasShippedProducts = products => {
    if (!products || !products.length) return false;
    return products.some((p) => {
    return products.some(p => {
        const status = String(p.shippingStatus || "").trim();
        // 有发货日期或车牌号视为已发货
        if (p.shippingDate || p.shippingCarNumber) return true;
        // 已进行发货、发货完成、已发货 均不可删除
        return status === "已进行发货" || status === "发货完成" || status === "已发货";
      return (
        status === "已进行发货" || status === "发货完成" || status === "已发货"
      );
    });
};
@@ -3034,12 +3387,13 @@
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
    const ids = selectedRows.value.map((item) => item.id);
    const ids = selectedRows.value.map(item => item.id);
    // 检查是否有已进行发货或发货完成的销售订单,若有则不允许删除
    const cannotDeleteNames = [];
    for (const row of selectedRows.value) {
        let products = row.children && row.children.length > 0 ? row.children : null;
      let products =
        row.children && row.children.length > 0 ? row.children : null;
        if (!products) {
            try {
                const res = await productList({ salesLedgerId: row.id, type: 1 });
@@ -3053,7 +3407,9 @@
        }
    }
    if (cannotDeleteNames.length > 0) {
        proxy.$modal.msgWarning("已进行发货或发货完成的销售订单不能删除:" + cannotDeleteNames.join("、"));
      proxy.$modal.msgWarning(
        "已进行发货或发货完成的销售订单不能删除:" + cannotDeleteNames.join("、")
      );
        return;
    }
@@ -3063,7 +3419,7 @@
        type: "warning",
    })
        .then(() => {
            delLedger(ids).then((res) => {
        delLedger(ids).then(res => {
                proxy.$modal.msgSuccess("删除成功");
                getList();
            });
@@ -3073,15 +3429,22 @@
        });
};
const handlePrintCommand = async (command) => {
    if (command !== "finishedProcessCard" && command !== "salesOrder" && command !== "salesDeliveryNote") return;
  const handlePrintCommand = async command => {
    if (
      command !== "finishedProcessCard" &&
      command !== "salesOrder" &&
      command !== "salesDeliveryNote"
    )
      return;
    if (command === "salesDeliveryNote") {
        if (selectedRows.value.length === 0) {
            proxy.$modal.msgWarning("请至少选择一条销售台账数据进行打印");
            return;
        }
        const customerNames = Array.from(
            new Set(selectedRows.value.map((item) => String(item?.customerName ?? "").trim()))
        new Set(
          selectedRows.value.map(item => String(item?.customerName ?? "").trim())
        )
        );
        if (customerNames.length > 1) {
            proxy.$modal.msgWarning("仅支持相同客户名称的销售台账合并发货打印");
@@ -3096,8 +3459,8 @@
    const selectedId = selectedRow?.id;
    if (command === "salesDeliveryNote") {
        const selectedIds = selectedRows.value
            .map((item) => item?.id)
            .filter((id) => id !== null && id !== undefined && id !== "");
        .map(item => item?.id)
        .filter(id => id !== null && id !== undefined && id !== "");
        if (selectedIds.length !== selectedRows.value.length) {
            proxy.$modal.msgWarning("当前选择数据存在缺少ID的记录,无法打印");
            return;
@@ -3201,10 +3564,14 @@
        productForm.value.taxInclusiveUnitPrice * settlePieceArea,
        productForm.value.quantity
    );
    const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
    const otherAmountTotal = (
      productForm.value.salesProductProcessList || []
    ).reduce((total, item) => {
        return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
    }, 0);
    productForm.value.taxInclusiveTotalPrice = (parseFloat(basePrice) + otherAmountTotal).toFixed(2);
    productForm.value.taxInclusiveTotalPrice = (
      parseFloat(basePrice) + otherAmountTotal
    ).toFixed(2);
    if (productForm.value.taxRate) {
        // 不含税总价计算
        productForm.value.taxExclusiveTotalPrice =
@@ -3237,7 +3604,9 @@
    }
    // 周长 = (宽 + 高) * 2,单位从 mm 转为 cm:/10
    productForm.value.perimeter = Number((((width + height) * 2) / 10).toFixed(2));
    productForm.value.perimeter = Number(
      (((width + height) * 2) / 10).toFixed(2)
    );
};
const recalcAreaFromWidthHeight = () => {
@@ -3256,7 +3625,10 @@
            productForm.value.settlePieceArea = 0;
        }
        productForm.value.settleTotalArea = Number(
            ((Number(productForm.value.settlePieceArea ?? 0) || 0) * (Number(productForm.value.quantity ?? 0) || 0)).toFixed(5)
        (
          (Number(productForm.value.settlePieceArea ?? 0) || 0) *
          (Number(productForm.value.quantity ?? 0) || 0)
        ).toFixed(5)
        );
        return;
    }
@@ -3287,7 +3659,9 @@
    isCalculating.value = true;
    
    // 计算含税单价 = (含税总价 - 其他金额总和) / 数量
    const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
    const otherAmountTotal = (
      productForm.value.salesProductProcessList || []
    ).reduce((total, item) => {
        return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
    }, 0);
    const basePrice = totalPrice - otherAmountTotal;
@@ -3313,7 +3687,9 @@
    // }
    if (isCalculating.value) return;
    
    const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice);
    const exclusiveTotalPrice = parseFloat(
      productForm.value.taxExclusiveTotalPrice
    );
    const quantity = parseFloat(productForm.value.quantity);
    const taxRate = parseFloat(productForm.value.taxRate);
    
@@ -3329,7 +3705,9 @@
    productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
    
    // 计算含税单价 = (含税总价 - 其他金额总和) / 数量
    const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
    const otherAmountTotal = (
      productForm.value.salesProductProcessList || []
    ).reduce((total, item) => {
        return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
    }, 0);
    const basePrice = inclusiveTotalPrice - otherAmountTotal;
@@ -3358,10 +3736,14 @@
    // 计算含税总价 = 单价 * 结算面积 * 数量 + 其他金额总和
    const basePrice = unitPrice * settlePieceArea * quantity;
    const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
    const otherAmountTotal = (
      productForm.value.salesProductProcessList || []
    ).reduce((total, item) => {
        return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
    }, 0);
    productForm.value.taxInclusiveTotalPrice = (basePrice + otherAmountTotal).toFixed(2);
    productForm.value.taxInclusiveTotalPrice = (
      basePrice + otherAmountTotal
    ).toFixed(2);
    // 如果有税率,计算不含税总价
    if (productForm.value.taxRate) {
@@ -3395,10 +3777,14 @@
    // 计算含税总价 = 单价 * 结算面积 * 数量 + 其他金额总和
    const basePrice = unitPrice * settlePieceArea * quantity;
    const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
    const otherAmountTotal = (
      productForm.value.salesProductProcessList || []
    ).reduce((total, item) => {
        return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
    }, 0);
    productForm.value.taxInclusiveTotalPrice = (basePrice + otherAmountTotal).toFixed(2);
    productForm.value.taxInclusiveTotalPrice = (
      basePrice + otherAmountTotal
    ).toFixed(2);
    // 如果有税率,计算不含税总价
    if (productForm.value.taxRate) {
@@ -3420,7 +3806,9 @@
    // }
    if (isCalculating.value) return;
    
    const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
    const inclusiveTotalPrice = parseFloat(
      productForm.value.taxInclusiveTotalPrice
    );
    const taxRate = parseFloat(productForm.value.taxRate);
    
    if (!inclusiveTotalPrice || !taxRate) {
@@ -3431,10 +3819,7 @@
    
    // 计算不含税总价
    productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
            inclusiveTotalPrice,
            taxRate
        );
      proxy.calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate);
    
    isCalculating.value = false;
};
@@ -3442,62 +3827,62 @@
 * 获取发货状态文本
 * @param row 行数据
 */
const getShippingStatusText = (row) => {
  const getShippingStatusText = row => {
    // 如果已发货(有发货日期或车牌号),显示"已发货"
    if (row.shippingDate || row.shippingCarNumber) {
        return '已发货';
      return "已发货";
    }
    
    // 获取发货状态字段
    const status = row.shippingStatus;
    
    // 如果状态为空或未定义,默认为"待发货"
    if (status === null || status === undefined || status === '') {
        return '待发货';
    if (status === null || status === undefined || status === "") {
      return "待发货";
    }
    
    // 状态是字符串
    const statusStr = String(status).trim();
    const statusTextMap = {
        '待发货': '待发货',
        '待审核': '待审核',
        '审核中': '审核中',
        '审核拒绝': '审核拒绝',
        '审核通过': '审核通过',
        '已发货': '已发货'
      待发货: "待发货",
      待审核: "待审核",
      审核中: "审核中",
      审核拒绝: "审核拒绝",
      审核通过: "审核通过",
      已发货: "已发货",
    };
    return statusTextMap[statusStr] || '待发货';
    return statusTextMap[statusStr] || "待发货";
};
/**
 * 获取发货状态标签类型(颜色)
 * @param row 行数据
 */
const getShippingStatusType = (row) => {
  const getShippingStatusType = row => {
    // 如果已发货(有发货日期或车牌号),显示绿色
    if (row.shippingDate || row.shippingCarNumber) {
        return 'success';
      return "success";
    }
    
    // 获取发货状态字段
    const status = row.shippingStatus;
    
    // 如果状态为空或未定义,默认为灰色(待发货)
    if (status === null || status === undefined || status === '') {
        return 'info';
    if (status === null || status === undefined || status === "") {
      return "info";
    }
    
    // 状态是字符串
    const statusStr = String(status).trim();
    const typeTextMap = {
        '待发货': 'info',
        '待审核': 'info',
        '审核中': 'warning',
        '审核拒绝': 'danger',
        '审核通过': 'success',
        '已发货': 'success'
      待发货: "info",
      待审核: "info",
      审核中: "warning",
      审核拒绝: "danger",
      审核通过: "success",
      已发货: "success",
    };
    return typeTextMap[statusStr] || 'info';
    return typeTextMap[statusStr] || "info";
};
/**
@@ -3505,8 +3890,7 @@
 * 只有在产品状态是充足,发货状态是待发货和审核拒绝的时候才可以发货
 * @param row 行数据
 */
const canShip = (row) => {
  const canShip = row => {
    // 产品状态必须是充足(approveStatus === 1)
    if (row.approveStatus !== 1) {
        return false;
@@ -3532,8 +3916,8 @@
    }
    
    // 发货状态必须是"待发货"或"审核拒绝"
    const statusStr = shippingStatus ? String(shippingStatus).trim() : '';
    return statusStr === '待发货' || statusStr === '审核拒绝';
    const statusStr = shippingStatus ? String(shippingStatus).trim() : "";
    return statusStr === "待发货" || statusStr === "审核拒绝";
};
const handleBulkDelivery = async () => {
@@ -3543,7 +3927,7 @@
    }
    // 只允许【未发货/审批失败】进入发货流程
    const canDeliveryLedgers = selectedRows.value.filter((r) => {
    const canDeliveryLedgers = selectedRows.value.filter(r => {
        const status = Number(r.deliveryStatus);
        return status === 1 || status === 3;
    });
@@ -3553,7 +3937,9 @@
    }
    // 已发货台账:弹窗提醒,不能再次发货(4 视为已发货)
    const shippedLedgers = selectedRows.value.filter((r) => Number(r.deliveryStatus) === 4);
    const shippedLedgers = selectedRows.value.filter(
      r => Number(r.deliveryStatus) === 4
    );
    if (shippedLedgers.length === selectedRows.value.length) {
        try {
            await ElMessageBox.alert("所选销售台账均已发货,不能再次发货。", "提示", {
@@ -3580,7 +3966,9 @@
        }
    }
    const customerNames = selectedRows.value.map((r) => String(r.customerName || "").trim());
    const customerNames = selectedRows.value.map(r =>
      String(r.customerName || "").trim()
    );
    const uniqueCustomers = Array.from(new Set(customerNames));
    // 客户名称不一致不允许发货
@@ -3607,11 +3995,12 @@
    try {
        const targets = [];
        for (const ledger of selectedRows.value) {
            //如果已经是“审批中(2)”或“已发货(4)”,则跳过,不允许重复操作
            const status = Number(ledger.deliveryStatus);
            if (status === 2 || status === 4) {
                console.warn(`台账编号 ${ledger.salesContractNo} 状态为 ${status},跳过发货`);
          console.warn(
            `台账编号 ${ledger.salesContractNo} 状态为 ${status},跳过发货`
          );
                continue;
            }
@@ -3621,7 +4010,7 @@
                products = res?.data || [];
            } catch (error) {
                products = [];
                console.error('请求发生异常', error);
          console.error("请求发生异常", error);
            }
            for (const product of products) {
                if (!canShip(product)) continue;
@@ -3652,18 +4041,18 @@
 *
 * @param row 下载文件的相关信息对象
 */
const fileListRef = ref(null)
const fileListDialogVisible = ref(false)
const downLoadFile = (row) => {
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
  const fileListRef = ref(null);
  const fileListDialogVisible = ref(false);
  const downLoadFile = row => {
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => {
        if (fileListRef.value) {
            fileListRef.value.open(res.salesLedgerFiles)
        fileListRef.value.open(res.salesLedgerFiles);
        }
    });
}
  };
// 打开发货弹框(单条)
const openDeliveryForm = (row) => {
  const openDeliveryForm = row => {
    // 只允许【未发货/审批失败】发货;已发货/审批中不允许
    const status = Number(row.deliveryStatus);
    if (status !== 1 && status !== 3) {
@@ -3683,7 +4072,7 @@
// 提交发货表单
const submitDelivery = () => {
  proxy.$refs["deliveryFormRef"].validate((valid) => {
    proxy.$refs["deliveryFormRef"].validate(valid => {
    if (valid) {
      // 审批人必填校验(所有节点都要选人)
      const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
@@ -3691,7 +4080,9 @@
        proxy.$modal.msgError("请为所有审批节点选择审批人!");
        return;
      }
      const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
        const approveUserIds = approverNodes.value
          .map(node => node.userId)
          .join(",");
      // 保存当前展开的行ID,以便发货后重新加载子表格数据
      const currentExpandedKeys = [...expandedRowKeys.value];
@@ -3702,7 +4093,9 @@
      }
      // 按台账维度去重,每个 salesLedgerId 只调用一次发货接口
      const uniqueLedgerIds = [...new Set(targets.map((item) => item.salesLedgerId).filter(Boolean))];
        const uniqueLedgerIds = [
          ...new Set(targets.map(item => item.salesLedgerId).filter(Boolean)),
        ];
        const run = async () => {
            for (const salesLedgerId of uniqueLedgerIds) {
@@ -3722,13 +4115,17 @@
          getList().then(() => {
            // 如果之前有展开的行,重新加载这些行的子表格数据
            if (currentExpandedKeys.length > 0) {
              const loadPromises = currentExpandedKeys.map((ledgerId) => {
                return productList({ salesLedgerId: ledgerId, type: 1 }).then((res) => {
                  const index = tableData.value.findIndex((item) => item.id === ledgerId);
                const loadPromises = currentExpandedKeys.map(ledgerId => {
                  return productList({ salesLedgerId: ledgerId, type: 1 }).then(
                    res => {
                      const index = tableData.value.findIndex(
                        item => item.id === ledgerId
                      );
                  if (index > -1) {
                    tableData.value[index].children = res.data;
                  }
                });
                    }
                  );
              });
              Promise.all(loadPromises).then(() => {
                expandedRowKeys.value = currentExpandedKeys;
@@ -3756,12 +4153,15 @@
};
onMounted(() => {
    getList();
    customerList().then((res) => {
    customerList().then(res => {
        customerOption.value = res;
    });
    userListNoPage().then(res => {
        userList.value = res.data;
    })
    });
    approveUserList({ approveType: 7 }).then(res => {
      userListApprove.value = res.data;
    });
    getCurrentFactoryName();
});
</script>
@@ -3772,19 +4172,19 @@
}
::v-deep .yellow {
  background-color: #FAF0DE;
    background-color: #faf0de;
}
::v-deep .pink {
  background-color: #FAE1DE;
    background-color: #fae1de;
}
::v-deep .red {
  background-color: #FAE1DE;
    background-color: #fae1de;
}
::v-deep .purple{
  background-color: #F4DEFA;
    background-color: #f4defa;
}
.other-amount-select {
src/views/salesManagement/salesQuotation/index.vue
@@ -2,22 +2,28 @@
  <div class="app-container">
    <el-card class="box-card">
      <!-- 搜索区域 -->
      <el-row :gutter="20" class="search-row">
      <el-row :gutter="20"
              class="search-row">
        <el-col :span="8">
          <el-input
            v-model="searchForm.quotationNo"
          <el-input v-model="searchForm.quotationNo"
            placeholder="请输入报价单号"
            clearable
            @keyup.enter="handleSearch"
          >
                    @keyup.enter="handleSearch">
            <template #prefix>
              <el-icon><Search /></el-icon>
              <el-icon>
                <Search />
              </el-icon>
            </template>
          </el-input>
        </el-col>
        <el-col :span="8">
          <el-select v-model="searchForm.customer" placeholder="请选择客户" clearable>
                        <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
          <el-select v-model="searchForm.customer"
                     placeholder="请选择客户"
                     clearable>
            <el-option v-for="item in customerOption"
                       :key="item.id"
                       :label="item.customerName"
                       :value="item.customerName">
                            {{
                                item.customerName + "——" + item.taxpayerIdentificationNumber
                            }}
@@ -33,78 +39,123 @@
<!--          </el-select>-->
<!--        </el-col>-->
        <el-col :span="8">
          <el-button type="primary" @click="handleSearch">搜索</el-button>
          <el-button type="primary"
                     @click="handleSearch">搜索</el-button>
          <el-button @click="resetSearch">重置</el-button>
          <el-button style="float: right;" type="primary" @click="handleAdd">
          <el-button style="float: right;"
                     type="primary"
                     @click="handleAdd">
            新增报价
          </el-button>
        </el-col>
      </el-row>
      <!-- 报价列表 -->
      <el-table
        :data="filteredList"
      <el-table :data="filteredList"
        style="width: 100%"
        v-loading="loading"
        border
        stripe
        height="calc(100vh - 22em)"
      >
                <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column prop="quotationNo" label="报价单号" />
        <el-table-column prop="customer" label="客户名称" />
        <el-table-column prop="salesperson" label="业务员" width="100" />
        <el-table-column prop="quotationDate" label="报价日期" width="120" />
        <el-table-column prop="validDate" label="有效期至" width="120" />
        <el-table-column prop="status" label="审批状态" width="120" align="center">
                height="calc(100vh - 22em)">
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column prop="quotationNo"
                         label="报价单号" />
        <el-table-column prop="customer"
                         label="客户名称" />
        <el-table-column prop="salesperson"
                         label="业务员"
                         width="100" />
        <el-table-column prop="quotationDate"
                         label="报价日期"
                         width="120" />
        <el-table-column prop="validDate"
                         label="有效期至"
                         width="120" />
        <el-table-column prop="status"
                         label="审批状态"
                         width="120"
                         align="center">
          <template #default="{ row }">
            <el-tag :type="getStatusType(row.status)" disable-transitions>
            <el-tag :type="getStatusType(row.status)"
                    disable-transitions>
              {{ row.status || '--' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="totalAmount" label="报价金额" width="120">
        <el-table-column prop="totalAmount"
                         label="报价金额"
                         width="120">
          <template #default="scope">
            ¥{{ scope.row.totalAmount.toFixed(2) }}
          </template>
        </el-table-column>
        <el-table-column label="操作" width="200" fixed="right" align="center">
        <el-table-column label="操作"
                         width="200"
                         fixed="right"
                         align="center">
          <template #default="scope">
            <el-button link type="primary" @click="handleEdit(scope.row)" :disabled="!['待审批','拒绝'].includes(scope.row.status)">编辑</el-button>
            <el-button link type="primary" @click="handleView(scope.row)" style="color: #67C23A">查看</el-button>
            <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
            <el-button link
                       type="primary"
                       @click="handleEdit(scope.row)"
                       :disabled="!['待审批','拒绝'].includes(scope.row.status)">编辑</el-button>
            <el-button link
                       type="primary"
                       @click="handleView(scope.row)"
                       style="color: #67C23A">查看</el-button>
            <el-button link
                       type="danger"
                       @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <pagination
        :total="pagination.total"
      <pagination :total="pagination.total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="pagination.currentPage"
        :limit="pagination.pageSize"
        @pagination="handleCurrentChange"
      />
                  @pagination="handleCurrentChange" />
    </el-card>
    <!-- 新增/编辑对话框 -->
    <FormDialog v-model="dialogVisible" :title="dialogTitle" width="85%" :close-on-click-modal="false" @close="dialogVisible = false" @confirm="handleSubmit" @cancel="dialogVisible = false">
    <FormDialog v-model="dialogVisible"
                :title="dialogTitle"
                width="85%"
                :close-on-click-modal="false"
                @close="dialogVisible = false"
                @confirm="handleSubmit"
                @cancel="dialogVisible = false">
      <div class="quotation-form-container">
        <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="quotation-form">
        <el-form :model="form"
                 :rules="rules"
                 ref="formRef"
                 label-width="120px"
                 class="quotation-form">
        <!-- 基本信息 -->
        <el-card class="form-card" shadow="hover">
          <el-card class="form-card"
                   shadow="hover">
          <template #header>
            <div class="card-header-wrapper">
              <el-icon class="card-icon"><Document /></el-icon>
                <el-icon class="card-icon">
                  <Document />
                </el-icon>
              <span class="card-title">基本信息</span>
            </div>
          </template>
          <div class="form-content">
            <el-row :gutter="24">
              <el-col :span="12">
                <el-form-item label="客户名称" prop="customer">
                  <el-select v-model="form.customer" placeholder="请选择客户" style="width: 100%" @change="handleCustomerChange" clearable>
                    <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
                  <el-form-item label="客户名称"
                                prop="customer">
                    <el-select v-model="form.customer"
                               placeholder="请选择客户"
                               style="width: 100%"
                               @change="handleCustomerChange"
                               clearable>
                      <el-option v-for="item in customerOption"
                                 :key="item.id"
                                 :label="item.customerName"
                                 :value="item.customerName">
                      {{
                        item.customerName + "——" + item.taxpayerIdentificationNumber
                      }}
@@ -113,9 +164,15 @@
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="业务员" prop="salesperson">
                  <el-select v-model="form.salesperson" placeholder="请选择业务员" style="width: 100%" clearable>
                    <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                  <el-form-item label="业务员"
                                prop="salesperson">
                    <el-select v-model="form.salesperson"
                               placeholder="请选择业务员"
                               style="width: 100%"
                               clearable>
                      <el-option v-for="item in userList"
                                 :key="item.nickName"
                                 :label="item.nickName"
                      :value="item.nickName" />
                  </el-select>
                </el-form-item>
@@ -123,50 +180,58 @@
            </el-row>
            <el-row :gutter="24">
              <el-col :span="12">
                <el-form-item label="报价日期" prop="quotationDate">
                  <el-date-picker
                    v-model="form.quotationDate"
                  <el-form-item label="报价日期"
                                prop="quotationDate">
                    <el-date-picker v-model="form.quotationDate"
                    type="date"
                    placeholder="选择报价日期"
                    style="width: 100%"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    clearable
                  />
                                    clearable />
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="有效期至" prop="validDate">
                  <el-date-picker
                    v-model="form.validDate"
                  <el-form-item label="有效期至"
                                prop="validDate">
                    <el-date-picker v-model="form.validDate"
                    type="date"
                    placeholder="选择有效期"
                    style="width: 100%"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    clearable
                  />
                                    clearable />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="24">
              <el-col :span="12">
                <el-form-item label="付款方式" prop="paymentMethod">
                  <el-input v-model="form.paymentMethod" placeholder="请输入付款方式" clearable />
                  <el-form-item label="付款方式"
                                prop="paymentMethod">
                    <el-input v-model="form.paymentMethod"
                              placeholder="请输入付款方式"
                              clearable />
                </el-form-item>
              </el-col>
            </el-row>
          </div>
        </el-card>
        <!-- 审批人信息 -->
        <el-card class="form-card" shadow="hover">
          <el-card class="form-card"
                   shadow="hover">
          <template #header>
            <div class="card-header-wrapper">
              <el-icon class="card-icon"><UserFilled /></el-icon>
                <el-icon class="card-icon">
                  <UserFilled />
                </el-icon>
              <span class="card-title">审批人选择</span>
              <el-button type="primary" size="small" @click="addApproverNode" class="header-btn">
                <el-icon><Plus /></el-icon>
                <el-button type="primary"
                           size="small"
                           @click="addApproverNode"
                           class="header-btn">
                  <el-icon>
                    <Plus />
                  </el-icon>
                新增节点
              </el-button>
            </div>
@@ -176,37 +241,31 @@
              <el-col :span="24">
                <el-form-item>
                  <div class="approver-nodes-container">
                    <div
                      v-for="(node, index) in approverNodes"
                      <div v-for="(node, index) in approverNodes"
                      :key="node.id"
                      class="approver-node-item"
                    >
                           class="approver-node-item">
                      <div class="approver-node-label">
                        <span class="node-step">{{ index + 1 }}</span>
                        <span class="node-text">审批人</span>
                        <el-icon class="arrow-icon"><ArrowRight /></el-icon>
                          <el-icon class="arrow-icon">
                            <ArrowRight />
                          </el-icon>
                      </div>
                      <el-select
                        v-model="node.userId"
                        <el-select v-model="node.userId"
                        placeholder="选择人员"
                        class="approver-select"
                        clearable
                      >
                        <el-option
                          v-for="user in userList"
                                   clearable>
                          <el-option v-for="user in userListApprove"
                          :key="user.userId"
                          :label="user.nickName"
                          :value="user.userId"
                        />
                                     :label="user.userName"
                                     :value="user.userId" />
                      </el-select>
                      <el-button
                        type="danger"
                        <el-button type="danger"
                        size="small"
                        :icon="Delete"
                        @click="removeApproverNode(index)"
                        v-if="approverNodes.length > 1"
                        class="remove-btn"
                      >删除</el-button>
                                   class="remove-btn">删除</el-button>
                    </div>
                  </div>
                </el-form-item>
@@ -214,109 +273,138 @@
            </el-row>
          </div>
        </el-card>
        <!-- 产品信息 -->
        <el-card class="form-card" shadow="hover">
          <el-card class="form-card"
                   shadow="hover">
          <template #header>
            <div class="card-header-wrapper">
              <el-icon class="card-icon"><Box /></el-icon>
                <el-icon class="card-icon">
                  <Box />
                </el-icon>
              <span class="card-title">产品信息</span>
              <el-button type="primary" size="small" @click="addProduct" class="header-btn">
                <el-icon><Plus /></el-icon>
                <el-button type="primary"
                           size="small"
                           @click="addProduct"
                           class="header-btn">
                  <el-icon>
                    <Plus />
                  </el-icon>
                添加产品
              </el-button>
            </div>
          </template>
          <div class="form-content">
            <el-table :data="form.products" border style="width: 100%" class="product-table" v-if="form.products.length > 0">
            <el-table-column prop="product" label="产品名称" width="200">
              <el-table :data="form.products"
                        border
                        style="width: 100%"
                        class="product-table"
                        v-if="form.products.length > 0">
                <el-table-column prop="product"
                                 label="产品名称"
                                 width="200">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.productId`" class="product-table-form-item">
                  <el-tree-select
                    v-model="scope.row.productId"
                    <el-form-item :prop="`products.${scope.$index}.productId`"
                                  class="product-table-form-item">
                      <el-tree-select v-model="scope.row.productId"
                    placeholder="请选择"
                    clearable
                    check-strictly
                    @change="getModels($event, scope.row)"
                    :data="productOptions"
                    :render-after-expand="false"
                    style="width: 100%"
                  />
                                      style="width: 100%" />
                </el-form-item>
              </template>
            </el-table-column>
            <el-table-column prop="specification" label="规格型号" width="200">
                <el-table-column prop="specification"
                                 label="规格型号"
                                 width="200">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.specificationId`" class="product-table-form-item">
                  <el-select
                    v-model="scope.row.specificationId"
                    <el-form-item :prop="`products.${scope.$index}.specificationId`"
                                  class="product-table-form-item">
                      <el-select v-model="scope.row.specificationId"
                    placeholder="请选择"
                    clearable
                    @change="getProductModel($event, scope.row)"
                    style="width: 100%"
                  >
                    <el-option
                      v-for="item in scope.row.modelOptions || []"
                                 style="width: 100%">
                        <el-option v-for="item in scope.row.modelOptions || []"
                      :key="item.id"
                      :label="item.model"
                      :value="item.id"
                    />
                                   :value="item.id" />
                  </el-select>
                </el-form-item>
              </template>
            </el-table-column>
            <el-table-column prop="unit" label="单位">
                <el-table-column prop="unit"
                                 label="单位">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.unit`" class="product-table-form-item">
                  <el-input v-model="scope.row.unit" placeholder="单位" clearable/>
                    <el-form-item :prop="`products.${scope.$index}.unit`"
                                  class="product-table-form-item">
                      <el-input v-model="scope.row.unit"
                                placeholder="单位"
                                clearable />
                </el-form-item>
              </template>
            </el-table-column>
            <el-table-column prop="unitPrice" label="单价">
                <el-table-column prop="unitPrice"
                                 label="单价">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.unitPrice`" class="product-table-form-item">
                  <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" />
                    <el-form-item :prop="`products.${scope.$index}.unitPrice`"
                                  class="product-table-form-item">
                      <el-input-number v-model="scope.row.unitPrice"
                                       :min="0"
                                       :precision="2"
                                       style="width: 100%" />
                </el-form-item>
              </template>
            </el-table-column>
            <el-table-column label="操作" width="80" align="center">
                <el-table-column label="操作"
                                 width="80"
                                 align="center">
              <template #default="scope">
                <el-button link type="danger" @click="removeProduct(scope.$index)">删除</el-button>
                    <el-button link
                               type="danger"
                               @click="removeProduct(scope.$index)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
          <el-empty v-else description="暂无产品,请点击添加产品" :image-size="80" />
              <el-empty v-else
                        description="暂无产品,请点击添加产品"
                        :image-size="80" />
          </div>
        </el-card>
        <!-- 备注信息 -->
        <el-card class="form-card" shadow="hover">
          <el-card class="form-card"
                   shadow="hover">
          <template #header>
            <div class="card-header-wrapper">
              <el-icon class="card-icon"><EditPen /></el-icon>
                <el-icon class="card-icon">
                  <EditPen />
                </el-icon>
              <span class="card-title">备注信息</span>
            </div>
          </template>
          <div class="form-content">
            <el-form-item label="备注" prop="remark">
              <el-input
                type="textarea"
              <el-form-item label="备注"
                            prop="remark">
                <el-input type="textarea"
                v-model="form.remark" 
                placeholder="请输入备注信息(选填)" 
                :rows="4"
                maxlength="500"
                show-word-limit
              ></el-input>
                          show-word-limit></el-input>
            </el-form-item>
          </div>
        </el-card>
      </el-form>
      </div>
    </FormDialog>
    <!-- 查看详情对话框 -->
    <el-dialog v-model="viewDialogVisible" title="报价详情" width="800px">
      <el-descriptions :column="2" border>
    <el-dialog v-model="viewDialogVisible"
               title="报价详情"
               width="800px">
      <el-descriptions :column="2"
                       border>
        <el-descriptions-item label="报价单号">{{ currentQuotation.quotationNo }}</el-descriptions-item>
        <el-descriptions-item label="客户名称">{{ currentQuotation.customer }}</el-descriptions-item>
        <el-descriptions-item label="业务员">{{ currentQuotation.salesperson }}</el-descriptions-item>
@@ -326,26 +414,32 @@
<!--        <el-descriptions-item label="报价状态">-->
<!--          <el-tag :type="getStatusType(currentQuotation.status)">{{ currentQuotation.status }}</el-tag>-->
<!--        </el-descriptions-item>-->
        <el-descriptions-item label="报价总额" :span="2">
        <el-descriptions-item label="报价总额"
                              :span="2">
          <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">¥{{ currentQuotation.totalAmount?.toFixed(2) }}</span>
        </el-descriptions-item>
      </el-descriptions>
      <div style="margin: 20px 0;">
        <h4>产品明细</h4>
        <el-table :data="currentQuotation.products" border style="width: 100%">
          <el-table-column prop="product" label="产品名称" />
          <el-table-column prop="specification" label="规格型号" />
          <el-table-column prop="unit" label="单位" />
          <el-table-column prop="unitPrice" label="单价">
        <el-table :data="currentQuotation.products"
                  border
                  style="width: 100%">
          <el-table-column prop="product"
                           label="产品名称" />
          <el-table-column prop="specification"
                           label="规格型号" />
          <el-table-column prop="unit"
                           label="单位" />
          <el-table-column prop="unitPrice"
                           label="单价">
            <template #default="scope">
              ¥{{ scope.row.unitPrice.toFixed(2) }}
            </template>
          </el-table-column>
        </el-table>
      </div>
      <div v-if="currentQuotation.remark" style="margin-top: 20px;">
      <div v-if="currentQuotation.remark"
           style="margin-top: 20px;">
        <h4>备注</h4>
        <p>{{ currentQuotation.remark }}</p>
      </div>
@@ -354,162 +448,186 @@
</template>
<script setup>
import { ref, reactive, computed, onMounted, markRaw, shallowRef } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search, Document, UserFilled, Box, EditPen, Plus, ArrowRight, Delete } from '@element-plus/icons-vue'
import Pagination from '@/components/PIMTable/Pagination.vue'
import FormDialog from '@/components/Dialog/FormDialog.vue'
import {getQuotationList,addQuotation,updateQuotation,deleteQuotation} from '@/api/salesManagement/salesQuotation.js'
  import { ref, reactive, computed, onMounted, markRaw, shallowRef } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import {
    Search,
    Document,
    UserFilled,
    Box,
    EditPen,
    Plus,
    ArrowRight,
    Delete,
  } from "@element-plus/icons-vue";
  import Pagination from "@/components/PIMTable/Pagination.vue";
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import {
    getQuotationList,
    addQuotation,
    updateQuotation,
    deleteQuotation,
  } from "@/api/salesManagement/salesQuotation.js";
import {userListNoPage} from "@/api/system/user.js";
  import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js";
import {customerList} from "@/api/salesManagement/salesLedger.js";
import {modelList, productTreeList} from "@/api/basicData/product.js";
// 响应式数据
const loading = ref(false)
  const loading = ref(false);
const searchForm = reactive({
  quotationNo: '',
  customer: '',
  status: ''
})
    quotationNo: "",
    customer: "",
    status: "",
  });
const quotationList = ref([])
  const quotationList = ref([]);
const productOptions = ref([]);
const modelOptions = ref([]);
const pagination = reactive({
  total: 3,
  currentPage: 1,
  pageSize: 100
})
    pageSize: 100,
  });
const dialogVisible = ref(false)
const viewDialogVisible = ref(false)
const dialogTitle = ref('新增报价')
  const dialogVisible = ref(false);
  const viewDialogVisible = ref(false);
  const dialogTitle = ref("新增报价");
const form = reactive({
  quotationNo: '',
  customer: '',
  salesperson: '',
  quotationDate: '',
  validDate: '',
  paymentMethod: '',
  status: '草稿',
  remark: '',
    quotationNo: "",
    customer: "",
    salesperson: "",
    quotationDate: "",
    validDate: "",
    paymentMethod: "",
    status: "草稿",
    remark: "",
  products: [],
  subtotal: 0,
  freight: 0,
  otherFee: 0,
  discountRate: 0,
  discountAmount: 0,
  totalAmount: 0
})
    totalAmount: 0,
  });
const baseRules = {
  customer: [{ required: true, message: '请选择客户', trigger: 'change' }],
  salesperson: [{ required: true, message: '请选择业务员', trigger: 'change' }],
  quotationDate: [{ required: true, message: '请选择报价日期', trigger: 'change' }],
  validDate: [{ required: true, message: '请选择有效期', trigger: 'change' }],
  paymentMethod: [{ required: true, message: '请输入付款方式', trigger: 'blur' }]
}
    customer: [{ required: true, message: "请选择客户", trigger: "change" }],
    salesperson: [{ required: true, message: "请选择业务员", trigger: "change" }],
    quotationDate: [
      { required: true, message: "请选择报价日期", trigger: "change" },
    ],
    validDate: [{ required: true, message: "请选择有效期", trigger: "change" }],
    paymentMethod: [
      { required: true, message: "请输入付款方式", trigger: "blur" },
    ],
  };
const productRowRules = {
  productId: [{ required: true, message: '请选择产品名称', trigger: 'change' }],
  specificationId: [{ required: true, message: '请选择规格型号', trigger: 'change' }],
  unit: [{ required: true, message: '请填写单位', trigger: 'blur' }],
  unitPrice: [{ required: true, message: '请填写单价', trigger: 'change' }]
}
    productId: [{ required: true, message: "请选择产品名称", trigger: "change" }],
    specificationId: [
      { required: true, message: "请选择规格型号", trigger: "change" },
    ],
    unit: [{ required: true, message: "请填写单位", trigger: "blur" }],
    unitPrice: [{ required: true, message: "请填写单价", trigger: "change" }],
  };
const rules = computed(() => {
  const r = { ...baseRules }
  ;(form.products || []).forEach((_, i) => {
    r[`products.${i}.productId`] = productRowRules.productId
    r[`products.${i}.specificationId`] = productRowRules.specificationId
    r[`products.${i}.unit`] = productRowRules.unit
    r[`products.${i}.unitPrice`] = productRowRules.unitPrice
  })
  return r
})
    const r = { ...baseRules };
    (form.products || []).forEach((_, i) => {
      r[`products.${i}.productId`] = productRowRules.productId;
      r[`products.${i}.specificationId`] = productRowRules.specificationId;
      r[`products.${i}.unit`] = productRowRules.unit;
      r[`products.${i}.unitPrice`] = productRowRules.unitPrice;
    });
    return r;
  });
const userList = ref([]);
  const userListApprove = ref([]);
const customerOption = ref([]);
// 审批人节点相关
const approverNodes = ref([
  { id: 1, userId: null }
])
let nextApproverId = 2
  const approverNodes = ref([{ id: 1, userId: null }]);
  let nextApproverId = 2;
const isEdit = ref(false)
const editId = ref(null)
const currentQuotation = ref({})
const formRef = ref()
  const isEdit = ref(false);
  const editId = ref(null);
  const currentQuotation = ref({});
  const formRef = ref();
// 添加审批人节点
function addApproverNode() {
  approverNodes.value.push({ id: nextApproverId++, userId: null })
    approverNodes.value.push({ id: nextApproverId++, userId: null });
}
// 删除审批人节点
function removeApproverNode(index) {
  approverNodes.value.splice(index, 1)
    approverNodes.value.splice(index, 1);
}
// 计算属性
const filteredList = computed(() => {
  let list = quotationList.value
  return list
})
    let list = quotationList.value;
    return list;
  });
// 方法
const getStatusType = (status) => {
  const getStatusType = status => {
  const statusMap = {
    '待审批': 'info',
    '审核中': 'primary',
    '通过': 'success',
    '拒绝': 'danger'
  }
  return statusMap[status] || 'info'
}
      待审批: "info",
      审核中: "primary",
      通过: "success",
      拒绝: "danger",
    };
    return statusMap[status] || "info";
  };
const resetSearch = () => {
  searchForm.quotationNo = ''
  searchForm.customer = ''
  searchForm.status = ''
    searchForm.quotationNo = "";
    searchForm.customer = "";
    searchForm.status = "";
  // 重置到第一页并重新查询
  pagination.currentPage = 1
  handleSearch()
}
    pagination.currentPage = 1;
    handleSearch();
  };
const handleAdd = async () => {
  dialogTitle.value = '新增报价'
  isEdit.value = false
  resetForm()
    dialogTitle.value = "新增报价";
    isEdit.value = false;
    resetForm();
  // 重置审批人节点
  approverNodes.value = [{ id: 1, userId: null }]
  nextApproverId = 2
  dialogVisible.value = true
    approverNodes.value = [{ id: 1, userId: null }];
    nextApproverId = 2;
    dialogVisible.value = true;
    let userLists = await userListNoPage();
    // 只复制需要的字段,避免将组件引用放入响应式对象
    userList.value = (userLists.data || []).map(item => ({
    userId: item.userId,
    nickName: item.nickName || '',
    userName: item.userName || ''
      nickName: item.nickName || "",
      userName: item.userName || "",
  }));
    approveUserList({ approveType: 6 }).then(res => {
      userListApprove.value = res.data;
    });
    getProductOptions();
    customerList().then((res) => {
    customerList().then(res => {
        // 只复制需要的字段,避免将组件引用放入响应式对象
        customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
      id: item.id,
      customerName: item.customerName || '',
      taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
    }))
        customerName: item.customerName || "",
        taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || "",
      }));
    });
}
  };
const getProductOptions = () => {
    // 返回 Promise,便于编辑时 await 确保能反显
    return productTreeList().then((res) => {
    return productTreeList().then(res => {
        productOptions.value = convertIdToValue(res);
        return productOptions.value
      return productOptions.value;
    });
};
function convertIdToValue(data) {
    return data.map((item) => {
    return data.map(item => {
        const { id, children, ...rest } = item;
        const newItem = {
            ...rest,
@@ -539,12 +657,12 @@
    if (!row) return;
    // 如果清空选择,则清空相关字段
    if (!value) {
        row.productId = '';
        row.product = '';
      row.productId = "";
      row.product = "";
        row.modelOptions = [];
        row.specificationId = '';
        row.specification = '';
        row.unit = '';
      row.specificationId = "";
      row.specification = "";
      row.unit = "";
        return;
    }
    // 更新 productId(v-model 已经自动更新,这里确保一致性)
@@ -555,7 +673,7 @@
        row.product = label;
    }
    // 获取规格型号列表,设置到当前行的 modelOptions
    modelList({ id: value }).then((res) => {
    modelList({ id: value }).then(res => {
        row.modelOptions = res || [];
    });
};
@@ -563,21 +681,21 @@
    if (!row) return;
    // 如果清空选择,则清空相关字段
    if (!value) {
        row.specificationId = '';
        row.specification = '';
        row.unit = '';
      row.specificationId = "";
      row.specification = "";
      row.unit = "";
        return;
    }
    // 更新 specificationId(v-model 已经自动更新,这里确保一致性)
    row.specificationId = value;
    const modelOptions = row.modelOptions || [];
    const index = modelOptions.findIndex((item) => item.id === value);
    const index = modelOptions.findIndex(item => item.id === value);
    if (index !== -1) {
        row.specification = modelOptions[index].model;
        row.unit = modelOptions[index].unit;
    } else {
        row.specification = '';
        row.unit = '';
      row.specification = "";
      row.unit = "";
    }
};
const findNodeById = (nodes, productId) => {
@@ -594,59 +712,63 @@
    }
    return null; // 没有找到节点,返回null
};
const handleView = (row) => {
  const handleView = row => {
  // 只复制需要的字段,避免将组件引用放入响应式对象
  currentQuotation.value = {
    quotationNo: row.quotationNo || '',
    customer: row.customer || '',
    salesperson: row.salesperson || '',
    quotationDate: row.quotationDate || '',
    validDate: row.validDate || '',
    paymentMethod: row.paymentMethod || '',
    status: row.status || '',
    remark: row.remark || '',
    products: row.products ? row.products.map(product => ({
      productId: product.productId || '',
      product: product.product || product.productName || '',
      specificationId: product.specificationId || '',
      specification: product.specification || '',
      quotationNo: row.quotationNo || "",
      customer: row.customer || "",
      salesperson: row.salesperson || "",
      quotationDate: row.quotationDate || "",
      validDate: row.validDate || "",
      paymentMethod: row.paymentMethod || "",
      status: row.status || "",
      remark: row.remark || "",
      products: row.products
        ? row.products.map(product => ({
            productId: product.productId || "",
            product: product.product || product.productName || "",
            specificationId: product.specificationId || "",
            specification: product.specification || "",
      quantity: product.quantity || 0,
      unit: product.unit || '',
            unit: product.unit || "",
      unitPrice: product.unitPrice || 0,
      amount: product.amount || 0
    })) : [],
    totalAmount: row.totalAmount || 0
  }
  viewDialogVisible.value = true
}
            amount: product.amount || 0,
          }))
        : [],
      totalAmount: row.totalAmount || 0,
    };
    viewDialogVisible.value = true;
  };
const handleEdit = async (row) => {
  dialogTitle.value = '编辑报价'
  isEdit.value = true
  editId.value = row.id
  form.id = row.id || form.id || null
  const handleEdit = async row => {
    dialogTitle.value = "编辑报价";
    isEdit.value = true;
    editId.value = row.id;
    form.id = row.id || form.id || null;
  // 先加载产品树数据,否则 el-tree-select 无法反显产品名称
  await getProductOptions()
    await getProductOptions();
  // 只复制需要的字段,避免将组件引用放入响应式对象
  form.quotationNo = row.quotationNo || ''
  form.customer = row.customer || ''
  form.salesperson = row.salesperson || ''
  form.quotationDate = row.quotationDate || ''
  form.validDate = row.validDate || ''
  form.paymentMethod = row.paymentMethod || ''
  form.status = row.status || '草稿'
  form.remark = row.remark || ''
  form.products = row.products ? await Promise.all(row.products.map(async (product) => {
    const productName = product.product || product.productName || ''
    form.quotationNo = row.quotationNo || "";
    form.customer = row.customer || "";
    form.salesperson = row.salesperson || "";
    form.quotationDate = row.quotationDate || "";
    form.validDate = row.validDate || "";
    form.paymentMethod = row.paymentMethod || "";
    form.status = row.status || "草稿";
    form.remark = row.remark || "";
    form.products = row.products
      ? await Promise.all(
          row.products.map(async product => {
            const productName = product.product || product.productName || "";
    // 优先用 productId;如果只有名称,尝试反查 id 以便树选择器反显
    const resolvedProductId = product.productId
      ? Number(product.productId)
      : findNodeIdByLabel(productOptions.value, productName) || ''
              : findNodeIdByLabel(productOptions.value, productName) || "";
    
    // 如果有产品ID,加载对应的规格型号列表
    let modelOptions = [];
    let resolvedSpecificationId = product.specificationId || '';
            let resolvedSpecificationId = product.specificationId || "";
    
    if (resolvedProductId) {
      try {
@@ -655,13 +777,15 @@
        
        // 如果返回的数据没有 specificationId,但有 specification 名称,根据名称查找 ID
        if (!resolvedSpecificationId && product.specification) {
          const foundModel = modelOptions.find(item => item.model === product.specification);
                  const foundModel = modelOptions.find(
                    item => item.model === product.specification
                  );
          if (foundModel) {
            resolvedSpecificationId = foundModel.id;
          }
        }
      } catch (error) {
        console.error('加载规格型号失败:', error);
                console.error("加载规格型号失败:", error);
      }
    }
    
@@ -669,238 +793,248 @@
      productId: resolvedProductId,
      product: productName,
      specificationId: resolvedSpecificationId,
      specification: product.specification || '',
              specification: product.specification || "",
      quantity: product.quantity || 0,
      unit: product.unit || '',
              unit: product.unit || "",
      unitPrice: product.unitPrice || 0,
      amount: product.amount || 0,
      modelOptions: modelOptions // 为每行添加独立的规格型号列表
    }
  })) : []
  form.subtotal = row.subtotal || 0
  form.freight = row.freight || 0
  form.otherFee = row.otherFee || 0
  form.discountRate = row.discountRate || 0
  form.discountAmount = row.discountAmount || 0
  form.totalAmount = row.totalAmount || 0
              modelOptions: modelOptions, // 为每行添加独立的规格型号列表
            };
          })
        )
      : [];
    form.subtotal = row.subtotal || 0;
    form.freight = row.freight || 0;
    form.otherFee = row.otherFee || 0;
    form.discountRate = row.discountRate || 0;
    form.discountAmount = row.discountAmount || 0;
    form.totalAmount = row.totalAmount || 0;
  
  // 反显审批人
  if (row.approveUserIds) {
    const userIds = row.approveUserIds.split(',')
      const userIds = row.approveUserIds.split(",");
    approverNodes.value = userIds.map((userId, idx) => ({
      id: idx + 1,
      userId: parseInt(userId.trim())
    }))
    nextApproverId = userIds.length + 1
        userId: parseInt(userId.trim()),
      }));
      nextApproverId = userIds.length + 1;
  } else {
    approverNodes.value = [{ id: 1, userId: null }]
    nextApproverId = 2
      approverNodes.value = [{ id: 1, userId: null }];
      nextApproverId = 2;
  }
  
  // 加载用户列表
  let userLists = await userListNoPage();
  userList.value = (userLists.data || []).map(item => ({
    userId: item.userId,
    nickName: item.nickName || '',
    userName: item.userName || ''
      nickName: item.nickName || "",
      userName: item.userName || "",
  }));
  
  dialogVisible.value = true
}
    dialogVisible.value = true;
  };
const handleDelete = (row) => {
  ElMessageBox.confirm('确认删除该报价单吗?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  const handleDelete = row => {
    ElMessageBox.confirm("确认删除该报价单吗?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
  }).then(() => {
    const index = quotationList.value.findIndex(item => item.id === row.id)
      const index = quotationList.value.findIndex(item => item.id === row.id);
    if (index > -1) {
      deleteQuotation(row.id).then(res=>{
        // console.log(res)
        if(res.code===200){
          ElMessage.success('删除成功')
          handleSearch()
            ElMessage.success("删除成功");
            handleSearch();
        }
      })
        });
      // quotationList.value.splice(index, 1)
      // pagination.total--
      // ElMessage.success('删除成功')
    }
  })
}
    });
  };
const resetForm = () => {
  form.customer = ''
  form.salesperson = ''
  form.quotationDate = ''
  form.validDate = ''
  form.paymentMethod = ''
  form.status = '草稿'
  form.remark = ''
  form.products = []
  form.subtotal = 0
  form.freight = 0
  form.otherFee = 0
  form.discountRate = 0
  form.discountAmount = 0
  form.totalAmount = 0
}
    form.customer = "";
    form.salesperson = "";
    form.quotationDate = "";
    form.validDate = "";
    form.paymentMethod = "";
    form.status = "草稿";
    form.remark = "";
    form.products = [];
    form.subtotal = 0;
    form.freight = 0;
    form.otherFee = 0;
    form.discountRate = 0;
    form.discountAmount = 0;
    form.totalAmount = 0;
  };
const addProduct = () => {
  form.products.push({
    productId: '',
    product: '',
    productName: '',
    specificationId: '',
    specification: '',
      productId: "",
      product: "",
      productName: "",
      specificationId: "",
      specification: "",
    quantity: 1,
    unit: '',
      unit: "",
    unitPrice: 0,
    amount: 0,
    modelOptions: [] // 为每行添加独立的规格型号列表
  })
}
      modelOptions: [], // 为每行添加独立的规格型号列表
    });
  };
const removeProduct = (index) => {
  form.products.splice(index, 1)
  calculateSubtotal()
}
  const removeProduct = index => {
    form.products.splice(index, 1);
    calculateSubtotal();
  };
const calculateAmount = (product) => {
  product.amount = product.quantity * product.unitPrice
  calculateSubtotal()
}
  const calculateAmount = product => {
    product.amount = product.quantity * product.unitPrice;
    calculateSubtotal();
  };
const calculateSubtotal = () => {
  form.subtotal = form.products.reduce((sum, product) => sum + product.amount, 0)
  calculateTotal()
}
    form.subtotal = form.products.reduce(
      (sum, product) => sum + product.amount,
      0
    );
    calculateTotal();
  };
const calculateTotal = () => {
  form.discountAmount = form.subtotal * (form.discountRate / 100)
  form.totalAmount = form.subtotal + form.freight + form.otherFee - form.discountAmount
}
    form.discountAmount = form.subtotal * (form.discountRate / 100);
    form.totalAmount =
      form.subtotal + form.freight + form.otherFee - form.discountAmount;
  };
const handleCustomerChange = () => {
  // 可以根据客户信息自动填充一些默认值
}
  };
const handleSubmit = () => {
  formRef.value.validate((valid) => {
    formRef.value.validate(valid => {
    if (valid) {
      if (form.products.length === 0) {
        ElMessage.warning('请至少添加一个产品')
        return
          ElMessage.warning("请至少添加一个产品");
          return;
      }
      // 审批人必填校验
      const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
        const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
      if (hasEmptyApprover) {
        ElMessage.error('请为所有审批节点选择审批人!')
        return
          ElMessage.error("请为所有审批节点选择审批人!");
          return;
      }
      
      // 收集所有节点的审批人id
      form.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
        form.approveUserIds = approverNodes.value
          .map(node => node.userId)
          .join(",");
      
      // 计算所有产品的单价总和
      form.totalAmount = form.products.reduce((sum, product) => {
        const price = Number(product.unitPrice) || 0
        return sum + price
      }, 0)
          const price = Number(product.unitPrice) || 0;
          return sum + price;
        }, 0);
      
      if (isEdit.value) {
        // 编辑
        const index = quotationList.value.findIndex(item => item.id === editId.value)
          const index = quotationList.value.findIndex(
            item => item.id === editId.value
          );
        if (index > -1) {
          updateQuotation(form).then(res=>{
            // console.log(res)
            if(res.code===200){
              ElMessage.success('编辑成功')
              dialogVisible.value = false
              handleSearch()
                ElMessage.success("编辑成功");
                dialogVisible.value = false;
                handleSearch();
            }
          })
            });
        }
      } else {
        // 新增
        addQuotation(form).then(res=>{
          if(res.code===200){
            ElMessage.success('新增成功')
            dialogVisible.value = false
            handleSearch()
              ElMessage.success("新增成功");
              dialogVisible.value = false;
              handleSearch();
          }
        })
          });
      }
      }
    });
  };
      
    }
  })
}
const handleCurrentChange = (val) => {
  pagination.currentPage = val.page
  pagination.pageSize = val.limit
  const handleCurrentChange = val => {
    pagination.currentPage = val.page;
    pagination.pageSize = val.limit;
  // 分页变化时重新查询列表
  handleSearch()
}
    handleSearch();
  };
const handleSearch = ()=>{
  const params = {
    // 后端分页参数:current / size
    current: pagination.currentPage,
    size: pagination.pageSize,
    ...searchForm
  }
      ...searchForm,
    };
  getQuotationList(params).then(res=>{
    // console.log(res)
    if(res.code===200){
      // 只复制需要的字段,避免将组件引用或其他对象放入响应式对象
      quotationList.value = (res.data.records || []).map(item => ({
        id: item.id,
        quotationNo: item.quotationNo || '',
        customer: item.customer || '',
        salesperson: item.salesperson || '',
        quotationDate: item.quotationDate || '',
        validDate: item.validDate || '',
        paymentMethod: item.paymentMethod || '',
        status: item.status || '草稿',
          quotationNo: item.quotationNo || "",
          customer: item.customer || "",
          salesperson: item.salesperson || "",
          quotationDate: item.quotationDate || "",
          validDate: item.validDate || "",
          paymentMethod: item.paymentMethod || "",
          status: item.status || "草稿",
        // 审批人(用于编辑时反显)
        approveUserIds: item.approveUserIds || '',
        remark: item.remark || '',
        products: item.products ? item.products.map(product => ({
          productId: product.productId || '',
          product: product.product || product.productName || '',
          specificationId: product.specificationId || '',
          specification: product.specification || '',
          approveUserIds: item.approveUserIds || "",
          remark: item.remark || "",
          products: item.products
            ? item.products.map(product => ({
                productId: product.productId || "",
                product: product.product || product.productName || "",
                specificationId: product.specificationId || "",
                specification: product.specification || "",
          quantity: product.quantity || 0,
          unit: product.unit || '',
                unit: product.unit || "",
          unitPrice: product.unitPrice || 0,
          amount: product.amount || 0
        })) : [],
                amount: product.amount || 0,
              }))
            : [],
        subtotal: item.subtotal || 0,
        freight: item.freight || 0,
        otherFee: item.otherFee || 0,
        discountRate: item.discountRate || 0,
        discountAmount: item.discountAmount || 0,
        totalAmount: item.totalAmount || 0
      }))
      pagination.total = res.data.total
          totalAmount: item.totalAmount || 0,
        }));
        pagination.total = res.data.total;
    }
  })
    customerList().then((res) => {
    });
    customerList().then(res => {
        // 只复制需要的字段,避免将组件引用放入响应式对象
        customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
      id: item.id,
      customerName: item.customerName || '',
      taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
    }))
        customerName: item.customerName || "",
        taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || "",
      }));
    });
}
  };
onMounted(()=>{
  handleSearch()
})
    handleSearch();
  });
</script>
<style scoped lang="scss">