张诺
5 小时以前 1568abb07297f8b00fe675a3924803eefbc1e084
客户档案 新增/编辑的时候 代理人和联系人位置上下调整
已添加1个文件
已修改2个文件
2654 ■■■■■ 文件已修改
src/views/basicData/customerFile/index.vue 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFileOpenSea/index.vue 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index2.vue 2523 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFile/index.vue
@@ -155,7 +155,40 @@
          </el-col>
          
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="30"
                v-for="(contact, index) in formYYs.contactList"
                :key="index">
          <el-col :span="12">
            <el-form-item label="联系人:"
                          prop="contactPerson">
              <el-input v-model="contact.contactPerson"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="联系电话:"
                          prop="contactPhone">
              <div style="display: flex; align-items: center;width: 100%;">
                <el-input v-model="contact.contactPhone"
                          placeholder="请输入"
                          clearable />
                <el-button @click="removeContact(index)"
                           type="danger"
                           circle
                           style="margin-left: 5px;">
                  <el-icon>
                    <Close />
                  </el-icon>
                </el-button>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
        <el-button @click="addNewContact"
                   style="margin-bottom: 10px;">+ æ–°å¢žè”系人</el-button>
                   <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="代理人"
                          prop="agent">
@@ -187,38 +220,6 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30"
                v-for="(contact, index) in formYYs.contactList"
                :key="index">
          <el-col :span="12">
            <el-form-item label="联系人:"
                          prop="contactPerson">
              <el-input v-model="contact.contactPerson"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="联系电话:"
                          prop="contactPhone">
              <div style="display: flex; align-items: center;width: 100%;">
                <el-input v-model="contact.contactPhone"
                          placeholder="请输入"
                          clearable />
                <el-button @click="removeContact(index)"
                           type="danger"
                           circle
                           style="margin-left: 5px;">
                  <el-icon>
                    <Close />
                  </el-icon>
                </el-button>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
        <el-button @click="addNewContact"
                   style="margin-bottom: 10px;">+ æ–°å¢žè”系人</el-button>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="维护人:"
src/views/basicData/customerFileOpenSea/index.vue
@@ -151,38 +151,6 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="代理人"
                          prop="agent">
              <el-select v-model="form.agent"
                         placeholder="请选择代理人"
                         clearable
                         filterable
                         :disabled="agentOptions.length === 0"
                         :no-data-text="agentNoDataText">
                <el-option v-for="(contact, index) in agentOptions"
                           :key="getAgentOptionKey(contact, index)"
                           :label="getAgentLabel(contact)"
                           :value="contact.contactPhone"
                           :disabled="!contact.contactPhone">
                  <span>{{ contact.contactPerson || "-" }}</span>
                  <span style="float: right; color: var(--el-text-color-secondary); font-size: 12px;">
                    {{ contact.contactPhone || "" }}
                  </span>
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="传真"
                          prop="fax">
              <el-input v-model="form.fax"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30"
                v-for="(contact, index) in formYYs.contactList"
                :key="index">
@@ -215,6 +183,38 @@
        </el-row>
        <el-button @click="addNewContact"
                   style="margin-bottom: 10px;">+ æ–°å¢žè”系人</el-button>
                   <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="代理人"
                          prop="agent">
              <el-select v-model="form.agent"
                         placeholder="请选择代理人"
                         clearable
                         filterable
                         :disabled="agentOptions.length === 0"
                         :no-data-text="agentNoDataText">
                <el-option v-for="(contact, index) in agentOptions"
                           :key="getAgentOptionKey(contact, index)"
                           :label="getAgentLabel(contact)"
                           :value="contact.contactPhone"
                           :disabled="!contact.contactPhone">
                  <span>{{ contact.contactPerson || "-" }}</span>
                  <span style="float: right; color: var(--el-text-color-secondary); font-size: 12px;">
                    {{ contact.contactPhone || "" }}
                  </span>
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="传真"
                          prop="fax">
              <el-input v-model="form.fax"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="维护人:"
