zhangwencui
2026-04-24 759f41097324fa1ade4060fc838d700d8c8fa55f
提交一版
已添加1个文件
已修改7个文件
2161 ■■■■■ 文件已修改
src/api/productionManagement/processRouteFile.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionOrder.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/purchaseApproval/index.vue 428 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/index.vue 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/New.vue 130 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionPlan/productionPlan/index.vue 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 1171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/processRouteFile.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
import request from "@/utils/request";
// é™„件列表
export function listProcessRouteFiles(query) {
  return request({
    url: "/technologyRoutingFile/listPage",
    method: "get",
    params: query,
  });
}
// æ–°å¢žé™„ä»¶
export function addProcessRouteFile(data) {
  return request({
    url: "/technologyRoutingFile/add",
    method: "post",
    data,
  });
}
// åˆ é™¤é™„ä»¶
export function delProcessRouteFile(ids) {
  return request({
    url: "/technologyRoutingFile/del",
    method: "delete",
    data: ids,
  });
}
src/api/productionManagement/productionOrder.js
@@ -12,7 +12,7 @@
export function productOrderListPage(query) {
  return request({
    url: "/productOrder/page",
    url: "/productionOrder/page",
    method: "get",
    params: query,
  });
@@ -39,7 +39,7 @@
// ç”Ÿäº§è®¢å•-新增
export function addProductOrder(data) {
  return request({
    url: "/productOrder/addProductOrder",
    url: "/productionOrder",
    method: "post",
    data: data,
  });
src/views/collaborativeApproval/purchaseApproval/index.vue
@@ -2,43 +2,52 @@
  <div class="app-container">
    <div class="search_form">
      <div>
        <el-form :model="searchForm" :inline="true">
        <el-form :model="searchForm"
                 :inline="true">
          <el-form-item label="供应商名称:">
            <el-input v-model="searchForm.supplierName" placeholder="请输入" clearable prefix-icon="Search"
            <el-input v-model="searchForm.supplierName"
                      placeholder="请输入"
                      clearable
                      prefix-icon="Search"
                      @change="handleQuery" />
          </el-form-item>
          <el-form-item label="采购合同号:">
            <el-input
                v-model="searchForm.purchaseContractNumber"
            <el-input v-model="searchForm.purchaseContractNumber"
                style="width: 240px"
                placeholder="请输入"
                @change="handleQuery"
                clearable
                :prefix-icon="Search"
            />
                      :prefix-icon="Search" />
          </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>
            <el-button type="primary" @click="handleQuery"> æœç´¢ </el-button>
            <el-button type="primary"
                       @click="handleQuery"> æœç´¢ </el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
    <div class="table_list">
      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除</el-button>
      </div>
      <el-table
        :data="tableData"
      <el-table :data="tableData"
        border
        v-loading="tableLoading"
        @selection-change="handleSelectionChange"
@@ -48,150 +57,125 @@
        :summary-method="summarizeMainTable"
        @expand-change="expandChange"
        height="calc(100vh - 18.5em)"
        :row-class-name="tableRowClassName"
      >
        <el-table-column align="center" type="selection" width="55" />
                :row-class-name="tableRowClassName">
        <el-table-column align="center"
                         type="selection"
                         width="55" />
        <el-table-column type="expand">
          <template #default="props">
            <el-table
              :data="props.row.children"
            <el-table :data="props.row.children"
              border
              show-summary
              :summary-method="summarizeChildrenTable"
            >
              <el-table-column
                align="center"
                      :summary-method="summarizeChildrenTable">
              <el-table-column align="center"
                label="序号"
                type="index"
                width="60"
              />
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column
                label="含税单价(元)"
                               width="60" />
              <el-table-column label="产品大类"
                               prop="productCategory" />
              <el-table-column label="规格型号"
                               prop="specificationModel" />
              <el-table-column label="单位"
                               prop="unit" />
              <el-table-column label="数量"
                               prop="quantity" />
              <el-table-column label="税率(%)"
                               prop="taxRate" />
              <el-table-column label="含税单价(元)"
                prop="taxInclusiveUnitPrice"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="含税总价(元)"
                               :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)"
                prop="taxInclusiveTotalPrice"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="不含税总价(元)"
                               :formatter="formattedNumber" />
              <el-table-column label="不含税总价(元)"
                prop="taxExclusiveTotalPrice"
                :formatter="formattedNumber"
              />
                               :formatter="formattedNumber" />
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column
          label="采购合同号"
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="采购合同号"
          prop="purchaseContractNumber"
          width="200"
          show-overflow-tooltip
        />
        <el-table-column
          label="销售合同号"
                         show-overflow-tooltip />
        <el-table-column label="销售合同号"
          prop="salesContractNo"
          width="200"
          show-overflow-tooltip
        />
        <el-table-column
          label="供应商名称"
                         show-overflow-tooltip />
        <el-table-column label="供应商名称"
          width="240"
          prop="supplierName"
          show-overflow-tooltip
        />
        <el-table-column label="订单状态" width="100" align="center">
                         show-overflow-tooltip />
        <el-table-column label="订单状态"
                         width="100"
                         align="center">
          <template #default="scope">
            <el-tag v-if="scope.row.isInvalid" type="danger" size="small">失效</el-tag>
            <el-tag v-else type="success" size="small">正常</el-tag>
            <el-tag v-if="scope.row.isInvalid"
                    type="danger"
                    size="small">失效</el-tag>
            <el-tag v-else
                    type="success"
                    size="small">正常</el-tag>
          </template>
        </el-table-column>
        <el-table-column
          label="项目名称"
        <el-table-column label="项目名称"
          prop="projectName"
          width="420"
          show-overflow-tooltip
        />
        <el-table-column
            label="审批状态"
                         show-overflow-tooltip />
        <el-table-column label="审批状态"
            prop="approvalStatus"
            width="200"
            show-overflow-tooltip
        >
                         show-overflow-tooltip>
          <template #default="scope">
            <el-tag
                size="small"
            >
            <el-tag size="small">
              {{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column
          label="付款方式"
        <el-table-column label="付款方式"
          width="100"
          prop="paymentMethod"
          show-overflow-tooltip
        />
        <el-table-column
          label="合同金额(元)"
                         show-overflow-tooltip />
        <el-table-column label="合同金额(元)"
          prop="contractAmount"
           width="200"
          show-overflow-tooltip
          :formatter="formattedNumber"
        />
        <el-table-column
          label="录入人"
                         :formatter="formattedNumber" />
        <el-table-column label="录入人"
          prop="recorderName"
           width="100"
          show-overflow-tooltip
        />
        <el-table-column
          label="录入日期"
                         show-overflow-tooltip />
        <el-table-column label="录入日期"
          prop="entryDate"
           width="100"
          show-overflow-tooltip
        />
        <el-table-column
          fixed="right"
                         show-overflow-tooltip />
        <el-table-column fixed="right"
          label="操作"
          min-width="150"
          align="center"
        >
                         align="center">
          <template #default="scope">
            <el-button
              link
            <el-button link
              type="primary"
              size="small"
              @click="approvePurchase(scope.row)"
              :disabled="scope.row.approvalStatus !== 0"
              >审批</el-button
            >
            <el-button
                link
                       :disabled="scope.row.approvalStatus !== 0">审批</el-button>
            <el-button link
                type="primary"
                size="small"
                @click="rejectPurchase(scope.row)"
                :disabled="scope.row.approvalStatus !== 0"
            >拒绝审批</el-button
            >
                       :disabled="scope.row.approvalStatus !== 0">拒绝审批</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
      <pagination v-show="total > 0"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="page.current"
        :limit="page.size"
        @pagination="paginationChange"
      />
                  @pagination="paginationChange" />
    </div>
  </div>
</template>
@@ -199,7 +183,14 @@
<script setup>
import { getToken } from "@/utils/auth";
import pagination from "@/components/PIMTable/Pagination.vue";
import { ref, onMounted, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
  import {
    ref,
    onMounted,
    reactive,
    toRefs,
    getCurrentInstance,
    nextTick,
  } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import { userListNoPage } from "@/api/system/user.js";
@@ -218,11 +209,11 @@
  productList,
  getPurchaseById,
  getOptions,
  createPurchaseNo, updateApprovalStatus,
    createPurchaseNo,
    updateApprovalStatus,
} from "@/api/procurementManagement/procurementLedger.js";
import useFormData from "@/hooks/useFormData.js";
import QRCode from "qrcode";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
@@ -254,9 +245,9 @@
// è®¢å•审批状态显示文本
const approvalStatusText = {
  0: '待审批',
  1: '审批通过',
  2: '审批失败'
    0: "待审批",
    1: "审批通过",
    2: "审批失败",
};
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
@@ -345,7 +336,7 @@
  headers: { Authorization: "Bearer " + getToken() },
});
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");
@@ -366,7 +357,7 @@
  getList();
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeChildrenTable = (param) => {
  const summarizeChildrenTable = param => {
  return proxy.summarizeTable(
    param,
    [
@@ -384,7 +375,7 @@
    }
  );
};
const paginationChange = (obj) => {
  const paginationChange = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
@@ -393,15 +384,15 @@
  tableLoading.value = true;
  const { entryDate, ...rest } = searchForm;
  purchaseListPage({ ...rest, ...page })
    .then((res) => {
      .then(res => {
      tableLoading.value = false;
      // tableData.value = res.data.records;
      // å¤„理数据,添加失效状态标记
      tableData.value = res.data.records.map(record => ({
        ...record,
        isInvalid: record.isWhite === 1
          isInvalid: record.isWhite === 1,
      }));
      tableData.value.map((item) => {
        tableData.value.map(item => {
        item.children = [];
      });
      total.value = res.data.total;
@@ -412,10 +403,10 @@
    });
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  selectedRows.value = selection;
};
const productSelected = (selectedRows) => {
  const productSelected = selectedRows => {
  productSelectedRows.value = selectedRows;
};
const expandedRowKeys = ref([]);
@@ -424,8 +415,8 @@
  if (expandedRows.length > 0) {
    expandedRowKeys.value = [];
    try {
      productList({ salesLedgerId: row.id, type: 2 }).then((res) => {
        const index = tableData.value.findIndex((item) => item.id === row.id);
        productList({ salesLedgerId: row.id, type: 2 }).then(res => {
          const index = tableData.value.findIndex(item => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data;
        }
@@ -439,11 +430,11 @@
  }
};
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  const summarizeMainTable = param => {
  return proxy.summarizeTable(param, ["contractAmount"]);
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeProTable = (param) => {
  const summarizeProTable = param => {
  return proxy.summarizeTable(param, [
    "taxInclusiveUnitPrice",
    "taxInclusiveTotalPrice",
@@ -457,25 +448,25 @@
  productData.value = [];
  fileList.value = [];
  if (operationType.value == "add") {
    createPurchaseNo().then((res) => {
      createPurchaseNo().then(res => {
      form.value.purchaseContractNumber = res.data;
    });
  }
  userListNoPage().then((res) => {
    userListNoPage().then(res => {
    userList.value = res.data;
  });
  getSalesNo().then((res) => {
    getSalesNo().then(res => {
    salesContractList.value = res;
  });
  getOptions().then((res) => {
    getOptions().then(res => {
    // ä¾›åº”商过滤出isWhite=0 çš„æ•°æ®
    supplierList.value = res.data.filter((item) => item.isWhite == 0);
      supplierList.value = res.data.filter(item => item.isWhite == 0);
  });
  form.value.recorderId = userStore.id;
  form.value.entryDate = getCurrentDate();
  if (type === "edit") {
    currentId.value = row.id;
    getPurchaseById({ id: row.id, type: 2 }).then((res) => {
      getPurchaseById({ id: row.id, type: 2 }).then(res => {
      form.value = { ...res };
      productData.value = form.value.productData;
      if (form.value.salesLedgerFiles) {
@@ -523,14 +514,14 @@
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.id);
    delLedgerFile(ids).then((res) => {
      delLedgerFile(ids).then(res => {
      proxy.$modal.msgSuccess("删除成功");
    });
  }
}
// æäº¤è¡¨å•
const submitForm = (n) => {
  proxy.$refs["formRef"].validate((valid) => {
  const submitForm = n => {
    proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      if (productData.value.length > 0) {
        form.value.productData = proxy.HaveJson(productData.value);
@@ -540,12 +531,12 @@
      }
      let tempFileIds = [];
      if (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 = 2;
      form.value.approvalStatus = n;
      addOrEditPurchase(form.value).then((res) => {
        addOrEditPurchase(form.value).then(res => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
        getList();
@@ -571,14 +562,15 @@
  getProductOptions();
};
const getProductOptions = () => {
  productTreeList().then((res) => {
    productTreeList().then(res => {
    productOptions.value = convertIdToValue(res);
  });
};
const getModels = (value) => {
  const getModels = value => {
  if (value) {
    productForm.value.productCategory = findNodeById(productOptions.value, value) || "";
    modelList({ id: value }).then((res) => {
      productForm.value.productCategory =
        findNodeById(productOptions.value, value) || "";
      modelList({ id: value }).then(res => {
      modelOptions.value = res;
    });
  } else {
@@ -586,8 +578,8 @@
    modelOptions.value = [];
  }
};
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;
    productForm.value.unit = modelOptions.value[index].unit;
@@ -611,7 +603,7 @@
  return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
};
function convertIdToValue(data) {
  return data.map((item) => {
    return data.map(item => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
@@ -626,7 +618,7 @@
}
// æäº¤äº§å“è¡¨å•
const submitProduct = () => {
  proxy.$refs["productFormRef"].validate((valid) => {
    proxy.$refs["productFormRef"].validate(valid => {
    if (valid) {
      if (operationType.value === "edit") {
        submitProductEdit();
@@ -647,10 +639,10 @@
const submitProductEdit = () => {
  productForm.value.salesLedgerId = currentId.value;
  productForm.value.type = 2;
  addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
    addOrUpdateSalesLedgerProduct(productForm.value).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;
    });
  });
@@ -662,9 +654,9 @@
    return;
  }
  if (operationType.value === "add") {
    productSelectedRows.value.forEach((selectedRow) => {
      productSelectedRows.value.forEach(selectedRow => {
      const index = productData.value.findIndex(
        (product) => product.id === selectedRow.id
          product => product.id === selectedRow.id
      );
      if (index !== -1) {
        productData.value.splice(index, 1);
@@ -673,7 +665,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: "确认",
@@ -681,11 +673,11 @@
      type: "warning",
    })
      .then(() => {
        delProduct(ids).then((res) => {
          delProduct(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          closeProductDia();
          getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
            (res) => {
              res => {
              productData.value = res.productData;
            }
          );
@@ -702,34 +694,46 @@
  productFormVisible.value = false;
};
// å®¡æ‰¹é€šè¿‡æ–¹æ³•
const approvePurchase = (row) => {
  ElMessageBox.confirm(`确认通过采购合同号为 ${row.purchaseContractNumber} çš„审批?`, '审批确认', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }).then(() => {
    updateApprovalStatus({ id: row.id, approvalStatus: 1}).then((res)=>{
      proxy.$modal.msgSuccess('审批成功');
  const approvePurchase = row => {
    ElMessageBox.confirm(
      `确认通过采购合同号为 ${row.purchaseContractNumber} çš„审批?`,
      "审批确认",
      {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
      }
    )
      .then(() => {
        updateApprovalStatus({ id: row.id, approvalStatus: 1 }).then(res => {
          proxy.$modal.msgSuccess("审批成功");
      getList();
        });
    })
  }).catch(() => {
    proxy.$modal.msg('已取消审批');
      .catch(() => {
        proxy.$modal.msg("已取消审批");
  });
};
// å®¡æ‰¹æ‹’绝方法
const rejectPurchase = (row) => {
  ElMessageBox.confirm(`确认拒绝采购合同号为 ${row.purchaseContractNumber} çš„审批?`, '审批确认', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }).then(() => {
    updateApprovalStatus({ id: row.id, approvalStatus: 2}).then((res)=>{
      proxy.$modal.msgSuccess('审批成功');
  const rejectPurchase = row => {
    ElMessageBox.confirm(
      `确认拒绝采购合同号为 ${row.purchaseContractNumber} çš„审批?`,
      "审批确认",
      {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
      }
    )
      .then(() => {
        updateApprovalStatus({ id: row.id, approvalStatus: 2 }).then(res => {
          proxy.$modal.msgSuccess("审批成功");
      getList();
        });
    })
  }).catch(() => {
    proxy.$modal.msg('已取消审批');
      .catch(() => {
        proxy.$modal.msg("已取消审批");
  });
};
@@ -752,12 +756,14 @@
  let ids = [];
  if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.recorderName !== userStore.nickName);
      const unauthorizedData = selectedRows.value.filter(
        item => item.recorderName !== userStore.nickName
      );
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
    ids = selectedRows.value.map((item) => item.id);
      ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
@@ -768,7 +774,7 @@
    type: "warning",
  })
    .then(() => {
      delPurchase(ids).then((res) => {
        delPurchase(ids).then(res => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
@@ -803,47 +809,59 @@
      );
  }
};
const reverseMathNum = (field) => {
  const reverseMathNum = field => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  const taxRate = Number(productForm.value.taxRate);
  if (!taxRate) return;
  if (field === 'taxInclusiveTotalPrice') {
    if (field === "taxInclusiveTotalPrice") {
    // å·²çŸ¥å«ç¨Žæ€»ä»·å’Œæ•°é‡ï¼Œåç®—含税单价
    if (productForm.value.quantity) {
      productForm.value.taxInclusiveUnitPrice =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
        productForm.value.taxInclusiveUnitPrice = (
          Number(productForm.value.taxInclusiveTotalPrice) /
          Number(productForm.value.quantity)
        ).toFixed(2);
    }
    // å·²çŸ¥å«ç¨Žæ€»ä»·å’Œå«ç¨Žå•价,反算数量
    else if (productForm.value.taxInclusiveUnitPrice) {
      productForm.value.quantity =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
        productForm.value.quantity = (
          Number(productForm.value.taxInclusiveTotalPrice) /
          Number(productForm.value.taxInclusiveUnitPrice)
        ).toFixed(2);
    }
    // åç®—不含税总价
    productForm.value.taxExclusiveTotalPrice =
      (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2);
  } else if (field === 'taxExclusiveTotalPrice') {
      productForm.value.taxExclusiveTotalPrice = (
        Number(productForm.value.taxInclusiveTotalPrice) /
        (1 + taxRate / 100)
      ).toFixed(2);
    } else if (field === "taxExclusiveTotalPrice") {
    // åç®—含税总价
    productForm.value.taxInclusiveTotalPrice =
      (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2);
      productForm.value.taxInclusiveTotalPrice = (
        Number(productForm.value.taxExclusiveTotalPrice) *
        (1 + taxRate / 100)
      ).toFixed(2);
    // å·²çŸ¥æ•°é‡ï¼Œåç®—含税单价
    if (productForm.value.quantity) {
      productForm.value.taxInclusiveUnitPrice =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
        productForm.value.taxInclusiveUnitPrice = (
          Number(productForm.value.taxInclusiveTotalPrice) /
          Number(productForm.value.quantity)
        ).toFixed(2);
    }
    // å·²çŸ¥å«ç¨Žå•价,反算数量
    else if (productForm.value.taxInclusiveUnitPrice) {
      productForm.value.quantity =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
        productForm.value.quantity = (
          Number(productForm.value.taxInclusiveTotalPrice) /
          Number(productForm.value.taxInclusiveUnitPrice)
        ).toFixed(2);
    }
  }
};
// é”€å”®åˆåŒé€‰æ‹©æ”¹å˜æ–¹æ³•
const salesLedgerChange = async (row) => {
  const salesLedgerChange = async row => {
  console.log("row", row);
  var index = salesContractList.value.findIndex((item) => item.id == row);
    var index = salesContractList.value.findIndex(item => item.id == row);
  console.log("index", index);
  if (index > -1) {
    form.value.projectName = salesContractList.value[index].projectName;
@@ -861,12 +879,12 @@
};
// æ˜¾ç¤ºäºŒç»´ç 
const showQRCode = async (row) => {
  const showQRCode = async row => {
  try {
    // æž„建二维码内容,只包含采购合同号(纯文本)
    const qrContent = row.purchaseContractNumber || '';
      const qrContent = row.purchaseContractNumber || "";
    // æ£€æŸ¥å†…容是否为空
    if (!qrContent || qrContent.trim() === '') {
      if (!qrContent || qrContent.trim() === "") {
      proxy.$modal.msgWarning("该行没有采购合同号,无法生成二维码");
      return;
    }
@@ -874,13 +892,13 @@
      width: 200,
      margin: 2,
      color: {
        dark: '#000000',
        light: '#FFFFFF'
      }
          dark: "#000000",
          light: "#FFFFFF",
        },
    });
    qrCodeDialogVisible.value = true;
  } catch (error) {
    console.error('生成二维码失败:', error);
      console.error("生成二维码失败:", error);
    proxy.$modal.msgError("生成二维码失败:" + error.message);
  }
};
@@ -892,7 +910,7 @@
    return;
  }
  
  const a = document.createElement('a');
    const a = document.createElement("a");
  a.href = qrCodeUrl.value;
  a.download = `采购合同号二维码_${new Date().getTime()}.png`;
  document.body.appendChild(a);
@@ -914,8 +932,12 @@
  scanRemark: "",
});
const scanAddRules = {
  purchaseContractNumber: [{ required: true, message: "请输入采购合同号", trigger: "blur" }],
  supplierName: [{ required: true, message: "请输入供应商名称", trigger: "blur" }],
    purchaseContractNumber: [
      { required: true, message: "请输入采购合同号", trigger: "blur" },
    ],
    supplierName: [
      { required: true, message: "请输入供应商名称", trigger: "blur" },
    ],
  projectName: [{ required: true, message: "请输入项目名称", trigger: "blur" }],
};
@@ -949,12 +971,12 @@
};
// è§£æžæ‰«ç å†…容(模拟解析二维码数据)
const parseScanContent = (content) => {
  const parseScanContent = content => {
  if (!content) return;
  
  // æ¨¡æ‹Ÿè§£æžäºŒç»´ç å†…容,这里可以根据实际需求调整解析逻辑
  // å‡è®¾æ‰«ç å†…容格式为:合同号|供应商|项目|金额|付款方式
  const parts = content.split('|');
    const parts = content.split("|");
  if (parts.length >= 3) {
    scanAddForm.purchaseContractNumber = parts[0] || "";
    scanAddForm.supplierName = parts[1] || "";
@@ -972,7 +994,7 @@
// æäº¤æ‰«ç æ–°å¢ž
const submitScanAdd = () => {
  proxy.$refs["scanAddFormRef"].validate((valid) => {
    proxy.$refs["scanAddFormRef"].validate(valid => {
    if (valid) {
      // æž„建新增数据
      const newData = {
@@ -984,7 +1006,7 @@
        recorderName: scanAddForm.recorderName,
        entryDate: getCurrentDate(),
        remark: scanAddForm.scanRemark,
        type: 2
          type: 2,
      };
      
      // æ¨¡æ‹Ÿæ–°å¢žæˆåŠŸ
@@ -998,7 +1020,7 @@
};
// æ‰“开扫码登记对话框
const openScanDialog = (row) => {
  const openScanDialog = row => {
  scanForm.purchaseContractNumber = row.purchaseContractNumber;
  scanForm.supplierName = row.supplierName;
  scanForm.projectName = row.projectName;
@@ -1018,7 +1040,7 @@
// æäº¤æ‰«ç ç™»è®°
const submitScan = () => {
  proxy.$refs["scanFormRef"].validate((valid) => {
    proxy.$refs["scanFormRef"].validate(valid => {
    if (valid) {
      // æ·»åŠ æ‰«ç è®°å½•
      scanRecords.value.push({
@@ -1048,7 +1070,7 @@
// æ·»åŠ è¡Œç±»åæ–¹æ³•
const tableRowClassName = ({ row }) => {
  return row.isInvalid ? 'invalid-row' : '';
    return row.isInvalid ? "invalid-row" : "";
};
onMounted(() => {
src/views/productionManagement/processRoute/index.vue
@@ -48,6 +48,14 @@
                     v-model:visible="isShowItemModal"
                     :record="record"
                     @completed="getList" />
    <FileListDialog ref="fileListDialogRef"
                    v-model="fileDialogVisible"
                    :show-upload-button="true"
                    :show-delete-button="true"
                    :delete-method="handleAttachmentDelete"
                    :process-route-id="currentProcessRouteId"
                    :name-column-label="'附件名称'"
                    @upload="handleAttachmentUpload" />
  </div>
</template>
@@ -57,7 +65,14 @@
  import EditProcess from "@/views/productionManagement/processRoute/Edit.vue";
  import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue";
  import { listPage, del } from "@/api/productionManagement/processRoute.js";
  import {
    listProcessRouteFiles,
    addProcessRouteFile,
    delProcessRouteFile,
  } from "@/api/productionManagement/processRouteFile.js";
  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
  import { useRouter } from "vue-router";
  import { ElMessage, ElMessageBox } from "element-plus";
  const router = useRouter();
  const data = reactive({
@@ -108,6 +123,13 @@
            showItemModal(row);
          },
        },
        {
          name: "附件",
          type: "text",
          clickFun: row => {
            openFileDialog(row);
          },
        },
      ],
    },
  ]);
@@ -123,8 +145,79 @@
    size: 100,
    total: 0,
  });
  // é™„件相关
  const fileDialogVisible = ref(false);
  const fileListDialogRef = ref(null);
  const currentProcessRouteId = ref(null);
  const filePage = reactive({
    current: 1,
    size: 1000,
    total: 0,
  });
  const { proxy } = getCurrentInstance();
  // é™„件:查询
  const fetchProcessRouteFiles = async processRouteId => {
    const params = {
      current: filePage.current,
      size: filePage.size,
      processRouteId,
    };
    const res = await listProcessRouteFiles(params);
    const records = res?.data?.records || [];
    filePage.total = res?.data?.total || records.length;
    const mapped = records.map(item => ({
      id: item.id,
      name: item.fileName || item.name,
      url: item.fileUrl || item.url,
      raw: item,
    }));
    fileListDialogRef.value?.setList(mapped);
  };
  // æ‰“开附件弹窗
  const openFileDialog = async row => {
    currentProcessRouteId.value = row.id;
    fileDialogVisible.value = true;
    await fetchProcessRouteFiles(row.id);
  };
  // åˆ·æ–°é™„件列表
  const refreshFileList = async () => {
    if (!currentProcessRouteId.value) return;
    await fetchProcessRouteFiles(currentProcessRouteId.value);
  };
  // ä¸Šä¼ é™„ä»¶
  const handleAttachmentUpload = async filePayload => {
    if (!currentProcessRouteId.value) return;
    const payload = {
      fileName: filePayload?.fileName || filePayload?.name,
      fileUrl: filePayload?.fileUrl || filePayload?.url,
      processRouteId: currentProcessRouteId.value,
    };
    await addProcessRouteFile(payload);
    ElMessage.success("文件上传成功");
    await refreshFileList();
  };
  // åˆ é™¤é™„ä»¶
  const handleAttachmentDelete = async row => {
    if (!row?.id) return false;
    try {
      await ElMessageBox.confirm("确认删除该附件?", "提示", {
        type: "warning",
      });
    } catch {
      return false;
    }
    await delProcessRouteFile([row.id]);
    ElMessage.success("删除成功");
    await refreshFileList();
  };
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
src/views/productionManagement/productionOrder/New.vue
@@ -1,14 +1,14 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
    <el-dialog v-model="isShow"
        title="新增生产订单"
        width="800"
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
        <el-form-item
            label="产品名称"
               @close="closeModal">
      <el-form label-width="140px"
               :model="formState"
               label-position="top"
               ref="formRef">
        <el-form-item label="产品名称"
            prop="productModelId"
            :rules="[
                {
@@ -16,29 +16,24 @@
                message: '请选择产品',
                trigger: 'change',
              }
            ]"
        >
          <el-button type="primary" @click="showProductSelectDialog = true">
            ]">
          <el-button type="primary"
                     @click="showProductSelectDialog = true">
            {{ formState.productName ? formState.productName : '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item
            label="规格"
            prop="productModelName"
        >
          <el-input v-model="formState.productModelName"  disabled />
        <el-form-item label="规格"
                      prop="productModelName">
          <el-input v-model="formState.productModelName"
                    disabled />
        </el-form-item>
        <el-form-item
            label="单位"
            prop="unit"
        >
          <el-input v-model="formState.unit"  disabled />
        <el-form-item label="单位"
                      prop="unit">
          <el-input v-model="formState.unit"
                    disabled />
        </el-form-item>
        <el-form-item label="工艺路线">
          <el-select v-model="formState.routeId"
          <el-select v-model="formState.technologyRoutingId"
                     placeholder="请选择工艺路线"
                     style="width: 100%;"
                     :loading="bindRouteLoading">
@@ -48,24 +43,30 @@
                       :value="item.id" />
          </el-select>
        </el-form-item>
        <el-form-item
            label="需求数量"
            prop="quantity"
        >
          <el-input-number v-model="formState.quantity" :step="1" :min="1" style="width: 100%" />
        <el-form-item label="需求数量"
                      prop="quantity">
          <el-input-number v-model="formState.quantity"
                           :step="1"
                           :min="0"
                           style="width: 100%" />
        </el-form-item>
        <el-form-item label="计划完成时间"
                      prop="planCompleteTime">
          <el-date-picker v-model="formState.planCompleteTime"
                          type="date"
                          placeholder="选择日期"
                          value-format="YYYY-MM-DD"
                          style="width: 100%" />
        </el-form-item>
      </el-form>
      <!-- äº§å“é€‰æ‹©å¼¹çª— -->
      <ProductSelectDialog
          v-model="showProductSelectDialog"
      <ProductSelectDialog v-model="showProductSelectDialog"
          @confirm="handleProductSelect"
          single
      />
                           single />
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button type="primary"
                     @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
@@ -76,8 +77,11 @@
<script setup>
import {ref, computed, getCurrentInstance} from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import {addProductOrder, listProcessRoute} from "@/api/productionManagement/productionOrder.js";
  import {
    addProductOrder,
    listProcessRoute,
  } from "@/api/productionManagement/productionOrder.js";
  import { listPage } from "@/api/productionManagement/processRoute.js";
const props = defineProps({
  visible: {
    type: Boolean,
@@ -87,21 +91,22 @@
  type: {
    type: String,
    required: true,
    default: 'qualified',
      default: "qualified",
  },
});
const emit = defineEmits(['update:visible', 'completed']);
  const emit = defineEmits(["update:visible", "completed"]);
// å“åº”式数据(替代选项式的 data)
const formState = ref({
  productId: undefined,
  productModelId: undefined,
  routeId: undefined,
    technologyRoutingId: undefined,
  productName: "",
  productModelName: "",
  unit: "",
  quantity: 0,
    planCompleteTime: "",
});
const isShow = computed({
@@ -109,29 +114,31 @@
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
      emit("update:visible", val);
  },
});
const showProductSelectDialog = ref(false);
let { proxy } = getCurrentInstance()
  let { proxy } = getCurrentInstance();
const closeModal = () => {
  // é‡ç½®è¡¨å•数据
  formState.value = {
    productId: undefined,
    productModelId: undefined,
    routeId: undefined,
      technologyRoutingId: undefined,
    productName: "",
    productModelName: "",
    quantity: '',
      unit: "",
      quantity: "",
      planCompleteTime: "",
  };
  isShow.value = false;
};
// äº§å“é€‰æ‹©å¤„理
const handleProductSelect = async (products) => {
  const handleProductSelect = async products => {
  if (products && products.length > 0) {
    const product = products[0];
    formState.value.productId = product.productId;
@@ -142,22 +149,24 @@
    showProductSelectDialog.value = false;
    fetchRouteOptions( product.id);
    // è§¦å‘表单验证更新
    proxy.$refs["formRef"]?.validateField('productModelId');
      proxy.$refs["formRef"]?.validateField("productModelId");
  }
};
const routeOptions = ref([]);
const bindRouteLoading = ref(false);
const fetchRouteOptions = (productModelId) => {
  formState.value.routeId = undefined;
  routeOptions.value = []
  const fetchRouteOptions = productModelId => {
    formState.value.technologyRoutingId = undefined;
    routeOptions.value = [];
  bindRouteLoading.value = true;
  listProcessRoute({ productModelId: productModelId }).then(res => {
    routeOptions.value = res.data || [];
  }).finally(() => {
    bindRouteLoading.value = false;
    listPage({ productModelId: productModelId })
      .then(res => {
        routeOptions.value = res.data.records || [];
  })
}
      .finally(() => {
        bindRouteLoading.value = false;
      });
  };
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
@@ -171,18 +180,21 @@
        proxy.$modal.msgError("请选择规格");
        return;
      }
        if (formState.value.quantity <= 0) {
          proxy.$modal.msgError("需求数量必须大于0");
          return;
        }
      addProductOrder(formState.value).then(res => {
        // å…³é—­æ¨¡æ€æ¡†
        isShow.value = false;
        // å‘ŠçŸ¥çˆ¶ç»„件已完成
        emit('completed');
          emit("completed");
        proxy.$modal.msgSuccess("提交成功");
      })
        });
    }
  })
    });
};
defineExpose({
  closeModal,
src/views/productionManagement/productionOrder/index.vue
@@ -49,8 +49,10 @@
        </el-form-item>
      </el-form>
      <div class="action-buttons">
        <el-button type="primary" @click="isShowNewModal = true">新增</el-button>
        <el-button type="danger" @click="handleDelete">删除</el-button>
        <el-button type="primary"
                   @click="isShowNewModal = true">新增</el-button>
        <el-button type="danger"
                   @click="handleDelete">删除</el-button>
        <el-button @click="handleOut">导出</el-button>
      </div>
    </div>
@@ -65,11 +67,9 @@
                @selection-change="handleSelectionChange"
                @pagination="pagination">
        <template #completionStatus="{ row }">
          <el-progress
            :percentage="toProgressPercentage(row?.completionStatus)"
          <el-progress :percentage="toProgressPercentage(row?.completionStatus)"
            :color="progressColor(toProgressPercentage(row?.completionStatus))"
            :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''"
          />
                       :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" />
        </template>
      </PIMTable>
    </div>
@@ -98,18 +98,12 @@
        </span>
      </template>
    </el-dialog>
    <MaterialLedgerDialog
      v-model="materialDialogVisible"
    <MaterialLedgerDialog v-model="materialDialogVisible"
      :order-row="currentMaterialOrder"
      @saved="getList"
    />
    <MaterialDetailDialog
      v-model="materialDetailDialogVisible"
                          @saved="getList" />
    <MaterialDetailDialog v-model="materialDetailDialogVisible"
      :order-row="currentMaterialDetailOrder"
      @confirmed="getList"
    />
                          @confirmed="getList" />
    <new-product-order v-if="isShowNewModal"
                         v-model:visible="isShowNewModal"
                         @completed="handleQuery" />
@@ -117,7 +111,15 @@
</template>
<script setup>
  import { computed, defineAsyncComponent, getCurrentInstance, onMounted, reactive, ref, toRefs } from "vue";
  import {
    computed,
    defineAsyncComponent,
    getCurrentInstance,
    onMounted,
    reactive,
    ref,
    toRefs,
  } from "vue";
  import { ElMessageBox } from "element-plus";
  import dayjs from "dayjs";
  import { useRouter } from "vue-router";
@@ -125,13 +127,17 @@
    productOrderListPage,
    listProcessRoute,
    bindingRoute,
    listProcessBom, delProductOrder,
    listProcessBom,
    delProductOrder,
  } from "@/api/productionManagement/productionOrder.js";
  import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
  import MaterialLedgerDialog from "@/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue";
  import MaterialDetailDialog from "@/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
  import { listPage } from "@/api/productionManagement/processRoute.js";
  const NewProductOrder = defineAsyncComponent(() =>
    import("@/views/productionManagement/productionOrder/New.vue")
  );
  const { proxy } = getCurrentInstance();
@@ -142,32 +148,32 @@
    {
      label: "生产订单号",
      prop: "npsNo",
      width: '150px',
      width: "150px",
    },
    {
      label: "销售合同号",
      prop: "salesContractNo",
      width: '150px',
      width: "150px",
    },
    {
      label: "客户名称",
      prop: "customerName",
      width: '200px',
      width: "200px",
    },
    {
      label: "产品名称",
      prop: "productCategory",
      width: '120px',
      prop: "productName",
      width: "120px",
    },
    {
      label: "规格",
      prop: "specificationModel",
      width: '120px',
      prop: "model",
      width: "120px",
    },
    {
      label: "工艺路线编号",
      prop: "processRouteCode",
      width: '200px',
      width: "200px",
    },
    {
      label: "需求数量",
@@ -197,8 +203,8 @@
      width: 120,
    },
    {
      label: "交付日期",
      prop: "deliveryDate",
      label: "计划完成时间",
      prop: "planCompleteTime",
      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
      width: 120,
    },
@@ -288,18 +294,18 @@
  // æ·»åŠ è¡¨è¡Œç±»åæ–¹æ³•
  const tableRowClassName = ({ row }) => {
    if (!row.deliveryDate) return '';
    if (row.isFh) return '';
    if (!row.planCompleteTime) 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";
    }
  };
@@ -329,8 +335,8 @@
    }
    bindRouteLoading.value = true;
    try {
      const res = await listProcessRoute({ productModelId: row.productModelId });
      routeOptions.value = res.data || [];
      const res = await listPage({ productModelId: row.productModelId });
      routeOptions.value = res.data.records || [];
    } catch (e) {
      console.error("获取工艺路线列表失败:", e);
      proxy.$modal.msgError("获取工艺路线列表失败");
@@ -452,14 +458,14 @@
  };
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  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;
@@ -468,12 +474,14 @@
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      delProductOrder(ids).then((res) => {
    })
      .then(() => {
        delProductOrder(ids).then(res => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    }).catch(() => {
      })
      .catch(() => {
      proxy.$modal.msg("已取消");
    });
  };
@@ -486,7 +494,11 @@
      type: "warning",
    })
      .then(() => {
        proxy.download("/productOrder/export", {...searchForm.value}, "生产订单.xlsx");
        proxy.download(
          "/productOrder/export",
          { ...searchForm.value },
          "生产订单.xlsx"
        );
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
@@ -512,11 +524,11 @@
}
:deep(.yellow) {
  background-color: #FAF0DE;
    background-color: #faf0de;
}
:deep(.pink) {
  background-color: #FAE1DE;
    background-color: #fae1de;
}
:deep(.red) {
@@ -524,10 +536,9 @@
}
:deep(.purple){
  background-color: #F4DEFA;
    background-color: #f4defa;
}
.table_list {
    margin-top: unset;
}
</style>
src/views/productionPlan/productionPlan/index.vue
@@ -2,16 +2,19 @@
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm"
               ref="queryRef"
               :inline="true">
        <!-- ç®€åŒ–版搜索条件 -->
        <el-form-item label="主生产计划号:">
        <el-form-item label="主生产计划号:"
                      prop="mpsNo">
          <el-input v-model="searchForm.mpsNo"
                    placeholder="请输入"
                    clearable
                    style="width: 160px;"
                    @keyup.enter="handleQuery" />
        </el-form-item>
        <el-form-item label="需求日期范围:">
        <el-form-item label="需求日期范围:"
                      prop="dateRange">
          <el-date-picker v-model="searchForm.dateRange"
                          type="daterange"
                          range-separator="至"
@@ -21,7 +24,8 @@
                          style="width: 240px;"
                          @change="handleQuery" />
        </el-form-item>
        <el-form-item label="下发状态:">
        <el-form-item label="下发状态:"
                      prop="status">
          <el-select v-model="searchForm.status"
                     placeholder="请选择状态"
                     clearable
@@ -36,36 +40,22 @@
          </el-select>
        </el-form-item>
        <!-- å±•开版搜索条件 -->
        <template v-if="searchFormExpanded">
          <el-form-item label="产品名称:">
        <el-form-item label="产品名称:"
                      prop="productName">
            <el-input v-model="searchForm.productName"
                      placeholder="请输入"
                      clearable
                      style="width: 160px;"
                      @keyup.enter="handleQuery" />
          </el-form-item>
          <el-form-item label="产品规格:">
        <el-form-item label="产品规格:"
                      prop="model">
            <el-input v-model="searchForm.model"
                      placeholder="请输入"
                      clearable
                      style="width: 160px;"
                      @keyup.enter="handleQuery" />
          </el-form-item>
          <el-form-item label="物料编码:">
            <el-input v-model="searchForm.materialCode"
                      placeholder="请输入"
                      clearable
                      style="width: 160px;"
                      @keyup.enter="handleQuery" />
          </el-form-item>
          <el-form-item label="申请单编号:">
            <el-input v-model="searchForm.applyNo"
                      placeholder="请输入"
                      clearable
                      style="width: 160px;"
                      @keyup.enter="handleQuery" />
          </el-form-item>
        </template>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery">搜索</el-button>
@@ -74,6 +64,8 @@
          <el-button type="primary"
                     @click="handleAdd">新增</el-button>
          <el-button type="warning"
                     @click="handleMerge">合并下发</el-button>
          <el-button type="warning"
                     @click="handleImport">导入</el-button>
          <el-button type="warning"
                     @click="handleExport">导出</el-button>
@@ -81,16 +73,6 @@
      </el-form>
      <div>
      </div>
    </div>
    <div class="search-header">
      <el-button type="text"
                 @click="toggleSearchForm">
        <el-icon>
          <ArrowUp v-if="searchFormExpanded" />
          <ArrowDown v-else />
        </el-icon>
        {{ searchFormExpanded ? '收起搜索条件' : '展开搜索条件' }}
      </el-button>
    </div>
    <div class="table_list">
      <PIMTable rowKey="id"
@@ -117,23 +99,16 @@
               label-width="120px">
        <el-row :gutter="20">
          <el-col :span="10">
            <el-form-item label="物料编码">
              <div class="info-display">{{ mergeForm.materialCode || '-' }}</div>
            </el-form-item>
          </el-col>
          <el-col :span="10">
            <el-form-item label="产品名称">
              <el-tag class="info-display">{{ mergeForm.productName || '-' }}</el-tag>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="10">
          <el-col>
            <el-form-item label="产品规格">
              <div class="info-display">{{ mergeForm.model || '-' }}</div>
            </el-form-item>
          </el-col>
          <el-col :span="10">
          </el-col>
        </el-row>
        <el-form-item label="计划完成时间">
@@ -142,7 +117,7 @@
                          value-format="YYYY-MM-DD"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item label="生产方数">
        <el-form-item label="生产数量">
          <el-input-number v-model="mergeForm.totalAssignedQuantity"
                           :min="0"
                           :max="sumAssignedQuantity"
@@ -182,16 +157,12 @@
        <el-form-item label="主生产计划号"
                      prop="mpsNo">
          <el-input v-model="form.mpsNo"
                    placeholder="请输入主生产计划号" />
        </el-form-item>
        <el-form-item label="申请单编号"
                      prop="applyNo">
          <el-input v-model="form.applyNo"
                    placeholder="请输入申请单编号" />
                    disabled
                    placeholder="新增后自动生成" />
        </el-form-item>
        <el-form-item label="产品名称"
                      prop="productMaterialId">
          <el-tree-select v-model="form.productMaterialId"
                      prop="productId">
          <el-tree-select v-model="form.productId"
                          placeholder="请选择"
                          clearable
                          :data="productOptions"
@@ -279,6 +250,7 @@
    productionPlanAdd,
    productionPlanUpdate,
    productionPlanDelete,
    productionPlanCombine,
  } from "@/api/productionPlan/productionPlan.js";
  import { productTreeList, modelListPage } from "@/api/basicData/product.js";
  import PIMTable from "./components/PIMTable.vue";
@@ -297,10 +269,10 @@
    return Promise.resolve();
  };
  const productionPlanCombine = payload => {
    console.log("Mock productionPlanCombine called with:", payload);
    return Promise.resolve({ code: 200, msg: "合并下发成功" });
  };
  // const productionPlanCombine = payload => {
  //   console.log("Mock productionPlanCombine called with:", payload);
  //   return Promise.resolve({ code: 200, msg: "合并下发成功" });
  // };
  const tableColumn = ref([
    {
@@ -309,14 +281,13 @@
      width: "150px",
    },
    {
      label: "申请单编号",
      prop: "applyNo",
      label: "来源",
      prop: "source",
      width: "150px",
      dataType: "tag",
      formatType: params => {
        return params == "内部" ? "info" : "primary";
    },
    {
      label: "物料编码",
      prop: "materialCode",
      width: "150px",
    },
    {
      label: "产品名称",
@@ -401,7 +372,7 @@
      label: "操作",
      align: "center",
      fixed: "right",
      width: 150,
      width: 250,
      operation: [
        {
          name: "编辑",
@@ -412,6 +383,23 @@
          },
          clickFun: row => {
            handleEdit(row);
          },
        },
        {
          name: "下发",
          type: "text",
          showHide: row => {
            return row.status == 0;
          },
          clickFun: row => {
            mergeForm.productName = row.productName || "";
            mergeForm.model = row.model || "";
            mergeForm.totalAssignedQuantity = Number(row.qtyRequired || 0);
            mergeForm.planCompleteTime = row.requiredDate || "";
            mergeForm.productId = row.productId || "";
            mergeForm.ids = [row.id];
            sumAssignedQuantity.value = Number(row.qtyRequired || 0);
            isShowNewModal.value = true;
          },
        },
        {
@@ -446,12 +434,11 @@
  const isShowNewModal = ref(false);
  // åˆå¹¶ä¸‹å‘表单数据
  const mergeForm = reactive({
    materialCode: "",
    productName: "",
    model: "",
    totalAssignedQuantity: 0,
    planCompleteTime: "",
    productMaterialId: "",
    productId: "",
  });
  // å¯¼å…¥ç›¸å…³
@@ -468,16 +455,15 @@
  const operationType = ref("add"); // add | edit
  const productOptions = ref([]);
  const specificationOptions = ref([]);
  const queryRef = ref(null);
  const formRef = ref(null);
  const form = reactive({
    id: undefined,
    mpsNo: "",
    applyNo: "",
    productMaterialId: undefined,
    productId: undefined,
    productModelId: undefined,
    productName: "",
    model: "",
    materialCode: "",
    qtyRequired: 0,
    unit: "方",
    requiredDate: "",
@@ -485,11 +471,7 @@
    remark: "",
  });
  const rules = reactive({
    mpsNo: [{ required: true, message: "请输入主生产计划号", trigger: "blur" }],
    applyNo: [{ required: true, message: "请输入申请单编号", trigger: "blur" }],
    productMaterialId: [
      { required: true, message: "请选择产品", trigger: "change" },
    ],
    productId: [{ required: true, message: "请选择产品", trigger: "change" }],
    productModelId: [
      { required: true, message: "请选择产品规格", trigger: "change" },
    ],
@@ -506,7 +488,6 @@
      path: "/productionPlan/trackProgress",
      query: {
        id: row.id,
        applyNo: row.applyNo,
        productName: row.productName,
        model: row.model,
      },
@@ -539,7 +520,6 @@
  const handleProductChange = value => {
    form.productModelId = undefined;
    form.model = undefined;
    form.materialCode = undefined;
    // æŸ¥æ‰¾é€‰ä¸­çš„产品名称
    const findProductName = (options, val) => {
      for (const option of options) {
@@ -569,14 +549,12 @@
  };
  const handleChangeSpecification = value => {
    form.materialCode = undefined;
    form.model = undefined;
    form.unit = "";
    const selectedModel = specificationOptions.value.find(
      item => item.id === value
    );
    if (selectedModel) {
      form.materialCode = selectedModel.materialCode;
      form.model = selectedModel.model;
      form.unit = selectedModel.unit || "方";
    }
@@ -587,8 +565,7 @@
      mpsNo: "",
      productName: "",
      model: "",
      materialCode: "",
      applyNo: "",
      status: "",
      dateRange: [],
    },
    searchFormExpanded: false,
@@ -609,12 +586,14 @@
  /** é‡ç½®æŒ‰é’®æ“ä½œ */
  const handleReset = () => {
    if (proxy.resetForm) {
      proxy.resetForm("queryRef");
    }
    Object.assign(searchForm.value, {
      mpsNo: "",
      productName: "",
      model: "",
      materialCode: "",
      applyNo: "",
      status: "",
      dateRange: [],
    });
    page.current = 1;
@@ -631,16 +610,18 @@
    // éåŽ†è¡¨æ ¼æ•°æ®ï¼ŒæŒ‰äº§å“ç±»åˆ«æ±‡æ€»
    tableData.value.forEach(row => {
      const category = row.materialCode;
      const category = row.productName || "未知产品";
      if (!summary[category]) {
        summary[category] = {
          materialCode: category,
          totalAssignedQuantity: 0,
        };
      }
      summary[category].totalAssignedQuantity += (
        Number(row.qtyRequired) - Number(row.assignedQuantity)
      ).toFixed(4);
      summary[category].totalAssignedQuantity += Number(
        (
          Number(row.qtyRequired || 0) - Number(row.assignedQuantity || 0)
        ).toFixed(4)
      );
    });
    // è½¬æ¢ä¸ºæ•°ç»„格式
@@ -651,8 +632,10 @@
    tableLoading.value = true;
    // æž„造搜索参数
    const params = { ...searchForm.value, ...page };
    params.requiredDateStart = params.dateRange ? params.dateRange[0] : "";
    params.requiredDateEnd = params.dateRange ? params.dateRange[1] : "";
    params.requiredDateStart =
      params.dateRange && params.dateRange.length > 0 ? params.dateRange[0] : "";
    params.requiredDateEnd =
      params.dateRange && params.dateRange.length > 1 ? params.dateRange[1] : "";
    delete params.dateRange;
    productionPlanListPage(params)
      .then(res => {
@@ -667,18 +650,18 @@
      });
  };
  // é€‰ä¸­çš„序列号
  const selectedserialNo = ref("");
  // é€‰ä¸­çš„产品规格ID
  const selectedProductModelId = ref("");
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
    // å¦‚果有选中的行,记录第一个选中行的序列号
    // å¦‚果有选中的行,记录第一个选中行的产品规格ID
    if (selection.length > 0) {
      selectedserialNo.value = selection[0].materialCode;
      selectedProductModelId.value = selection[0].productModelId;
    } else {
      // å¦‚果没有选中的行,清空序列号
      selectedserialNo.value = "";
      // å¦‚果没有选中的行,清空产品规格ID
      selectedProductModelId.value = "";
    }
  };
@@ -691,11 +674,11 @@
      return false;
    }
    // å¦‚果没有选中的行,所有行都可选择
    if (!selectedserialNo.value) {
    if (!selectedProductModelId.value) {
      return true;
    }
    // å¦‚果有选中的行,只有序列号相同的行才可选择
    return row.materialCode === selectedserialNo.value;
    // å¦‚果有选中的行,只有产品规格ID相同的行才可选择
    return row.productModelId === selectedProductModelId.value;
  };
  // æ‹‰å–数据按钮操作
  const loadProdDataLoading = ref(false);
@@ -711,26 +694,18 @@
    const firstRow = selectedRows.value[0];
    const productName = firstRow.productName || "";
    // è®¡ç®—总制造数量
    // è®¡ç®—总制造数量 (默认qtyRequired的和)
    const totalAssignedQuantity = selectedRows.value.reduce((sum, row) => {
      return (
        sum +
        (row.qtyRequired == null
          ? 0
          : Number(
              Number(row.qtyRequired) - Number(row.assignedQuantity).toFixed(4)
            ))
      );
      return sum + Number(row.qtyRequired || 0);
    }, 0);
    sumAssignedQuantity.value = totalAssignedQuantity;
    console.log(totalAssignedQuantity);
    // è®¾ç½®è¡¨å•数据
    mergeForm.materialCode = selectedserialNo.value;
    mergeForm.productName = productName;
    mergeForm.model = firstRow.model || "";
    mergeForm.totalAssignedQuantity = totalAssignedQuantity;
    mergeForm.planCompleteTime = firstRow.planCompleteTime || "";
    mergeForm.productMaterialId = firstRow.productMaterialId || "";
    mergeForm.planCompleteTime = firstRow.requiredDate || "";
    mergeForm.productId = firstRow.productId || "";
    mergeForm.ids = selectedRows.value.map(row => row.id);
    // æ‰“开弹窗
@@ -740,18 +715,14 @@
  // å¤„理合并下发提交
  const handleMergeSubmit = () => {
    if (mergeForm.totalAssignedQuantity === 0) {
      ElMessage.warning("请输入生产方数");
      ElMessage.warning("请输入生产数量");
      return;
    }
    console.log(sumAssignedQuantity.value, "sumAssignedQuantity");
    // è®¡ç®—当前选中行的总数量
    const totalVolume = selectedRows.value.reduce((sum, row) => {
      return sum + (Number(row.qtyRequired) - Number(row.assignedQuantity) || 0);
    }, 0);
    // éªŒè¯totalAssignedQuantity不能大于总方数
    if (mergeForm.totalAssignedQuantity > sumAssignedQuantity.value) {
      ElMessage.error("生产方数不能大于当前计算的总值");
      ElMessage.error("生产数量不能大于当前计算的总值");
      return;
    }
@@ -861,12 +832,10 @@
    Object.assign(form, {
      id: undefined,
      mpsNo: "",
      applyNo: "",
      productName: "",
      productMaterialId: undefined,
      productId: undefined,
      productModelId: undefined,
      model: "",
      materialCode: "",
      qtyRequired: 0,
      unit: "方",
      requiredDate: "",
@@ -883,12 +852,10 @@
    Object.assign(form, {
      id: row.id,
      mpsNo: row.mpsNo || "",
      applyNo: row.applyNo || "",
      productName: row.productName || "",
      productMaterialId: row.productMaterialId || undefined,
      productId: row.productId || undefined,
      productModelId: row.productModelId || undefined,
      model: row.model || "",
      materialCode: row.materialCode || "",
      qtyRequired: row.qtyRequired || 0,
      unit: row.unit || "方",
      requiredDate: row.requiredDate || "",
@@ -897,7 +864,7 @@
    });
    dialogVisible.value = true;
    fetchProductOptions();
    fetchSpecificationOptions(row.productMaterialId);
    fetchSpecificationOptions(row.productId);
  };
  // åˆ é™¤
@@ -1178,6 +1145,7 @@
    color: #303133;
    font-size: 14px;
    min-height: 32px;
    width: 100%;
    display: flex;
    align-items: center;
  }
src/views/salesManagement/salesLedger/index.vue
@@ -1,25 +1,41 @@
<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-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search"
          <el-input v-model="searchForm.customerName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
            @change="handleQuery" />
        </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>
          <el-button type="primary" @click="handleQuery"> æœç´¢ </el-button>
          <el-button type="primary"
                     @click="handleQuery"> æœç´¢ </el-button>
        </el-form-item>
      </el-form>
    </div>
@@ -27,52 +43,92 @@
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="openForm('add')">
          <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 @click="handleDelete">删除</el-button>
          <el-button type="primary" plain @click="handlePrint">打印</el-button>
          <el-button type="danger"
                     plain
                     @click="handleDelete">删除</el-button>
          <el-button type="primary"
                     plain
                     @click="handlePrint">打印</el-button>
        </div>
      </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="(param) => summarizeChildrenTable(param, props.row)">
              <el-table-column align="center" label="序号" type="index"/>
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
            <el-table :data="props.row.children"
                      border
                      show-summary
                      :summary-method="(param) => summarizeChildrenTable(param, props.row)">
              <el-table-column align="center"
                               label="序号"
                               type="index" />
              <el-table-column label="产品大类"
                               prop="productCategory" />
              <el-table-column label="规格型号"
                               prop="specificationModel" />
              <el-table-column label="单位"
                               prop="unit" />
                            <el-table-column label="产品状态"
                                                             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">
              <el-table-column label="发货状态"
                               width="140"
                               align="center">
                                <template #default="scope">
                                    <el-tag :type="getShippingStatusType(scope.row)" size="small">
                  <el-tag :type="getShippingStatusType(scope.row)"
                          size="small">
                                        {{ getShippingStatusText(scope.row) }}
                                    </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>
@@ -87,16 +143,25 @@
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="sensitiveAmountFormatter" />
              <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="sensitiveAmountFormatter" />
              <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="sensitiveAmountFormatter" />
              <el-table-column label="数量"
                               prop="quantity" />
              <el-table-column label="税率(%)"
                               prop="taxRate" />
              <el-table-column label="含税单价(元)"
                               prop="taxInclusiveUnitPrice"
                               :formatter="sensitiveAmountFormatter" />
              <el-table-column label="含税总价(元)"
                               prop="taxInclusiveTotalPrice"
                               :formatter="sensitiveAmountFormatter" />
              <el-table-column label="不含税总价(元)"
                               prop="taxExclusiveTotalPrice"
                               :formatter="sensitiveAmountFormatter" />
            <!--操作-->
              <el-table-column Width="60px" label="操作" align="center">
              <el-table-column Width="60px"
                               label="操作"
                               align="center">
                <template #default="scope">
                  <el-button
                    link
                  <el-button link
                    type="primary"
                    :disabled="!canShip(scope.row)"
                    @click="openDeliveryForm(scope.row)">
@@ -107,50 +172,121 @@
            </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="paymentMethod" 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="paymentMethod"
                         show-overflow-tooltip />
        <el-table-column label="合同金额(元)"
                         prop="contractAmount"
                         width="220"
                         show-overflow-tooltip
          :formatter="formattedNumber" />
        <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="130" 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="130"
                         align="center">
          <template #default="scope">
            <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit || scope.row.hasProductionRecord || !canEditLedger(scope.row)">编辑</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 || scope.row.hasProductionRecord || !canEditLedger(scope.row)">编辑</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'" filterable>
                <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'"
                         filterable>
                <el-option v-for="item in userList"
                           :key="item.nickName"
                           :label="item.nickName"
                  :value="item.nickName" />
              </el-select>
            </el-form-item>
@@ -158,9 +294,17 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户名称:" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable>
                <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"
                         placeholder="请选择"
                         clearable
                         :disabled="operationType === 'view'"
                         filterable>
                <el-option v-for="item in customerOption"
                           :key="item.id"
                           :label="item.customerName"
                           :value="item.id">
                  {{
                    item.customerName + "——" + item.taxpayerIdentificationNumber
                  }}
@@ -169,72 +313,146 @@
            </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="付款方式">
                            <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" />
              <el-input v-model="form.paymentMethod"
                        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="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 :gutter="30">
          <el-col :span="12">
            <el-form-item label="交货日期:" prop="entryDate">
              <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="entryDate">
              <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>
                    <el-form-item label="产品信息:" prop="entryDate">
                        <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button>
                        <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >删除</el-button>
          <el-form-item label="产品信息:"
                        prop="entryDate">
            <el-button v-if="operationType !== 'view'"
                       type="primary"
                       @click="openProductForm('add')">添加</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" />
                    <el-table-column label="规格型号" prop="specificationModel" />
                    <el-table-column label="单位" prop="unit" />
                    <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 fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'">
          <el-table-column align="center"
                           label="序号"
                           type="index"
                           width="60" />
          <el-table-column label="产品大类"
                           prop="productCategory" />
          <el-table-column label="规格型号"
                           prop="specificationModel" />
          <el-table-column label="单位"
                           prop="unit" />
          <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="isProduction"
                           width="150">
                        <template #default="scope">
                            <el-button link type="primary" size="small"
              <el-tag :type="scope.row.isProduction ? 'success' : 'info'">
                {{ scope.row.isProduction ? '是' : '否' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column fixed="right"
                           label="操作"
                           min-width="60"
                           align="center"
                           v-if="operationType !== 'view'">
            <template #default="scope">
              <el-button link
                         type="primary"
                         size="small"
                                :disabled="isProductShipped(scope.row)"
                                @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
                        </template>
@@ -242,19 +460,35 @@
                </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 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="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="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
@@ -266,164 +500,238 @@
                </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="'40%'"
            :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="24">
                        <el-form-item label="产品大类:" prop="productCategory">
                            <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable filterable check-strictly
                                            @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
            <el-form-item label="产品大类:"
                          prop="productCategory">
              <el-tree-select v-model="productForm.productCategory"
                              placeholder="请选择"
                              clearable
                              filterable
                              check-strictly
                              @change="getModels"
                              :data="productOptions"
                              :render-after-expand="false"
                              style="width: 100%" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="规格型号:" prop="productModelId">
                            <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel" filterable>
                                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
            <el-form-item label="规格型号:"
                          prop="productModelId">
              <el-select v-model="productForm.productModelId"
                         placeholder="请选择"
                         clearable
                         @change="getProductModel"
                         filterable>
                <el-option v-for="item in modelOptions"
                           :key="item.id"
                           :label="item.model"
                           :value="item.id" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="单位:" prop="unit">
                            <el-input v-model="productForm.unit" placeholder="请输入" clearable />
            <el-form-item label="单位:"
                          prop="unit">
              <el-input v-model="productForm.unit"
                        placeholder="请输入"
                        clearable />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="税率(%):" prop="taxRate">
                            <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate">
                                <el-option label="1" value="1" />
                                <el-option label="6" value="6" />
                                <el-option label="13" value="13" />
            <el-form-item label="税率(%):"
                          prop="taxRate">
              <el-select v-model="productForm.taxRate"
                         placeholder="请选择"
                         clearable
                         @change="calculateFromTaxRate">
                <el-option label="1"
                           value="1" />
                <el-option label="6"
                           value="6" />
                <el-option label="13"
                           value="13" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
                            <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%"
            <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" />
                               placeholder="请输入"
                               clearable
                               @change="calculateFromUnitPrice" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="数量:" prop="quantity">
                            <el-input-number  :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请输入" clearable
            <el-form-item label="数量:"
                          prop="quantity">
              <el-input-number :step="0.1"
                               :min="0"
                               v-model="productForm.quantity"
                               placeholder="请输入"
                               clearable
                                                                :precision="2"
                                                                @change="calculateFromQuantity" style="width: 100%" />
                               @change="calculateFromQuantity"
                               style="width: 100%" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <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="12">
                        <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-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="发票类型:" prop="invoiceType">
                            <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable>
                                <el-option label="增普票" value="增普票" />
                                <el-option label="增专票" value="增专票" />
            <el-form-item label="发票类型:"
                          prop="invoiceType">
              <el-select v-model="productForm.invoiceType"
                         placeholder="请选择"
                         clearable>
                <el-option label="增普票"
                           value="增普票" />
                <el-option label="增专票"
                           value="增专票" />
                            </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="是否生产:"
                          prop="isProduction">
              <el-radio-group v-model="productForm.isProduction">
                <el-radio label="是"
                          :value="true" />
                <el-radio label="否"
                          :value="false" />
              </el-radio-group>
                        </el-form-item>
                    </el-col>
                </el-row>
            </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"
@@ -434,8 +742,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>
@@ -443,43 +750,45 @@
                <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="printPreviewVisible"
    <el-dialog v-model="printPreviewVisible"
            title="打印预览"
            width="90%"
            :close-on-click-modal="false"
            class="print-preview-dialog"
        >
               class="print-preview-dialog">
            <div class="print-preview-container">
                <div class="print-preview-header">
                    <el-button type="primary" @click="executePrint">执行打印</el-button>
          <el-button type="primary"
                     @click="executePrint">执行打印</el-button>
                    <el-button @click="printPreviewVisible = false">关闭预览</el-button>
                </div>
                <div class="print-preview-content">
                    <div v-if="printData.length === 0" style="text-align: center; padding: 50px; color: #999;">
          <div v-if="printData.length === 0"
               style="text-align: center; padding: 50px; color: #999;">
                        æš‚无打印数据
                    </div>
                    <div v-else style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;">
          <div v-else
               style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;">
                        å…± {{ printData.length }} æ¡æ•°æ®å¾…打印
                    </div>
                    <div v-for="(item, index) in printData" :key="index" class="print-page">
          <div v-for="(item, index) in printData"
               :key="index"
               class="print-page">
                        <div class="delivery-note">
                            <div class="header">
                                <div class="document-title">零售发货单</div>
                            </div>
                            <div class="info-section">
                                <div class="info-row">
                                    <div>
@@ -500,7 +809,6 @@
                                    <span class="value">{{ item.salesContractNo }}</span>
                                </div>
                            </div>
                            <div class="table-section">
                                <table class="product-table">
                                    <thead>
@@ -514,7 +822,8 @@
                                    </tr>
                                    </thead>
                                    <tbody>
                                    <tr v-for="product in item.products" :key="product.id">
                    <tr v-for="product in item.products"
                        :key="product.id">
                                        <td>{{ product.productCategory || '' }}</td>
                                        <td>{{ product.specificationModel || '' }}</td>
                                        <td>{{ product.unit || '' }}</td>
@@ -523,7 +832,8 @@
                                        <td>{{ product.taxInclusiveTotalPrice || '0' }}</td>
                                    </tr>
                                    <tr v-if="!item.products || item.products.length === 0">
                                        <td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td>
                      <td colspan="6"
                          style="text-align: center; color: #999;">暂无产品数据</td>
                                    </tr>
                                    </tbody>
                                    <tfoot>
@@ -538,7 +848,6 @@
                                    </tfoot>
                                </table>
                            </div>
                            <div class="footer-section">
                                <div class="footer-row">
                                    <div class="footer-item">
@@ -571,33 +880,35 @@
            </div>
        </el-dialog>
        <!-- å‘货弹框 -->
        <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-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>
@@ -614,8 +925,8 @@
import { UploadFilled, Download } from "@element-plus/icons-vue";
import useUserStore from "@/store/modules/user";
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 { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import {
    ledgerListPage,
@@ -626,7 +937,8 @@
    delLedger,
    addOrUpdateSalesLedgerProduct,
    delProduct,
    delLedgerFile, getProductInventory,
    delLedgerFile,
    getProductInventory,
} from "@/api/salesManagement/salesLedger.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
@@ -701,6 +1013,7 @@
        taxInclusiveTotalPrice: "",
        taxExclusiveTotalPrice: "",
        invoiceType: "",
      isProduction: false,
    },
    productRules: {
        productCategory: [{ required: true, message: "请选择", trigger: "change" }],
@@ -721,6 +1034,7 @@
            { required: true, message: "请输入", trigger: "blur" },
        ],
        invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
      isProduction: [{ required: true, message: "请选择", trigger: "change" }],
    },
});
const { productForm, productRules } = toRefs(productFormData);
@@ -760,9 +1074,7 @@
    type: "货车", // è´§è½¦, å¿«é€’
  },
  deliveryRules: {
    type: [
      { required: true, message: "请选择发货类型", trigger: "change" }
    ]
      type: [{ required: true, message: "请选择发货类型", trigger: "change" }],
  },
});
const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
@@ -775,8 +1087,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 æ ¼å¼!");
@@ -789,13 +1101,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("导入成功");
@@ -809,13 +1121,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");
@@ -837,7 +1149,7 @@
    expandedRowKeys.value = [];
    getList();
};
const paginationChange = (obj) => {
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
@@ -850,10 +1162,10 @@
    // ç§»é™¤å½•入日期的默认值设置,只保留范围日期字段
    delete params.entryDate;
    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;
@@ -866,7 +1178,7 @@
// èŽ·å–äº§å“å¤§ç±»tree数据
const getProductOptions = () => {
    // è¿”回 Promise,便于在编辑产品时等待加载完成
    return productTreeList().then((res) => {
    return productTreeList().then(res => {
        productOptions.value = convertIdToValue(res);
        return productOptions.value;
    });
@@ -877,7 +1189,7 @@
    }
    return parseFloat(cellValue).toFixed(2);
};
const findLedgerRecordByRow = (row) => {
  const findLedgerRecordByRow = row => {
    if (!row) return null;
    if (
        row.maintainer !== undefined ||
@@ -888,28 +1200,42 @@
        return row;
    }
    if (row.salesLedgerId !== undefined && row.salesLedgerId !== null) {
        return tableData.value.find((item) => String(item.id) === String(row.salesLedgerId)) || null;
      return (
        tableData.value.find(
          item => String(item.id) === String(row.salesLedgerId)
        ) || null
      );
    }
    return null;
};
const isCurrentUserMaintainer = (row) => {
  const isCurrentUserMaintainer = row => {
    const ledgerRecord = findLedgerRecordByRow(row);
    if (!ledgerRecord) return true;
    const currentUserId = String(userStore.id ?? "");
    const currentNickName = String(userStore.nickName ?? "").trim();
    const maintainerId = ledgerRecord.maintainerId ?? ledgerRecord.entryPerson;
    const maintainerName =
        ledgerRecord.maintainerName ?? ledgerRecord.maintainer ?? ledgerRecord.entryPersonName;
    if (maintainerId !== undefined && maintainerId !== null && String(maintainerId) !== "") {
      ledgerRecord.maintainerName ??
      ledgerRecord.maintainer ??
      ledgerRecord.entryPersonName;
    if (
      maintainerId !== undefined &&
      maintainerId !== null &&
      String(maintainerId) !== ""
    ) {
        return String(maintainerId) === currentUserId;
    }
    if (maintainerName !== undefined && maintainerName !== null && String(maintainerName).trim() !== "") {
    if (
      maintainerName !== undefined &&
      maintainerName !== null &&
      String(maintainerName).trim() !== ""
    ) {
        return String(maintainerName).trim() === currentNickName;
    }
    return true;
};
const canEditLedger = (row) => isCurrentUserMaintainer(row);
const canDeleteLedger = (row) => isCurrentUserMaintainer(row);
  const canEditLedger = row => isCurrentUserMaintainer(row);
  const canDeleteLedger = row => isCurrentUserMaintainer(row);
const sensitiveAmountFormatter = (row, column, cellValue) => {
    if (!isCurrentUserMaintainer(row)) {
        return "*****";
@@ -917,14 +1243,14 @@
    return formattedNumber(row, column, cellValue);
};
// èŽ·å–tree子数据
const getModels = (value) => {
  const getModels = value => {
    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;
        productForm.value.unit = modelOptions.value[index].unit;
@@ -948,7 +1274,7 @@
    return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
};
function convertIdToValue(data) {
    return data.map((item) => {
    return data.map(item => {
        const { id, children, ...rest } = item;
        const newItem = {
            ...rest,
@@ -975,12 +1301,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([]);
@@ -989,8 +1315,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;
                }
@@ -1006,22 +1332,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",
@@ -1036,7 +1362,13 @@
            if (index === 0) {
                return "合计";
            }
            if (["taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice"].includes(column.property)) {
        if (
          [
            "taxInclusiveUnitPrice",
            "taxInclusiveTotalPrice",
            "taxExclusiveTotalPrice",
          ].includes(column.property)
        ) {
                return "*****";
            }
            return "";
@@ -1060,7 +1392,7 @@
    selectedQuotation.value = null;
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    listCustomerPrivatePool({current: -1,size:-1}).then((res) => {
    listCustomerPrivatePool({ current: -1, size: -1 }).then(res => {
        customerOption.value = res.data.records;
    });
    form.value.entryPerson = userStore.id;
@@ -1071,7 +1403,7 @@
        form.value.executionDate = getCurrentDate();
    } 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);
            productData.value = form.value.productData;
@@ -1097,7 +1429,7 @@
    // å…ˆç¡®ä¿å®¢æˆ·åˆ—表已加载,便于后续回填 customerId
    if (!customerOption.value || customerOption.value.length === 0) {
        try {
            listCustomerPrivatePool({current: -1,size:-1}).then((res) => {
        listCustomerPrivatePool({ current: -1, size: -1 }).then(res => {
                customerOption.value = res.data.records;
            });
        } catch (e) {
@@ -1133,14 +1465,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;
    
@@ -1149,9 +1481,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;
@@ -1162,12 +1498,15 @@
    
    // äº§å“ä¿¡æ¯æ˜ å°„:报价 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 taxRate = "13"; // é»˜è®¤ 13%,便于直接提交(如需可在产品中自行修改)
        const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
        const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate);
      const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
        taxInclusiveTotalPrice,
        taxRate
      );
        return {
            // å°è´¦å­—段
            productCategory: p.product || p.productName || "",
@@ -1179,6 +1518,7 @@
            taxInclusiveTotalPrice: taxInclusiveTotalPrice,
            taxExclusiveTotalPrice: taxExclusiveTotalPrice,
            invoiceType: "增普票",
        isProduction: true,
        };
    });
    
@@ -1218,16 +1558,16 @@
    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);
            if (productData.value !== null && productData.value.length > 0) {
                form.value.productData = proxy.HaveJson(productData.value);
            } else {
@@ -1236,11 +1576,11 @@
            }
            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;
            addOrUpdateSalesLedger(form.value).then((res) => {
        addOrUpdateSalesLedger(form.value).then(res => {
                proxy.$modal.msgSuccess("提交成功");
                closeDia();
                expandedRowKeys.value = [];
@@ -1266,22 +1606,29 @@
    
    productOperationType.value = type;
    productForm.value = {};
    if (type === "add") {
      productForm.value.isProduction = true;
    }
    proxy.resetForm("productFormRef");
    if (type === "edit") {
        productForm.value = { ...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;
@@ -1292,13 +1639,13 @@
            console.error("加载产品规格型号失败", e);
        }
    } else {
        getProductOptions()
      getProductOptions();
    }
    productFormVisible.value = true;
};
// æäº¤äº§å“è¡¨å•
const submitProduct = () => {
    proxy.$refs["productFormRef"].validate((valid) => {
    proxy.$refs["productFormRef"].validate(valid => {
        if (valid) {
            if (operationType.value === "edit") {
                submitProductEdit();
@@ -1306,7 +1653,7 @@
                if(productOperationType.value === "add"){
                    productData.value.push({ ...productForm.value });
                }else{
                    productData.value[productIndex.value] = { ...productForm.value }
            productData.value[productIndex.value] = { ...productForm.value };
                }
                closeProductDia();
            }
@@ -1315,11 +1662,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;
        });
    });
@@ -1332,16 +1679,18 @@
    }
    
    // æ£€æŸ¥æ˜¯å¦æœ‰å·²å‘货或审核通过的产品
    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) => {
      productSelectedRows.value.forEach(selectedRow => {
            const index = productData.value.findIndex(
                (product) => product.id === selectedRow.id
          product => product.id === selectedRow.id
            );
            if (index !== -1) {
                productData.value.splice(index, 1);
@@ -1350,7 +1699,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: "确认",
@@ -1358,11 +1707,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;
                        }
                    );
@@ -1413,7 +1762,7 @@
        });
};
/** åˆ¤æ–­å•个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */
const isProductShipped = (product) => {
  const isProductShipped = product => {
    if (!product) return false;
    const status = String(product.shippingStatus || "").trim();
    // å¦‚果发货状态是"已发货"或"审核通过",则不可编辑和删除
@@ -1421,14 +1770,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 === "已发货"
      );
    });
};
@@ -1438,17 +1789,20 @@
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
    const unauthorizedRows = selectedRows.value.filter((row) => !canDeleteLedger(row));
    const unauthorizedRows = selectedRows.value.filter(
      row => !canDeleteLedger(row)
    );
    if (unauthorizedRows.length > 0) {
        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 });
@@ -1462,7 +1816,9 @@
        }
    }
    if (cannotDeleteNames.length > 0) {
        proxy.$modal.msgWarning("已进行发货或发货完成的销售订单不能删除:" + cannotDeleteNames.join("、"));
      proxy.$modal.msgWarning(
        "已进行发货或发货完成的销售订单不能删除:" + cannotDeleteNames.join("、")
      );
        return;
    }
@@ -1472,7 +1828,7 @@
        type: "warning",
    })
        .then(() => {
            delLedger(ids).then((res) => {
        delLedger(ids).then(res => {
                proxy.$modal.msgSuccess("删除成功");
                getList();
            });
@@ -1499,12 +1855,15 @@
        for (const row of selectedRows.value) {
            try {
                // è°ƒç”¨productList接口查询产品数据
                const productRes = await productList({ salesLedgerId: row.id, type: 1 });
          const productRes = await productList({
            salesLedgerId: row.id,
            type: 1,
          });
                
                // å°†äº§å“æ•°æ®æ•´åˆåˆ°é”€å”®å°è´¦è®°å½•中
                const rowWithProducts = {
                    ...row,
                    products: productRes.data || []
            products: productRes.data || [],
                };
                
                printDataWithProducts.push(rowWithProducts);
@@ -1513,17 +1872,16 @@
                // å³ä½¿æŸä¸ªè®°å½•的产品数据获取失败,也要包含该记录
                printDataWithProducts.push({
                    ...row,
                    products: []
            products: [],
                });
            }
        }
        
        printData.value = printDataWithProducts;
        console.log('打印数据(包含产品):', printData.value);
      console.log("打印数据(包含产品):", printData.value);
        printPreviewVisible.value = true;
    } catch (error) {
        console.error('获取产品数据失败:', error);
      console.error("获取产品数据失败:", error);
        proxy.$modal.msgError("获取产品数据失败,请重试");
    } finally {
        proxy.$modal.closeLoading();
@@ -1531,11 +1889,11 @@
};
// æ‰§è¡Œæ‰“印
const executePrint = () => {
    console.log('开始执行打印,数据条数:', printData.value.length);
    console.log('打印数据:', printData.value);
    console.log("开始执行打印,数据条数:", printData.value.length);
    console.log("打印数据:", printData.value);
    
    // åˆ›å»ºä¸€ä¸ªæ–°çš„æ‰“印窗口
    const printWindow = window.open('', '_blank', 'width=800,height=600');
    const printWindow = window.open("", "_blank", "width=800,height=600");
    
    // æž„建打印内容
    let printContent = `
@@ -1686,16 +2044,22 @@
            <div class="info-row">
              <div>
                <span class="label">发货日期:</span>
                <span class="value">${formatDate(item.createTime)}</span>
                                                      <span class="value">${formatDate(
                                                        item.createTime
                                                      )}</span>
              </div>
              <div>
                <span class="label">客户名称:</span>
                <span class="value">${item.customerName}</span>
                                                      <span class="value">${
                                                        item.customerName
                                                      }</span>
              </div>
            </div>
            <div class="info-row">
              <span class="label">单号:</span>
              <span class="value">${item.salesContractNo || ''}</span>
                                                    <span class="value">${
                                                      item.salesContractNo || ""
                                                    }</span>
            </div>
          </div>
@@ -1712,18 +2076,41 @@
                </tr>
              </thead>
              <tbody>
                ${item.products && item.products.length > 0 ?
            item.products.map(product => `
                                                      ${
                                                        item.products &&
                                                        item.products.length > 0
                                                          ? item.products
                                                              .map(
                                                                product => `
                    <tr>
                      <td>${product.productCategory || ''}</td>
                      <td>${product.specificationModel || ''}</td>
                      <td>${product.unit || ''}</td>
                      <td>${product.taxInclusiveUnitPrice || '0'}</td>
                      <td>${product.quantity || '0'}</td>
                      <td>${product.taxInclusiveTotalPrice || '0'}</td>
                                                            <td>${
                                                              product.productCategory ||
                                                              ""
                                                            }</td>
                                                            <td>${
                                                              product.specificationModel ||
                                                              ""
                                                            }</td>
                                                            <td>${
                                                              product.unit || ""
                                                            }</td>
                                                            <td>${
                                                              product.taxInclusiveUnitPrice ||
                                                              "0"
                                                            }</td>
                                                            <td>${
                                                              product.quantity ||
                                                              "0"
                                                            }</td>
                                                            <td>${
                                                              product.taxInclusiveTotalPrice ||
                                                              "0"
                                                            }</td>
                    </tr>
                  `).join('') :
            '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>'
                                                        `
                                                              )
                                                              .join("")
                                                          : '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>'
        }
              </tbody>
              <tfoot>
@@ -1732,8 +2119,12 @@
                  <td class="total-value"></td>
                  <td class="total-value"></td>
                  <td class="total-value"></td>
                  <td class="total-value">${getTotalQuantityForPrint(item.products)}</td>
                  <td class="total-value">${getTotalAmountForPrint(item.products)}</td>
                                                        <td class="total-value">${getTotalQuantityForPrint(
                                                          item.products
                                                        )}</td>
                                                        <td class="total-value">${getTotalAmountForPrint(
                                                          item.products
                                                        )}</td>
                </tr>
              </tfoot>
            </table>
@@ -1757,11 +2148,16 @@
            <div class="footer-row">
              <div class="footer-item">
                <span class="label">操作员:</span>
                <span class="value">${userStore.nickName || '撕开前'}</span>
                                                      <span class="value">${
                                                        userStore.nickName ||
                                                        "撕开前"
                                                      }</span>
              </div>
              <div class="footer-item">
                <span class="label">打印日期:</span>
                <span class="value">${formatDateTime(new Date())}</span>
                                                      <span class="value">${formatDateTime(
                                                        new Date()
                                                      )}</span>
              </div>
            </div>
          </div>
@@ -1789,7 +2185,7 @@
    };
};
// æ ¼å¼åŒ–日期
const formatDate = (dateString) => {
  const formatDate = dateString => {
    if (!dateString) return getCurrentDate();
    const date = new Date(dateString);
    const year = date.getFullYear();
@@ -1798,7 +2194,7 @@
    return `${year}/${month}/${day}`;
};
// æ ¼å¼åŒ–日期时间
const formatDateTime = (date) => {
  const formatDateTime = date => {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
@@ -1808,8 +2204,8 @@
    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
};
// è®¡ç®—产品总数量
const getTotalQuantity = (products) => {
    if (!products || products.length === 0) return '0';
  const getTotalQuantity = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
        return sum + (parseFloat(product.quantity) || 0);
    }, 0);
@@ -1817,8 +2213,8 @@
};
// è®¡ç®—产品总金额
const getTotalAmount = (products) => {
    if (!products || products.length === 0) return '0';
  const getTotalAmount = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
        return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
    }, 0);
@@ -1826,16 +2222,16 @@
};
// ç”¨äºŽæ‰“印的计算函数
const getTotalQuantityForPrint = (products) => {
    if (!products || products.length === 0) return '0';
  const getTotalQuantityForPrint = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
        return sum + (parseFloat(product.quantity) || 0);
    }, 0);
    return total.toFixed(2);
};
const getTotalAmountForPrint = (products) => {
    if (!products || products.length === 0) return '0';
  const getTotalAmountForPrint = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
        return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
    }, 0);
@@ -1902,7 +2298,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);
    
@@ -1918,7 +2316,9 @@
    productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
    
    // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
    productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2);
    productForm.value.taxInclusiveUnitPrice = (
      inclusiveTotalPrice / quantity
    ).toFixed(2);
    
    isCalculating.value = false;
};
@@ -1995,7 +2395,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) {
@@ -2006,10 +2408,7 @@
    
    // è®¡ç®—不含税总价
    productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
            inclusiveTotalPrice,
            taxRate
        );
      proxy.calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate);
    
    isCalculating.value = false;
};
@@ -2017,62 +2416,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";
};
/**
@@ -2080,7 +2479,7 @@
 * åªæœ‰åœ¨äº§å“çŠ¶æ€æ˜¯å……è¶³ï¼Œå‘è´§çŠ¶æ€æ˜¯å¾…å‘è´§å’Œå®¡æ ¸æ‹’ç»çš„æ—¶å€™æ‰å¯ä»¥å‘è´§
 * @param row è¡Œæ•°æ®
 */
const canShip = (row) => {
  const canShip = row => {
    // äº§å“çŠ¶æ€å¿…é¡»æ˜¯å……è¶³ï¼ˆapproveStatus === 1)
    if (row.approveStatus !== 1) {
        return false;
@@ -2095,8 +2494,8 @@
    }
    
    // å‘货状态必须是"待发货"或"审核拒绝"
    const statusStr = shippingStatus ? String(shippingStatus).trim() : '';
    return statusStr === '待发货' || statusStr === '审核拒绝';
    const statusStr = shippingStatus ? String(shippingStatus).trim() : "";
    return statusStr === "待发货" || statusStr === "审核拒绝";
};
/**
@@ -2104,21 +2503,23 @@
 *
 * @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 => {
    // æ£€æŸ¥æ˜¯å¦å¯ä»¥å‘è´§
    if (!canShip(row)) {
        proxy.$modal.msgWarning("只有在产品状态是充足,发货状态是待发货或审核拒绝的时候才可以发货");
      proxy.$modal.msgWarning(
        "只有在产品状态是充足,发货状态是待发货或审核拒绝的时候才可以发货"
      );
        return;
    }
    
@@ -2131,7 +2532,7 @@
// æäº¤å‘货表单
const submitDelivery = () => {
  proxy.$refs["deliveryFormRef"].validate((valid) => {
    proxy.$refs["deliveryFormRef"].validate(valid => {
    if (valid) {
      // ä¿å­˜å½“前展开的行ID,以便发货后重新加载子表格数据
      const currentExpandedKeys = [...expandedRowKeys.value];
@@ -2140,8 +2541,7 @@
        salesLedgerId: salesLedgerId,
        salesLedgerProductId: currentDeliveryRow.value.id,
        type: deliveryForm.value.type,
      })
        .then(() => {
        }).then(() => {
          proxy.$modal.msgSuccess("发货成功");
          closeDeliveryDia();
          // åˆ·æ–°ä¸»è¡¨æ•°æ®
@@ -2150,12 +2550,16 @@
            if (currentExpandedKeys.length > 0) {
              // ä½¿ç”¨ Promise.all å¹¶è¡ŒåŠ è½½æ‰€æœ‰å±•å¼€è¡Œçš„å­è¡¨æ ¼æ•°æ®
              const loadPromises = currentExpandedKeys.map(ledgerId => {
                return productList({ salesLedgerId: ledgerId, type: 1 }).then((res) => {
                  const index = tableData.value.findIndex((item) => item.id === 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(() => {
                // æ¢å¤å±•开状态
@@ -2163,7 +2567,7 @@
              });
            }
          });
        })
        });
    }
  });
};
@@ -2183,7 +2587,7 @@
    getList();
    userListNoPage().then(res => {
        userList.value = res.data;
    })
    });
    getCurrentFactoryName();
});
</script>
@@ -2194,19 +2598,19 @@
}
:deep(.yellow) {
  background-color: #FAF0DE;
    background-color: #faf0de;
}
:deep(.pink) {
  background-color: #FAE1DE;
    background-color: #fae1de;
}
:deep(.red) {
  background-color: #FAE1DE;
    background-color: #fae1de;
}
:deep(.purple){
  background-color: #F4DEFA;
    background-color: #f4defa;
}
.table_list {
@@ -2313,7 +2717,8 @@
        border-collapse: collapse;
        border: 1px solid #000;
        
        th, td {
      th,
      td {
            border: 1px solid #000;
            padding: 6px;
            text-align: center;