zhangwencui
2026-04-24 759f41097324fa1ade4060fc838d700d8c8fa55f
提交一版
已添加1个文件
已修改7个文件
7695 ■■■■ 文件已修改
src/api/productionManagement/processRouteFile.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionOrder.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/purchaseApproval/index.vue 1960 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/index.vue 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/New.vue 300 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 163 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionPlan/productionPlan/index.vue 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 4919 ●●●● 补丁 | 查看 | 原始文档 | 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,678 +2,771 @@
  <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"
                style="width: 240px"
                placeholder="请输入"
                @change="handleQuery"
                clearable
                :prefix-icon="Search"
            />
            <el-input v-model="searchForm.purchaseContractNumber"
                      style="width: 240px"
                      placeholder="请输入"
                      @change="handleQuery"
                      clearable
                      :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"
        border
        v-loading="tableLoading"
        @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys"
        :row-key="(row) => row.id"
        show-summary
        :summary-method="summarizeMainTable"
        @expand-change="expandChange"
        height="calc(100vh - 18.5em)"
        :row-class-name="tableRowClassName"
      >
        <el-table-column align="center" type="selection" width="55" />
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange"
                :expand-row-keys="expandedRowKeys"
                :row-key="(row) => row.id"
                show-summary
                :summary-method="summarizeMainTable"
                @expand-change="expandChange"
                height="calc(100vh - 18.5em)"
                :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"
              border
              show-summary
              :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="含税单价(元)"
                prop="taxInclusiveUnitPrice"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="含税总价(元)"
                prop="taxInclusiveTotalPrice"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="不含税总价(元)"
                prop="taxExclusiveTotalPrice"
                :formatter="formattedNumber"
              />
            <el-table :data="props.row.children"
                      border
                      show-summary
                      :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="含税单价(元)"
                               prop="taxInclusiveUnitPrice"
                               :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)"
                               prop="taxInclusiveTotalPrice"
                               :formatter="formattedNumber" />
              <el-table-column label="不含税总价(元)"
                               prop="taxExclusiveTotalPrice"
                               :formatter="formattedNumber" />
            </el-table>
          </template>
        </el-table-column>
        <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="销售合同号"
          prop="salesContractNo"
          width="200"
          show-overflow-tooltip
        />
        <el-table-column
          label="供应商名称"
          width="240"
          prop="supplierName"
          show-overflow-tooltip
        />
        <el-table-column label="订单状态" width="100" align="center">
        <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="销售合同号"
                         prop="salesContractNo"
                         width="200"
                         show-overflow-tooltip />
        <el-table-column label="供应商名称"
                         width="240"
                         prop="supplierName"
                         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="项目名称"
          prop="projectName"
          width="420"
          show-overflow-tooltip
        />
        <el-table-column
            label="审批状态"
            prop="approvalStatus"
            width="200"
            show-overflow-tooltip
        >
        <el-table-column label="项目名称"
                         prop="projectName"
                         width="420"
                         show-overflow-tooltip />
        <el-table-column label="审批状态"
                         prop="approvalStatus"
                         width="200"
                         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="付款方式"
          width="100"
          prop="paymentMethod"
          show-overflow-tooltip
        />
        <el-table-column
          label="合同金额(元)"
          prop="contractAmount"
           width="200"
          show-overflow-tooltip
          :formatter="formattedNumber"
        />
        <el-table-column
          label="录入人"
          prop="recorderName"
           width="100"
          show-overflow-tooltip
        />
        <el-table-column
          label="录入日期"
          prop="entryDate"
           width="100"
          show-overflow-tooltip
        />
        <el-table-column
          fixed="right"
          label="操作"
          min-width="150"
          align="center"
        >
        <el-table-column label="付款方式"
                         width="100"
                         prop="paymentMethod"
                         show-overflow-tooltip />
        <el-table-column label="合同金额(元)"
                         prop="contractAmount"
                         width="200"
                         show-overflow-tooltip
                         :formatter="formattedNumber" />
        <el-table-column label="录入人"
                         prop="recorderName"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="录入日期"
                         prop="entryDate"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column fixed="right"
                         label="操作"
                         min-width="150"
                         align="center">
          <template #default="scope">
            <el-button
              link
              type="primary"
              size="small"
              @click="approvePurchase(scope.row)"
              :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
            >
            <el-button link
                       type="primary"
                       size="small"
                       @click="approvePurchase(scope.row)"
                       :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>
          </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>
  </div>
</template>
<script setup>
import { getToken } from "@/utils/auth";
import pagination from "@/components/PIMTable/Pagination.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";
import {
  getSalesLedgerWithProducts,
  addOrUpdateSalesLedgerProduct,
  delProduct,
  delLedgerFile,
  getProductInfoByContractNo,
} from "@/api/salesManagement/salesLedger.js";
import {
  addOrEditPurchase,
  delPurchase,
  getSalesNo,
  purchaseListPage,
  productList,
  getPurchaseById,
  getOptions,
  createPurchaseNo, updateApprovalStatus,
} from "@/api/procurementManagement/procurementLedger.js";
import useFormData from "@/hooks/useFormData.js";
import QRCode from "qrcode";
  import { getToken } from "@/utils/auth";
  import pagination from "@/components/PIMTable/Pagination.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";
  import {
    getSalesLedgerWithProducts,
    addOrUpdateSalesLedgerProduct,
    delProduct,
    delLedgerFile,
    getProductInfoByContractNo,
  } from "@/api/salesManagement/salesLedger.js";
  import {
    addOrEditPurchase,
    delPurchase,
    getSalesNo,
    purchaseListPage,
    productList,
    getPurchaseById,
    getOptions,
    createPurchaseNo,
    updateApprovalStatus,
  } from "@/api/procurementManagement/procurementLedger.js";
  import useFormData from "@/hooks/useFormData.js";
  import QRCode from "qrcode";
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const productData = ref([]);
  const selectedRows = ref([]);
  const productSelectedRows = ref([]);
  const modelOptions = ref([]);
  const userList = ref([]);
  const productOptions = ref([]);
  const salesContractList = ref([]);
  const supplierList = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
  });
  const total = ref(0);
  const fileList = ref([]);
  import useUserStore from "@/store/modules/user";
  import { modelList, productTreeList } from "@/api/basicData/product.js";
  import dayjs from "dayjs";
  import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const productData = ref([]);
const selectedRows = ref([]);
const productSelectedRows = ref([]);
const modelOptions = ref([]);
const userList = ref([]);
const productOptions = ref([]);
const salesContractList = ref([]);
const supplierList = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
});
const total = ref(0);
const fileList = ref([]);
import useUserStore from "@/store/modules/user";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
  const userStore = useUserStore();