src/views/salesManagement/salesLedger/index2.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2523 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm" :inline="true">
        <el-form-item label="客户名称:">
          <el-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="订单编号:">
          <el-input v-model="form.salesContractNo" placeholder="可手动输入或自动生成" clearable :disabled="operationType === 'view'" />
                  </el-form-item>
        <el-form-item label="录入日期:">
          <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
            placeholder="请选择" clearable @change="changeDaterange" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery"> æœç´¢ </el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="openForm('add')">
            æ–°å¢žå°è´¦
          </el-button>
          <el-button type="primary" plain @click="handleImport">导入</el-button>
          <el-button @click="handleOut">导出</el-button>
          <el-button type="danger" plain @click="handleDelete">删除</el-button>
          <el-button type="primary" plain @click="handlePrint">打印</el-button>
        </div>
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" fixed="left"/>
        <el-table-column type="expand" width="60" fixed="left">
          <template #default="props">
            <el-table :data="props.row.children" border show-summary :summary-method="(param) => summarizeChildrenTable(param, props.row)">
              <el-table-column align="center" label="序号" type="index"/>
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
                            <el-table-column label="产品状态"
                                                             width="100px"
                                                             align="center">
                <template #default="scope">
                    <el-tag v-if="getShippingStatusText(scope.row) === '已发货'"
                                                    type="success">已出库</el-tag>
                                    <el-tag v-else-if="scope.row.hasSufficientStock"
                                                    type="success">充足</el-tag>
                                    <el-tag v-else
                                                    type="danger">不足</el-tag>
                </template>
              </el-table-column>
                            <el-table-column label="发货状态" width="140" align="center">
                                <template #default="scope">
                                    <el-tag :type="getShippingStatusType(scope.row)" size="small">
                                        {{ getShippingStatusText(scope.row) }}
                                    </el-tag>
                                </template>
                            </el-table-column>
                            <el-table-column label="快递公司" prop="expressCompany" show-overflow-tooltip />
                            <el-table-column label="快递单号" prop="expressNumber" show-overflow-tooltip />
              <el-table-column label="发货车牌" minWidth="100px" align="center">
                <template #default="scope">
                  <div>
                    <el-tag type="success" v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag>
                    <el-tag v-else type="info">-</el-tag>
                  </div>
                </template>
              </el-table-column>
                            <el-table-column label="发货日期"
                                                             minWidth="100px"
                                                             align="center">
                <template #default="scope">
                  <div>
                    <div v-if="scope.row.shippingDate">{{ scope.row.shippingDate }}</div>
                                        <el-tag v-else
                                                        type="info">-</el-tag>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="sensitiveAmountFormatter" />
              <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="sensitiveAmountFormatter" />
              <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="sensitiveAmountFormatter" />
            <!--操作-->
              <el-table-column Width="60px" label="操作" align="center">
                <template #default="scope">
                  <el-button
                    link
                    type="primary"
                    size="small"
                    :disabled="!canShip(scope.row)"
                    @click="openDeliveryForm(scope.row)">
                    å‘è´§
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="订单编号" prop="salesContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip />
        <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip />
