liyong
8 天以前 b175633a8a9599a09667d7d7c01a13d77c7733ee
feat(customerService): 新增售后单表单组件及销售台账管理功能

- 创建了售后单表单组件,包含客户名称、售后类型、关联销售单号等字段
- 实现了产品选择对话框和关联产品的表格展示功能
- 集成了销售台账管理界面,支持合同号、客户名称等条件查询
- 添加了销售台账的新增、编辑、删除、导入导出等操作功能
- 实现了销售台账子表格展开显示产品详细信息的功能
- 完善了发货状态和产品状态的标签显示逻辑
- 修复了退货订单中出库单号显示错误的问题
已修改4个文件
3272 ■■■■ 文件已修改
src/api/inventoryManagement/stockInventory.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/feedbackRegistration/components/formDia.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/returnOrder/components/formDia.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 3261 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockInventory.js
@@ -87,3 +87,11 @@
    });
};
export const getStockInventoryByModelId = (productModelId) => {
    return request({
        url: "/stockInventory/getByModelId",
        method: "get",
        params: { productModelId },
    });
};
src/views/customerService/feedbackRegistration/components/formDia.vue
@@ -212,6 +212,7 @@
    taxInclusiveUnitPrice: row?.taxInclusiveUnitPrice ?? 0,
    taxInclusiveTotalPrice: row?.taxInclusiveTotalPrice ?? 0,
    taxExclusiveTotalPrice: row?.taxExclusiveTotalPrice ?? 0,
    noQuantity: row?.noQuantity ?? 0,
  }
}
src/views/salesManagement/returnOrder/components/formDia.vue
@@ -405,7 +405,7 @@
  }).then(res => {
    if(res.code === 200){
      outboundOptions.value = res.data.map(item => ({
        label: item.salesContractNo, // Or whatever the outbound number field is
        label: item.shippingNo, // Or whatever the outbound number field is
        value: item.id,
      }))
    }
src/views/salesManagement/salesLedger/index.vue
@@ -8,21 +8,21 @@
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
                    @change="handleQuery"/>
        </el-form-item>
        <el-form-item label="销售合同号:">
          <el-input v-model="searchForm.salesContractNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
                    @change="handleQuery"/>
        </el-form-item>
        <el-form-item label="项目名称:">
          <el-input v-model="searchForm.projectName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
                    @change="handleQuery"/>
        </el-form-item>
        <el-form-item label="录入日期:">
          <el-date-picker v-model="searchForm.entryDate"
@@ -31,11 +31,12 @@
                          type="daterange"
                          placeholder="请选择"
                          clearable
                          @change="changeDaterange" />
                          @change="changeDaterange"/>
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery"> 搜索 </el-button>
                     @click="handleQuery"> 搜索
          </el-button>
        </el-form-item>
      </el-form>
    </div>
@@ -49,14 +50,17 @@
          </el-button>
          <el-button type="primary"
                     plain
                     @click="handleImport">导入</el-button>
                     @click="handleImport">导入
          </el-button>
          <el-button @click="handleOut">导出</el-button>
          <el-button type="danger"
                     plain
                     @click="handleDelete">删除</el-button>
                     @click="handleDelete">删除
          </el-button>
          <el-button type="primary"
                     plain
                     @click="handlePrint">打印</el-button>
                     @click="handlePrint">打印
          </el-button>
        </div>
      </div>
      <el-table :data="tableData"
@@ -74,7 +78,7 @@
        <el-table-column align="center"
                         type="selection"
                         width="55"
                         fixed="left" />
                         fixed="left"/>
        <el-table-column type="expand"
                         width="60"
                         fixed="left">
@@ -85,23 +89,28 @@
                      :summary-method="(param) => summarizeChildrenTable(param, props.row)">
              <el-table-column align="center"
                               label="序号"
                               type="index" />
                               type="index"/>
              <el-table-column label="产品大类"
                               prop="productCategory" />
                               prop="productCategory"/>
              <el-table-column label="规格型号"
                               prop="specificationModel" />
                               prop="specificationModel"/>
              <el-table-column label="单位"
                               prop="unit" />
                               prop="unit"/>
              <el-table-column label="产品状态"
                               width="100px"
                               align="center">
                <template #default="scope">
                  <el-tag v-if="scope.row.approveStatus === 1 && (!scope.row.shippingDate || !scope.row.shippingCarNumber)"
                          type="success">充足</el-tag>
                  <el-tag v-else-if="scope.row.approveStatus === 0 && (scope.row.shippingDate || scope.row.shippingCarNumber)"
                          type="success">已出库</el-tag>
                  <el-tag
                      v-if="scope.row.approveStatus === 1 && scope.row.noQuantity !== 0"
                      type="success">充足
                  </el-tag>
                  <el-tag
                      v-else-if="scope.row.approveStatus === 0 && scope.row.noQuantity === 0"
                      type="success">已出库
                  </el-tag>
                  <el-tag v-else
                          type="danger">不足</el-tag>
                          type="danger">不足
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="发货状态"
@@ -116,19 +125,21 @@
              </el-table-column>
              <el-table-column label="快递公司"
                               prop="expressCompany"
                               show-overflow-tooltip />
                               show-overflow-tooltip/>
              <el-table-column label="快递单号"
                               prop="expressNumber"
                               show-overflow-tooltip />
                               show-overflow-tooltip/>
              <el-table-column label="发货车牌"
                               minWidth="100px"
                               align="center">
                <template #default="scope">
                  <div>
                    <el-tag type="success"
                            v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag>
                            v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}
                    </el-tag>
                    <el-tag v-else
                            type="info">-</el-tag>
                            type="info">-
                    </el-tag>
                  </div>
                </template>
              </el-table-column>
@@ -139,23 +150,26 @@
                  <div>
                    <div v-if="scope.row.shippingDate">{{ scope.row.shippingDate }}</div>
                    <el-tag v-else
                            type="info">-</el-tag>
                            type="info">-
                    </el-tag>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="数量"
                               prop="quantity" />
                               prop="quantity"/>
              <el-table-column label="待发货数量"
                               prop="noQuantity"/>
              <el-table-column label="税率(%)"
                               prop="taxRate" />
                               prop="taxRate"/>
              <el-table-column label="含税单价(元)"
                               prop="taxInclusiveUnitPrice"
                               :formatter="sensitiveAmountFormatter" />
                               :formatter="sensitiveAmountFormatter"/>
              <el-table-column label="含税总价(元)"
                               prop="taxInclusiveTotalPrice"
                               :formatter="sensitiveAmountFormatter" />
                               :formatter="sensitiveAmountFormatter"/>
              <el-table-column label="不含税总价(元)"
                               prop="taxExclusiveTotalPrice"
                               :formatter="sensitiveAmountFormatter" />
                               :formatter="sensitiveAmountFormatter"/>
              <!--操作-->
              <el-table-column Width="60px"
                               label="操作"
@@ -175,51 +189,51 @@
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
                         width="60"/>
        <el-table-column label="销售合同号"
                         prop="salesContractNo"
                         width="180"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column label="客户名称"
                         prop="customerName"
                         width="300"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column label="业务员"
                         prop="salesman"
                         width="100"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column label="项目名称"
                         prop="projectName"
                         width="180"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column label="付款方式"
                         prop="paymentMethod"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column label="合同金额(元)"
                         prop="contractAmount"
                         width="220"
                         show-overflow-tooltip
                         :formatter="formattedNumber" />
                         :formatter="formattedNumber"/>
        <el-table-column label="录入人"
                         prop="entryPersonName"
                         width="100"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column label="录入日期"
                         prop="entryDate"
                         width="120"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column label="签订日期"
                         prop="executionDate"
                         width="120"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column label="交付日期"
                         prop="deliveryDate"
                         width="120"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column label="备注"
                         prop="remarks"
                         width="200"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column fixed="right"
                         label="操作"
                         width="130"
@@ -228,10 +242,12 @@
            <el-button link
                       type="primary"
                       @click="openForm('edit', scope.row)"
                       :disabled="!scope.row.isEdit || scope.row.hasProductionRecord || !canEditLedger(scope.row)">编辑</el-button>
                       :disabled="!scope.row.isEdit || scope.row.hasProductionRecord || !canEditLedger(scope.row)">编辑
            </el-button>
            <el-button link
                       type="primary"
                       @click="openFileDialog(scope.row)">附件</el-button>
                       @click="openFileDialog(scope.row)">附件
            </el-button>
          </template>
        </el-table-column>
      </el-table>
@@ -240,7 +256,7 @@
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange" />
                  @pagination="paginationChange"/>
    </div>
    <FormDialog v-model="dialogFormVisible"
                :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'"
@@ -273,7 +289,7 @@
              <el-input v-model="form.salesContractNo"
                        placeholder="自动生成"
                        clearable
                        disabled />
                        disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -287,7 +303,7 @@
                <el-option v-for="item in userList"
                           :key="item.nickName"
                           :label="item.nickName"
                           :value="item.nickName" />
                           :value="item.nickName"/>
              </el-select>
            </el-form-item>
          </el-col>
@@ -318,7 +334,7 @@
              <el-input v-model="form.projectName"
                        placeholder="请输入"
                        clearable
                        :disabled="operationType === 'view'" />
                        :disabled="operationType === 'view'"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -333,7 +349,7 @@
                              type="date"
                              placeholder="请选择"
                              clearable
                              :disabled="operationType === 'view'" />
                              :disabled="operationType === 'view'"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -341,7 +357,7 @@
              <el-input v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable
                        :disabled="operationType === 'view'" />
                        :disabled="operationType === 'view'"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -359,7 +375,7 @@
                <el-option v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
                           :value="item.userId" />
                           :value="item.userId"/>
              </el-select>
            </el-form-item>
          </el-col>
@@ -372,7 +388,7 @@
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
                              clearable/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -386,7 +402,7 @@
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
                              clearable/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -395,11 +411,13 @@
                        prop="entryDate">
            <el-button v-if="operationType !== 'view'"
                       type="primary"
                       @click="openProductForm('add')">添加</el-button>
                       @click="openProductForm('add')">添加
            </el-button>
            <el-button v-if="operationType !== 'view'"
                       plain
                       type="danger"
                       @click="deleteProduct">删除</el-button>
                       @click="deleteProduct">删除
            </el-button>
          </el-form-item>
        </el-row>
        <el-table :data="productData"
@@ -411,30 +429,30 @@
                           type="selection"
                           width="55"
                           v-if="operationType !== 'view'"
                           :selectable="(row) => !isProductShipped(row)" />
                           :selectable="(row) => !isProductShipped(row)"/>
          <el-table-column align="center"
                           label="序号"
                           type="index"
                           width="60" />
                           width="60"/>
          <el-table-column label="产品大类"
                           prop="productCategory" />
                           prop="productCategory"/>
          <el-table-column label="规格型号"
                           prop="specificationModel" />
                           prop="specificationModel"/>
          <el-table-column label="单位"
                           prop="unit" />
                           prop="unit"/>
          <el-table-column label="数量"
                           prop="quantity" />
                           prop="quantity"/>
          <el-table-column label="税率(%)"
                           prop="taxRate" />
                           prop="taxRate"/>
          <el-table-column label="含税单价(元)"
                           prop="taxInclusiveUnitPrice"
                           :formatter="formattedNumber" />
                           :formatter="formattedNumber"/>
          <el-table-column label="含税总价(元)"
                           prop="taxInclusiveTotalPrice"
                           :formatter="formattedNumber" />
                           :formatter="formattedNumber"/>
          <el-table-column label="不含税总价(元)"
                           prop="taxExclusiveTotalPrice"
                           :formatter="formattedNumber" />
                           :formatter="formattedNumber"/>
          <el-table-column label="是否生产"
                           prop="isProduction"
                           width="150">
@@ -454,7 +472,8 @@
                         type="primary"
                         size="small"
                         :disabled="isProductShipped(scope.row)"
                         @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
                         @click="openProductForm('edit', scope.row,scope.$index)">编辑
              </el-button>
            </template>
          </el-table-column>
        </el-table>
@@ -467,7 +486,7 @@
                        clearable
                        type="textarea"
                        :rows="2"
                        :disabled="operationType === 'view'" />
                        :disabled="operationType === 'view'"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -475,7 +494,7 @@
          <el-col :span="24">
            <el-form-item label="附件材料:"
                          prop="salesLedgerFiles">
              <FileUpload v-model:file-list="fileList" />
              <FileUpload v-model:file-list="fileList"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -491,14 +510,15 @@
                  placeholder="请输入报价单号"
                  clearable
                  style="max-width: 260px;"
                  @change="fetchQuotationList" />
                  @change="fetchQuotationList"/>
        <el-input v-model="quotationSearchForm.customer"
                  placeholder="请输入客户名称"
                  clearable
                  style="max-width: 260px;"
                  @change="fetchQuotationList" />
                  @change="fetchQuotationList"/>
        <el-button type="primary"
                   @click="fetchQuotationList">搜索</el-button>
                   @click="fetchQuotationList">搜索
        </el-button>
        <el-button @click="resetQuotationSearch">重置</el-button>
      </div>
      <el-table :data="quotationList"
@@ -509,26 +529,26 @@
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
                         width="60"/>
        <el-table-column prop="quotationNo"
                         label="报价单号"
                         width="180"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column prop="customer"
                         label="客户名称"
                         min-width="220"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column prop="salesperson"
                         label="业务员"
                         width="120"
                         show-overflow-tooltip />
                         show-overflow-tooltip/>
        <el-table-column prop="quotationDate"
                         label="报价日期"
                         width="140" />
                         width="140"/>
        <el-table-column prop="status"
                         label="审批状态"
                         width="120"
                         align="center" />
                         align="center"/>
        <el-table-column prop="totalAmount"
                         label="报价金额(元)"
                         width="160"
@@ -544,7 +564,8 @@
          <template #default="scope">
            <el-button type="primary"
                       link
                       @click="applyQuotation(scope.row)">选择</el-button>
                       @click="applyQuotation(scope.row)">选择
            </el-button>
          </template>
        </el-table-column>
      </el-table>
@@ -553,7 +574,7 @@
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="quotationPage.current"
                  :limit="quotationPage.size"
                  @pagination="quotationPaginationChange" />
                  @pagination="quotationPaginationChange"/>
      <template #footer>
        <el-button @click="quotationDialogVisible = false">关闭</el-button>
      </template>
@@ -582,7 +603,7 @@
                              @change="getModels"
                              :data="productOptions"
                              :render-after-expand="false"
                              style="width: 100%" />
                              style="width: 100%"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -598,7 +619,7 @@
                <el-option v-for="item in modelOptions"
                           :key="item.id"
                           :label="item.model"
                           :value="item.id" />
                           :value="item.id"/>
              </el-select>
            </el-form-item>
          </el-col>
@@ -609,7 +630,7 @@
                          prop="unit">
              <el-input v-model="productForm.unit"
                        placeholder="请输入"
                        clearable />
                        clearable/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -620,11 +641,11 @@
                         clearable
                         @change="calculateFromTaxRate">
                <el-option label="1"
                           value="1" />
                           value="1"/>
                <el-option label="6"
                           value="6" />
                           value="6"/>
                <el-option label="13"
                           value="13" />
                           value="13"/>
              </el-select>
            </el-form-item>
          </el-col>
@@ -640,7 +661,7 @@
                               :precision="2"
                               placeholder="请输入"
                               clearable
                               @change="calculateFromUnitPrice" />
                               @change="calculateFromUnitPrice"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -653,7 +674,7 @@
                               clearable
                               :precision="2"
                               @change="calculateFromQuantity"
                               style="width: 100%" />
                               style="width: 100%"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -664,7 +685,7 @@
              <el-input v-model="productForm.taxInclusiveTotalPrice"
                        placeholder="请输入"
                        clearable
                        @change="calculateFromTotalPrice" />
                        @change="calculateFromTotalPrice"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -673,7 +694,7 @@
              <el-input v-model="productForm.taxExclusiveTotalPrice"
                        placeholder="请输入"
                        clearable
                        @change="calculateFromExclusiveTotalPrice" />
                        @change="calculateFromExclusiveTotalPrice"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -685,9 +706,9 @@
                         placeholder="请选择"
                         clearable>
                <el-option label="增普票"
                           value="增普票" />
                           value="增普票"/>
                <el-option label="增专票"
                           value="增专票" />
                           value="增专票"/>
              </el-select>
            </el-form-item>
          </el-col>
@@ -696,9 +717,9 @@
                          prop="isProduction">
              <el-radio-group v-model="productForm.isProduction">
                <el-radio label="是"
                          :value="true" />
                          :value="true"/>
                <el-radio label="否"
                          :value="false" />
                          :value="false"/>
              </el-radio-group>
            </el-form-item>
          </el-col>
@@ -733,14 +754,16 @@
            仅支持 xls/xlsx,大小不超过 10MB。
            <el-button link
                       type="primary"
                       @click="downloadTemplate">下载导入模板</el-button>
                       @click="downloadTemplate">下载导入模板
            </el-button>
          </div>
        </template>
      </el-upload>
    </FormDialog>
<!-- // todo 附件预览相关 -->
    <!-- // todo 附件预览相关 -->
    <!-- 附件列表弹窗 -->
    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" record-type="sales_ledger" :record-id="recordId"  />
    <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="sales_ledger"
              :record-id="recordId"/>
    <!-- 打印预览弹窗 -->
    <el-dialog v-model="printPreviewVisible"
               title="打印预览"
@@ -750,7 +773,8 @@
      <div class="print-preview-container">
        <div class="print-preview-header">
          <el-button type="primary"
                     @click="executePrint">执行打印</el-button>
                     @click="executePrint">执行打印
          </el-button>
          <el-button @click="printPreviewVisible = false">关闭预览</el-button>
        </div>
        <div class="print-preview-content">
@@ -792,39 +816,40 @@
              <div class="table-section">
                <table class="product-table">
                  <thead>
                    <tr>
                      <th>产品名称</th>
                      <th>规格型号</th>
                      <th>单位</th>
                      <th>单价</th>
                      <th>零售数量</th>
                      <th>零售金额</th>
                    </tr>
                  <tr>
                    <th>产品名称</th>
                    <th>规格型号</th>
                    <th>单位</th>
                    <th>单价</th>
                    <th>零售数量</th>
                    <th>零售金额</th>
                  </tr>
                  </thead>
                  <tbody>
                    <tr v-for="product in item.products"
                        :key="product.id">
                      <td>{{ product.productCategory || '' }}</td>
                      <td>{{ product.specificationModel || '' }}</td>
                      <td>{{ product.unit || '' }}</td>
                      <td>{{ product.taxInclusiveUnitPrice || '0' }}</td>
                      <td>{{ product.quantity || '0' }}</td>
                      <td>{{ product.taxInclusiveTotalPrice || '0' }}</td>
                    </tr>
                    <tr v-if="!item.products || item.products.length === 0">
                      <td colspan="6"
                          style="text-align: center; color: #999;">暂无产品数据</td>
                    </tr>
                  <tr v-for="product in item.products"
                      :key="product.id">
                    <td>{{ product.productCategory || '' }}</td>
                    <td>{{ product.specificationModel || '' }}</td>
                    <td>{{ product.unit || '' }}</td>
                    <td>{{ product.taxInclusiveUnitPrice || '0' }}</td>
                    <td>{{ product.quantity || '0' }}</td>
                    <td>{{ product.taxInclusiveTotalPrice || '0' }}</td>
                  </tr>
                  <tr v-if="!item.products || item.products.length === 0">
                    <td colspan="6"
                        style="text-align: center; color: #999;">暂无产品数据
                    </td>
                  </tr>
                  </tbody>
                  <tfoot>
                    <tr>
                      <td class="label">合计</td>
                      <td class="total-value"></td>
                      <td class="total-value"></td>
                      <td class="total-value"></td>
                      <td class="total-value">{{ getTotalQuantity(item.products) }}</td>
                      <td class="total-value">{{ getTotalAmount(item.products) }}</td>
                    </tr>
                  <tr>
                    <td class="label">合计</td>
                    <td class="total-value"></td>
                    <td class="total-value"></td>
                    <td class="total-value"></td>
                    <td class="total-value">{{ getTotalQuantity(item.products) }}</td>
                    <td class="total-value">{{ getTotalAmount(item.products) }}</td>
                  </tr>
                  </tfoot>
                </table>
              </div>
@@ -877,10 +902,47 @@
                         placeholder="请选择发货类型"
                         style="width: 100%">
                <el-option label="货车"
                           value="货车" />
                           value="货车"/>
                <el-option label="快递"
                           value="快递" />
                           value="快递"/>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="批号:"
                          prop="batchNo">
              <el-table :data="deliveryForm.batchNoList"
                        border
                        size="small"
                        max-height="260"
                        style="width: 100%;">
                <el-table-column label="批号"
                                 prop="batchNo"
                                 min-width="180"/>
                <el-table-column label="数量"
                                 min-width="120"
                                 align="center">
                  <template #default="scope">
                    {{ getDeliveryBatchQuantity(scope.row) }}
                  </template>
                </el-table-column>
                <el-table-column label="发货数量"
                                 min-width="160"
                                 align="center">
                  <template #default="scope">
                    <el-input-number v-model="scope.row.deliveryQuantity"
                                     :min="0"
                                     :max="getDeliveryBatchDeliveryMax(scope.row)"
                                     :precision="2"
                                     :step="0.01"
                                     controls-position="right"
                                     @change="handleDeliveryBatchQuantityChange(scope.row)"
                                     style="width: 100%;"/>
                  </template>
                </el-table-column>
              </el-table>
            </el-form-item>
          </el-col>
        </el-row>
@@ -888,7 +950,8 @@
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitDelivery">确认发货</el-button>
                     @click="submitDelivery">确认发货
          </el-button>
          <el-button @click="closeDeliveryDia">取消</el-button>
        </div>
      </template>
@@ -897,249 +960,309 @@
</template>
<script setup>
  import { getToken } from "@/utils/auth";
  import pagination from "@/components/PIMTable/Pagination.vue";
  import { onMounted, ref, getCurrentInstance } from "vue";
  import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
  import { ElMessageBox, ElMessage } from "element-plus";
  import useUserStore from "@/store/modules/user";
  import { userListNoPage } from "@/api/system/user.js";
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
  import {
    ledgerListPage,
    productList,
    customerList,
    addOrUpdateSalesLedger,
    getSalesLedgerWithProducts,
    delLedger,
    addOrUpdateSalesLedgerProduct,
    delProduct,
    delLedgerFile,
    getProductInventory,
  } from "@/api/salesManagement/salesLedger.js";
  import { modelList, productTreeList } from "@/api/basicData/product.js";
  import useFormData from "@/hooks/useFormData.js";
  import dayjs from "dayjs";
  import { getCurrentDate } from "@/utils/index.js";
  import { useRouter, useRoute } from "vue-router";
  import { listCustomerPrivatePool } from "@/api/basicData/customerFile.js";
  import FileUpload from "@/components/AttachmentUpload/file/index.vue";
import {getToken} from "@/utils/auth";
import pagination from "@/components/PIMTable/Pagination.vue";
import {onMounted, ref, getCurrentInstance} from "vue";
import {addShippingInfo} from "@/api/salesManagement/deliveryLedger.js";
import {ElMessageBox, ElMessage} from "element-plus";
import useUserStore from "@/store/modules/user";
import {userListNoPage} from "@/api/system/user.js";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import {getQuotationList} from "@/api/salesManagement/salesQuotation.js";
import {
  ledgerListPage,
  productList,
  customerList,
  addOrUpdateSalesLedger,
  getSalesLedgerWithProducts,
  delLedger,
  addOrUpdateSalesLedgerProduct,
  delProduct,
  delLedgerFile,
  getProductInventory,
} from "@/api/salesManagement/salesLedger.js";
import {getStockInventoryByModelId} from "@/api/inventoryManagement/stockInventory.js";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import {getCurrentDate} from "@/utils/index.js";
import {useRouter, useRoute} from "vue-router";
import {listCustomerPrivatePool} from "@/api/basicData/customerFile.js";
import FileUpload from "@/components/AttachmentUpload/file/index.vue";
  const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
  const router = useRouter();
  const route = useRoute();
  const userStore = useUserStore();
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const productData = ref([]);
  const selectedRows = ref([]);
  const productSelectedRows = ref([]);
  const userList = ref([]);
  const customerOption = ref([]);
  const productOptions = ref([]);
  const modelOptions = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
  });
  const total = ref(0);
  const fileList = ref([]);
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
const {proxy} = getCurrentInstance();
const tableData = ref([]);
const productData = ref([]);
const selectedRows = ref([]);
const productSelectedRows = ref([]);
const userList = ref([]);
const customerOption = ref([]);
const productOptions = ref([]);
const modelOptions = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
});
const total = ref(0);
const fileList = ref([]);
  // 用户信息表单弹框数据
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const data = reactive({
    searchForm: {
      customerName: "", // 客户名称
      salesContractNo: "", // 销售合同编号
      entryDate: null, // 录入日期
      entryDateStart: undefined,
      entryDateEnd: undefined,
    },
    form: {
      salesContractNo: "",
      salesman: "",
      customerId: "",
      entryPerson: "",
      entryDate: "",
      deliveryDate: "",
      maintenanceTime: "",
      productData: [],
      executionDate: "",
      hasProductionRecord: false,
    },
    rules: {
      salesman: [{ required: true, message: "请选择", trigger: "change" }],
      customerId: [{ required: true, message: "请选择", trigger: "change" }],
      entryPerson: [{ required: true, message: "请选择", trigger: "change" }],
      entryDate: [{ required: true, message: "请选择", trigger: "change" }],
      deliveryDate: [{ 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 currentId = ref("");
  const productFormData = reactive({
    productForm: {
      productCategory: "",
      specificationModel: "",
      unit: "",
      quantity: "",
      taxInclusiveUnitPrice: "",
      taxRate: "",
      taxInclusiveTotalPrice: "",
      taxExclusiveTotalPrice: "",
      invoiceType: "",
      isProduction: false,
    },
    productRules: {
      productCategory: [{ required: true, message: "请选择", trigger: "change" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
      specificationModel: [
        { 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" }],
      taxInclusiveTotalPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      taxExclusiveTotalPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
      isProduction: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
  const { productForm, productRules } = toRefs(productFormData);
  // 防止循环计算的标志
  const isCalculating = ref(false);
  // 打印相关
  const printPreviewVisible = ref(false);
  const printData = ref([]);
// 用户信息表单弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
const data = reactive({
  searchForm: {
    customerName: "", // 客户名称
    salesContractNo: "", // 销售合同编号
    entryDate: null, // 录入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
  form: {
    salesContractNo: "",
    salesman: "",
    customerId: "",
    entryPerson: "",
    entryDate: "",
    deliveryDate: "",
    maintenanceTime: "",
    productData: [],
    executionDate: "",
    hasProductionRecord: false,
  },
  rules: {
    salesman: [{required: true, message: "请选择", trigger: "change"}],
    customerId: [{required: true, message: "请选择", trigger: "change"}],
    entryPerson: [{required: true, message: "请选择", trigger: "change"}],
    entryDate: [{required: true, message: "请选择", trigger: "change"}],
    deliveryDate: [{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 currentId = ref("");
const productFormData = reactive({
  productForm: {
    productCategory: "",
    specificationModel: "",
    unit: "",
    quantity: "",
    taxInclusiveUnitPrice: "",
    taxRate: "",
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
    isProduction: false,
  },
  productRules: {
    productCategory: [{required: true, message: "请选择", trigger: "change"}],
    productModelId: [{required: true, message: "请选择", trigger: "change"}],
    specificationModel: [
      {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"}],
    taxInclusiveTotalPrice: [
      {required: true, message: "请输入", trigger: "blur"},
    ],
    taxExclusiveTotalPrice: [
      {required: true, message: "请输入", trigger: "blur"},
    ],
    invoiceType: [{required: true, message: "请选择", trigger: "change"}],
    isProduction: [{required: true, message: "请选择", trigger: "change"}],
  },
});
const {productForm, productRules} = toRefs(productFormData);
// 防止循环计算的标志
const isCalculating = ref(false);
// 打印相关
const printPreviewVisible = ref(false);
const printData = ref([]);
  // 报价单导入相关
  const quotationDialogVisible = ref(false);
  const quotationLoading = ref(false);
  const quotationList = ref([]);
  const quotationSearchForm = reactive({
    quotationNo: "",
    customer: "",
  });
  // 报价单弹框分页
  const quotationPage = reactive({
    current: 1,
    size: 10,
    total: 0,
  });
  const selectedQuotation = ref(null);
// 报价单导入相关
const quotationDialogVisible = ref(false);
const quotationLoading = ref(false);
const quotationList = ref([]);
const quotationSearchForm = reactive({
  quotationNo: "",
  customer: "",
});
// 报价单弹框分页
const quotationPage = reactive({
  current: 1,
  size: 10,
  total: 0,
});
const selectedQuotation = ref(null);
  // 发货相关
  const deliveryFormVisible = ref(false);
  const currentDeliveryRow = ref(null);
  const deliveryFormData = reactive({
    deliveryForm: {
      type: "货车", // 货车, 快递
    },
    deliveryRules: {
      type: [{ required: true, message: "请选择发货类型", trigger: "change" }],
    },
  });
  const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
// 发货相关
const deliveryFormVisible = ref(false);
const currentDeliveryRow = ref(null);
const getDeliveryBatchQuantity = item => {
  const quantity = item?.qualitity
      ?? item?.quantity
      ?? item?.unLockedQuantity
      ?? item?.qualifiedUnLockedQuantity
      ?? item?.qualifiedQuantity
      ?? item?.stockQuantity;
  return quantity ?? 0;
};
const getCurrentDeliveryRowQuantity = () => {
  return Number(currentDeliveryRow.value?.noQuantity || 0);
};
const getDeliveryBatchDeliveryMax = row => {
  const productQuantity = getCurrentDeliveryRowQuantity();
  const batchQuantity = Number(getDeliveryBatchQuantity(row) || 0);
  const otherBatchTotal = (deliveryForm.value.batchNoList || []).reduce(
      (sum, item) => {
        if (item?.id === row?.id) return sum;
        return sum + Number(item?.deliveryQuantity || 0);
      },
      0
  );
  const remainingProductQuantity = Math.max(
      0,
      productQuantity - otherBatchTotal
  );
  return Math.max(0, Math.min(batchQuantity, remainingProductQuantity));
};
const handleDeliveryBatchQuantityChange = row => {
  const max = getDeliveryBatchDeliveryMax(row);
  const currentValue = Number(row?.deliveryQuantity || 0);
  if (currentValue > max) {
    row.deliveryQuantity = max;
    proxy.$modal.msgWarning("发货数量不能超过这个产品的数量");
  }
};
const getSelectedDeliveryBatchRows = () => {
  return (deliveryForm.value.batchNoList || []).filter(
      item => Number(item?.deliveryQuantity || 0) > 0
  );
};
const getDeliveryBatchNoList = async productModelId => {
  if (!productModelId) return [];
  const res = await getStockInventoryByModelId(productModelId);
  const rawList = Array.isArray(res?.data)
      ? res.data
      : res?.data?.records || res?.data?.rows || [];
  const seenIds = new Set();
  return rawList.filter(item => {
    if (!item?.id || !item?.batchNo || seenIds.has(item.id)) {
      return false;
    }
    seenIds.add(item.id);
    return true;
  }).map(item => ({
    ...item,
    deliveryQuantity: 0,
  }));
};
const deliveryFormData = reactive({
  deliveryForm: {
    type: "货车", // 货车, 快递
  },
  deliveryRules: {
    type: [{required: true, message: "请选择发货类型", trigger: "change"}],
  },
});
const {deliveryForm, deliveryRules} = toRefs(deliveryFormData);
  // 导入相关
  const importUploadRef = ref(null);
  const importUpload = reactive({
    title: "导入销售台账",
    open: false,
    url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
    headers: { Authorization: "Bearer " + getToken() },
    isUploading: false,
    beforeUpload: file => {
      const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
      const isLt10M = file.size / 1024 / 1024 < 10;
      if (!isExcel) {
        proxy.$modal.msgError("上传文件只能是 xlsx/xls 格式!");
        return false;
// 导入相关
const importUploadRef = ref(null);
const importUpload = reactive({
  title: "导入销售台账",
  open: false,
  url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
  headers: {Authorization: "Bearer " + getToken()},
  isUploading: false,
  beforeUpload: file => {
    const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
    const isLt10M = file.size / 1024 / 1024 < 10;
    if (!isExcel) {
      proxy.$modal.msgError("上传文件只能是 xlsx/xls 格式!");
      return false;
    }
    if (!isLt10M) {
      proxy.$modal.msgError("上传文件大小不能超过 10MB!");
      return false;
    }
    return true;
  },
  onChange: (file, fileList) => {
    console.log("文件状态改变", file, fileList);
  },
  onProgress: (event, file, fileList) => {
    console.log("上传中...", event.percent);
  },
  onSuccess: (response, file, fileList) => {
    console.log("上传成功", response, file, fileList);
    importUpload.isUploading = false;
    if (response.code === 200) {
      proxy.$modal.msgSuccess("导入成功");
      importUpload.open = false;
      if (importUploadRef.value) {
        importUploadRef.value.clearFiles();
      }
      if (!isLt10M) {
        proxy.$modal.msgError("上传文件大小不能超过 10MB!");
        return false;
      }
      return true;
    },
    onChange: (file, fileList) => {
      console.log("文件状态改变", file, fileList);
    },
    onProgress: (event, file, fileList) => {
      console.log("上传中...", event.percent);
    },
    onSuccess: (response, file, fileList) => {
      console.log("上传成功", response, file, fileList);
      importUpload.isUploading = false;
      if (response.code === 200) {
        proxy.$modal.msgSuccess("导入成功");
        importUpload.open = false;
        if (importUploadRef.value) {
          importUploadRef.value.clearFiles();
        }
        getList();
      } else {
        proxy.$modal.msgError(response.msg || "导入失败");
      }
    },
    onError: (error, file, fileList) => {
      console.error("上传失败", error, file, fileList);
      importUpload.isUploading = false;
      proxy.$modal.msgError("导入失败,请重试");
    },
  });
  const changeDaterange = value => {
    if (value) {
      searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
      searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
      getList();
    } else {
      searchForm.entryDateStart = undefined;
      searchForm.entryDateEnd = undefined;
      proxy.$modal.msgError(response.msg || "导入失败");
    }
    handleQuery();
  };
  },
  onError: (error, file, fileList) => {
    console.error("上传失败", error, file, fileList);
    importUpload.isUploading = false;
    proxy.$modal.msgError("导入失败,请重试");
  },
});
  // 查询列表
  /** 搜索按钮操作 */
  const handleQuery = () => {
    // 只有在点击搜索按钮时才重置页码到第一页
    // 避免表单字段change事件干扰分页
    if (arguments.length === 0) {
      page.current = 1;
    }
    expandedRowKeys.value = [];
    getList();
  };
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    const { entryDate, ...rest } = searchForm;
    // 将范围日期字段传递给后端
    const params = { ...rest, ...page };
    // 移除录入日期的默认值设置,只保留范围日期字段
    delete params.entryDate;
    return ledgerListPage(params)
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 handleQuery = () => {
  // 只有在点击搜索按钮时才重置页码到第一页
  // 避免表单字段change事件干扰分页
  if (arguments.length === 0) {
    page.current = 1;
  }
  expandedRowKeys.value = [];
  getList();
};
const paginationChange = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  const {entryDate, ...rest} = searchForm;
  // 将范围日期字段传递给后端
  const params = {...rest, ...page};
  // 移除录入日期的默认值设置,只保留范围日期字段
  delete params.entryDate;
  return ledgerListPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.records;
@@ -1152,619 +1275,624 @@
      .catch(() => {
        tableLoading.value = false;
      });
  };
  // 获取产品大类tree数据
  const getProductOptions = () => {
    // 返回 Promise,便于在编辑产品时等待加载完成
    return productTreeList().then(res => {
      productOptions.value = convertIdToValue(res);
      return productOptions.value;
    });
  };
  const formattedNumber = (row, column, cellValue) => {
    if (cellValue === undefined || cellValue === null || cellValue === "") {
      return "0.00";
    }
    return parseFloat(cellValue).toFixed(2);
  };
  const findLedgerRecordByRow = row => {
    if (!row) return null;
    if (
};
// 获取产品大类tree数据
const getProductOptions = () => {
  // 返回 Promise,便于在编辑产品时等待加载完成
  return productTreeList().then(res => {
    productOptions.value = convertIdToValue(res);
    return productOptions.value;
  });
};
const formattedNumber = (row, column, cellValue) => {
  if (cellValue === undefined || cellValue === null || cellValue === "") {
    return "0.00";
  }
  return parseFloat(cellValue).toFixed(2);
};
const findLedgerRecordByRow = row => {
  if (!row) return null;
  if (
      row.maintainer !== undefined ||
      row.maintainerName !== undefined ||
      row.entryPerson !== undefined ||
      row.entryPersonName !== undefined
    ) {
      return row;
    }
    if (row.salesLedgerId !== undefined && row.salesLedgerId !== null) {
      return (
  ) {
    return row;
  }
  if (row.salesLedgerId !== undefined && row.salesLedgerId !== null) {
    return (
        tableData.value.find(
          item => String(item.id) === String(row.salesLedgerId)
            item => String(item.id) === String(row.salesLedgerId)
        ) || null
      );
    }
    return null;
  };
  const isCurrentUserMaintainer = row => {
    const ledgerRecord = findLedgerRecordByRow(row);
    if (!ledgerRecord) return true;
    const currentUserId = String(userStore.id ?? "");
    const currentNickName = String(userStore.nickName ?? "").trim();
    const maintainerId = ledgerRecord.maintainerId ?? ledgerRecord.entryPerson;
    const maintainerName =
    );
  }
  return null;
};
const isCurrentUserMaintainer = row => {
  const ledgerRecord = findLedgerRecordByRow(row);
  if (!ledgerRecord) return true;
  const currentUserId = String(userStore.id ?? "");
  const currentNickName = String(userStore.nickName ?? "").trim();
  const maintainerId = ledgerRecord.maintainerId ?? ledgerRecord.entryPerson;
  const maintainerName =
      ledgerRecord.maintainerName ??
      ledgerRecord.maintainer ??
      ledgerRecord.entryPersonName;
    if (
  if (
      maintainerId !== undefined &&
      maintainerId !== null &&
      String(maintainerId) !== ""
    ) {
      return String(maintainerId) === currentUserId;
    }
    if (
  ) {
    return String(maintainerId) === currentUserId;
  }
  if (
      maintainerName !== undefined &&
      maintainerName !== null &&
      String(maintainerName).trim() !== ""
    ) {
      return String(maintainerName).trim() === currentNickName;
    }
    return true;
  };
  const canEditLedger = row => isCurrentUserMaintainer(row);
  const canDeleteLedger = row => isCurrentUserMaintainer(row);
  const sensitiveAmountFormatter = (row, column, cellValue) => {
    if (!isCurrentUserMaintainer(row)) {
      return "*****";
    }
    return formattedNumber(row, column, cellValue);
  };
  // 获取tree子数据
  const getModels = value => {
    productForm.value.productCategory = findNodeById(productOptions.value, value);
    modelList({ id: value }).then(res => {
      modelOptions.value = res;
    });
  };
  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; // 找到节点,返回该节点
      }
      if (nodes[i].children && nodes[i].children.length > 0) {
        const foundNode = findNodeById(nodes[i].children, productId);
        if (foundNode) {
          return foundNode; // 在子节点中找到,返回该节点
        }
      }
    }
    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;
    });
  ) {
    return String(maintainerName).trim() === currentNickName;
  }
  // 根据名称反查产品大类 id,便于仅存名称时的反显
  function findNodeIdByLabel(nodes, label) {
    if (!label) return null;
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      if (node.label === label) return node.value;
      if (node.children && node.children.length > 0) {
        const found = findNodeIdByLabel(node.children, label);
        if (found !== null && found !== undefined) return found;
      }
    }
    return null;
  return true;
};
const canEditLedger = row => isCurrentUserMaintainer(row);
const canDeleteLedger = row => isCurrentUserMaintainer(row);
const sensitiveAmountFormatter = (row, column, cellValue) => {
  if (!isCurrentUserMaintainer(row)) {
    return "*****";
  }
  // 表格选择数据
  const handleSelectionChange = selection => {
    // 过滤掉子数据
    selectedRows.value = selection.filter(item => item.children !== undefined);
    console.log("selection", selectedRows.value);
  };
  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: 1 }).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);
  return formattedNumber(row, column, cellValue);
};
// 获取tree子数据
const getModels = value => {
  productForm.value.productCategory = findNodeById(productOptions.value, value);
  modelList({id: value}).then(res => {
    modelOptions.value = res;
  });
};
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; // 找到节点,返回该节点
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // 在子节点中找到,返回该节点
      }
    } else {
      expandedRowKeys.value = [];
    }
  };
  }
  return null; // 没有找到节点,返回null
};
  // 添加表行类名方法
  const tableRowClassName = ({ row }) => {
    if (!row.deliveryDate) return "";
    if (row.isFh) return "";
    const diff = row.deliveryDaysDiff;
    if (diff === 15) {
      return "yellow";
    } else if (diff === 10) {
      return "pink";
    } else if (diff === 2) {
      return "purple";
    } else if (diff < 2) {
      return "red";
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);
    }
  };
  // 主表合计方法
  const summarizeMainTable = param => {
    return proxy.summarizeTable(param, [
      "contractAmount",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
    ]);
  };
  // 子表合计方法
  const summarizeChildrenTable = (param, parentRow) => {
    if (!isCurrentUserMaintainer(parentRow)) {
      const { columns } = param;
      return columns.map((column, index) => {
        if (index === 0) {
          return "合计";
    return newItem;
  });
}
// 根据名称反查产品大类 id,便于仅存名称时的反显
function findNodeIdByLabel(nodes, label) {
  if (!label) return null;
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];
    if (node.label === label) return node.value;
    if (node.children && node.children.length > 0) {
      const found = findNodeIdByLabel(node.children, label);
      if (found !== null && found !== undefined) return found;
    }
  }
  return null;
}
// 表格选择数据
const handleSelectionChange = selection => {
  // 过滤掉子数据
  selectedRows.value = selection.filter(item => item.children !== undefined);
  console.log("selection", selectedRows.value);
};
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: 1}).then(res => {
        const index = tableData.value.findIndex(item => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data;
        }
        if (
        expandedRowKeys.value.push(row.id);
      });
    } catch (error) {
      console.log(error);
    }
  } else {
    expandedRowKeys.value = [];
  }
};
// 添加表行类名方法
const tableRowClassName = ({row}) => {
  if (!row.deliveryDate) return "";
  if (row.isFh) return "";
  const diff = row.deliveryDaysDiff;
  if (diff === 15) {
    return "yellow";
  } else if (diff === 10) {
    return "pink";
  } else if (diff === 2) {
    return "purple";
  } else if (diff < 2) {
    return "red";
  }
};
// 主表合计方法
const summarizeMainTable = param => {
  return proxy.summarizeTable(param, [
    "contractAmount",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
};
// 子表合计方法
const summarizeChildrenTable = (param, parentRow) => {
  if (!isCurrentUserMaintainer(parentRow)) {
    const {columns} = param;
    return columns.map((column, index) => {
      if (index === 0) {
        return "合计";
      }
      if (
          [
            "taxInclusiveUnitPrice",
            "taxInclusiveTotalPrice",
            "taxExclusiveTotalPrice",
          ].includes(column.property)
        ) {
          return "*****";
        }
        return "";
      });
    }
    return proxy.summarizeTable(param, [
      "taxInclusiveUnitPrice",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
    ]);
  };
  // 打开弹框
  const openForm = async (type, row) => {
    if (type === "edit" && row && !canEditLedger(row)) {
      proxy.$modal.msgWarning("当前系统登录人不是维护人,不能编辑数据");
      return;
    }
    operationType.value = type;
    form.value = {};
    productData.value = [];
    selectedQuotation.value = null;
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    listCustomerPrivatePool({ current: -1, size: -1 }).then(res => {
      customerOption.value = res.data.records;
    });
    form.value.entryPerson = userStore.id;
    if (type === "add") {
      // 新增时设置录入日期为当天
      form.value.entryDate = getCurrentDate();
      // 签订日期默认为当天
      form.value.executionDate = getCurrentDate();
    } else {
      currentId.value = row.id;
      getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => {
        form.value = { ...res };
        form.value.entryPerson = Number(res.entryPerson);
        productData.value = form.value.productData;
        fileList.value = form.value.storageBlobVOs;
      });
    }
    // let userAll = await userStore.getInfo()
    // userList.value.forEach(element => {
    //   if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
    //     form.value.entryPerson = userAll.user.userId // 设置默认业务员为当前用户
    //   }
    // });
    form.value.entryDate = getCurrentDate(); // 设置默认录入日期为当前日期
    dialogFormVisible.value = true;
  };
  // 打开报价单选择弹窗(仅审批通过)
  const openQuotationDialog = async () => {
    if (operationType.value === "view") return;
    quotationDialogVisible.value = true;
    // 打开弹窗时重置分页到第一页
    quotationPage.current = 1;
    // 先确保客户列表已加载,便于后续回填 customerId
    if (!customerOption.value || customerOption.value.length === 0) {
      try {
        listCustomerPrivatePool({ current: -1, size: -1 }).then(res => {
          customerOption.value = res.data.records;
        });
      } catch (e) {
        // ignore,允许用户后续手动选择客户
      ) {
        return "*****";
      }
    }
    await fetchQuotationList();
  };
      return "";
    });
  }
  return proxy.summarizeTable(param, [
    "taxInclusiveUnitPrice",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
};
// 打开弹框
const openForm = async (type, row) => {
  if (type === "edit" && row && !canEditLedger(row)) {
    proxy.$modal.msgWarning("当前系统登录人不是维护人,不能编辑数据");
    return;
  }
  operationType.value = type;
  form.value = {};
  productData.value = [];
  selectedQuotation.value = null;
  let userLists = await userListNoPage();
  userList.value = userLists.data;
  listCustomerPrivatePool({current: -1, size: -1}).then(res => {
    customerOption.value = res.data.records;
  });
  form.value.entryPerson = userStore.id;
  if (type === "add") {
    // 新增时设置录入日期为当天
    form.value.entryDate = getCurrentDate();
    // 签订日期默认为当天
    form.value.executionDate = getCurrentDate();
  } else {
    currentId.value = row.id;
    getSalesLedgerWithProducts({id: row.id, type: 1}).then(res => {
      form.value = {...res};
      form.value.entryPerson = Number(res.entryPerson);
      productData.value = form.value.productData;
      fileList.value = form.value.storageBlobVOs;
    });
  }
  // let userAll = await userStore.getInfo()
  // userList.value.forEach(element => {
  //   if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
  //     form.value.entryPerson = userAll.user.userId // 设置默认业务员为当前用户
  //   }
  // });
  form.value.entryDate = getCurrentDate(); // 设置默认录入日期为当前日期
  dialogFormVisible.value = true;
};
  const fetchQuotationList = async () => {
    quotationLoading.value = true;
// 打开报价单选择弹窗(仅审批通过)
const openQuotationDialog = async () => {
  if (operationType.value === "view") return;
  quotationDialogVisible.value = true;
  // 打开弹窗时重置分页到第一页
  quotationPage.current = 1;
  // 先确保客户列表已加载,便于后续回填 customerId
  if (!customerOption.value || customerOption.value.length === 0) {
    try {
      const params = {
        // 后端分页字段:current / size
        current: quotationPage.current,
        size: quotationPage.size,
        ...quotationSearchForm,
        status: "通过",
      };
      const res = await getQuotationList(params);
      quotationList.value = res?.data?.records || [];
      quotationPage.total = res?.data?.total || 0;
    } finally {
      quotationLoading.value = false;
      listCustomerPrivatePool({current: -1, size: -1}).then(res => {
        customerOption.value = res.data.records;
      });
    } catch (e) {
      // ignore,允许用户后续手动选择客户
    }
  };
  }
  await fetchQuotationList();
};
  const resetQuotationSearch = async () => {
    quotationSearchForm.quotationNo = "";
    quotationSearchForm.customer = "";
    quotationPage.current = 1;
    await fetchQuotationList();
  };
const fetchQuotationList = async () => {
  quotationLoading.value = true;
  try {
    const params = {
      // 后端分页字段:current / size
      current: quotationPage.current,
      size: quotationPage.size,
      ...quotationSearchForm,
      status: "通过",
    };
    const res = await getQuotationList(params);
    quotationList.value = res?.data?.records || [];
    quotationPage.total = res?.data?.total || 0;
  } finally {
    quotationLoading.value = false;
  }
};
  // 报价单弹框分页切换
  const quotationPaginationChange = obj => {
    quotationPage.current = obj.page;
    quotationPage.size = obj.limit;
    fetchQuotationList();
  };
const resetQuotationSearch = async () => {
  quotationSearchForm.quotationNo = "";
  quotationSearchForm.customer = "";
  quotationPage.current = 1;
  await fetchQuotationList();
};
  // 选中报价单后回填到台账表单
  const applyQuotation = row => {
    if (!row) return;
    selectedQuotation.value = row;
// 报价单弹框分页切换
const quotationPaginationChange = obj => {
  quotationPage.current = obj.page;
  quotationPage.size = obj.limit;
  fetchQuotationList();
};
    // 业务员
    form.value.salesman = (row.salesperson || "").trim();
// 选中报价单后回填到台账表单
const applyQuotation = row => {
  if (!row) return;
  selectedQuotation.value = row;
    // 客户名称 -> customerId
    const qCustomerName = String(row.customer || "").trim();
    const customer = (customerOption.value || []).find(c => {
      const name = String(c.customerName || "").trim();
      return (
  // 业务员
  form.value.salesman = (row.salesperson || "").trim();
  // 客户名称 -> customerId
  const qCustomerName = String(row.customer || "").trim();
  const customer = (customerOption.value || []).find(c => {
    const name = String(c.customerName || "").trim();
    return (
        name === qCustomerName ||
        name.includes(qCustomerName) ||
        qCustomerName.includes(name)
      );
    });
    if (customer?.id) {
      form.value.customerId = customer.id;
    } else {
      // 如果找不到,保留原值(允许用户手动选择/不打断已有输入)
      form.value.customerId = form.value.customerId || "";
    }
    );
  });
  if (customer?.id) {
    form.value.customerId = customer.id;
  } else {
    // 如果找不到,保留原值(允许用户手动选择/不打断已有输入)
    form.value.customerId = form.value.customerId || "";
  }
    // 产品信息映射:报价 products -> 台账 productData
    const products = Array.isArray(row.products) ? row.products : [];
    productData.value = products.map(p => {
      const quantity = Number(p.quantity ?? 0) || 0;
      const unitPrice = Number(p.unitPrice ?? 0) || 0;
      const taxRate = "13"; // 默认 13%,便于直接提交(如需可在产品中自行修改)
      const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
      const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
  // 产品信息映射:报价 products -> 台账 productData
  const products = Array.isArray(row.products) ? row.products : [];
  productData.value = products.map(p => {
    const quantity = Number(p.quantity ?? 0) || 0;
    const unitPrice = Number(p.unitPrice ?? 0) || 0;
    const taxRate = "13"; // 默认 13%,便于直接提交(如需可在产品中自行修改)
    const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
        taxInclusiveTotalPrice,
        taxRate
      );
      return {
        // 台账字段
        productCategory: p.product || p.productName || "",
        specificationModel: p.specification || "",
        unit: p.unit || "",
        quantity: quantity,
        taxRate: taxRate,
        taxInclusiveUnitPrice: unitPrice.toFixed(2),
        taxInclusiveTotalPrice: taxInclusiveTotalPrice,
        taxExclusiveTotalPrice: taxExclusiveTotalPrice,
        invoiceType: "增普票",
        isProduction: true,
      };
    });
    );
    return {
      // 台账字段
      productCategory: p.product || p.productName || "",
      specificationModel: p.specification || "",
      unit: p.unit || "",
      quantity: quantity,
      taxRate: taxRate,
      taxInclusiveUnitPrice: unitPrice.toFixed(2),
      taxInclusiveTotalPrice: taxInclusiveTotalPrice,
      taxExclusiveTotalPrice: taxExclusiveTotalPrice,
      invoiceType: "增普票",
      isProduction: true,
    };
  });
    quotationDialogVisible.value = false;
  };
  function changs(val) {
    console.log(val);
  }
  // 提交表单
  const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        console.log("productData.value--", productData.value);
        if (productData.value !== null && productData.value.length > 0) {
          form.value.productData = proxy.HaveJson(productData.value);
        } else {
          proxy.$modal.msgWarning("请添加产品信息");
          return;
        }
        form.value.storageBlobDTOs = fileList;
        form.value.type = 1;
        addOrUpdateSalesLedger(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
          expandedRowKeys.value = [];
          getList();
        });
  quotationDialogVisible.value = false;
};
function changs(val) {
  console.log(val);
}
// 提交表单
const submitForm = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      console.log("productData.value--", productData.value);
      if (productData.value !== null && productData.value.length > 0) {
        form.value.productData = proxy.HaveJson(productData.value);
      } else {
        proxy.$modal.msgWarning("请添加产品信息");
        return;
      }
    });
  };
  // 关闭弹框
  const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
  };
  const productIndex = ref(0);
  // 打开产品弹框
  const openProductForm = async (type, row, index) => {
    // 编辑时检查产品是否已发货或审核通过
    if (type === "edit" && isProductShipped(row)) {
      proxy.$modal.msgWarning("已发货或审核通过的产品不能编辑");
      return;
      form.value.storageBlobDTOs = fileList;
      form.value.type = 1;
      addOrUpdateSalesLedger(form.value).then(res => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
        expandedRowKeys.value = [];
        getList();
      });
    }
  });
};
// 关闭弹框
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
};
    productOperationType.value = type;
    productForm.value = {};
    if (type === "add") {
      productForm.value.isProduction = true;
    }
    proxy.resetForm("productFormRef");
    if (type === "edit") {
      productForm.value = { ...row };
      productIndex.value = index;
      // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表
      try {
        const options =
const productIndex = ref(0);
// 打开产品弹框
const openProductForm = async (type, row, index) => {
  // 编辑时检查产品是否已发货或审核通过
  if (type === "edit" && isProductShipped(row)) {
    proxy.$modal.msgWarning("已发货或审核通过的产品不能编辑");
    return;
  }
  productOperationType.value = type;
  productForm.value = {};
  if (type === "add") {
    productForm.value.isProduction = true;
  }
  proxy.resetForm("productFormRef");
  if (type === "edit") {
    productForm.value = {...row};
    productIndex.value = index;
    // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表
    try {
      const options =
          productOptions.value && productOptions.value.length > 0
            ? productOptions.value
            : await getProductOptions();
        const categoryId = findNodeIdByLabel(
              ? productOptions.value
              : await getProductOptions();
      const categoryId = findNodeIdByLabel(
          options,
          productForm.value.productCategory
        );
        if (categoryId) {
          const models = await modelList({ id: categoryId });
          modelOptions.value = models || [];
          // 根据当前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
          const currentModel = (modelOptions.value || []).find(
      );
      if (categoryId) {
        const models = await modelList({id: categoryId});
        modelOptions.value = models || [];
        // 根据当前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
        const currentModel = (modelOptions.value || []).find(
            m => m.model === productForm.value.specificationModel
          );
          if (currentModel) {
            productForm.value.productModelId = currentModel.id;
          }
        }
      } catch (e) {
        // 加载失败时保持可编辑,不中断弹窗
        console.error("加载产品规格型号失败", e);
      }
    } else {
      getProductOptions();
    }
    productFormVisible.value = true;
  };
  // 提交产品表单
  const submitProduct = () => {
    proxy.$refs["productFormRef"].validate(valid => {
      if (valid) {
        if (operationType.value === "edit") {
          submitProductEdit();
        } else {
          if (productOperationType.value === "add") {
            productData.value.push({ ...productForm.value });
          } else {
            productData.value[productIndex.value] = { ...productForm.value };
          }
          closeProductDia();
        }
      }
    });
  };
  const submitProductEdit = () => {
    productForm.value.salesLedgerId = currentId.value;
    productForm.value.type = 1;
    addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeProductDia();
      getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(res => {
        productData.value = res.productData;
      });
    });
  };
  // 删除产品
  const deleteProduct = () => {
    if (productSelectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    // 检查是否有已发货或审核通过的产品
    const shippedProducts = productSelectedRows.value.filter(row =>
      isProductShipped(row)
    );
    if (shippedProducts.length > 0) {
      proxy.$modal.msgWarning("已发货或审核通过的产品不能删除");
      return;
    }
    if (operationType.value === "add") {
      productSelectedRows.value.forEach(selectedRow => {
        const index = productData.value.findIndex(
          product => product.id === selectedRow.id
        );
        if (index !== -1) {
          productData.value.splice(index, 1);
        if (currentModel) {
          productForm.value.productModelId = currentModel.id;
        }
      });
    } else {
      let ids = [];
      if (productSelectedRows.value.length > 0) {
        ids = productSelectedRows.value.map(item => item.id);
      }
      ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
      })
    } catch (e) {
      // 加载失败时保持可编辑,不中断弹窗
      console.error("加载产品规格型号失败", e);
    }
  } else {
    getProductOptions();
  }
  productFormVisible.value = true;
};
// 提交产品表单
const submitProduct = () => {
  proxy.$refs["productFormRef"].validate(valid => {
    if (valid) {
      if (operationType.value === "edit") {
        submitProductEdit();
      } else {
        if (productOperationType.value === "add") {
          productData.value.push({...productForm.value});
        } else {
          productData.value[productIndex.value] = {...productForm.value};
        }
        closeProductDia();
      }
    }
  });
};
const submitProductEdit = () => {
  productForm.value.salesLedgerId = currentId.value;
  productForm.value.type = 1;
  addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
    proxy.$modal.msgSuccess("提交成功");
    closeProductDia();
    getSalesLedgerWithProducts({id: currentId.value, type: 1}).then(res => {
      productData.value = res.productData;
    });
  });
};
// 删除产品
const deleteProduct = () => {
  if (productSelectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  // 检查是否有已发货或审核通过的产品
  const shippedProducts = productSelectedRows.value.filter(row =>
      isProductShipped(row)
  );
  if (shippedProducts.length > 0) {
    proxy.$modal.msgWarning("已发货或审核通过的产品不能删除");
    return;
  }
  if (operationType.value === "add") {
    productSelectedRows.value.forEach(selectedRow => {
      const index = productData.value.findIndex(
          product => 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: 1 }).then(
              res => {
                productData.value = res.productData;
              }
            getSalesLedgerWithProducts({id: currentId.value, type: 1}).then(
                res => {
                  productData.value = res.productData;
                }
            );
          });
        })
        .catch(() => {
          proxy.$modal.msg("已取消");
        });
    }
  };
  // 关闭产品弹框
  const closeProductDia = () => {
    proxy.resetForm("productFormRef");
    productFormVisible.value = false;
  };
  // 导入
  const handleImport = () => {
    importUpload.title = "导入销售台账";
    importUpload.open = true;
    if (importUploadRef.value) {
      importUploadRef.value.clearFiles();
    }
  };
  }
};
// 关闭产品弹框
const closeProductDia = () => {
  proxy.resetForm("productFormRef");
  productFormVisible.value = false;
};
// 导入
const handleImport = () => {
  importUpload.title = "导入销售台账";
  importUpload.open = true;
  if (importUploadRef.value) {
    importUploadRef.value.clearFiles();
  }
};
  // 下载导入模板
  const downloadTemplate = () => {
    proxy.download("/sales/ledger/exportTemplate", {}, "销售台账导入模板.xlsx");
  };