const userStore = useUserStore();
  // äºŒç»´ç ç›¸å…³å˜é‡
  const qrCodeDialogVisible = ref(false);
  const qrCodeUrl = ref("");
// äºŒç»´ç ç›¸å…³å˜é‡
const qrCodeDialogVisible = ref(false);
const qrCodeUrl = ref("");
  // è®¢å•审批状态显示文本
  const approvalStatusText = {
    0: "待审批",
    1: "审批通过",
    2: "审批失败",
  };
// è®¢å•审批状态显示文本
const approvalStatusText = {
  0: '待审批',
  1: '审批通过',
  2: '审批失败'
};
  // ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const data = reactive({
    searchForm: {
      supplierName: "", // ä¾›åº”商名称
      purchaseContractNumber: "", // é‡‡è´­åˆåŒç¼–号
      salesContractNo: "", // é”€å”®åˆåŒç¼–号
      projectName: "", // é¡¹ç›®åç§°
      entryDate: null, // å½•入日期
      entryDateStart: undefined,
      entryDateEnd: undefined,
    },
    form: {
      purchaseContractNumber: "",
      salesLedgerId: "",
      projectName: "",
      recorderId: "",
      entryDate: "",
      productData: [],
      supplierName: "",
      supplierId: "",
      paymentMethod: "",
      executionDate: "",
      approvalStatus: "0",
    },
    rules: {
      purchaseContractNumber: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      projectName: [{ required: true, message: "请输入", trigger: "blur" }],
      supplierId: [{ required: true, message: "请输入", trigger: "blur" }],
      entryDate: [{ required: true, message: "请选择", trigger: "change" }],
      executionDate: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
  const { form, rules } = toRefs(data);
  const { form: searchForm } = useFormData(data.searchForm);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
const data = reactive({
  searchForm: {
    supplierName: "", // ä¾›åº”商名称
    purchaseContractNumber: "", // é‡‡è´­åˆåŒç¼–号
    salesContractNo: "", // é”€å”®åˆåŒç¼–号
    projectName: "", // é¡¹ç›®åç§°
    entryDate: null, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
  form: {
    purchaseContractNumber: "",
    salesLedgerId: "",
    projectName: "",
    recorderId: "",
    entryDate: "",
    productData: [],
    supplierName: "",
    supplierId: "",
    paymentMethod: "",
        executionDate: "",
    approvalStatus: "0",
  },
  rules: {
    purchaseContractNumber: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    projectName: [{ required: true, message: "请输入", trigger: "blur" }],
    supplierId: [{ required: true, message: "请输入", trigger: "blur" }],
        entryDate: [{ required: true, message: "请选择", trigger: "change" }],
        executionDate: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const {  form, rules } = toRefs(data);
const { form: searchForm } = useFormData(data.searchForm);
  // äº§å“è¡¨å•弹框数据
  const productFormVisible = ref(false);
  const productOperationType = ref("");
  const productOperationIndex = ref("");
  const currentId = ref("");
  const productFormData = reactive({
    productForm: {
      productId: "",
      productCategory: "",
      productModelId: "",
      specificationModel: "",
      unit: "",
      quantity: "",
      taxInclusiveUnitPrice: "",
      taxRate: "",
      taxInclusiveTotalPrice: "",
      taxExclusiveTotalPrice: "",
      invoiceType: "",
      warnNum: "",
    },
    productRules: {
      productId: [{ required: true, message: "请选择", trigger: "change" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
      unit: [{ required: true, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      taxInclusiveUnitPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      taxRate: [{ required: true, message: "请选择", trigger: "change" }],
      warnNum: [{ required: false, message: "请选择", trigger: "change" }],
      taxInclusiveTotalPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      taxExclusiveTotalPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
  const { productForm, productRules } = toRefs(productFormData);
  const upload = reactive({
    // ä¸Šä¼ çš„地址
    url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
    // è®¾ç½®ä¸Šä¼ çš„请求头部
    headers: { Authorization: "Bearer " + getToken() },
  });
// äº§å“è¡¨å•弹框数据
const productFormVisible = ref(false);
const productOperationType = ref("");
const productOperationIndex = ref("");
const currentId = ref("");
const productFormData = reactive({
  productForm: {
    productId: "",
    productCategory: "",
    productModelId: "",
    specificationModel: "",
    unit: "",
    quantity: "",
    taxInclusiveUnitPrice: "",
    taxRate: "",
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
        warnNum: "",
  },
  productRules: {
    productId: [{ required: true, message: "请选择", trigger: "change" }],
    productModelId: [{ required: true, message: "请选择", trigger: "change" }],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    taxInclusiveUnitPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxRate: [{ required: true, message: "请选择", trigger: "change" }],
        warnNum: [{ required: false, message: "请选择", trigger: "change" }],
    taxInclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxExclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const { productForm, productRules } = toRefs(productFormData);
const upload = reactive({
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
});
  const changeDaterange = value => {
    if (value) {
      searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
      searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
    } else {
      searchForm.entryDateStart = undefined;
      searchForm.entryDateEnd = undefined;
    }
    handleQuery();
  };
const changeDaterange = (value) => {
  if (value) {
    searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  } else {
    searchForm.entryDateStart = undefined;
    searchForm.entryDateEnd = undefined;
  }
  handleQuery();
};
const formattedNumber = (row, column, cellValue) => {
  return parseFloat(cellValue).toFixed(2);
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeChildrenTable = (param) => {
  return proxy.summarizeTable(
    param,
    [
  const formattedNumber = (row, column, cellValue) => {
    return parseFloat(cellValue).toFixed(2);
  };
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeChildrenTable = param => {
    return proxy.summarizeTable(
      param,
      [
        "taxInclusiveUnitPrice",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice",
        "ticketsNum",
        "ticketsAmount",
        "futureTickets",
        "futureTicketsAmount",
      ],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      }
    );
  };
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    const { entryDate, ...rest } = searchForm;
    purchaseListPage({ ...rest, ...page })
      .then(res => {
        tableLoading.value = false;
        // tableData.value = res.data.records;
        // å¤„理数据,添加失效状态标记
        tableData.value = res.data.records.map(record => ({
          ...record,
          isInvalid: record.isWhite === 1,
        }));
        tableData.value.map(item => {
          item.children = [];
        });
        total.value = res.data.total;
        expandedRowKeys.value = [];
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  const productSelected = selectedRows => {
    productSelectedRows.value = selectedRows;
  };
  const expandedRowKeys = ref([]);
  // å±•开行
  const expandChange = (row, expandedRows) => {
    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);
          if (index > -1) {
            tableData.value[index].children = res.data;
          }
          expandedRowKeys.value.push(row.id);
        });
      } catch (error) {
        console.log(error);
      }
    } else {
      expandedRowKeys.value = [];
    }
  };
  // ä¸»è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable = param => {
    return proxy.summarizeTable(param, ["contractAmount"]);
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeProTable = param => {
    return proxy.summarizeTable(param, [
      "taxInclusiveUnitPrice",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
      "ticketsNum",
      "ticketsAmount",
      "futureTickets",
      "futureTicketsAmount",
    ],
    {
      ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
    }
  );
};
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  const { entryDate, ...rest } = searchForm;
  purchaseListPage({ ...rest, ...page })
    .then((res) => {
      tableLoading.value = false;
      // tableData.value = res.data.records;
      // å¤„理数据,添加失效状态标记
      tableData.value = res.data.records.map(record => ({
        ...record,
        isInvalid: record.isWhite === 1
      }));
      tableData.value.map((item) => {
        item.children = [];
    ]);
  };
  // æ‰“开弹框
  const openForm = (type, row) => {
    operationType.value = type;
    form.value = {};
    productData.value = [];
    fileList.value = [];
    if (operationType.value == "add") {
      createPurchaseNo().then(res => {
        form.value.purchaseContractNumber = res.data;
      });
      total.value = res.data.total;
      expandedRowKeys.value = [];
    })
    .catch(() => {
      tableLoading.value = false;
    }
    userListNoPage().then(res => {
      userList.value = res.data;
    });
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
const productSelected = (selectedRows) => {
  productSelectedRows.value = selectedRows;
};
const expandedRowKeys = ref([]);
// å±•开行
const expandChange = (row, expandedRows) => {
  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);
        if (index > -1) {
          tableData.value[index].children = res.data;
    getSalesNo().then(res => {
      salesContractList.value = res;
    });
    getOptions().then(res => {
      // ä¾›åº”商过滤出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 => {
        form.value = { ...res };
        productData.value = form.value.productData;
        if (form.value.salesLedgerFiles) {
          fileList.value = form.value.salesLedgerFiles;
        } else {
          fileList.value = [];
        }
        expandedRowKeys.value.push(row.id);
      });
    } catch (error) {
      console.log(error);
    }
  } else {
    expandedRowKeys.value = [];
    dialogFormVisible.value = true;
  };
  // ä¸Šä¼ å‰æ ¡æ£€
  function handleBeforeUpload(file) {
    // æ ¡æ£€æ–‡ä»¶å¤§å°
    if (file.size > 1024 * 1024 * 10) {
      proxy.$modal.msgError("上传文件大小不能超过10MB!");
      return false;
    }
    proxy.$modal.loading("正在上传文件,请稍候...");
    return true;
  }
};
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(param, ["contractAmount"]);
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeProTable = (param) => {
  return proxy.summarizeTable(param, [
    "taxInclusiveUnitPrice",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
};
// æ‰“开弹框
const openForm = (type, row) => {
  operationType.value = type;
  form.value = {};
  productData.value = [];
  fileList.value = [];
  if (operationType.value == "add") {
    createPurchaseNo().then((res) => {
      form.value.purchaseContractNumber = res.data;
    });
  // ä¸Šä¼ å¤±è´¥
  function handleUploadError(err) {
    proxy.$modal.msgError("上传文件失败");
    proxy.$modal.closeLoading();
  }
  userListNoPage().then((res) => {
    userList.value = res.data;
  });
  getSalesNo().then((res) => {
    salesContractList.value = res;
  });
  getOptions().then((res) => {
    // ä¾›åº”商过滤出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) => {
      form.value = { ...res };
      productData.value = form.value.productData;
      if (form.value.salesLedgerFiles) {
        fileList.value = form.value.salesLedgerFiles;
      } else {
        fileList.value = [];
  // ä¸Šä¼ æˆåŠŸå›žè°ƒ
  function handleUploadSuccess(res, file, uploadFiles) {
    proxy.$modal.closeLoading();
    if (res.code === 200) {
      file.tempId = res.data.tempId;
      proxy.$modal.msgSuccess("上传成功");
    } else {
      proxy.$modal.msgError(res.msg);
      proxy.$refs.fileUpload.handleRemove(file);
    }
  }
  // ç§»é™¤æ–‡ä»¶
  function handleRemove(file) {
    console.log("handleRemove", file.id);
    if (file.size > 1024 * 1024 * 10) {
      // ä»…前端清理,不调用删除接口和提示
      return;
    }
    if (operationType.value === "edit") {
      let ids = [];
      ids.push(file.id);
      delLedgerFile(ids).then(res => {
        proxy.$modal.msgSuccess("删除成功");
      });
    }
  }
  // æäº¤è¡¨å•
  const submitForm = n => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        if (productData.value.length > 0) {
          form.value.productData = proxy.HaveJson(productData.value);
        } else {
          proxy.$modal.msgWarning("请添加产品信息");
          return;
        }
        let tempFileIds = [];
        if (fileList.value.length > 0) {
          tempFileIds = fileList.value.map(item => item.tempId);
        }
        form.value.tempFileIds = tempFileIds;
        form.value.type = 2;
        form.value.approvalStatus = n;
        addOrEditPurchase(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
          getList();
        });
      }
    });
  }
  dialogFormVisible.value = true;
};
// ä¸Šä¼ å‰æ ¡æ£€
function handleBeforeUpload(file) {
  // æ ¡æ£€æ–‡ä»¶å¤§å°
  if (file.size > 1024 * 1024 * 10) {
    proxy.$modal.msgError("上传文件大小不能超过10MB!");
    return false;
  }
  proxy.$modal.loading("正在上传文件,请稍候...");
  return true;
}
// ä¸Šä¼ å¤±è´¥
function handleUploadError(err) {
  proxy.$modal.msgError("上传文件失败");
  proxy.$modal.closeLoading();
}
// ä¸Šä¼ æˆåŠŸå›žè°ƒ
function handleUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    file.tempId = res.data.tempId;
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.fileUpload.handleRemove(file);
  }
}
// ç§»é™¤æ–‡ä»¶
function handleRemove(file) {
  console.log("handleRemove", file.id);
  if (file.size > 1024 * 1024 * 10) {
    // ä»…前端清理,不调用删除接口和提示
    return;
  }
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.id);
    delLedgerFile(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
  };
  // å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
  };
  // æ‰“开产品弹框
  const openProductForm = (type, row, index) => {
    productOperationType.value = type;
    productOperationIndex.value = index;
    productForm.value = {};
    proxy.resetForm("productFormRef");
    if (type === "edit") {
      productForm.value = { ...row };
    }
    productFormVisible.value = true;
    getProductOptions();
  };
  const getProductOptions = () => {
    productTreeList().then(res => {
      productOptions.value = convertIdToValue(res);
    });
  };
  const getModels = value => {
    if (value) {
      productForm.value.productCategory =
        findNodeById(productOptions.value, value) || "";
      modelList({ id: value }).then(res => {
        modelOptions.value = res;
      });
    } else {
      productForm.value.productCategory = "";
      modelOptions.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;
    } else {
      productForm.value.specificationModel = null;
      productForm.value.unit = null;
    }
  };
  const findNodeById = (nodes, productId) => {
    for (let i = 0; i < nodes.length; i++) {
      if (nodes[i].value === productId) {
        return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹çš„label
      }
      if (nodes[i].children && nodes[i].children.length > 0) {
        const foundNode = findNodeById(nodes[i].children, productId);
        if (foundNode) {
          return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œç›´æŽ¥è¿”å›žï¼ˆå·²ç»æ˜¯label字符串)
        }
      }
    }
    return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
  };
  function convertIdToValue(data) {
    return data.map(item => {
      const { id, children, ...rest } = item;
      const newItem = {
        ...rest,
        value: id, // å°† id æ”¹ä¸º value
      };
      if (children && children.length > 0) {
        newItem.children = convertIdToValue(children);
      }
      return newItem;
    });
  }
}
// æäº¤è¡¨å•
const submitForm = (n) => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      if (productData.value.length > 0) {
        form.value.productData = proxy.HaveJson(productData.value);
      } else {
        proxy.$modal.msgWarning("请添加产品信息");
  // æäº¤äº§å“è¡¨å•
  const submitProduct = () => {
    proxy.$refs["productFormRef"].validate(valid => {
      if (valid) {
        if (operationType.value === "edit") {
          submitProductEdit();
        } else {
          if (productOperationType.value === "add") {
            productData.value.push({ ...productForm.value });
            console.log("productData.value---", productData.value);
          } else {
            productData.value[productOperationIndex.value] = {
              ...productForm.value,
            };
          }
          closeProductDia();
        }
      }
    });
  };
  const submitProductEdit = () => {
    productForm.value.salesLedgerId = currentId.value;
    productForm.value.type = 2;
    addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeProductDia();
      getPurchaseById({ id: currentId.value, type: 2 }).then(res => {
        productData.value = res.productData;
      });
    });
  };
  // åˆ é™¤äº§å“
  const deleteProduct = () => {
    if (productSelectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    if (operationType.value === "add") {
      productSelectedRows.value.forEach(selectedRow => {
        const index = productData.value.findIndex(
          product => product.id === selectedRow.id
        );
        if (index !== -1) {
          productData.value.splice(index, 1);
        }
      });
    } else {
      let ids = [];
      if (productSelectedRows.value.length > 0) {
        ids = productSelectedRows.value.map(item => item.id);
      }
      ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          delProduct(ids).then(res => {
            proxy.$modal.msgSuccess("删除成功");
            closeProductDia();
            getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
              res => {
                productData.value = res.productData;
              }
            );
          });
        })
        .catch(() => {
          proxy.$modal.msg("已取消");
        });
    }
  };
  // å…³é—­äº§å“å¼¹æ¡†
  const closeProductDia = () => {
    proxy.resetForm("productFormRef");
    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("审批成功");
          getList();
        });
      })
      .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("审批成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消审批");
      });
  };
  // å¯¼å‡º
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        proxy.download("/purchase/ledger/export", {}, "采购台账.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // åˆ é™¤
  const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
      const unauthorizedData = selectedRows.value.filter(
        item => item.recorderName !== userStore.nickName
      );
      if (unauthorizedData.length > 0) {
        proxy.$modal.msgWarning("不可删除他人维护的数据");
        return;
      }
      let tempFileIds = [];
      if (fileList.value.length > 0) {
        tempFileIds = fileList.value.map((item) => item.tempId);
      }
      form.value.tempFileIds = tempFileIds;
      form.value.type = 2;
      form.value.approvalStatus = n;
      addOrEditPurchase(form.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
        getList();
      });
    }
  });
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
};
// æ‰“开产品弹框
const openProductForm = (type, row, index) => {
  productOperationType.value = type;
  productOperationIndex.value = index;
  productForm.value = {};
  proxy.resetForm("productFormRef");
  if (type === "edit") {
    productForm.value = { ...row };
  }
  productFormVisible.value = true;
  getProductOptions();
};
const getProductOptions = () => {
  productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
  });
};
const getModels = (value) => {
  if (value) {
    productForm.value.productCategory = findNodeById(productOptions.value, value) || "";
    modelList({ id: value }).then((res) => {
      modelOptions.value = res;
    });
  } else {
    productForm.value.productCategory = "";
    modelOptions.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;
  } else {
    productForm.value.specificationModel = null;
    productForm.value.unit = null;
  }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹çš„label
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œç›´æŽ¥è¿”å›žï¼ˆå·²ç»æ˜¯label字符串)
      }
    }
  }
  return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
};
function convertIdToValue(data) {
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id, // å°† id æ”¹ä¸º value
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
// æäº¤äº§å“è¡¨å•
const submitProduct = () => {
  proxy.$refs["productFormRef"].validate((valid) => {
    if (valid) {
      if (operationType.value === "edit") {
        submitProductEdit();
      } else {
        if (productOperationType.value === "add") {
          productData.value.push({ ...productForm.value });
          console.log("productData.value---", productData.value);
        } else {
          productData.value[productOperationIndex.value] = {
            ...productForm.value,
          };
        }
        closeProductDia();
      }
    }
  });
};
const submitProductEdit = () => {
  productForm.value.salesLedgerId = currentId.value;
  productForm.value.type = 2;
  addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
    proxy.$modal.msgSuccess("提交成功");
    closeProductDia();
    getPurchaseById({ id: currentId.value, type: 2 }).then((res) => {
      productData.value = res.productData;
    });
  });
};
// åˆ é™¤äº§å“
const deleteProduct = () => {
  if (productSelectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  if (operationType.value === "add") {
    productSelectedRows.value.forEach((selectedRow) => {
      const index = productData.value.findIndex(
        (product) => product.id === selectedRow.id
      );
      if (index !== -1) {
        productData.value.splice(index, 1);
      }
    });
  } else {
    let ids = [];
    if (productSelectedRows.value.length > 0) {
      ids = productSelectedRows.value.map((item) => item.id);
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
@@ -681,384 +774,313 @@
      type: "warning",
    })
      .then(() => {
        delProduct(ids).then((res) => {
        delPurchase(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          closeProductDia();
          getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
            (res) => {
              productData.value = res.productData;
            }
          );
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  }
};
// å…³é—­äº§å“å¼¹æ¡†
const closeProductDia = () => {
  proxy.resetForm("productFormRef");
  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('审批成功');
      getList();
    })
  }).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('审批成功');
      getList();
    })
  }).catch(() => {
    proxy.$modal.msg('已取消审批');
  });
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/purchase/ledger/export", {}, "采购台账.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        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);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      delPurchase(ids).then((res) => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
const mathNum = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (!productForm.value.taxInclusiveUnitPrice) {
    return;
  }
  if (!productForm.value.quantity) {
    return;
  }
  // å«ç¨Žæ€»ä»·è®¡ç®—
  productForm.value.taxInclusiveTotalPrice =
    proxy.calculateTaxIncludeTotalPrice(
      productForm.value.taxInclusiveUnitPrice,
      productForm.value.quantity
    );
  if (productForm.value.taxRate) {
    // ä¸å«ç¨Žæ€»ä»·è®¡ç®—
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        productForm.value.taxInclusiveTotalPrice,
        productForm.value.taxRate
      );
  }
};
const reverseMathNum = (field) => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  const taxRate = Number(productForm.value.taxRate);
  if (!taxRate) return;
  if (field === 'taxInclusiveTotalPrice') {
    // å·²çŸ¥å«ç¨Žæ€»ä»·å’Œæ•°é‡ï¼Œåç®—含税单价
    if (productForm.value.quantity) {
      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.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);
    // å·²çŸ¥æ•°é‡ï¼Œåç®—含税单价
    if (productForm.value.quantity) {
      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);
    }
  }
};
// é”€å”®åˆåŒé€‰æ‹©æ”¹å˜æ–¹æ³•
const salesLedgerChange = async (row) => {
  console.log("row", row);
  var index = salesContractList.value.findIndex((item) => item.id == row);
  console.log("index", index);
  if (index > -1) {
    form.value.projectName = salesContractList.value[index].projectName;
    await querygProductInfoByContractNo();
  }
};
const querygProductInfoByContractNo = async () => {
  const { code, data } = await getProductInfoByContractNo({
    contractNo: form.value.salesLedgerId,
  });
  if (code == 200) {
    productData.value = data;
  }
};
// æ˜¾ç¤ºäºŒç»´ç 
const showQRCode = async (row) => {
  try {
    // æž„建二维码内容,只包含采购合同号(纯文本)
    const qrContent = row.purchaseContractNumber || '';
    // æ£€æŸ¥å†…容是否为空
    if (!qrContent || qrContent.trim() === '') {
      proxy.$modal.msgWarning("该行没有采购合同号,无法生成二维码");
  };
  const mathNum = () => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    qrCodeUrl.value = await QRCode.toDataURL(qrContent, {
      width: 200,
      margin: 2,
      color: {
        dark: '#000000',
        light: '#FFFFFF'
    if (!productForm.value.taxInclusiveUnitPrice) {
      return;
    }
    if (!productForm.value.quantity) {
      return;
    }
    // å«ç¨Žæ€»ä»·è®¡ç®—
    productForm.value.taxInclusiveTotalPrice =
      proxy.calculateTaxIncludeTotalPrice(
        productForm.value.taxInclusiveUnitPrice,
        productForm.value.quantity
      );
    if (productForm.value.taxRate) {
      // ä¸å«ç¨Žæ€»ä»·è®¡ç®—
      productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
          productForm.value.taxInclusiveTotalPrice,
          productForm.value.taxRate
        );
    }
  };
  const reverseMathNum = field => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    const taxRate = Number(productForm.value.taxRate);
    if (!taxRate) return;
    if (field === "taxInclusiveTotalPrice") {
      // å·²çŸ¥å«ç¨Žæ€»ä»·å’Œæ•°é‡ï¼Œåç®—含税单价
      if (productForm.value.quantity) {
        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.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);
      // å·²çŸ¥æ•°é‡ï¼Œåç®—含税单价
      if (productForm.value.quantity) {
        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);
      }
    }
  };
  // é”€å”®åˆåŒé€‰æ‹©æ”¹å˜æ–¹æ³•
  const salesLedgerChange = async row => {
    console.log("row", row);
    var index = salesContractList.value.findIndex(item => item.id == row);
    console.log("index", index);
    if (index > -1) {
      form.value.projectName = salesContractList.value[index].projectName;
      await querygProductInfoByContractNo();
    }
  };
  const querygProductInfoByContractNo = async () => {
    const { code, data } = await getProductInfoByContractNo({
      contractNo: form.value.salesLedgerId,
    });
    if (code == 200) {
      productData.value = data;
    }
  };
  // æ˜¾ç¤ºäºŒç»´ç 
  const showQRCode = async row => {
    try {
      // æž„建二维码内容,只包含采购合同号(纯文本)
      const qrContent = row.purchaseContractNumber || "";
      // æ£€æŸ¥å†…容是否为空
      if (!qrContent || qrContent.trim() === "") {
        proxy.$modal.msgWarning("该行没有采购合同号,无法生成二维码");
        return;
      }
      qrCodeUrl.value = await QRCode.toDataURL(qrContent, {
        width: 200,
        margin: 2,
        color: {
          dark: "#000000",
          light: "#FFFFFF",
        },
      });
      qrCodeDialogVisible.value = true;
    } catch (error) {
      console.error("生成二维码失败:", error);
      proxy.$modal.msgError("生成二维码失败:" + error.message);
    }
  };
  // ä¸‹è½½äºŒç»´ç 
  const downloadQRCode = () => {
    if (!qrCodeUrl.value) {
      proxy.$modal.msgWarning("二维码未生成");
      return;
    }
    const a = document.createElement("a");
    a.href = qrCodeUrl.value;
    a.download = `采购合同号二维码_${new Date().getTime()}.png`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    proxy.$modal.msgSuccess("下载成功");
  };
  // æ‰«ç æ–°å¢žå¯¹è¯æ¡†ç›¸å…³å˜é‡
  const scanAddDialogVisible = ref(false);
  const scanAddForm = reactive({
    scanContent: "",
    purchaseContractNumber: "",
    supplierName: "",
    projectName: "",
    contractAmount: "",
    paymentMethod: "",
    recorderName: "",
    scanRemark: "",
  });
  const scanAddRules = {
    purchaseContractNumber: [
      { required: true, message: "请输入采购合同号", trigger: "blur" },
    ],
    supplierName: [
      { required: true, message: "请输入供应商名称", trigger: "blur" },
    ],
    projectName: [{ required: true, message: "请输入项目名称", trigger: "blur" }],
  };
  // æ‰«ç ç™»è®°å¯¹è¯æ¡†ç›¸å…³å˜é‡
  const scanDialogVisible = ref(false);
  const scanForm = reactive({
    purchaseContractNumber: "",
    supplierName: "",
    projectName: "",
    scanTime: "",
    scannerName: "",
    scanStatus: "未扫码",
    scanRemark: "",
  });
  const scanRules = {
    scanRemark: [{ required: true, message: "请输入扫码备注", trigger: "blur" }],
  };
  const scanRecords = ref([]);
  // æ‰“开扫码新增对话框
  const openScanAddDialog = () => {
    scanAddForm.scanContent = "";
    scanAddForm.purchaseContractNumber = "";
    scanAddForm.supplierName = "";
    scanAddForm.projectName = "";
    scanAddForm.contractAmount = "";
    scanAddForm.paymentMethod = "";
    scanAddForm.recorderName = userStore.nickName;
    scanAddForm.scanRemark = "";
    scanAddDialogVisible.value = true;
  };
  // è§£æžæ‰«ç å†…容(模拟解析二维码数据)
  const parseScanContent = content => {
    if (!content) return;
    // æ¨¡æ‹Ÿè§£æžäºŒç»´ç å†…容,这里可以根据实际需求调整解析逻辑
    // å‡è®¾æ‰«ç å†…容格式为:合同号|供应商|项目|金额|付款方式
    const parts = content.split("|");
    if (parts.length >= 3) {
      scanAddForm.purchaseContractNumber = parts[0] || "";
      scanAddForm.supplierName = parts[1] || "";
      scanAddForm.projectName = parts[2] || "";
      scanAddForm.contractAmount = parts[3] || "";
      scanAddForm.paymentMethod = parts[4] || "";
    }
  };
  // å…³é—­æ‰«ç æ–°å¢žå¯¹è¯æ¡†
  const closeScanAddDialog = () => {
    scanAddDialogVisible.value = false;
    proxy.resetForm("scanAddFormRef");
  };
  // æäº¤æ‰«ç æ–°å¢ž
  const submitScanAdd = () => {
    proxy.$refs["scanAddFormRef"].validate(valid => {
      if (valid) {
        // æž„建新增数据
        const newData = {
          purchaseContractNumber: scanAddForm.purchaseContractNumber,
          supplierName: scanAddForm.supplierName,
          projectName: scanAddForm.projectName,
          contractAmount: scanAddForm.contractAmount,
          paymentMethod: scanAddForm.paymentMethod,
          recorderName: scanAddForm.recorderName,
          entryDate: getCurrentDate(),
          remark: scanAddForm.scanRemark,
          type: 2,
        };
        // æ¨¡æ‹Ÿæ–°å¢žæˆåŠŸ
        proxy.$modal.msgSuccess("扫码新增成功!");
        closeScanAddDialog();
        // å¯ä»¥é€‰æ‹©æ˜¯å¦åˆ·æ–°åˆ—表
        // getList();
      }
    });
    qrCodeDialogVisible.value = true;
  } catch (error) {
    console.error('生成二维码失败:', error);
    proxy.$modal.msgError("生成二维码失败:" + error.message);
  };
  // æ‰“开扫码登记对话框
  const openScanDialog = row => {
    scanForm.purchaseContractNumber = row.purchaseContractNumber;
    scanForm.supplierName = row.supplierName;
    scanForm.projectName = row.projectName;
    scanForm.scanTime = getCurrentDateTime();
    scanForm.scannerName = userStore.nickName;
    scanForm.scanStatus = "未扫码";
    scanForm.scanRemark = "";
    scanRecords.value = [];
    scanDialogVisible.value = true;
  };
  // å…³é—­æ‰«ç ç™»è®°å¯¹è¯æ¡†
  const closeScanDialog = () => {
    scanDialogVisible.value = false;
    proxy.resetForm("scanFormRef");
  };
  // æäº¤æ‰«ç ç™»è®°
  const submitScan = () => {
    proxy.$refs["scanFormRef"].validate(valid => {
      if (valid) {
        // æ·»åŠ æ‰«ç è®°å½•
        scanRecords.value.push({
          ...scanForm,
          id: Date.now(), // æ¨¡æ‹ŸID
          scanTime: getCurrentDateTime(),
        });
        scanForm.scanStatus = "已扫码";
        scanForm.scanRemark = scanForm.scanRemark || "无";
        proxy.$modal.msgSuccess("扫码登记成功!");
        closeScanDialog();
      }
    });
  };
  // èŽ·å–å½“å‰æ—¥æœŸæ—¶é—´
  function getCurrentDateTime() {
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, "0");
    const day = String(now.getDate()).padStart(2, "0");
    const hours = String(now.getHours()).padStart(2, "0");
    const minutes = String(now.getMinutes()).padStart(2, "0");
    const seconds = String(now.getSeconds()).padStart(2, "0");
    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  }
};
// ä¸‹è½½äºŒç»´ç 
const downloadQRCode = () => {
  if (!qrCodeUrl.value) {
    proxy.$modal.msgWarning("二维码未生成");
    return;
  }
  const a = document.createElement('a');
  a.href = qrCodeUrl.value;
  a.download = `采购合同号二维码_${new Date().getTime()}.png`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  proxy.$modal.msgSuccess("下载成功");
};
  // æ·»åŠ è¡Œç±»åæ–¹æ³•
  const tableRowClassName = ({ row }) => {
    return row.isInvalid ? "invalid-row" : "";
  };