<!--        <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip />-->
        <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip />
        <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip
          :formatter="formattedNumber" />
        <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip />
        <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
        <el-table-column label="交付日期" prop="deliveryDate" width="120" show-overflow-tooltip />
        <el-table-column label="其它说明事项" prop="remarks" width="200" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="200" align="center">
          <template #default="scope">
            <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit || scope.row.hasProductionRecord || !canEditLedger(scope.row)">编辑</el-button>
            <el-button link type="primary" @click="downLoadFile(scope.row)">附件</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" :width="'70%'"
      :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
                <!-- æŠ¥ä»·å•导入入口:放在表单顶部,选择后反显客户/业务员等 -->
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="订单编号:" prop="salesContractNo">
              <el-input v-model="form.salesContractNo" placeholder="可手动输入或自动生成" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="业务员:" prop="salesman">
              <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable>
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <!-- ç¬¬2行:客户名称 + ç­¾è®¢åœ°ç‚¹ -->
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户名称:" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable>
                <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
                  {{ item.customerName + "——" + item.taxpayerIdentificationNumber }}
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="签订地点:" prop="placeOfSinging">
              <el-input v-model="form.placeOfSinging" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- ç¬¬3行:签订日期 + ä»˜æ¬¾æ–¹å¼ -->
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="签订日期:" prop="executionDate">
              <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="请选择" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款方式" prop="paymentMethod">
              <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- ç¬¬4行:录入人 + å½•入日期 -->
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="录入人:" prop="entryPerson">
              <el-select v-model="form.entryPerson" filterable default-first-option :reserve-keyword="false" placeholder="请选择" clearable @change="changs">
                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="录入日期:" prop="entryDate">
              <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- ç¬¬5行:交货日期(单独居中) -->
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="交货日期:" prop="deliveryDate">
              <el-date-picker style="width: 100%" v-model="form.deliveryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12"></el-col>
        </el-row>
                <el-row>
                    <el-form-item label="产品信息:" prop="entryDate">
                        <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button>
                        <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >删除</el-button>
                    </el-form-item>
                </el-row>
                <el-table :data="productData" border @selection-change="productSelected" show-summary
                                    :summary-method="summarizeMainTable">
                    <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'"
                        :selectable="(row) => !isProductShipped(row)" />
                    <el-table-column align="center" label="序号" type="index" width="60" />
                    <el-table-column label="产品大类" prop="productCategory" />
                    <el-table-column label="规格型号" prop="specificationModel" />
                    <el-table-column label="单位" prop="unit" />
                    <el-table-column label="数量" prop="quantity" />
                    <el-table-column label="税率(%)" prop="taxRate" />
                    <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
                    <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
                    <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
                    <el-table-column fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'">
                        <template #default="scope">
                            <el-button link type="primary" size="small"
                                :disabled="isProductShipped(scope.row)"
                                @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
                        </template>
                    </el-table-column>
                </el-table>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="其它说明事项:" prop="remarks">
                            <el-input v-model="form.remarks" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="附件材料:" prop="salesLedgerFiles">
                            <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                                                 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                                                 :on-success="handleUploadSuccess" :on-remove="handleRemove">
                                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                                <template #tip v-if="operationType !== 'view'">
                                    <div class="el-upload__tip">
                                        æ–‡ä»¶æ ¼å¼æ”¯æŒ
                                        doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                                    </div>
                                </template>
                            </el-upload>
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
        </FormDialog>
        <!-- ä»ŽæŠ¥ä»·å•导入(仅审批通过) -->
        <el-dialog
            v-model="quotationDialogVisible"
            title="选择审批通过的销售报价单"
            width="80%"
            :close-on-click-modal="false"
        >
            <div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;">
                <el-input
                    v-model="quotationSearchForm.quotationNo"
                    placeholder="请输入报价单号"
                    clearable
                    style="max-width: 260px;"
                    @change="fetchQuotationList"
                />
                <el-input
                    v-model="quotationSearchForm.customer"
                    placeholder="请输入客户名称"
                    clearable
                    style="max-width: 260px;"
                    @change="fetchQuotationList"
                />
                <el-button type="primary" @click="fetchQuotationList">搜索</el-button>
                <el-button @click="resetQuotationSearch">重置</el-button>
            </div>
            <el-table
                :data="quotationList"
                border
                stripe
                v-loading="quotationLoading"
                height="420px"
            >
                <el-table-column align="center" label="序号" type="index" width="60" />
                <el-table-column prop="quotationNo" label="报价单号" width="180" show-overflow-tooltip />
                <el-table-column prop="customer" label="客户名称" min-width="220" show-overflow-tooltip />
                <el-table-column prop="salesperson" label="业务员" width="120" show-overflow-tooltip />
                <el-table-column prop="quotationDate" label="报价日期" width="140" />
                <el-table-column prop="status" label="审批状态" width="120" align="center" />
                <el-table-column prop="totalAmount" label="报价金额(元)" width="160" align="right">
                    <template #default="scope">
                        {{ Number(scope.row.totalAmount ?? 0).toFixed(3) }}
                    </template>
                </el-table-column>
                <el-table-column fixed="right" label="操作" width="120" align="center">
                    <template #default="scope">
                        <el-button type="primary" link @click="applyQuotation(scope.row)">选择</el-button>
                    </template>
                </el-table-column>
            </el-table>
            <pagination
                v-show="quotationPage.total > 0"
                :total="quotationPage.total"
                layout="total, sizes, prev, pager, next, jumper"
                :page="quotationPage.current"
                :limit="quotationPage.size"
                @pagination="quotationPaginationChange"
            />
            <template #footer>
                <el-button @click="quotationDialogVisible = false">关闭</el-button>
            </template>
        </el-dialog>
        <FormDialog
            v-model="productFormVisible"
            :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
            :width="'40%'"
            :operation-type="productOperationType"
            @close="closeProductDia"
            @confirm="submitProduct"
            @cancel="closeProductDia">
            <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="产品大类:" prop="productCategory">
                            <el-select
                                v-model="productForm.productCategory"
                                placeholder="请选择或手动输入"
                                clearable
                                filterable
                                allow-create
                                default-first-option
                                @change="getModels"
                            >
                                <el-option v-for="item in flatProductOptions" :key="item.id" :label="item.label" :value="item.value" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="规格型号:" prop="productModelId">
                            <el-select v-model="productForm.productModelId" placeholder="请选择或手动输入" clearable @change="getProductModel" filterable allow-create default-first-option>
                                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="单位:" prop="unit">
                            <el-input v-model="productForm.unit" placeholder="请输入" clearable />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="税率(%):" prop="taxRate">
                            <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate">
                                <el-option label="0" :value="0" />
                                <el-option label="1" :value="1" />
                                <el-option label="6" :value="6" />
                                <el-option label="13" :value="13" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
                            <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%"
                                                             :precision="3"
                                                             placeholder="请输入" clearable @change="calculateFromQuantity" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="数量:" prop="quantity">
                            <el-input-number  :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请输入" clearable
                                                                :precision="3"
                                                                @change="calculateFromQuantity" style="width: 100%" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
                            <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice">
                            <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="发票类型:" prop="invoiceType">
                            <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable>
                                <el-option label="增普票" value="增普票" />
                                <el-option label="增专票" value="增专票" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
        </FormDialog>
        <!-- å¯¼å…¥å¼¹çª— -->
        <FormDialog
            v-model="importUpload.open"
            :title="importUpload.title"
            :width="'600px'"
            @close="importUpload.open = false"
            @confirm="submitImportFile"
            @cancel="importUpload.open = false"
        >
            <el-upload
                ref="importUploadRef"
                :limit="1"
                accept=".xlsx,.xls"
                :action="importUpload.url"
                :headers="importUpload.headers"
                :before-upload="importUpload.beforeUpload"
                :on-success="importUpload.onSuccess"
                :on-error="importUpload.onError"
                :on-progress="importUpload.onProgress"
                :on-change="importUpload.onChange"
                :auto-upload="false"
                drag
            >
                <i class="el-icon-upload"></i>
                <div class="el-upload__text">
                    å°†æ–‡ä»¶æ‹–到此处,或<em>点击上传</em>
                </div>
                <template #tip>
                    <div class="el-upload__tip">
                        ä»…支持 xls/xlsx,大小不超过 10MB。
                        <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button>
                    </div>
                </template>
            </el-upload>
        </FormDialog>
        <!-- é™„件列表弹窗 -->
        <FileListDialog
            ref="fileListRef"
            v-model="fileListDialogVisible"
            title="附件列表"
        />
        <!-- æ‰“印预览弹窗 -->
        <el-dialog
            v-model="printPreviewVisible"
            title="打印预览"
            width="90%"
            :close-on-click-modal="false"
            class="print-preview-dialog"
        >
            <div class="print-preview-container">
                <div class="print-preview-header">
                    <el-button type="primary" @click="executePrint">执行打印</el-button>
                    <el-button @click="printPreviewVisible = false">关闭预览</el-button>
                </div>
                <div class="print-preview-content">
                    <div v-if="printData.length === 0" style="text-align: center; padding: 50px; color: #999;">
                        æš‚无打印数据
                    </div>
                    <div v-else style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;">
                        å…± {{ printData.length }} æ¡æ•°æ®å¾…打印
                    </div>
                    <div v-for="(item, index) in printData" :key="index" class="print-page">
                        <div class="delivery-note">
                            <div class="header">
                                <div class="company-name">阳光印刷有限责任公司</div>
                                <div class="document-title">零售发货单</div>
                            </div>
                            <div class="info-section">
                                <div class="info-row">
                                    <div>
                                        <span class="label">发货日期:</span>
                                        <span class="value">{{ formatDate(item.createTime) }}</span>
                                    </div>
                                    <div>
                                        <span class="label">发货车牌号:</span>
                                        <span class="value">{{ item.shippingCarNumber }}</span>
                                    </div>
                                </div>
                                <div class="info-row">
                                    <div>
                                        <span class="label">客户名称:</span>
                                        <span class="value">{{ item.customerName }}</span>
                                    </div>
                                    <span class="label">单号:</span>
                                    <span class="value">{{ item.salesContractNo }}</span>
                                </div>
                            </div>
                            <div class="table-section">
                                <table class="product-table">
                                    <thead>
                                    <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>
                                    </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>
                                    </tfoot>
                                </table>
                            </div>
                            <div class="footer-section">
                                <div class="footer-row">
                                    <div class="footer-item">
                                        <span class="label">收货电话:</span>
                                        <span class="value"></span>
                                    </div>
                                    <div class="footer-item">
                                        <span class="label">收货人:</span>
                                        <span class="value"></span>
                                    </div>
                                    <div class="footer-item address-item">
                                        <span class="label">收货地址:</span>
                                        <span class="value address-value"></span>
                                    </div>
                                </div>
                                <div class="footer-row">
                                    <div class="footer-item">
                                        <span class="label">操作员:</span>
                                        <span class="value">{{ userStore.nickName || '撕开前' }}</span>
                                    </div>
                                    <div class="footer-item">
                                        <span class="label">打印日期:</span>
                                        <span class="value">{{ formatDateTime(new Date()) }}</span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </el-dialog>
        <!-- å‘货弹框 -->
        <el-dialog
            v-model="deliveryFormVisible"
            title="发货信息"
        width="40%"
            @close="closeDeliveryDia"
        >
            <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef">
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="发货类型:" prop="type">
                            <el-select
                                v-model="deliveryForm.type"
                                placeholder="请选择发货类型"
                                style="width: 100%"
                            >
                                <el-option label="货车" value="货车" />
                                <el-option label="快递" value="快递" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
        <!-- å®¡æ‰¹äººé€‰æ‹©ï¼ˆä»¿ååŒå®¡æ‰¹é‡Œçš„审批人节点选择) -->
        <el-row>
          <el-col :span="24">
            <el-form-item>
              <template #label>
                <span>审批人选择:</span>
                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button>
              </template>
              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
                <div
                  v-for="(node, index) in approverNodes"
                  :key="node.id"
                  style="margin-right: 20px; text-align: center; margin-bottom: 10px;"
                >
                  <div>
                    <span>审批人</span>
                    â†’
                  </div>
                  <el-select
                    v-model="node.userId"
                    placeholder="选择人员"
                    filterable
                    style="width: 140px; margin-bottom: 8px;"
                  >
                    <el-option
                      v-for="user in userList"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                  </el-select>
                  <div>
                    <el-button
                      type="danger"
                      @click="removeApproverNode(index)"
                      v-if="approverNodes.length > 1"
                    >删除</el-button>
                  </div>
                </div>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitDelivery">确认发货</el-button>
                    <el-button @click="closeDeliveryDia">取消</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</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 { UploadFilled, Download } from "@element-plus/icons-vue";