// 下载导入模板
const downloadTemplate = () => {
  proxy.download("/sales/ledger/exportTemplate", {}, "销售台账导入模板.xlsx");
};
  // 提交导入文件
  const submitImportFile = () => {
    importUpload.isUploading = true;
    proxy.$refs["importUploadRef"].submit();
  };
// 提交导入文件
const submitImportFile = () => {
  importUpload.isUploading = true;
  proxy.$refs["importUploadRef"].submit();
};
  // 导出
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
// 导出
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/sales/ledger/export", {}, "销售台账.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  /** 判断单个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */
  const isProductShipped = product => {
    if (!product) return false;
    const status = String(product.shippingStatus || "").trim();
    // 如果发货状态是"已发货"或"审核通过",则不可编辑和删除
    return status === "已发货" || status === "审核通过";
  };
};
/** 判断单个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */
const isProductShipped = product => {
  if (!product) return false;
  const status = String(product.shippingStatus || "").trim();
  // 如果发货状态是"已发货"或"审核通过",则不可编辑和删除
  return status === "已发货" || status === "审核通过";
};
  /** 判断销售订单下是否存在已发货/发货完成的产品(不可删除) */
  const hasShippedProducts = products => {
    if (!products || !products.length) return false;
    return products.some(p => {
      const status = String(p.shippingStatus || "").trim();
      // 有发货日期或车牌号视为已发货
      if (p.shippingDate || p.shippingCarNumber) return true;
      // 已进行发货、发货完成、已发货 均不可删除
      return (
/** 判断销售订单下是否存在已发货/发货完成的产品(不可删除) */
const hasShippedProducts = products => {
  if (!products || !products.length) return false;
  return products.some(p => {
    const status = String(p.shippingStatus || "").trim();
    // 有发货日期或车牌号视为已发货
    if (p.shippingDate || p.shippingCarNumber) return true;
    // 已进行发货、发货完成、已发货 均不可删除
    return (
        status === "已进行发货" || status === "发货完成" || status === "已发货"
      );
    });
  };
  // 删除
  const handleDelete = async () => {
    if (selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    const unauthorizedRows = selectedRows.value.filter(
      row => !canDeleteLedger(row)
    );
    if (unauthorizedRows.length > 0) {
      proxy.$modal.msgWarning("当前登录用户不是录入人,不能删除该数据");
      return;
    }
    const ids = selectedRows.value.map(item => item.id);
  });
};
    // 检查是否有已进行发货或发货完成的销售订单,若有则不允许删除
    const cannotDeleteNames = [];
    for (const row of selectedRows.value) {
      let products =
// 删除
const handleDelete = async () => {
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  const unauthorizedRows = selectedRows.value.filter(
      row => !canDeleteLedger(row)
  );
  if (unauthorizedRows.length > 0) {
    proxy.$modal.msgWarning("当前登录用户不是录入人,不能删除该数据");
    return;
  }
  const ids = selectedRows.value.map(item => item.id);
  // 检查是否有已进行发货或发货完成的销售订单,若有则不允许删除
  const cannotDeleteNames = [];
  for (const row of selectedRows.value) {
    let products =
        row.children && row.children.length > 0 ? row.children : null;
      if (!products) {
        try {
          const res = await productList({ salesLedgerId: row.id, type: 1 });
          products = res.data || [];
        } catch {
          products = [];
        }
      }
      if (hasShippedProducts(products)) {
        cannotDeleteNames.push(row.salesContractNo || `ID:${row.id}`);
    if (!products) {
      try {
        const res = await productList({salesLedgerId: row.id, type: 1});
        products = res.data || [];
      } catch {
        products = [];
      }
    }
    if (cannotDeleteNames.length > 0) {
      proxy.$modal.msgWarning(
    if (hasShippedProducts(products)) {
      cannotDeleteNames.push(row.salesContractNo || `ID:${row.id}`);
    }
  }
  if (cannotDeleteNames.length > 0) {
    proxy.$modal.msgWarning(
        "已进行发货或发货完成的销售订单不能删除:" + cannotDeleteNames.join("、")
      );
      return;
    }
    );
    return;
  }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        delLedger(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
@@ -1774,67 +1902,67 @@
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
};
  // 打印功能
  const handlePrint = async () => {
    if (selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择要打印的数据");
      return;
    }
// 打印功能
const handlePrint = async () => {
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择要打印的数据");
    return;
  }
    // 显示加载状态
    proxy.$modal.loading("正在获取产品数据,请稍候...");
  // 显示加载状态
  proxy.$modal.loading("正在获取产品数据,请稍候...");
    try {
      // 为每个选中的销售台账记录查询对应的产品数据
      const printDataWithProducts = [];
  try {
    // 为每个选中的销售台账记录查询对应的产品数据
    const printDataWithProducts = [];
      for (const row of selectedRows.value) {
        try {
          // 调用productList接口查询产品数据
          const productRes = await productList({
            salesLedgerId: row.id,
            type: 1,
          });
    for (const row of selectedRows.value) {
      try {
        // 调用productList接口查询产品数据
        const productRes = await productList({
          salesLedgerId: row.id,
          type: 1,
        });
          // 将产品数据整合到销售台账记录中
          const rowWithProducts = {
            ...row,
            products: productRes.data || [],
          };
        // 将产品数据整合到销售台账记录中
        const rowWithProducts = {
          ...row,
          products: productRes.data || [],
        };
          printDataWithProducts.push(rowWithProducts);
        } catch (error) {
          console.error(`获取销售台账 ${row.id} 的产品数据失败:`, error);
          // 即使某个记录的产品数据获取失败,也要包含该记录
          printDataWithProducts.push({
            ...row,
            products: [],
          });
        }
        printDataWithProducts.push(rowWithProducts);
      } catch (error) {
        console.error(`获取销售台账 ${row.id} 的产品数据失败:`, error);
        // 即使某个记录的产品数据获取失败,也要包含该记录
        printDataWithProducts.push({
          ...row,
          products: [],
        });
      }
      printData.value = printDataWithProducts;
      console.log("打印数据(包含产品):", printData.value);
      printPreviewVisible.value = true;
    } catch (error) {
      console.error("获取产品数据失败:", error);
      proxy.$modal.msgError("获取产品数据失败,请重试");
    } finally {
      proxy.$modal.closeLoading();
    }
  };
  // 执行打印
  const executePrint = () => {
    console.log("开始执行打印,数据条数:", printData.value.length);
    console.log("打印数据:", printData.value);
    // 创建一个新的打印窗口
    const printWindow = window.open("", "_blank", "width=800,height=600");
    printData.value = printDataWithProducts;
    console.log("打印数据(包含产品):", printData.value);
    printPreviewVisible.value = true;
  } catch (error) {
    console.error("获取产品数据失败:", error);
    proxy.$modal.msgError("获取产品数据失败,请重试");
  } finally {
    proxy.$modal.closeLoading();
  }
};
// 执行打印
const executePrint = () => {
  console.log("开始执行打印,数据条数:", printData.value.length);
  console.log("打印数据:", printData.value);
    // 构建打印内容
    let printContent = `
  // 创建一个新的打印窗口
  const printWindow = window.open("", "_blank", "width=800,height=600");
  // 构建打印内容
  let printContent = `
                                                    <!DOCTYPE html>
                                                    <html>
                                                    <head>
@@ -1969,9 +2097,9 @@
                                                    <body>
                                                  `;
    // 为每条数据生成打印页面
    printData.value.forEach((item, index) => {
      printContent += `
  // 为每条数据生成打印页面
  printData.value.forEach((item, index) => {
    printContent += `
                                                      <div class="print-page">
                                                        <div class="delivery-note">
                                                          <div class="header">
@@ -1983,22 +2111,22 @@
                                                              <div>
                                                                <span class="label">发货日期:</span>
                                                                <span class="value">${formatDate(
                                                                  item.createTime
                                                                )}</span>
        item.createTime
    )}</span>
                                                              </div>
                                                              <div>
                                                                <span class="label">客户名称:</span>
                                                                <span class="value">${
                                                                  item.customerName
                                                                }</span>
        item.customerName
    }</span>
                                                              </div>
                                                            </div>
                                                            <div class="info-row">
                                                              <span class="label">单号:</span>
                                                              <span class="value">${
                                                                item.salesContractNo ||
                                                                ""
                                                              }</span>
        item.salesContractNo ||
        ""
    }</span>
                                                            </div>
                                                          </div>
@@ -2016,43 +2144,43 @@
                                                              </thead>
                                                              <tbody>
                                                                ${
                                                                  item.products &&
                                                                  item.products
                                                                    .length > 0
                                                                    ? item.products
                                                                        .map(
                                                                          product => `
        item.products &&
        item.products
            .length > 0
            ? item.products
                .map(
                    product => `
                                                                    <tr>
                                                                      <td>${
                                                                        product.productCategory ||
                                                                        ""
                                                                      }</td>
                        product.productCategory ||
                        ""
                    }</td>
                                                                      <td>${
                                                                        product.specificationModel ||
                                                                        ""
                                                                      }</td>
                        product.specificationModel ||
                        ""
                    }</td>
                                                                      <td>${
                                                                        product.unit ||
                                                                        ""
                                                                      }</td>
                        product.unit ||
                        ""
                    }</td>
                                                                      <td>${
                                                                        product.taxInclusiveUnitPrice ||
                                                                        "0"
                                                                      }</td>
                        product.taxInclusiveUnitPrice ||
                        "0"
                    }</td>
                                                                      <td>${
                                                                        product.quantity ||
                                                                        "0"
                                                                      }</td>
                        product.quantity ||
                        "0"
                    }</td>
                                                                      <td>${
                                                                        product.taxInclusiveTotalPrice ||
                                                                        "0"
                                                                      }</td>
                        product.taxInclusiveTotalPrice ||
                        "0"
                    }</td>
                                                                    </tr>
                                                                  `
                                                                        )
                                                                        .join("")
                                                                    : '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>'
                                                                }
                )
                .join("")
            : '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>'
    }
                                                              </tbody>
                                                              <tfoot>
                                                                <tr>
@@ -2061,11 +2189,11 @@
                                                                  <td class="total-value"></td>
                                                                  <td class="total-value"></td>
                                                                  <td class="total-value">${getTotalQuantityForPrint(
                                                                    item.products
                                                                  )}</td>
        item.products
    )}</td>
                                                                  <td class="total-value">${getTotalAmountForPrint(
                                                                    item.products
                                                                  )}</td>
        item.products
    )}</td>
                                                                </tr>
                                                              </tfoot>
                                                            </table>
@@ -2090,640 +2218,669 @@
                                                              <div class="footer-item">
                                                                <span class="label">操作员:</span>
                                                                <span class="value">${
                                                                  userStore.nickName ||
                                                                  "撕开前"
                                                                }</span>
        userStore.nickName ||
        "撕开前"
    }</span>
                                                              </div>
                                                              <div class="footer-item">
                                                                <span class="label">打印日期:</span>
                                                                <span class="value">${formatDateTime(
                                                                  new Date()
                                                                )}</span>
        new Date()
    )}</span>
                                                              </div>
                                                            </div>
                                                          </div>
                                                        </div>
                                                      </div>
                                                    `;
    });
  });
    printContent += `
  printContent += `
                                                    </body>
                                                    </html>
                                                  `;
    // 写入内容到新窗口
    printWindow.document.write(printContent);
    printWindow.document.close();
  // 写入内容到新窗口
  printWindow.document.write(printContent);
  printWindow.document.close();
    // 等待内容加载完成后打印
    printWindow.onload = () => {
      setTimeout(() => {
        printWindow.print();
        printWindow.close();
        printPreviewVisible.value = false;
      }, 500);
    };
  // 等待内容加载完成后打印
  printWindow.onload = () => {
    setTimeout(() => {
      printWindow.print();
      printWindow.close();
      printPreviewVisible.value = false;
    }, 500);
  };
  // 格式化日期
  const formatDate = dateString => {
    if (!dateString) return getCurrentDate();
    const date = new Date(dateString);
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    return `${year}/${month}/${day}`;
  };
  // 格式化日期时间
  const formatDateTime = date => {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");
    const seconds = String(date.getSeconds()).padStart(2, "0");
    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
  };
  // 计算产品总数量
  const getTotalQuantity = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.quantity) || 0);
    }, 0);
    return total.toFixed(2);
  };
};
// 格式化日期
const formatDate = dateString => {
  if (!dateString) return getCurrentDate();
  const date = new Date(dateString);
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  return `${year}/${month}/${day}`;
};
// 格式化日期时间
const formatDateTime = date => {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  const seconds = String(date.getSeconds()).padStart(2, "0");
  return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
};
// 计算产品总数量
const getTotalQuantity = products => {
  if (!products || products.length === 0) return "0";
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.quantity) || 0);
  }, 0);
  return total.toFixed(2);
};
  // 计算产品总金额
  const getTotalAmount = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
    }, 0);
    return total.toFixed(2);
  };