// æ‰«ç æ–°å¢žå¯¹è¯æ¡†ç›¸å…³å˜é‡
const scanAddDialogVisible = ref(false);
const scanAddForm = reactive({
  scanContent: "",
  purchaseContractNumber: "",
  supplierName: "",
  projectName: "",
  contractAmount: "",
  paymentMethod: "",
  recorderName: "",
  scanRemark: "",
});
const scanAddRules = {
  purchaseContractNumber: [{ required: true, message: "请输入采购合同号", trigger: "blur" }],
  supplierName: [{ required: true, message: "请输入供应商名称", trigger: "blur" }],
  projectName: [{ required: true, message: "请输入项目名称", trigger: "blur" }],
};
// æ‰«ç ç™»è®°å¯¹è¯æ¡†ç›¸å…³å˜é‡
const scanDialogVisible = ref(false);
const scanForm = reactive({
  purchaseContractNumber: "",
  supplierName: "",
  projectName: "",
  scanTime: "",
  scannerName: "",
  scanStatus: "未扫码",
  scanRemark: "",
});
const scanRules = {
  scanRemark: [{ required: true, message: "请输入扫码备注", trigger: "blur" }],
};
const scanRecords = ref([]);
// æ‰“开扫码新增对话框
const openScanAddDialog = () => {
  scanAddForm.scanContent = "";
  scanAddForm.purchaseContractNumber = "";
  scanAddForm.supplierName = "";
  scanAddForm.projectName = "";
  scanAddForm.contractAmount = "";
  scanAddForm.paymentMethod = "";
  scanAddForm.recorderName = userStore.nickName;
  scanAddForm.scanRemark = "";
  scanAddDialogVisible.value = true;
};
// è§£æžæ‰«ç å†…容(模拟解析二维码数据)
const parseScanContent = (content) => {
  if (!content) return;
  // æ¨¡æ‹Ÿè§£æžäºŒç»´ç å†…容,这里可以根据实际需求调整解析逻辑
  // å‡è®¾æ‰«ç å†…容格式为:合同号|供应商|项目|金额|付款方式
  const parts = content.split('|');
  if (parts.length >= 3) {
    scanAddForm.purchaseContractNumber = parts[0] || "";
    scanAddForm.supplierName = parts[1] || "";
    scanAddForm.projectName = parts[2] || "";
    scanAddForm.contractAmount = parts[3] || "";
    scanAddForm.paymentMethod = parts[4] || "";
  }
};
// å…³é—­æ‰«ç æ–°å¢žå¯¹è¯æ¡†
const closeScanAddDialog = () => {
  scanAddDialogVisible.value = false;
  proxy.resetForm("scanAddFormRef");
};
// æäº¤æ‰«ç æ–°å¢ž
const submitScanAdd = () => {
  proxy.$refs["scanAddFormRef"].validate((valid) => {
    if (valid) {
      // æž„建新增数据
      const newData = {
        purchaseContractNumber: scanAddForm.purchaseContractNumber,
        supplierName: scanAddForm.supplierName,
        projectName: scanAddForm.projectName,
        contractAmount: scanAddForm.contractAmount,
        paymentMethod: scanAddForm.paymentMethod,
        recorderName: scanAddForm.recorderName,
        entryDate: getCurrentDate(),
        remark: scanAddForm.scanRemark,
        type: 2
      };
      // æ¨¡æ‹Ÿæ–°å¢žæˆåŠŸ
      proxy.$modal.msgSuccess("扫码新增成功!");
      closeScanAddDialog();
      // å¯ä»¥é€‰æ‹©æ˜¯å¦åˆ·æ–°åˆ—表
      // getList();
    }
  onMounted(() => {
    getList();
  });
};
// æ‰“开扫码登记对话框
const openScanDialog = (row) => {
  scanForm.purchaseContractNumber = row.purchaseContractNumber;
  scanForm.supplierName = row.supplierName;
  scanForm.projectName = row.projectName;
  scanForm.scanTime = getCurrentDateTime();
  scanForm.scannerName = userStore.nickName;
  scanForm.scanStatus = "未扫码";
  scanForm.scanRemark = "";
  scanRecords.value = [];
  scanDialogVisible.value = true;
};
// å…³é—­æ‰«ç ç™»è®°å¯¹è¯æ¡†
const closeScanDialog = () => {
  scanDialogVisible.value = false;
  proxy.resetForm("scanFormRef");
};
// æäº¤æ‰«ç ç™»è®°
const submitScan = () => {
  proxy.$refs["scanFormRef"].validate((valid) => {
    if (valid) {
      // æ·»åŠ æ‰«ç è®°å½•
      scanRecords.value.push({
        ...scanForm,
        id: Date.now(), // æ¨¡æ‹ŸID
        scanTime: getCurrentDateTime(),
      });
      scanForm.scanStatus = "已扫码";
      scanForm.scanRemark = scanForm.scanRemark || "无";
      proxy.$modal.msgSuccess("扫码登记成功!");
      closeScanDialog();
    }
  });
};
// èŽ·å–å½“å‰æ—¥æœŸæ—¶é—´
function getCurrentDateTime() {
  const now = new Date();
  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, "0");
  const day = String(now.getDate()).padStart(2, "0");
  const hours = String(now.getHours()).padStart(2, "0");
  const minutes = String(now.getMinutes()).padStart(2, "0");
  const seconds = String(now.getSeconds()).padStart(2, "0");
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// æ·»åŠ è¡Œç±»åæ–¹æ³•
const tableRowClassName = ({ row }) => {
  return row.isInvalid ? 'invalid-row' : '';
};
onMounted(() => {
  getList();
});
</script>
<style scoped lang="scss">
.invalid-row {
  opacity: 0.6;
  background-color: #f5f7fa;
}
  .invalid-row {
    opacity: 0.6;
    background-color: #f5f7fa;
  }
</style>
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,44 +1,39 @@
<template>
  <div>
    <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="产品名称"
            prop="productModelId"
            :rules="[
    <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="产品名称"
                      prop="productModelId"
                      :rules="[
                {
                required: true,
                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"
          @confirm="handleProductSelect"
          single
      />
      <ProductSelectDialog v-model="showProductSelectDialog"
                           @confirm="handleProductSelect"
                           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>
@@ -74,119 +75,130 @@
</template>
<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 { ref, computed, getCurrentInstance } from "vue";
  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
  import {
    addProductOrder,
    listProcessRoute,
  } from "@/api/productionManagement/productionOrder.js";
  import { listPage } from "@/api/productionManagement/processRoute.js";
  const props = defineProps({
    visible: {
      type: Boolean,
      required: true,
    },
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
    type: {
      type: String,
      required: true,
      default: "qualified",
    },
  });
  type: {
    type: String,
    required: true,
    default: 'qualified',
  },
});
  const emit = defineEmits(["update:visible", "completed"]);
const emit = defineEmits(['update:visible', 'completed']);
// å“åº”式数据(替代选项式的 data)
const formState = ref({
  productId: undefined,
  productModelId: undefined,
  routeId: undefined,
  productName: "",
  productModelName: "",
  unit: "",
  quantity: 0,
});
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
const showProductSelectDialog = ref(false);
let { proxy } = getCurrentInstance()
const closeModal = () => {
  // é‡ç½®è¡¨å•数据
  formState.value = {
  // å“åº”式数据(替代选项式的 data)
  const formState = ref({
    productId: undefined,
    productModelId: undefined,
    routeId: undefined,
    technologyRoutingId: undefined,
    productName: "",
    productModelName: "",
    quantity: '',
    unit: "",
    quantity: 0,
    planCompleteTime: "",
  });
  const isShow = computed({
    get() {
      return props.visible;
    },
    set(val) {
      emit("update:visible", val);
    },
  });
  const showProductSelectDialog = ref(false);
  let { proxy } = getCurrentInstance();
  const closeModal = () => {
    // é‡ç½®è¡¨å•数据
    formState.value = {
      productId: undefined,
      productModelId: undefined,
      technologyRoutingId: undefined,
      productName: "",
      productModelName: "",
      unit: "",
      quantity: "",
      planCompleteTime: "",
    };
    isShow.value = false;
  };
  isShow.value = false;
};
// äº§å“é€‰æ‹©å¤„理
const handleProductSelect = async (products) => {
  if (products && products.length > 0) {
    const product = products[0];
    formState.value.productId = product.productId;
    formState.value.productName = product.productName;
    formState.value.productModelName = product.model;
    formState.value.productModelId = product.id;
    formState.value.unit = product.unit;
    showProductSelectDialog.value = false;
    fetchRouteOptions( product.id);
    // è§¦å‘表单验证更新
    proxy.$refs["formRef"]?.validateField('productModelId');
  }
};
const routeOptions = ref([]);
const bindRouteLoading = ref(false);
const fetchRouteOptions = (productModelId) => {
  formState.value.routeId = undefined;
  routeOptions.value = []
  bindRouteLoading.value = true;
  listProcessRoute({ productModelId: productModelId }).then(res => {
    routeOptions.value = res.data || [];
  }).finally(() => {
    bindRouteLoading.value = false;
  })
}
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      // éªŒè¯æ˜¯å¦é€‰æ‹©äº†äº§å“å’Œè§„æ ¼
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择产品");
        return;
      }
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择规格");
        return;
      }
      addProductOrder(formState.value).then(res => {
        // å…³é—­æ¨¡æ€æ¡†
        isShow.value = false;
        // å‘ŠçŸ¥çˆ¶ç»„件已完成
        emit('completed');
        proxy.$modal.msgSuccess("提交成功");
      })
  // äº§å“é€‰æ‹©å¤„理
  const handleProductSelect = async products => {
    if (products && products.length > 0) {
      const product = products[0];
      formState.value.productId = product.productId;
      formState.value.productName = product.productName;
      formState.value.productModelName = product.model;
      formState.value.productModelId = product.id;
      formState.value.unit = product.unit;
      showProductSelectDialog.value = false;
      fetchRouteOptions(product.id);
      // è§¦å‘表单验证更新
      proxy.$refs["formRef"]?.validateField("productModelId");
    }
  })
};
  };
  const routeOptions = ref([]);
  const bindRouteLoading = ref(false);
  const fetchRouteOptions = productModelId => {
    formState.value.technologyRoutingId = undefined;
    routeOptions.value = [];
    bindRouteLoading.value = true;
    listPage({ productModelId: productModelId })
      .then(res => {
        routeOptions.value = res.data.records || [];
      })
      .finally(() => {
        bindRouteLoading.value = false;
      });
  };