import useUserStore from "@/store/modules/user";
import { userListNoPage } from "@/api/system/user.js";
import FileListDialog from '@/components/Dialog/FileListDialog.vue';
import FormDialog from '@/components/Dialog/FormDialog.vue';
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import {
    ledgerListPage,
    productList,
    customerList,
    addOrUpdateSalesLedger,
    getSalesLedgerWithProducts,
    delLedger,
    addOrUpdateSalesLedgerProduct,
    delProduct,
    delLedgerFile, getProductInventory,
    exportSalesContract
} 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 {listCustomerPrivatePool} from "@/api/basicData/customerFile.js";
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 flatProductOptions = 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: "",
    },
    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" }],
        placeOfSinging: [{ required: true, message: "请输入", trigger: "blur" }],
    },
});
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: 0,
        taxInclusiveTotalPrice: "",
        taxExclusiveTotalPrice: "",
        invoiceType: "",
    },
    productRules: {
        productCategory: [{ required: true, message: "请选择", trigger: "change" }],
        productModelId: [{ required: false, 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" }],
    },
});
const { productForm, productRules } = toRefs(productFormData);
// é˜²æ­¢å¾ªçŽ¯è®¡ç®—çš„æ ‡å¿—
const isCalculating = ref(false);
const upload = reactive({
    // ä¸Šä¼ çš„地址
    url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
    // è®¾ç½®ä¸Šä¼ çš„请求头部
    headers: { Authorization: "Bearer " + getToken() },
});
// æ‰“印相关
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 deliveryFormVisible = ref(false);
const currentDeliveryRow = ref(null);
const deliveryFormData = reactive({
  deliveryForm: {
    type: "货车", // è´§è½¦, å¿«é€’
  },
  deliveryRules: {
    type: [
      { required: true, message: "请选择发货类型", trigger: "change" }
    ]
  },
});
const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
// å‘货审批人节点(仿协同审批 infoFormDia.vue)
const approverNodes = ref([{ id: 1, userId: null }]);
let nextApproverId = 2;
const addApproverNode = () => {
  approverNodes.value.push({ id: nextApproverId++, userId: null });
};
const removeApproverNode = (index) => {
  approverNodes.value.splice(index, 1);
};
// å¯¼å…¥ç›¸å…³
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();
            }
            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");
    } 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;
            tableData.value.map((item) => {
                item.children = [];
            });
            total.value = res.total;
            return res;
        })
        .catch(() => {
            tableLoading.value = false;
        });
};
// èŽ·å–äº§å“å¤§ç±»tree数据
const getProductOptions = () => {
    // è¿”回 Promise,便于在编辑产品时等待加载完成
    return productTreeList().then((res) => {
        productOptions.value = convertIdToValue(res);
        flatProductOptions.value = flattenProductOptions(productOptions.value);
        return productOptions.value;
    });
};
const formattedNumber = (row, column, cellValue) => {
    if (cellValue === undefined || cellValue === null || cellValue === "") {
        return "0.00";
    }
    return parseFloat(cellValue).toFixed(3);
};
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 tableData.value.find((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 =
        ledgerRecord.maintainerName ?? ledgerRecord.maintainer ?? ledgerRecord.entryPersonName;
    if (maintainerId !== undefined && maintainerId !== null && String(maintainerId) !== "") {
        return String(maintainerId) === currentUserId;
    }
    if (maintainerName !== undefined && maintainerName !== null && String(maintainerName).trim() !== "") {
        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 = value || "";
    const categoryId = findNodeIdByLabel(productOptions.value, value);
    if (!categoryId) {
        modelOptions.value = [];
        productForm.value.productModelId = "";
        productForm.value.specificationModel = value ? productForm.value.specificationModel : "";
        return;
    }
    modelList({ id: categoryId }).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 = value || "";
        if (!value) {
            productForm.value.unit = "";
        }
    }
};
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;
    });
}
function flattenProductOptions(nodes, result = []) {
    (nodes || []).forEach((node) => {
        result.push({
            id: node.value,
            label: node.label,
            value: node.label,
        });
        if (node.children && node.children.length > 0) {
            flattenProductOptions(node.children, result);
        }
    });
    return result;
}
// æ ¹æ®åç§°åæŸ¥äº§å“å¤§ç±» 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;
                }
                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",
    ], {
        contractAmount: { decimalPlaces: 3 },
        taxInclusiveTotalPrice: { decimalPlaces: 3 },
        taxExclusiveTotalPrice: { decimalPlaces: 3 },
    });
};
// å­è¡¨åˆè®¡æ–¹æ³•
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",
    ], {
    taxInclusiveUnitPrice: { decimalPlaces: 3 },
    taxInclusiveTotalPrice: { decimalPlaces: 3 },
    taxExclusiveTotalPrice: { decimalPlaces: 3 },
  });
};
// æ‰“开弹框
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.salesLedgerFiles;
        });
    }
    // 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,允许用户后续手动选择客户
        }
    }
    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 resetQuotationSearch = async () => {
    quotationSearchForm.quotationNo = "";
    quotationSearchForm.customer = "";
    quotationPage.current = 1;
    await fetchQuotationList();
};
// æŠ¥ä»·å•弹框分页切换
const quotationPaginationChange = (obj) => {
    quotationPage.current = obj.page;
    quotationPage.size = obj.limit;
    fetchQuotationList();
};
// é€‰ä¸­æŠ¥ä»·å•后回填到台账表单
const applyQuotation = (row) => {
    if (!row) return;
    selectedQuotation.value = row;
    // ä¸šåŠ¡å‘˜
    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 || "";
    }
    // äº§å“ä¿¡æ¯æ˜ å°„:报价 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 = Number(p.taxRate ?? 0) || 0; // é»˜è®¤ 13%,便于直接提交(如需可在产品中自行修改)
        const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(3);
        const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate, 3);
        return {
            // å°è´¦å­—段
            productCategory: p.product || p.productName || "",
            specificationModel: p.specification || "",
            unit: p.unit || "",
            quantity: quantity,
            taxRate: taxRate,
            taxInclusiveUnitPrice: unitPrice.toFixed(3),
            taxInclusiveTotalPrice: taxInclusiveTotalPrice,
            taxExclusiveTotalPrice: taxExclusiveTotalPrice,
            invoiceType: "增普票",
        };
    });
    quotationDialogVisible.value = false;
};
function changs(val) {
    console.log(val);
}
// ä¸Šä¼ å‰æ ¡æ£€
function handleBeforeUpload(file) {
    // æ ¡æ£€æ–‡ä»¶å¤§å°
    // if (file.size > 1024 * 1024 * 10) {
    //   proxy.$modal.msgError("上传文件大小不能超过10MB!");
    //   return false;
    // }
    proxy.$modal.loading("正在上传文件,请稍候...");
    return true;
}
// ä¸Šä¼ å¤±è´¥
function handleUploadError(err) {
    proxy.$modal.msgError("上传文件失败");
    proxy.$modal.closeLoading();
}
// ä¸Šä¼ æˆåŠŸå›žè°ƒ
function handleUploadSuccess(res, file, uploadFiles) {
    proxy.$modal.closeLoading();
    if (res.code === 200) {
        file.tempId = res.data.tempId;
        proxy.$modal.msgSuccess("上传成功");
    } else {
        proxy.$modal.msgError(res.msg);
        proxy.$refs.fileUpload.handleRemove(file);
    }
}
// ç§»é™¤æ–‡ä»¶
function handleRemove(file) {
    if (operationType.value === "edit") {
        let ids = [];
        ids.push(file.id);
        delLedgerFile(ids).then((res) => {
            proxy.$modal.msgSuccess("删除成功");
        });
    }
}
// æäº¤è¡¨å•
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;
            }
            let tempFileIds = [];
            if (fileList.value !== null && fileList.value.length > 0) {
                tempFileIds = fileList.value.map((item) => item.tempId);
            }
            form.value.tempFileIds = tempFileIds;
            form.value.type = 1;
            addOrUpdateSalesLedger(form.value).then((res) => {
                proxy.$modal.msgSuccess("提交成功");
                closeDia();
                expandedRowKeys.value = [];
                getList();
            });
        }
    });
};
// å…³é—­å¼¹æ¡†
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;
    }
    productOperationType.value = type;
    productForm.value = {
        taxRate: 0,
        invoiceType: ""
    };
    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(options, productForm.value.productCategory);
            if (categoryId) {
                const models = await modelList({ id: categoryId });
                modelOptions.value = models || [];
                // æ ¹æ®å½“前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
                const currentModel = (modelOptions.value || []).find(
                    (m) => m.model === productForm.value.specificationModel
                );
                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) {
            productForm.value.taxRate = productForm.value.taxRate?productForm.value.taxRate:0;
            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;
                        }
                    );
                });
            })
            .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 downloadTemplate = () => {
    proxy.download("/sales/ledger/exportTemplate", {}, "销售台账导入模板.xlsx");
};
// æäº¤å¯¼å…¥æ–‡ä»¶
const submitImportFile = () => {
    importUpload.isUploading = true;
    proxy.$refs["importUploadRef"].submit();
};
// å¯¼å‡º
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 === "审核通过";
};
/** åˆ¤æ–­é”€å”®è®¢å•下是否存在已发货/发货完成的产品(不可删除) */
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 = 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 (cannotDeleteNames.length > 0) {
        proxy.$modal.msgWarning("已进行发货或发货完成的销售订单不能删除:" + cannotDeleteNames.join("、"));
        return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            delLedger(ids).then((res) => {
                proxy.$modal.msgSuccess("删除成功");
                getList();
            });
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
// æ‰“印功能
const handlePrint = async () => {
    if (selectedRows.value.length === 0) {
        proxy.$modal.msgWarning("请选择要打印的数据");
        return;
    }
    // æ˜¾ç¤ºåŠ è½½çŠ¶æ€
    proxy.$modal.loading("正在获取产品数据,请稍候...");
    try {
        // ä¸ºæ¯ä¸ªé€‰ä¸­çš„销售台账记录查询对应的产品数据
        const printDataWithProducts = [];
        for (const row of selectedRows.value) {
            try {
                // è°ƒç”¨productList接口查询产品数据
                const productRes = await productList({ salesLedgerId: row.id, type: 1 });
                // å°†äº§å“æ•°æ®æ•´åˆåˆ°é”€å”®å°è´¦è®°å½•中
                const rowWithProducts = {
                    ...row,
                    products: productRes.data || []
                };
                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');
    // æž„建打印内容
    let printContent = `
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>打印预览</title>
      <style>
        body {
          margin: 0;
          padding: 0;
          font-family: "SimSun", serif;
          background: white;
        }
                                                     .print-page {
            width: 200mm;
            height: 75mm;
            padding: 10mm;
            padding-left: 20mm;
            background: white;
            box-sizing: border-box;
            page-break-after: always;
            page-break-inside: avoid;
          }
         .print-page:last-child {
           page-break-after: avoid;
         }
        .delivery-note {
          width: 100%;
          height: 100%;
          font-size: 12px;
          line-height: 1.2;
          display: flex;
          flex-direction: column;
          color: #000;
        }
        .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: 12px;
        }
        .value {
          margin-right: 20px;
          min-width: 80px;
          font-size: 12px;
        }
                 .table-section {
                 margin-bottom: 40px;
          //  flex: 0.6;
         }
        .product-table {
          width: 100%;
          border-collapse: collapse;
          border: 1px solid #000;
        }
                 .product-table th, .product-table td {
           border: 1px solid #000;
           padding: 6px;
           text-align: center;
           font-size: 12px;
           line-height: 1.4;
         }
        .product-table th {
          font-weight: bold;
        }
        .total-value {
          font-weight: bold;
        }
        .footer-section {
          margin-top: auto;
        }
        .footer-row {
          display: flex;
          margin-bottom: 3px;
          line-height: 22px;
          justify-content: space-between;
        }
        .footer-item {
          display: flex;
          margin-right: 20px;
        }
        .footer-item .label {
          font-weight: bold;
          width: 80px;
          font-size: 12px;
        }
        .footer-item .value {
          min-width: 80px;
          font-size: 12px;
        }
        .address-item .address-value {
          min-width: 200px;
        }
        @media print {
          body {
            margin: 0;
            padding: 0;
          }
                     .print-page {
             margin: 0;
             padding: 10mm;
             /* padding-left: 20mm; */
             page-break-inside: avoid;
             page-break-after: always;
           }
           .print-page:last-child {
             page-break-after: avoid;
           }
        }
      </style>
    </head>
    <body>
  `;
    // ä¸ºæ¯æ¡æ•°æ®ç”Ÿæˆæ‰“印页面
    printData.value.forEach((item, index) => {
        printContent += `
      <div class="print-page">
        <div class="delivery-note">
          <div class="header">
            <div class="document-title">零售发货单</div>
          </div>
          <div class="info-section">
            <div class="info-row">
              <div>
                <span class="label">发货日期:</span>
                <span class="value">${formatDate(item.createTime)}</span>
              </div>
              <div>
                <span class="label">客户名称:</span>
                <span class="value">${item.customerName}</span>
              </div>
            </div>
            <div class="info-row">
              <span class="label">单号:</span>
              <span class="value">${item.salesContractNo || ''}</span>
            </div>
          </div>
          <div class="table-section">
            <table class="product-table">
              <thead>
                <tr>
                  <th>产品名称</th>
                  <th>规格型号</th>
                  <th>单位</th>
                  <th>单价</th>
                  <th>零售数量</th>
                  <th>零售金额</th>
                </tr>
              </thead>
              <tbody>
                ${item.products && item.products.length > 0 ?
            item.products.map(product => `
                    <tr>
                      <td>${product.productCategory || ''}</td>
                      <td>${product.specificationModel || ''}</td>
                      <td>${product.unit || ''}</td>
                      <td>${product.taxInclusiveUnitPrice || '0'}</td>
                      <td>${product.quantity || '0'}</td>
                      <td>${product.taxInclusiveTotalPrice || '0'}</td>
                    </tr>
                  `).join('') :
            '<tr><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">${getTotalQuantityForPrint(item.products)}</td>
                  <td class="total-value">${getTotalAmountForPrint(item.products)}</td>
                </tr>
              </tfoot>
            </table>
          </div>
          <div class="footer-section">
            <div class="footer-row">
              <div class="footer-item">
                <span class="label">收货电话:</span>
                <span class="value"></span>
              </div>
              <div class="footer-item">
                <span class="label">收货人:</span>
                <span class="value"></span>
              </div>
              <div class="footer-item address-item">
                <span class="label">收货地址:</span>
                <span class="value address-value"></span>
              </div>
            </div>
            <div class="footer-row">
              <div class="footer-item">
                <span class="label">操作员:</span>
                <span class="value">${userStore.nickName || '撕开前'}</span>
              </div>
              <div class="footer-item">
                <span class="label">打印日期:</span>
                <span class="value">${formatDateTime(new Date())}</span>
              </div>
            </div>
          </div>
        </div>
      </div>
    `;
    });
    printContent += `
    </body>
    </html>
  `;
    // å†™å…¥å†…容到新窗口
    printWindow.document.write(printContent);
    printWindow.document.close();
    // ç­‰å¾…内容加载完成后打印
    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(3);
};
// è®¡ç®—产品总金额
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(3);
};
// ç”¨äºŽæ‰“印的计算函数
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(3);
};
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(3);
};
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,
            3
        );
    if (productForm.value.taxRate) {
        // ä¸å«ç¨Žæ€»ä»·è®¡ç®—
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                productForm.value.taxInclusiveTotalPrice,
                productForm.value.taxRate,
                3
            );
    }
};
// æ ¹æ®å«ç¨Žæ€»ä»·è®¡ç®—含税单价和数量
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(3);
    // å¦‚果有税率,计算不含税总价
    if (productForm.value.taxRate) {
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                totalPrice,
                productForm.value.taxRate,
                3
            );
    }
    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(3);
    // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
    productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(3);
    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(3);
    // å¦‚果有税率,计算不含税总价
    if (productForm.value.taxRate) {
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                productForm.value.taxInclusiveTotalPrice,
                productForm.value.taxRate,
                3
            );
    }else{
    productForm.value.taxExclusiveTotalPrice = (unitPrice * quantity).toFixed(3);
  }
    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(3);
    // å¦‚果有税率,计算不含税总价
    if (productForm.value.taxRate) {
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                productForm.value.taxInclusiveTotalPrice,
                productForm.value.taxRate,
                3
            );
    }
    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,
            3
        );
    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] || '待发货';
};
/**
 * èŽ·å–å‘è´§çŠ¶æ€æ ‡ç­¾ç±»åž‹ï¼ˆé¢œè‰²ï¼‰
 * @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) => {
    // äº§å“çŠ¶æ€å¿…é¡»æ˜¯å……è¶³ï¼ˆåŸºäºŽ hasSufficientStock åˆ¤æ–­ï¼‰
    if (!row || !row.hasSufficientStock) {
        return false;
    }
    // èŽ·å–å‘è´§çŠ¶æ€
    const shippingStatus = row.shippingStatus;
    // å¦‚果已发货(有发货日期或车牌号),不能再次发货
    if (row.shippingDate || row.shippingCarNumber) {
        return false;
    }
    // å‘货状态必须是"待发货"或"审核拒绝"
    const statusStr = shippingStatus ? String(shippingStatus).trim() : '';
    return statusStr === '待发货' || statusStr === '审核拒绝';
};
/**
 * å¯¼å‡ºé”€å”®åˆåŒ
 *
 * @param row å¯¼å‡ºé”€å”®åˆåŒçš„相关信息对象
 */