// 计算产品总金额
const getTotalAmount = products => {
  if (!products || products.length === 0) return "0";
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
  }, 0);
  return total.toFixed(2);
};
  // 用于打印的计算函数
  const getTotalQuantityForPrint = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.quantity) || 0);
    }, 0);
    return total.toFixed(2);
  };
// 用于打印的计算函数
const getTotalQuantityForPrint = products => {
  if (!products || products.length === 0) return "0";
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.quantity) || 0);
  }, 0);
  return total.toFixed(2);
};
  const getTotalAmountForPrint = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
    }, 0);
    return total.toFixed(2);
  };
const getTotalAmountForPrint = products => {
  if (!products || products.length === 0) return "0";
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
  }, 0);
  return total.toFixed(2);
};
  const mathNum = () => {
    console.log("productForm.value", productForm.value);
    if (!productForm.value.taxInclusiveUnitPrice) {
      return;
    }
    if (!productForm.value.quantity) {
      return;
    }
    // 含税总价计算
    productForm.value.taxInclusiveTotalPrice =
const mathNum = () => {
  console.log("productForm.value", productForm.value);
  if (!productForm.value.taxInclusiveUnitPrice) {
    return;
  }
  if (!productForm.value.quantity) {
    return;
  }
  // 含税总价计算
  productForm.value.taxInclusiveTotalPrice =
      proxy.calculateTaxIncludeTotalPrice(
        productForm.value.taxInclusiveUnitPrice,
        productForm.value.quantity
          productForm.value.taxInclusiveUnitPrice,
          productForm.value.quantity
      );
    if (productForm.value.taxRate) {
      // 不含税总价计算
      productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
          productForm.value.taxInclusiveTotalPrice,
          productForm.value.taxRate
        );
    }
  };
  // 根据含税总价计算含税单价和数量
  const calculateFromTotalPrice = () => {
    if (isCalculating.value) return;
    const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
    const quantity = parseFloat(productForm.value.quantity);
    if (!totalPrice || !quantity || quantity <= 0) {
      return;
    }
    isCalculating.value = true;
    // 计算含税单价 = 含税总价 / 数量
    productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
    // 如果有税率,计算不含税总价
    if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
          totalPrice,
          productForm.value.taxRate
        );
    }
    isCalculating.value = false;
  };
  // 根据不含税总价计算含税单价和数量
  const calculateFromExclusiveTotalPrice = () => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    if (isCalculating.value) return;
    const exclusiveTotalPrice = parseFloat(
      productForm.value.taxExclusiveTotalPrice
    );
    const quantity = parseFloat(productForm.value.quantity);
    const taxRate = parseFloat(productForm.value.taxRate);
    if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
      return;
    }
    isCalculating.value = true;
    // 先计算含税总价 = 不含税总价 / (1 - 税率/100)
    const taxRateDecimal = taxRate / 100;
    const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
    productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
    // 计算含税单价 = 含税总价 / 数量
    productForm.value.taxInclusiveUnitPrice = (
      inclusiveTotalPrice / quantity
    ).toFixed(2);
    isCalculating.value = false;
  };
  // 根据数量变化计算总价
  const calculateFromQuantity = () => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    if (isCalculating.value) return;
    const quantity = parseFloat(productForm.value.quantity);
    const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
    if (!quantity || quantity <= 0 || !unitPrice) {
      return;
    }
    isCalculating.value = true;
    // 计算含税总价
    productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    // 如果有税率,计算不含税总价
    if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
          productForm.value.taxInclusiveTotalPrice,
          productForm.value.taxRate
        );
    }
    isCalculating.value = false;
  };
  // 根据含税单价变化计算总价
  const calculateFromUnitPrice = () => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    if (isCalculating.value) return;
    const quantity = parseFloat(productForm.value.quantity);
    const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
    if (!quantity || quantity <= 0 || !unitPrice) {
      return;
    }
    isCalculating.value = true;
    // 计算含税总价
    productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    // 如果有税率,计算不含税总价
    if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
          productForm.value.taxInclusiveTotalPrice,
          productForm.value.taxRate
        );
    }
    isCalculating.value = false;
  };
  // 根据税率变化计算不含税总价
  const calculateFromTaxRate = () => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    if (isCalculating.value) return;
    const inclusiveTotalPrice = parseFloat(
      productForm.value.taxInclusiveTotalPrice
    );
    const taxRate = parseFloat(productForm.value.taxRate);
    if (!inclusiveTotalPrice || !taxRate) {
      return;
    }
    isCalculating.value = true;
    // 计算不含税总价
  if (productForm.value.taxRate) {
    // 不含税总价计算
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate);
        proxy.calculateTaxExclusiveTotalPrice(
            productForm.value.taxInclusiveTotalPrice,
            productForm.value.taxRate
        );
  }
};
    isCalculating.value = false;
  };
  /**
   * 获取发货状态文本
   * @param row 行数据
   */
  const getShippingStatusText = row => {
    // 如果已发货(有发货日期或车牌号),显示"已发货"
    if (row.shippingDate || row.shippingCarNumber) {
      return "已发货";
    }
// 根据含税总价计算含税单价和数量
const calculateFromTotalPrice = () => {
  if (isCalculating.value) return;
    // 获取发货状态字段
    const status = row.shippingStatus;
  const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
  const quantity = parseFloat(productForm.value.quantity);
    // 如果状态为空或未定义,默认为"待发货"
    if (status === null || status === undefined || status === "") {
      return "待发货";
    }
    // 状态是字符串
    const statusStr = String(status).trim();
    const statusTextMap = {
      待发货: "待发货",
      待审核: "待审核",
      审核中: "审核中",
      审核拒绝: "审核拒绝",
      审核通过: "审核通过",
      已发货: "已发货",
    };
    return statusTextMap[statusStr] || "待发货";
  };
  /**
   * 获取发货状态标签类型(颜色)
   * @param row 行数据
   */
  const getShippingStatusType = row => {
    // 如果已发货(有发货日期或车牌号),显示绿色
    if (row.shippingDate || row.shippingCarNumber) {
      return "success";
    }
    // 获取发货状态字段
    const status = row.shippingStatus;
    // 如果状态为空或未定义,默认为灰色(待发货)
    if (status === null || status === undefined || status === "") {
      return "info";
    }
    // 状态是字符串
    const statusStr = String(status).trim();
    const typeTextMap = {
      待发货: "info",
      待审核: "info",
      审核中: "warning",
      审核拒绝: "danger",
      审核通过: "success",
      已发货: "success",
    };
    return typeTextMap[statusStr] || "info";
  };
  /**
   * 判断是否可以发货
   * 只有在产品状态是充足,发货状态是待发货和审核拒绝的时候才可以发货
   * @param row 行数据
   */
  const canShip = row => {
    // 产品状态必须是充足(approveStatus === 1)
    if (row.approveStatus !== 1) {
      return false;
    }
    // 获取发货状态
    const shippingStatus = row.shippingStatus;
    // 如果已发货(有发货日期或车牌号),不能再次发货
    if (row.shippingDate || row.shippingCarNumber) {
      return false;
    }
    // 发货状态必须是"待发货"或"审核拒绝"
    const statusStr = shippingStatus ? String(shippingStatus).trim() : "";
    return statusStr === "待发货" || statusStr === "审核拒绝";
  };
  // 打开附件弹窗
  const recordId =ref(0)
  const fileDialogVisible = ref(false)
  // 打开附件弹框
  const openFileDialog = async (row) => {
    recordId.value = row.id
    fileDialogVisible.value = true
  if (!totalPrice || !quantity || quantity <= 0) {
    return;
  }
  // 打开发货弹框
  const openDeliveryForm = row => {
    // 检查是否可以发货
    if (!canShip(row)) {
      proxy.$modal.msgWarning(
        "只有在产品状态是充足,发货状态是待发货或审核拒绝的时候才可以发货"
      );
      return;
    }
  isCalculating.value = true;
    currentDeliveryRow.value = row;
    deliveryForm.value = {
      type: "货车",
    };
    deliveryFormVisible.value = true;
  // 计算含税单价 = 含税总价 / 数量
  productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
  // 如果有税率,计算不含税总价
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
            totalPrice,
            productForm.value.taxRate
        );
  }
  isCalculating.value = false;
};
// 根据不含税总价计算含税单价和数量
const calculateFromExclusiveTotalPrice = () => {
  if (!productForm.value.taxRate) {
    proxy.$modal.msgWarning("请先选择税率");
    return;
  }
  if (isCalculating.value) return;
  const exclusiveTotalPrice = parseFloat(
      productForm.value.taxExclusiveTotalPrice
  );
  const quantity = parseFloat(productForm.value.quantity);
  const taxRate = parseFloat(productForm.value.taxRate);
  if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
    return;
  }
  isCalculating.value = true;
  // 先计算含税总价 = 不含税总价 / (1 - 税率/100)
  const taxRateDecimal = taxRate / 100;
  const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
  productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
  // 计算含税单价 = 含税总价 / 数量
  productForm.value.taxInclusiveUnitPrice = (
      inclusiveTotalPrice / quantity
  ).toFixed(2);
  isCalculating.value = false;
};
// 根据数量变化计算总价
const calculateFromQuantity = () => {
  if (!productForm.value.taxRate) {
    proxy.$modal.msgWarning("请先选择税率");
    return;
  }
  if (isCalculating.value) return;
  const quantity = parseFloat(productForm.value.quantity);
  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
  if (!quantity || quantity <= 0 || !unitPrice) {
    return;
  }
  isCalculating.value = true;
  // 计算含税总价
  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
  // 如果有税率,计算不含税总价
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
            productForm.value.taxInclusiveTotalPrice,
            productForm.value.taxRate
        );
  }
  isCalculating.value = false;
};
// 根据含税单价变化计算总价
const calculateFromUnitPrice = () => {
  if (!productForm.value.taxRate) {
    proxy.$modal.msgWarning("请先选择税率");
    return;
  }
  if (isCalculating.value) return;
  const quantity = parseFloat(productForm.value.quantity);
  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
  if (!quantity || quantity <= 0 || !unitPrice) {
    return;
  }
  isCalculating.value = true;
  // 计算含税总价
  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
  // 如果有税率,计算不含税总价
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
            productForm.value.taxInclusiveTotalPrice,
            productForm.value.taxRate
        );
  }
  isCalculating.value = false;
};
// 根据税率变化计算不含税总价
const calculateFromTaxRate = () => {
  if (!productForm.value.taxRate) {
    proxy.$modal.msgWarning("请先选择税率");
    return;
  }
  if (isCalculating.value) return;
  const inclusiveTotalPrice = parseFloat(
      productForm.value.taxInclusiveTotalPrice
  );
  const taxRate = parseFloat(productForm.value.taxRate);
  if (!inclusiveTotalPrice || !taxRate) {
    return;
  }
  isCalculating.value = true;
  // 计算不含税总价
  productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate);
  isCalculating.value = false;
};
/**
 * 获取发货状态文本
 * @param row 行数据
 */