defineExpose({
  closeModal,
  handleSubmit,
  isShow,
});
  const handleSubmit = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        // éªŒè¯æ˜¯å¦é€‰æ‹©äº†äº§å“å’Œè§„æ ¼
        if (!formState.value.productModelId) {
          proxy.$modal.msgError("请选择产品");
          return;
        }
        if (!formState.value.productModelId) {
          proxy.$modal.msgError("请选择规格");
          return;
        }
        if (formState.value.quantity <= 0) {
          proxy.$modal.msgError("需求数量必须大于0");
          return;
        }
        addProductOrder(formState.value).then(res => {
          // å…³é—­æ¨¡æ€æ¡†
          isShow.value = false;
          // å‘ŠçŸ¥çˆ¶ç»„件已完成
          emit("completed");
          proxy.$modal.msgSuccess("提交成功");
        });
      }
    });
  };
  defineExpose({
    closeModal,
    handleSubmit,
    isShow,
  });
</script>
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)"
            :color="progressColor(toProgressPercentage(row?.completionStatus))"
            :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''"
          />
          <el-progress :percentage="toProgressPercentage(row?.completionStatus)"
                       :color="progressColor(toProgressPercentage(row?.completionStatus))"
                       :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" />
        </template>
      </PIMTable>
    </div>