const exportSalesContracts = (row) => {
    exportSalesContract({ id: row.id }).then((res) => {
        if (res) {
            const downloadUrl = window.URL.createObjectURL(res);
      const link = document.createElement('a');
      link.href = downloadUrl;
      console.log(row.executionDate)
      link.download = row.projectName+row.executionDate + "销售合同.docx"; // è®¾ç½®ä¸‹è½½æ–‡ä»¶å
      link.style.display = 'none'; // éšè—a标签
      document.body.appendChild(link);
      link.click(); // è§¦å‘点击下载
      // 4. æ¸…理资源(避免内存泄漏)
      document.body.removeChild(link);
      window.URL.revokeObjectURL(downloadUrl);
      // 5. æç¤ºå¯¼å‡ºæˆåŠŸ
      proxy.$modal.msgSuccess("导出销售合同成功");
        } else {
            proxy.$modal.msgError(res.msg || "导出销售合同失败");
        }
    });
}
/**
 * ä¸‹è½½æ–‡ä»¶
 *
 * @param row ä¸‹è½½æ–‡ä»¶çš„相关信息对象
 */
const fileListRef = ref(null)
const fileListDialogVisible = ref(false)
const downLoadFile = (row) => {
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
        if (fileListRef.value) {
            fileListRef.value.open(res.salesLedgerFiles)
        }
    });
}
// æ‰“开发货弹框
const openDeliveryForm = (row) => {
    // æ£€æŸ¥æ˜¯å¦å¯ä»¥å‘è´§
    if (!canShip(row)) {
        proxy.$modal.msgWarning("只有在产品状态是充足,发货状态是待发货或审核拒绝的时候才可以发货");
        return;
    }
    currentDeliveryRow.value = row;
  deliveryForm.value = {
    type: "货车",
  };
  // é‡ç½®å®¡æ‰¹äººèŠ‚ç‚¹ï¼ˆé»˜è®¤ä¸€ä¸ªç©ºèŠ‚ç‚¹ï¼‰
  approverNodes.value = [{ id: 1, userId: null }];
  nextApproverId = 2;
    deliveryFormVisible.value = true;
};
// æäº¤å‘货表单
const submitDelivery = () => {
  proxy.$refs["deliveryFormRef"].validate((valid) => {
    if (valid) {
      // å®¡æ‰¹äººå¿…填校验(所有节点都要选人)
      const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
      if (hasEmptyApprover) {
        proxy.$modal.msgError("请为所有审批节点选择审批人!");
        return;
      }
      const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
      // ä¿å­˜å½“前展开的行ID,以便发货后重新加载子表格数据
      const currentExpandedKeys = [...expandedRowKeys.value];
      const salesLedgerId = currentDeliveryRow.value.salesLedgerId;
      addShippingInfo({
        salesLedgerId: salesLedgerId,
        salesLedgerProductId: currentDeliveryRow.value.id,
        type: deliveryForm.value.type,
                approveUserIds,
      })
        .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);
                  if (index > -1) {
                    tableData.value[index].children = res.data;
                  }
                });
              });
              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(() => {
    getList();
    userListNoPage().then(res => {
        userList.value = res.data;
    })
    getCurrentFactoryName();
});
</script>
<style scoped lang="scss">
.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;
    }
}
.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 {
    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: 80px;
                font-size: 14px;
            }
            .value {
                min-width: 80px;
                font-size: 14px;
            }
            &.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;
    }
}
</style>