const getShippingStatusText = row => {
  // 如果已发货(有发货日期或车牌号),显示"已发货"
  // if (row.shippingDate || row.shippingCarNumber) {
  //   return "已发货";
  // }
  // 获取发货状态字段
  const status = row.shippingStatus;
  // 如果状态为空或未定义,默认为"待发货"
  if (status === null || status === undefined || status === "") {
    return "待发货";
  }
  // 状态是字符串
  const statusStr = String(status).trim();
  const statusTextMap = {
    待发货: "待发货",
    待审核: "待审核",
    审核中: "审核中",
    审核拒绝: "审核拒绝",
    审核通过: "审核通过",
    已发货: "已发货",
  };
  return statusTextMap[statusStr] || "待发货";
};
  // 提交发货表单
  const submitDelivery = () => {
    proxy.$refs["deliveryFormRef"].validate(valid => {
      if (valid) {
        // 保存当前展开的行ID,以便发货后重新加载子表格数据
        const currentExpandedKeys = [...expandedRowKeys.value];
        const salesLedgerId = currentDeliveryRow.value.salesLedgerId;
        addShippingInfo({
          salesLedgerId: salesLedgerId,
          salesLedgerProductId: currentDeliveryRow.value.id,
          type: deliveryForm.value.type,
        }).then(() => {
          proxy.$modal.msgSuccess("发货成功");
          closeDeliveryDia();
          // 刷新主表数据
          getList().then(() => {
            // 如果之前有展开的行,重新加载这些行的子表格数据
            if (currentExpandedKeys.length > 0) {
              // 使用 Promise.all 并行加载所有展开行的子表格数据
              const loadPromises = currentExpandedKeys.map(ledgerId => {
                return productList({ salesLedgerId: ledgerId, type: 1 }).then(
/**
 * 获取发货状态标签类型(颜色)
 * @param row 行数据
 */
const getShippingStatusType = row => {
  // 如果已发货(有发货日期或车牌号),显示绿色
  if (row.shippingStatus === "已发货") {
    return "success";
  }
  // 获取发货状态字段
  const status = row.shippingStatus;
  // 如果状态为空或未定义,默认为灰色(待发货)
  if (status === null || status === undefined || status === "") {
    return "info";
  }
  // 状态是字符串
  const statusStr = String(status).trim();
  const typeTextMap = {
    待发货: "info",
    待审核: "info",
    审核中: "warning",
    审核拒绝: "danger",
    审核通过: "success",
    已发货: "success",
  };
  return typeTextMap[statusStr] || "info";
};
/**
 * 判断是否可以发货
 * 只有在产品状态是充足,发货状态是待发货和审核拒绝的时候才可以发货
 * @param row 行数据
 */
const canShip = row => {
  // 产品状态必须是充足(approveStatus === 1)
  if (row.approveStatus !== 1) {
    return false;
  }
  // 获取发货状态
  const shippingStatus = row.shippingStatus;
  // 如果已发货(有发货日期或车牌号),不能再次发货
  if (shippingStatus === "已发货") {
    return false;
  }
  // 发货状态必须是"待发货"或"审核拒绝"
  const statusStr = shippingStatus ? String(shippingStatus).trim() : "";
  return statusStr === "待发货" || statusStr === "审核拒绝";
};
// 打开附件弹窗
const recordId = ref(0)
const fileDialogVisible = ref(false)
// 打开附件弹框
const openFileDialog = async (row) => {
  recordId.value = row.id
  fileDialogVisible.value = true
}
// 打开发货弹框
const openDeliveryForm = async row => {
  // 检查是否可以发货
  if (!canShip(row)) {
    proxy.$modal.msgWarning(
        "只有在产品状态是充足,发货状态是待发货或审核拒绝的时候才可以发货"
    );
    return;
  }
  currentDeliveryRow.value = row;
  const batchNoList = await getDeliveryBatchNoList(
      row.productModelId || row.modelId
  );
  deliveryForm.value = {
    type: "货车",
    batchNo: [],
    batchNoList,
  };
  deliveryFormVisible.value = true;
};
// 提交发货表单
const submitDelivery = () => {
  proxy.$refs["deliveryFormRef"].validate(valid => {
    if (valid) {
      const selectedBatchRows = getSelectedDeliveryBatchRows();
      if (selectedBatchRows.length === 0) {
        proxy.$modal.msgWarning("请至少填写一个批号的发货数量");
        return;
      }
      const totalDeliveryQuantity = selectedBatchRows.reduce(
          (sum, item) => sum + Number(item.deliveryQuantity || 0),
          0
      );
      const currentRowQuantity = Number(currentDeliveryRow.value?.quantity || 0);
      if (currentRowQuantity > 0 && totalDeliveryQuantity > currentRowQuantity) {
        proxy.$modal.msgWarning("批号发货总数不能超过当前产品数量");
        return;
      }
      // 保存当前展开的行ID,以便发货后重新加载子表格数据
      const currentExpandedKeys = [...expandedRowKeys.value];
      const salesLedgerId = currentDeliveryRow.value.salesLedgerId;
      deliveryForm.value.batchNo = selectedBatchRows.map(item => item.id);
      const productModelId = currentDeliveryRow.value.productModelId || currentDeliveryRow.value.modelId;
      addShippingInfo({
        salesLedgerId: salesLedgerId,
        salesLedgerProductId: currentDeliveryRow.value.id,
        type: deliveryForm.value.type,
        batchNo: deliveryForm.value.batchNo,
        batchNoDetailList: selectedBatchRows.map(item => ({
          stockInventoryId: item.id,
          batchNo: item.batchNo,
          quantity: Number(item.deliveryQuantity || 0),
          productModelId: productModelId,
        })),
      }).then(() => {
        proxy.$modal.msgSuccess("发货成功");
        closeDeliveryDia();
        // 刷新主表数据
        getList().then(() => {
          // 如果之前有展开的行,重新加载这些行的子表格数据
          if (currentExpandedKeys.length > 0) {
            // 使用 Promise.all 并行加载所有展开行的子表格数据
            const loadPromises = currentExpandedKeys.map(ledgerId => {
              return productList({salesLedgerId: ledgerId, type: 1}).then(
                  res => {
                    const index = tableData.value.findIndex(
                      item => item.id === ledgerId
                        item => item.id === ledgerId
                    );
                    if (index > -1) {
                      tableData.value[index].children = res.data;
                    }
                  }
                );
              });
              Promise.all(loadPromises).then(() => {
                // 恢复展开状态
                expandedRowKeys.value = currentExpandedKeys;
              });
            }
          });
              );
            });
            Promise.all(loadPromises).then(() => {
              // 恢复展开状态
              expandedRowKeys.value = currentExpandedKeys;
            });
          }
        });
      }
    });
  };
  // 关闭发货弹框
  const closeDeliveryDia = () => {
    proxy.resetForm("deliveryFormRef");
    deliveryFormVisible.value = false;
    currentDeliveryRow.value = null;
  };
  const currentFactoryName = ref("");
  const getCurrentFactoryName = async () => {
    let res = await userStore.getInfo();
    currentFactoryName.value = res.user.currentFactoryName;
  };
  onMounted(() => {
    searchForm.salesContractNo = route.query.salesContractNo;
    getList();
    userListNoPage().then(res => {
      userList.value = res.data;
    });
    getCurrentFactoryName();
      });
    }
  });
};
// 关闭发货弹框
const closeDeliveryDia = () => {
  proxy.resetForm("deliveryFormRef");
  deliveryFormVisible.value = false;
  currentDeliveryRow.value = null;
};
const currentFactoryName = ref("");
const getCurrentFactoryName = async () => {
  let res = await userStore.getInfo();
  currentFactoryName.value = res.user.currentFactoryName;
};
onMounted(() => {
  searchForm.salesContractNo = route.query.salesContractNo;
  getList();
  userListNoPage().then(res => {
    userList.value = res.data;
  });
  getCurrentFactoryName();
});
</script>
<style scoped lang="scss">
  .ml-10 {
    margin-left: 10px;
.ml-10 {
  margin-left: 10px;
}
:deep(.yellow) {
  background-color: #faf0de;
}
:deep(.pink) {
  background-color: #fae1de;
}
:deep(.red) {
  background-color: #fae1de;
}
:deep(.purple) {
  background-color: #f4defa;
}
.table_list {
  margin-top: unset;
}
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
}
.print-preview-dialog {
  .el-dialog__body {
    padding: 0;
    max-height: 80vh;
    overflow-y: auto;
  }
}
  :deep(.yellow) {
    background-color: #faf0de;
  }
  :deep(.pink) {
    background-color: #fae1de;
  }
  :deep(.red) {
    background-color: #fae1de;
  }
  :deep(.purple) {
    background-color: #f4defa;
  }
  .table_list {
    margin-top: unset;
  }
  .actions {
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
  }
  .print-preview-dialog {
    .el-dialog__body {
      padding: 0;
      max-height: 80vh;
      overflow-y: auto;
    }
  }
  .print-preview-container {
    .print-preview-header {
      padding: 15px;
      border-bottom: 1px solid #e4e7ed;
      text-align: center;
      .el-button {
        margin: 0 10px;
      }
    }
    .print-preview-content {
      padding: 20px;
      background-color: #f5f5f5;
      min-height: 400px;
    }
  }
  .print-page {
    width: 220mm;
    height: 90mm;
    padding: 10mm;
    margin: 0 auto;
    background: white;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    margin-bottom: 10px;
    box-sizing: border-box;
  }
  .delivery-note {
    width: 100%;
    height: 100%;
    font-family: "SimSun", serif;
    font-size: 10px;
    line-height: 1.2;
    display: flex;
    flex-direction: column;
  }
  .header {
.print-preview-container {
  .print-preview-header {
    padding: 15px;
    border-bottom: 1px solid #e4e7ed;
    text-align: center;
    margin-bottom: 8px;
    .company-name {
      font-size: 18px;
      font-weight: bold;
      margin-bottom: 4px;
    }
    .document-title {
      font-size: 16px;
      font-weight: bold;
    .el-button {
      margin: 0 10px;
    }
  }
  .info-section {
    margin-bottom: 8px;
    display: flex;
    justify-content: space-between;
    align-items: center;
  .print-preview-content {
    padding: 20px;
    background-color: #f5f5f5;
    min-height: 400px;
  }
}
    .info-row {
      line-height: 20px;
.print-page {
  width: 220mm;
  height: 90mm;
  padding: 10mm;
  margin: 0 auto;
  background: white;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  margin-bottom: 10px;
  box-sizing: border-box;
}
.delivery-note {
  width: 100%;
  height: 100%;
  font-family: "SimSun", serif;
  font-size: 10px;
  line-height: 1.2;
  display: flex;
  flex-direction: column;
}
.header {
  text-align: center;
  margin-bottom: 8px;
  .company-name {
    font-size: 18px;
    font-weight: bold;
    margin-bottom: 4px;
  }
  .document-title {
    font-size: 16px;
    font-weight: bold;
  }
}
.info-section {
  margin-bottom: 8px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  .info-row {
    line-height: 20px;
    .label {
      font-weight: bold;
      width: 60px;
      font-size: 14px;
    }
    .value {
      margin-right: 20px;
      min-width: 80px;
      font-size: 14px;
    }
  }
}
.table-section {
  margin-bottom: 4px;
  flex: 1;
  .product-table {
    width: 100%;
    border-collapse: collapse;
    border: 1px solid #000;
    th,
    td {
      border: 1px solid #000;
      padding: 6px;
      text-align: center;
      font-size: 14px;
      line-height: 1.4;
    }
    th {
      font-weight: bold;
    }
    .total-label {
      text-align: right;
      font-weight: bold;
    }
    .total-value {
      font-weight: bold;
    }
  }
}
.footer-section {
  .footer-row {
    display: flex;
    margin-bottom: 3px;
    line-height: 20px;
    justify-content: space-between;
    .footer-item {
      display: flex;
      margin-right: 20px;
      .label {
        font-weight: bold;
        width: 60px;
        width: 80px;
        font-size: 14px;
      }
      .value {
        margin-right: 20px;
        min-width: 80px;
        font-size: 14px;
      }
    }
  }
  .table-section {
    margin-bottom: 4px;
    flex: 1;
    .product-table {
      width: 100%;
      border-collapse: collapse;
      border: 1px solid #000;
      th,
      td {
        border: 1px solid #000;
        padding: 6px;
        text-align: center;
        font-size: 14px;
        line-height: 1.4;
      }
      th {
        font-weight: bold;
      }
      .total-label {
        text-align: right;
        font-weight: bold;
      }
      .total-value {
        font-weight: bold;
      }
    }
  }
  .footer-section {
    .footer-row {
      display: flex;
      margin-bottom: 3px;
      line-height: 20px;
      justify-content: space-between;
      .footer-item {
        display: flex;
        margin-right: 20px;
        .label {
          font-weight: bold;
          width: 80px;
          font-size: 14px;
        }
        .value {
          min-width: 80px;
          font-size: 14px;
        }
        &.address-item {
          .address-value {
            min-width: 200px;
          }
      &.address-item {
        .address-value {
          min-width: 200px;
        }
      }
    }
  }
}
  @media print {
    .app-container {
      display: none;
    }
    .print-page {
      box-shadow: none;
      margin: 0;
      padding: 10mm;
      padding-left: 20mm;
      page-break-inside: avoid;
      page-break-after: always;
    }
    .print-page:last-child {
      page-break-after: avoid;
    }
@media print {
  .app-container {
    display: none;
  }
  .print-page {
    box-shadow: none;
    margin: 0;
    padding: 10mm;
    padding-left: 20mm;
    page-break-inside: avoid;
    page-break-after: always;
  }
  .print-page:last-child {
    page-break-after: avoid;
  }
}
</style>