@@ -98,26 +98,28 @@
        </span>
      </template>
    </el-dialog>
    <MaterialLedgerDialog
      v-model="materialDialogVisible"
      :order-row="currentMaterialOrder"
      @saved="getList"
    />
    <MaterialDetailDialog
      v-model="materialDetailDialogVisible"
      :order-row="currentMaterialDetailOrder"
      @confirmed="getList"
    />
    <MaterialLedgerDialog v-model="materialDialogVisible"
                          :order-row="currentMaterialOrder"
                          @saved="getList" />
    <MaterialDetailDialog v-model="materialDetailDialogVisible"
                          :order-row="currentMaterialDetailOrder"
                          @confirmed="getList" />
    <new-product-order v-if="isShowNewModal"
                         v-model:visible="isShowNewModal"
                         @completed="handleQuery" />
                       v-model:visible="isShowNewModal"
                       @completed="handleQuery" />
  </div>
</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,14 +474,16 @@
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      delProductOrder(ids).then((res) => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
    })
      .then(() => {
        delProductOrder(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
    }).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("已取消");
@@ -501,33 +513,32 @@
</script>
<style scoped lang="scss">
.search_form{
  align-items: start;
}
  .search_form {
    align-items: start;
  }
.action-buttons {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
}
  .action-buttons {
    display: flex;
    flex-wrap: nowrap;
    gap: 8px;
  }
:deep(.yellow) {
  background-color: #FAF0DE;
}
  :deep(.yellow) {
    background-color: #faf0de;
  }
:deep(.pink) {
  background-color: #FAE1DE;
}
  :deep(.pink) {
    background-color: #fae1de;
  }
:deep(.red) {
  background-color: #f80202;
}
  :deep(.red) {
    background-color: #f80202;
  }
:deep(.purple){
  background-color: #F4DEFA;
}
.table_list {
    margin-top: unset;
}
  :deep(.purple) {
    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-input v-model="searchForm.productName"
                      placeholder="请输入"
                      clearable
                      style="width: 160px;"
                      @keyup.enter="handleQuery" />
          </el-form-item>
          <el-form-item label="产品规格:">
            <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 label="产品名称:"
                      prop="productName">
          <el-input v-model="searchForm.productName"
                    placeholder="请输入"
                    clearable
                    style="width: 160px;"
                    @keyup.enter="handleQuery" />
        </el-form-item>
        <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>
          <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",
    },
    {
      label: "物料编码",
      prop: "materialCode",
      width: "150px",
      dataType: "tag",
      formatType: params => {
        return params == "内部" ? "info" : "primary";
      },
    },
    {
      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
ÎļþÌ«´ó