| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" |
| | | :inline="true"> |
| | | <el-form-item label="客户名称:"> |
| | | <el-select v-model="searchForm.customerId" |
| | | filterable |
| | | placeholder="请选择客户名称" |
| | | clearable |
| | | style="width: 220px" |
| | | @change="handleQuery"> |
| | | <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-form-item label="销售合同号:"> |
| | | <el-input v-model="searchForm.salesContractNo" |
| | | placeholder="请输入" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="项目名称:"> |
| | | <el-input v-model="searchForm.projectName" |
| | | placeholder="请输入" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="产品宽(mm):"> |
| | | <el-input v-model="searchForm.width" |
| | | placeholder="请输入" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="产品高(mm):"> |
| | | <el-input v-model="searchForm.height" |
| | | placeholder="请输入" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="录入日期:"> |
| | | <el-date-picker v-model="searchForm.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | placeholder="请选择" |
| | | <template v-if="isFormPageMode"> |
| | | <div class="sales-ledger-page-header"> |
| | | <div class="sales-ledger-page-header-content"> |
| | | <div class="sales-ledger-page-title">{{ pageFormTitle }}</div> |
| | | <div class="sales-ledger-page-subtitle">{{ pageFormSubtitle }}</div> |
| | | </div> |
| | | <el-button class="sales-ledger-page-back" |
| | | @click="exitFormPage()">返回台账</el-button> |
| | | </div> |
| | | <div class="sales-ledger-page-form"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | @keydown.capture="handleTabScrollFollow" |
| | | ref="formRef"> |
| | | <el-row v-if="operationType === 'add'" |
| | | style="margin-bottom: 10px;"> |
| | | <el-col :span="24" |
| | | style="text-align: right;"> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="openQuotationDialog"> |
| | | 从销售报价导入 |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="销售合同号:" |
| | | prop="salesContractNo"> |
| | | <el-input v-model="form.salesContractNo" |
| | | placeholder="自动生成" |
| | | clearable |
| | | @change="changeDaterange" /> |
| | | </el-form-item> |
| | | <el-form-item label="发货状态:"> |
| | | <el-select v-model="searchForm.deliveryStatus" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 140px"> |
| | | <el-option label="未发货" |
| | | :value="1" /> |
| | | <el-option label="审批中" |
| | | :value="2" /> |
| | | <el-option label="审批失败" |
| | | :value="3" /> |
| | | <el-option label="已发货" |
| | | :value="4" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="入库状态:"> |
| | | <el-select v-model="searchForm.stockStatus" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 140px"> |
| | | <el-option label="未入库" |
| | | :value="0" /> |
| | | <el-option label="部分入库" |
| | | :value="1" /> |
| | | <el-option label="已入库" |
| | | :value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleQuery"> 搜索 </el-button> |
| | | </el-form-item> |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="业务员:" |
| | | prop="salesman"> |
| | | <el-select v-model="form.salesman" |
| | | placeholder="请选择" |
| | | clearable |
| | | :disabled="isReviewedEdit"> |
| | | <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> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客户名称:" |
| | | prop="customerId"> |
| | | <el-select v-model="form.customerId" |
| | | filterable |
| | | placeholder="请选择" |
| | | clearable |
| | | :disabled="isReviewedEdit"> |
| | | <el-option v-for="item in customerOption" |
| | | :key="item.id" |
| | | :label="item.customerName" |
| | | :value="item.id"> |
| | | {{ item.customerName + (item.taxpayerIdentificationNumber ? "——" + item.taxpayerIdentificationNumber : "") }} |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="项目名称:" |
| | | prop="projectName"> |
| | | <el-input v-model="form.projectName" |
| | | placeholder="请输入" |
| | | clearable |
| | | :disabled="isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="签订日期:" |
| | | prop="executionDate"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.executionDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="请选择" |
| | | clearable |
| | | :disabled="isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="交货日期:" |
| | | prop="deliveryDate"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.deliveryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="请选择" |
| | | clearable |
| | | :disabled="isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <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" |
| | | :disabled="isReviewedEdit"> |
| | | <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 |
| | | :disabled="isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-form-item label="产品信息:" |
| | | prop="entryDate"> |
| | | <el-button plain |
| | | type="danger" |
| | | :disabled="isReviewedEdit" |
| | | @click="deleteProduct">删除</el-button> |
| | | <el-button plain |
| | | type="primary" |
| | | :disabled="isReviewedEdit" |
| | | @click="pinSelectedProductRow">固定</el-button> |
| | | </el-form-item> |
| | | </el-row> |
| | | <el-table :data="productData" |
| | | border |
| | | class="compact-product-table" |
| | | @selection-change="productSelected" |
| | | show-summary |
| | | :summary-method="summarizeProductTable"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" |
| | | :selectable="(row) => !isProductShipped(row)" /> |
| | | <el-table-column align="center" |
| | | label="序号" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="产品大类" |
| | | prop="productCategory" |
| | | min-width="120"> |
| | | <template #default="scope"> |
| | | <el-tree-select v-if="scope.row.__editing" |
| | | v-model="scope.row.__productCategoryId" |
| | | placeholder="请选择" |
| | | clearable |
| | | filterable |
| | | check-strictly |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" |
| | | :filter-node-method="filterProductCategoryNode" |
| | | @change="(val) => handleInlineProductCategoryChange(scope.row, val)" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.productCategory ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="规格型号" |
| | | prop="specificationModel" |
| | | min-width="140"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.productModelId" |
| | | placeholder="请选择" |
| | | clearable |
| | | filterable |
| | | style="width: 100%" |
| | | @change="(val) => handleInlineProductModelChange(scope.row, val)" |
| | | :disabled="isReviewedEdit"> |
| | | <el-option v-for="item in modelOptions" |
| | | :key="item.id" |
| | | :label="item.model" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | <span v-else>{{ scope.row.specificationModel ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="厚度(mm)" |
| | | prop="thickness" |
| | | min-width="95"> |
| | | <template #default="scope"> |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | v-model="scope.row.thickness" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.thickness ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="楼层编号" |
| | | prop="floorCode" |
| | | min-width="120" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.floorCode" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.floorCode ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="含税单价(元)" |
| | | prop="taxInclusiveUnitPrice" |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | :controls="false" |
| | | :step="0.01" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | v-model="scope.row.taxInclusiveUnitPrice" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineUnitPriceChange(scope.row)" |
| | | @input="() => handleInlineUnitPriceChange(scope.row)" /> |
| | | <span v-else>{{ formattedNumber(null, null, scope.row.taxInclusiveUnitPrice) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="宽(mm)" |
| | | prop="width" |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | :controls="false" |
| | | v-model="scope.row.width" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | | style="width:100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineSizeChange(scope.row)" |
| | | @input="() => handleInlineSizeChange(scope.row)" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.width ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="高(mm)" |
| | | prop="height" |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | :controls="false" |
| | | v-model="scope.row.height" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineSizeChange(scope.row)" |
| | | @input="() => handleInlineSizeChange(scope.row)" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.height ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="数量" |
| | | prop="quantity" |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | v-model="scope.row.quantity" |
| | | :step="1" |
| | | :min="0" |
| | | :precision="0" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineQuantityChange(scope.row)" |
| | | @input="() => handleInlineQuantityChange(scope.row)" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.quantity ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="结算单片面积(㎡)" |
| | | prop="settlePieceArea" |
| | | min-width="120"> |
| | | <template #default="scope"> |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | v-model="scope.row.settlePieceArea" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="4" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineSettleAreaChange(scope.row)" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.settlePieceArea ? Number(scope.row.settlePieceArea).toFixed(4) : "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="面积(m²)" |
| | | prop="actualTotalArea" |
| | | min-width="110"> |
| | | <template #default="scope"> |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | v-model="scope.row.actualTotalArea" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="4" |
| | | style="width: 100%" |
| | | placeholder="自动计算" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.actualTotalArea ? Number(scope.row.actualTotalArea).toFixed(4) : "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="税率(%)" |
| | | prop="taxRate" |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.taxRate" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="() => handleInlineTaxRateChange(scope.row)" |
| | | :disabled="isReviewedEdit"> |
| | | <el-option label="1" |
| | | value="1" /> |
| | | <el-option label="3" |
| | | value="3" /> |
| | | <el-option label="6" |
| | | value="6" /> |
| | | <el-option label="9" |
| | | value="9" /> |
| | | <el-option label="13" |
| | | value="13" /> |
| | | </el-select> |
| | | <span v-else>{{ scope.row.taxRate ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="含税总价(元)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | min-width="100" /> |
| | | <el-table-column label="不含税总价(元)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | min-width="100" /> |
| | | <el-table-column label="加工要求" |
| | | prop="processRequirement" |
| | | min-width="100" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.processRequirement" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.processRequirement ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="发票类型" |
| | | prop="invoiceType" |
| | | min-width="90"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.invoiceType" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isReviewedEdit"> |
| | | <el-option label="增普票" |
| | | value="增普票" /> |
| | | <el-option label="增专票" |
| | | value="增专票" /> |
| | | </el-select> |
| | | <span v-else>{{ scope.row.invoiceType ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="备注" |
| | | prop="remark" |
| | | min-width="100" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.remark" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.remark ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="重箱" |
| | | prop="heavyBox" |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.heavyBox" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.heavyBox ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" |
| | | label="操作" |
| | | min-width="150" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <template v-if="scope.row.__editing"> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="saveProductInline(scope.row, scope.$index)">保存</el-button> |
| | | <el-button link |
| | | type="danger" |
| | | size="small" |
| | | @click="cancelProductInline(scope.row, scope.$index)">取消</el-button> |
| | | <el-popover :width="560" |
| | | trigger="click" |
| | | :hide-after="0" |
| | | :visible="scope.row.__otherAmountPopoverVisible" |
| | | @update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)"> |
| | | <template #reference> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="openOtherAmountInline(scope.row)"> |
| | | 额外加工({{ (scope.row.salesProductProcessList || []).length || 0 }}) |
| | | </el-button> |
| | | </template> |
| | | <div style="display:flex; align-items:center; justify-content:space-between; gap: 10px; margin-bottom: 8px;"> |
| | | <div style="font-weight: 600; color:#303133;"> |
| | | 额外加工 |
| | | </div> |
| | | <el-button type="primary" |
| | | plain |
| | | size="small" |
| | | :disabled="isReviewedEdit" |
| | | @click="startAddOtherAmountForRow(scope.row)"> |
| | | 新增 |
| | | </el-button> |
| | | </div> |
| | | <div v-if="scope.row.__inlineOtherAmountAdding" |
| | | style="display:flex; flex-direction:column; gap: 8px; margin-bottom: 10px;" |
| | | @click.stop> |
| | | <el-select v-model="scope.row.__inlineOtherAmountAddId" |
| | | filterable |
| | | clearable |
| | | placeholder="请选择额外加工项目" |
| | | style="width: 100%;"> |
| | | <el-option v-for="item in otherAmountSelectOptions" |
| | | :key="item.id" |
| | | :label="item.processName" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | <div style="display:flex; justify-content:flex-end; gap: 8px;"> |
| | | <el-button size="small" |
| | | @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null"> |
| | | 取消 |
| | | </el-button> |
| | | <el-button type="primary" |
| | | size="small" |
| | | :disabled="isReviewedEdit || scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''" |
| | | @click="confirmAddOtherAmountForRow(scope.row)"> |
| | | 确认添加 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <div v-if="Array.isArray(scope.row.salesProductProcessList) && scope.row.salesProductProcessList.length > 0" |
| | | style="display:flex; flex-wrap:wrap; gap: 8px;"> |
| | | <div v-for="(item, idx) in scope.row.salesProductProcessList" |
| | | :key="String(item.id) + '_' + idx" |
| | | style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);"> |
| | | <el-tag type="info" |
| | | style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;"> |
| | | {{ item.processName }} |
| | | </el-tag> |
| | | <el-input-number :controls="false" v-model="item.quantity" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="0" |
| | | style="width: 120px;" |
| | | placeholder="数量" |
| | | :disabled="isReviewedEdit" |
| | | @change="handleOtherAmountQuantityChange(scope.row)" /> |
| | | <el-button type="danger" |
| | | link |
| | | size="small" |
| | | :disabled="isReviewedEdit" |
| | | @click="removeOtherAmountAtForRow(scope.row, idx)"> |
| | | 删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <div v-else |
| | | style="color:#909399; font-size: 13px;"> |
| | | 暂无额外加工 |
| | | </div> |
| | | </el-popover> |
| | | </template> |
| | | <template v-else> |
| | | <el-button v-if="isReviewedEdit" |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | @click="editPriceOnly(scope.row)"> |
| | | 修改单价 |
| | | </el-button> |
| | | <template v-if="!isReviewedEdit"> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | @click="editProductInline(scope.row, scope.$index)"> |
| | | 编辑 |
| | | </el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | @click="copyProductInline(scope.row, scope.$index)"> |
| | | 复制 |
| | | </el-button> |
| | | </template> |
| | | <el-popover :width="560" |
| | | trigger="click" |
| | | :hide-after="0" |
| | | :visible="scope.row.__otherAmountPopoverVisible" |
| | | @update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)"> |
| | | <template #reference> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | @click="openOtherAmountInline(scope.row)"> |
| | | 额外加工({{ (scope.row.salesProductProcessList || []).length || 0 }}) |
| | | </el-button> |
| | | </template> |
| | | <div style="display:flex; align-items:center; justify-content:space-between; gap: 10px; margin-bottom: 8px;"> |
| | | <div style="font-weight: 600; color:#303133;"> |
| | | 额外加工 |
| | | </div> |
| | | <el-button type="primary" |
| | | plain |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit" |
| | | @click="startAddOtherAmountForRow(scope.row)"> |
| | | 新增 |
| | | </el-button> |
| | | </div> |
| | | <div v-if="scope.row.__inlineOtherAmountAdding" |
| | | style="display:flex; flex-direction:column; gap: 8px; margin-bottom: 10px;" |
| | | @click.stop> |
| | | <el-select v-model="scope.row.__inlineOtherAmountAddId" |
| | | filterable |
| | | clearable |
| | | placeholder="请选择额外加工项目" |
| | | style="width: 100%;" |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit"> |
| | | <el-option v-for="item in otherAmountSelectOptions" |
| | | :key="item.id" |
| | | :label="item.processName" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | <div style="display:flex; justify-content:flex-end; gap: 8px;"> |
| | | <el-button size="small" |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit" |
| | | @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null"> |
| | | 取消 |
| | | </el-button> |
| | | <el-button type="primary" |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit || scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''" |
| | | @click="confirmAddOtherAmountForRow(scope.row)"> |
| | | 确认添加 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <div v-if="Array.isArray(scope.row.salesProductProcessList) && scope.row.salesProductProcessList.length > 0" |
| | | style="display:flex; flex-wrap:wrap; gap: 8px;"> |
| | | <div v-for="(item, idx) in scope.row.salesProductProcessList" |
| | | :key="String(item.id) + '_' + idx" |
| | | style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);"> |
| | | <el-tag type="info" |
| | | style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;"> |
| | | {{ item.processName }} |
| | | </el-tag> |
| | | <el-input-number :controls="false" v-model="item.quantity" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="0" |
| | | style="width: 120px;" |
| | | placeholder="数量" |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit" |
| | | @change="handleOtherAmountQuantityChange(scope.row)" /> |
| | | <el-button type="danger" |
| | | link |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit" |
| | | @click="removeOtherAmountAtForRow(scope.row, idx)"> |
| | | 删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <div v-else |
| | | style="color:#909399; font-size: 13px;"> |
| | | 暂无额外加工 |
| | | </div> |
| | | </el-popover> |
| | | </template> |
| | | </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="isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="客户备注:" |
| | | prop="customerRemarks"> |
| | | <el-input v-model="form.customerRemarks" |
| | | placeholder="请输入" |
| | | clearable |
| | | type="textarea" |
| | | :rows="2" |
| | | :disabled="isReviewedEdit" /> |
| | | </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">上传</el-button> |
| | | <template #tip> |
| | | <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> |
| | | <div class="sales-ledger-page-actions"> |
| | | <el-button type="primary" @click="submitForm">提交</el-button> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | </template> |
| | | <template v-else> |
| | | <div class="sales-ledger-status-bar"> |
| | | <button v-for="tab in salesLedgerStatusTabs" |
| | | :key="tab.key" |
| | | type="button" |
| | | class="sales-ledger-status-tab" |
| | | :class="{ 'is-active': activeStatusTab === tab.key }" |
| | | @click="handleStatusTabChange(tab.key)"> |
| | | {{ tab.label }} |
| | | </button> |
| | | </div> |
| | | <ReverseAuditHistory v-if="activeStatusTab === 'reverseReviewed'" /> |
| | | <div v-else> |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" label-width="110px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :xs="24" :sm="12" :md="8" :lg="8"> |
| | | <el-form-item label="客户名称:"> |
| | | <el-select v-model="searchForm.customerId" |
| | | filterable |
| | | placeholder="请选择客户名称" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="handleQuery"> |
| | | <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 :xs="24" :sm="12" :md="8" :lg="8"> |
| | | <el-form-item label="销售合同号:"> |
| | | <el-input v-model="searchForm.salesContractNo" |
| | | placeholder="请输入" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 100%" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="8" :lg="8"> |
| | | <el-form-item label="项目名称:"> |
| | | <el-input v-model="searchForm.projectName" |
| | | placeholder="请输入" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 100%" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="8" :lg="8"> |
| | | <el-form-item label="录入日期:"> |
| | | <el-date-picker v-model="searchForm.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="changeDaterange" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="8" :lg="8"> |
| | | <el-form-item label="产品宽(mm):"> |
| | | <el-input v-model="searchForm.width" |
| | | placeholder="请输入" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 100%" |
| | | @input="val => searchForm.width = val.replace(/[^\d.]/g, '')" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="8" :lg="8"> |
| | | <el-form-item label="产品高(mm):"> |
| | | <el-input v-model="searchForm.height" |
| | | placeholder="请输入" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 100%" |
| | | @input="val => searchForm.height = val.replace(/[^\d.]/g, '')" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="8" :lg="8"> |
| | | <el-form-item label="发货状态:"> |
| | | <el-select v-model="searchForm.deliveryStatus" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 100%"> |
| | | <el-option label="未发货" :value="1" /> |
| | | <el-option label="审批中" :value="2" /> |
| | | <el-option label="审批不通过" :value="3" /> |
| | | <el-option label="审批通过" :value="4" /> |
| | | <el-option label="已发货" :value="5" /> |
| | | <el-option label="部分发货" :value="6" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="8" :lg="8"> |
| | | <el-form-item label="入库状态:"> |
| | | <el-select v-model="searchForm.stockStatus" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 100%"> |
| | | <el-option label="未入库" :value="0" /> |
| | | <el-option label="部分入库" :value="1" /> |
| | | <el-option label="已入库" :value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="8" :lg="8"> |
| | | <el-form-item label="订单状态:"> |
| | | <el-select v-model="searchForm.orderStatus" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 100%"> |
| | | <el-option label="进行中" :value="0" /> |
| | | <el-option label="已完成" :value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="24"> |
| | | <el-form-item label=" "> |
| | | <div style="width: 100%; text-align: right;"> |
| | | <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div> |
| | | <div class="actions sales-ledger-toolbar"> |
| | | <div class="sales-ledger-toolbar-group"> |
| | | <OtherAmountMaintenanceButton /> |
| | | <ProcessFlowMaintenanceButton /> |
| | | </div> |
| | | <ProcessFlowConfigSelectDialog v-model:visible="processFlowSelectDialogVisible" |
| | | :default-route-id="processFlowSelectDefaultRouteId" |
| | | :default-record-list="processFlowSelectDefaultRecordList" |
| | | :bound-route-name="processFlowSelectBoundRouteName" |
| | | @confirm="handleProcessFlowSelectConfirm" /> |
| | | <el-space wrap> |
| | | <el-button type="primary" |
| | | @click="handleSalesStock">入库</el-button> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">新增台账</el-button> |
| | | <el-button type="primary" |
| | | @click="handleBulkDelivery">发货</el-button> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handleImport">导入</el-button> |
| | | <el-dropdown @command="handleHistoryImportCommand"> |
| | | <div class="sales-ledger-toolbar-actions"> |
| | | <el-space v-if="activeStatusTab === 'pendingReview'" |
| | | wrap |
| | | class="sales-ledger-toolbar-group"> |
| | | <el-button type="primary" |
| | | plain> |
| | | 历史迁移<el-icon class="el-icon--right"> |
| | | <ArrowDown /> |
| | | </el-icon> |
| | | </el-button> |
| | | <template #dropdown> |
| | | <el-dropdown-menu> |
| | | <el-dropdown-item command="notShipped">未发货</el-dropdown-item> |
| | | <el-dropdown-item command="shipped">已发货</el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </template> |
| | | </el-dropdown> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">删除</el-button> |
| | | <el-dropdown @command="handlePrintCommand"> |
| | | @click="handleAudit" |
| | | :disabled="isBatchButtonDisabled('audit')">审核</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete" |
| | | :disabled="isBatchButtonDisabled('delete')">删除</el-button> |
| | | <el-button type="primary" |
| | | plain> |
| | | 打印单据<el-icon class="el-icon--right"> |
| | | <ArrowDown /> |
| | | </el-icon> |
| | | </el-button> |
| | | <template #dropdown> |
| | | <el-dropdown-menu> |
| | | <el-dropdown-item command="finishedProcessCard">生产流程卡(成品)</el-dropdown-item> |
| | | <el-dropdown-item command="salesOrder">销售订单</el-dropdown-item> |
| | | <el-dropdown-item command="salesDeliveryNote">销售发货单</el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </template> |
| | | </el-dropdown> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handlePrintLabel">打印标签</el-button> |
| | | </el-space> |
| | | @click="openForm('add')">新增台账</el-button> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handleImport">导入</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | </el-space> |
| | | <el-space v-else-if="activeStatusTab === 'reviewed'" |
| | | wrap |
| | | class="sales-ledger-toolbar-group"> |
| | | <el-button type="primary" |
| | | @click="handleReverseAudit" |
| | | :disabled="isBatchButtonDisabled('reverseAudit')">反审</el-button> |
| | | <el-button type="warning" |
| | | @click="handleMarkCompleted" |
| | | :disabled="isBatchButtonDisabled('markCompleted')">标记完成</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSalesStock" |
| | | :disabled="isBatchButtonDisabled('stock')">入库</el-button> |
| | | <el-button type="primary" |
| | | @click="handleBulkDelivery" |
| | | :disabled="isBatchButtonDisabled('delivery')">发货</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | |
| | | <el-button @click="handleExportProcessRoute" |
| | | :disabled="isBatchButtonDisabled('export')">导出工艺路线</el-button> |
| | | </el-space> |
| | | <el-space v-else-if="activeStatusTab === 'stocked'" |
| | | wrap |
| | | class="sales-ledger-toolbar-group"> |
| | | <el-button type="primary" |
| | | @click="handleReverseAudit" |
| | | :disabled="isBatchButtonDisabled('reverseAudit')">反审</el-button> |
| | | <el-button type="warning" |
| | | @click="handleMarkCompleted" |
| | | :disabled="isBatchButtonDisabled('markCompleted')">标记完成</el-button> |
| | | <el-button type="primary" |
| | | @click="handleBulkDelivery" |
| | | :disabled="isBatchButtonDisabled('delivery')">发货</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | </el-space> |
| | | <el-space v-else-if="activeStatusTab === 'delivered'" |
| | | wrap |
| | | class="sales-ledger-toolbar-group"> |
| | | <el-button type="warning" |
| | | @click="handleMarkCompleted" |
| | | :disabled="isBatchButtonDisabled('markCompleted')">标记完成</el-button> |
| | | <el-button type="primary" |
| | | @click="handleReverseAudit" |
| | | :disabled="isBatchButtonDisabled('reverseAudit')">反审</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | </el-space> |
| | | <el-space v-else-if="activeStatusTab === 'completed'" |
| | | wrap |
| | | class="sales-ledger-toolbar-group"> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handlePrintCommand('salesOrder')" |
| | | :disabled="isBatchButtonDisabled('print')">打印单据</el-button> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handlePrintLabel" |
| | | :disabled="isBatchButtonDisabled('print')">打印标签</el-button> |
| | | </el-space> |
| | | <el-space v-else-if="activeStatusTab === 'all'" |
| | | wrap |
| | | class="sales-ledger-toolbar-group"> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">新增台账</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | </el-space> |
| | | <el-space wrap class="sales-ledger-toolbar-group sales-ledger-toolbar-group--muted"> |
| | | <el-dropdown @command="handleHistoryImportCommand"> |
| | | <el-button type="primary" |
| | | plain> |
| | | 历史迁移<el-icon class="el-icon--right"> |
| | | <ArrowDown /> |
| | | </el-icon> |
| | | </el-button> |
| | | <template #dropdown> |
| | | <el-dropdown-menu> |
| | | <el-dropdown-item command="notShipped">未出库</el-dropdown-item> |
| | | <el-dropdown-item command="shipped">已出库</el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </template> |
| | | </el-dropdown> |
| | | </el-space> |
| | | <el-space wrap class="sales-ledger-toolbar-group sales-ledger-toolbar-group--muted"> |
| | | <el-dropdown @command="handlePrintCommand"> |
| | | <el-button type="primary" |
| | | plain |
| | | :disabled="isBatchButtonDisabled('print')"> |
| | | 打印单据<el-icon class="el-icon--right"> |
| | | <ArrowDown /> |
| | | </el-icon> |
| | | </el-button> |
| | | <template #dropdown> |
| | | <el-dropdown-menu> |
| | | <el-dropdown-item command="finishedProcessCard">生产流程卡(成品)</el-dropdown-item> |
| | | <el-dropdown-item command="salesOrder">销售订单</el-dropdown-item> |
| | | <el-dropdown-item command="salesDeliveryNote">销售发货单</el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </template> |
| | | </el-dropdown> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handlePrintLabel" |
| | | :disabled="isBatchButtonDisabled('print')">打印标签</el-button> |
| | | </el-space> |
| | | </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)"> |
| | | height="calc(100vh - 23em)" |
| | | :summary-method="summarizeMainTable"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" |
| | | fixed="left" /> |
| | | <el-table-column type="expand" |
| | | width="60" |
| | | fixed="left"> |
| | | <template #default="props"> |
| | | <el-table :data="props.row.children" |
| | | border |
| | | show-summary |
| | | :summary-method="summarizeChildrenTable"> |
| | | <el-table-column align="center" |
| | | label="序号" |
| | | type="index" /> |
| | | <el-table-column label="楼层编号" |
| | | prop="floorCode" |
| | | min-width="100" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="产品大类" |
| | | prop="productCategory" /> |
| | | <el-table-column label="规格型号" |
| | | prop="specificationModel" /> |
| | | <el-table-column label="厚度" |
| | | prop="thickness" |
| | | min-width="90"> |
| | | <template #default="scope"> |
| | | {{ scope.row.thickness ?? "" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="宽(mm)" |
| | | prop="width" |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | {{ scope.row.width ?? "" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="高(mm)" |
| | | prop="height" |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | {{ scope.row.height ?? "" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="周长(cm)" |
| | | prop="perimeter" |
| | | min-width="90"> |
| | | <template #default="scope"> |
| | | {{ scope.row.perimeter ?? "" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="总面积(m²)" |
| | | prop="actualTotalArea" |
| | | min-width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.actualTotalArea ?? "" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="加工要求" |
| | | prop="processRequirement" |
| | | min-width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="备注" |
| | | prop="remark" |
| | | min-width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="重箱" |
| | | prop="heavyBox" |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | {{ scope.row.heavyBox ?? "" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="产品状态" |
| | | width="100px" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="scope.row.approveStatus === 1 && (!scope.row.shippingDate || !scope.row.shippingCarNumber)" |
| | | type="success">充足</el-tag> |
| | | <el-tag v-else-if="scope.row.approveStatus === 0 && (scope.row.shippingDate || scope.row.shippingCarNumber)" |
| | | type="success">已出库</el-tag> |
| | | <el-tag v-else |
| | | type="danger">不足</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="入库状态" |
| | | width="100px" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="scope.row.productStockStatus == 1" |
| | | type="warning">部分入库</el-tag> |
| | | <el-tag v-else-if="scope.row.productStockStatus == 2" |
| | | type="success">已入库</el-tag> |
| | | <el-tag v-else-if="scope.row.productStockStatus == 0" |
| | | type="info">未入库</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="formattedNumber" /> |
| | | <el-table-column label="含税总价(元)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="不含税总价(元)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" /> |
| | | <!--操作--> |
| | | <!-- <el-table-column Width="60px" label="操作" align="center"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | :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="销售合同号" |
| | | <el-table-column label="订单号" |
| | | prop="salesContractNo" |
| | | width="180" |
| | | show-overflow-tooltip /> |
| | | min-width="160" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | @click="openForm('view', scope.row)">{{ scope.row.salesContractNo || "-" }}</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="客户名称" |
| | | prop="customerName" |
| | | width="300" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="业务员" |
| | | prop="salesman" |
| | | width="100" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="项目名称" |
| | | prop="projectName" |
| | | width="180" |
| | | min-width="80" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="合同金额(元)" |
| | | prop="contractAmount" |
| | | width="220" |
| | | min-width="120" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="面积" |
| | | prop="productTotalArea" |
| | | width="120" |
| | | min-width="80" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="数量" |
| | | prop="productTotalQuantity" |
| | | width="120" |
| | | min-width="80" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="发货状态" |
| | | width="140" |
| | | min-width="100" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="Number(scope.row.deliveryStatus) === 1" |
| | |
| | | type="primary">审批通过</el-tag> |
| | | <el-tag v-else-if="Number(scope.row.deliveryStatus) === 5" |
| | | type="success">已发货</el-tag> |
| | | <el-tag v-else-if="Number(scope.row.deliveryStatus) === 6" |
| | | type="warning">部分发货</el-tag> |
| | | <el-tag v-else |
| | | type="info">-</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="审核状态" |
| | | min-width="90" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="Number(scope.row.reviewStatus) === 0" |
| | | type="warning">待审核</el-tag> |
| | | <el-tag v-else-if="Number(scope.row.reviewStatus) === 1" |
| | | type="success">已审核</el-tag> |
| | | <el-tag v-else-if="Number(scope.row.reviewStatus) === 2" |
| | | type="danger">已反审</el-tag> |
| | | <el-tag v-else |
| | | type="info">待审核</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="入库状态" |
| | | width="120" |
| | | min-width="90" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="Number(scope.row.stockStatus) === 0" |
| | |
| | | type="success">部分入库</el-tag> |
| | | <el-tag v-else-if="Number(scope.row.stockStatus) === 2" |
| | | type="success">已入库</el-tag> |
| | | <el-tag v-else-if="Number(scope.row.stockStatus) === 3" |
| | | type="warning">审批中</el-tag> |
| | | <el-tag v-else |
| | | type="info">-</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="订单状态" |
| | | min-width="90" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="Number(scope.row.orderStatus) === 1" |
| | | type="success">已完成</el-tag> |
| | | <el-tag v-else |
| | | type="info">进行中</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="标签打印" |
| | | min-width="90" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <span>{{ scope.row.labelPrintCount ?? 0 }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="单据打印" |
| | | min-width="90" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <span>{{ scope.row.documentPrintCount ?? 0 }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="录入人" |
| | | prop="entryPersonName" |
| | | width="100" |
| | | min-width="80" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="录入日期" |
| | | prop="entryDate" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="签订日期" |
| | | prop="executionDate" |
| | | width="120" |
| | | min-width="110" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="交付日期" |
| | | prop="deliveryDate" |
| | | width="120" |
| | | min-width="110" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="备注" |
| | | prop="remarks" |
| | | width="200" |
| | | min-width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="客户备注" |
| | | prop="customerRemarks" |
| | | width="200" |
| | | min-width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | label="操作" |
| | | width="280" |
| | | width="250" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | @click="openForm('edit', scope.row)" |
| | | :disabled="!scope.row.isEdit">编辑</el-button> |
| | | :disabled="Number(scope.row.orderStatus) === 1">编辑</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="openProcessFlowSelect(scope.row)" |
| | | :disabled="!scope.row.isEdit">工艺路线</el-button> |
| | | :disabled="Number(scope.row.reviewStatus) !== 1 || Number(scope.row.orderStatus) === 1">工艺路线</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="downLoadFile(scope.row)">附件</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="openLedgerQrDialog(scope.row)">二维码</el-button> |
| | | @click="openLedgerQrDialog(scope.row)" |
| | | :disabled="Number(scope.row.orderStatus) === 1">二维码</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | </div> |
| | | <FormDialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" |
| | | </div> |
| | | <!-- 导出工艺路线 弹窗 --> |
| | | <el-dialog title="选择导出时间范围" |
| | | v-model="exportProcessRouteDialogVisible" |
| | | width="450px" |
| | | append-to-body> |
| | | <el-form> |
| | | <el-form-item label="完成时间:"> |
| | | <el-date-picker v-model="processRouteExportDateRange" |
| | | type="datetimerange" |
| | | range-separator="至" |
| | | start-placeholder="开始时间" |
| | | end-placeholder="结束时间" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | clearable |
| | | style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div style="text-align: right"> |
| | | <el-button @click="exportProcessRouteDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="confirmExportProcessRoute">确认导出</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | <FormDialog v-if="!isFormPageMode" |
| | | v-model="dialogFormVisible" |
| | | :title="isCompletedOrder ? '查看销售台账页面(已完成)' : operationType === 'add' ? '新增销售台账页面' : operationType === 'view' ? '查看销售台账页面' : '编辑销售台账页面'" |
| | | :width="'70%'" |
| | | :operation-type="operationType" |
| | | :operation-type="isCompletedOrder || operationType === 'view' ? 'detail' : operationType" |
| | | @close="closeDia" |
| | | @confirm="submitForm" |
| | | @cancel="closeDia"> |
| | |
| | | <el-select v-model="form.salesman" |
| | | placeholder="请选择" |
| | | clearable |
| | | :disabled="operationType === 'view'"> |
| | | :disabled="operationType === 'view' || isReviewedEdit"> |
| | | <el-option v-for="item in userList" |
| | | :key="item.nickName" |
| | | :label="item.nickName" |
| | |
| | | filterable |
| | | placeholder="请选择" |
| | | clearable |
| | | :disabled="operationType === 'view'"> |
| | | :disabled="operationType === 'view' || isReviewedEdit"> |
| | | <el-option v-for="item in customerOption" |
| | | :key="item.id" |
| | | :label="item.customerName" |
| | |
| | | <el-input v-model="form.projectName" |
| | | placeholder="请输入" |
| | | clearable |
| | | :disabled="operationType === 'view'" /> |
| | | :disabled="operationType === 'view' || isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | type="date" |
| | | placeholder="请选择" |
| | | clearable |
| | | :disabled="operationType === 'view'" /> |
| | | :disabled="operationType === 'view' || isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="请选择" |
| | | clearable /> |
| | | clearable |
| | | :disabled="operationType === 'view' || isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | :reserve-keyword="false" |
| | | placeholder="请选择" |
| | | clearable |
| | | @change="changs"> |
| | | @change="changs" |
| | | :disabled="operationType === 'view' || isReviewedEdit"> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="请选择" |
| | | clearable /> |
| | | clearable |
| | | :disabled="operationType === 'view' || isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | <el-form-item label="产品信息:" |
| | | prop="entryDate"> |
| | | <el-button v-if="operationType !== 'view'" |
| | | type="primary" |
| | | :disabled="hasEditingProductRow()" |
| | | @click="addProductInline"> |
| | | 添加 |
| | | </el-button> |
| | | <el-button v-if="operationType !== 'view'" |
| | | plain |
| | | type="danger" |
| | | :disabled="isReviewedEdit" |
| | | @click="deleteProduct">删除</el-button> |
| | | <el-button v-if="operationType !== 'view'" |
| | | plain |
| | | type="primary" |
| | | :disabled="isReviewedEdit" |
| | | @click="pinSelectedProductRow">固定</el-button> |
| | | </el-form-item> |
| | | </el-row> |
| | | <el-table :data="productData" |
| | | border |
| | | class="compact-product-table" |
| | | @selection-change="productSelected" |
| | | show-summary |
| | | :summary-method="summarizeMainTable"> |
| | | :summary-method="summarizeProductTable"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" |
| | |
| | | width="60" /> |
| | | <el-table-column label="产品大类" |
| | | prop="productCategory" |
| | | min-width="160"> |
| | | min-width="120"> |
| | | <template #default="scope"> |
| | | <el-tree-select v-if="scope.row.__editing" |
| | | v-model="scope.row.__productCategoryId" |
| | |
| | | :render-after-expand="false" |
| | | style="width: 100%" |
| | | :filter-node-method="filterProductCategoryNode" |
| | | @change="(val) => handleInlineProductCategoryChange(scope.row, val)" /> |
| | | @change="(val) => handleInlineProductCategoryChange(scope.row, val)" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.productCategory ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="规格型号" |
| | | prop="specificationModel" |
| | | min-width="200"> |
| | | min-width="140"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.productModelId" |
| | |
| | | clearable |
| | | filterable |
| | | style="width: 100%" |
| | | @change="(val) => handleInlineProductModelChange(scope.row, val)"> |
| | | @change="(val) => handleInlineProductModelChange(scope.row, val)" |
| | | :disabled="isReviewedEdit"> |
| | | <el-option v-for="item in modelOptions" |
| | | :key="item.id" |
| | | :label="item.model" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="厚度(mm)" |
| | | prop="thickness" |
| | | min-width="160"> |
| | | min-width="95"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | v-model="scope.row.thickness" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable /> |
| | | clearable |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.thickness ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="宽(mm)" |
| | | prop="width" |
| | | min-width="160"> |
| | | <el-table-column label="楼层编号" |
| | | prop="floorCode" |
| | | min-width="120" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | | v-model="scope.row.width" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | | style="width:100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineSizeChange(scope.row)" |
| | | @input="() => handleInlineSizeChange(scope.row)" /> |
| | | <span v-else>{{ scope.row.width ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="高(mm)" |
| | | prop="height" |
| | | min-width="160"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | | v-model="scope.row.height" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineSizeChange(scope.row)" |
| | | @input="() => handleInlineSizeChange(scope.row)" /> |
| | | <span v-else>{{ scope.row.height ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="结算单片面积(㎡)" |
| | | prop="settlePieceArea" |
| | | min-width="200"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | | v-model="scope.row.settlePieceArea" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="4" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineSettleAreaChange(scope.row)" /> |
| | | <span v-else>{{ scope.row.settlePieceArea ? Number(scope.row.settlePieceArea).toFixed(4) : "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="数量" |
| | | prop="quantity" |
| | | min-width="150"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | | v-model="scope.row.quantity" |
| | | :step="1" |
| | | :min="0" |
| | | :precision="0" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineQuantityChange(scope.row)" |
| | | @input="() => handleInlineQuantityChange(scope.row)" /> |
| | | <span v-else>{{ scope.row.quantity ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="面积(m²)" |
| | | prop="actualTotalArea" |
| | | min-width="200"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | | v-model="scope.row.actualTotalArea" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="4" |
| | | style="width: 100%" |
| | | placeholder="自动计算" /> |
| | | <span v-else>{{ scope.row.actualTotalArea ? Number(scope.row.actualTotalArea).toFixed(4) : "" }}</span> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.floorCode" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.floorCode ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="含税单价(元)" |
| | | prop="taxInclusiveUnitPrice" |
| | | min-width="160"> |
| | | min-width="105"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | :step="0.01" |
| | | :min="0" |
| | | :precision="2" |
| | |
| | | <span v-else>{{ formattedNumber(null, null, scope.row.taxInclusiveUnitPrice) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="宽(mm)" |
| | | prop="width" |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | v-model="scope.row.width" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | | style="width:100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineSizeChange(scope.row)" |
| | | @input="() => handleInlineSizeChange(scope.row)" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.width ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="高(mm)" |
| | | prop="height" |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | v-model="scope.row.height" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineSizeChange(scope.row)" |
| | | @input="() => handleInlineSizeChange(scope.row)" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.height ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="数量" |
| | | prop="quantity" |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | v-model="scope.row.quantity" |
| | | :step="1" |
| | | :min="0" |
| | | :precision="0" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineQuantityChange(scope.row)" |
| | | @input="() => handleInlineQuantityChange(scope.row)" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.quantity ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="结算单片面积(㎡)" |
| | | prop="settlePieceArea" |
| | | min-width="120"> |
| | | <template #default="scope"> |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | v-model="scope.row.settlePieceArea" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="4" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="() => handleInlineSettleAreaChange(scope.row)" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.settlePieceArea ? Number(scope.row.settlePieceArea).toFixed(4) : "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="面积(m²)" |
| | | prop="actualTotalArea" |
| | | min-width="110"> |
| | | <template #default="scope"> |
| | | <el-input-number :controls="false" v-if="scope.row.__editing" |
| | | v-model="scope.row.actualTotalArea" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="4" |
| | | style="width: 100%" |
| | | placeholder="自动计算" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.actualTotalArea ? Number(scope.row.actualTotalArea).toFixed(4) : "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="税率(%)" |
| | | prop="taxRate" |
| | | min-width="120"> |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.taxRate" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="() => handleInlineTaxRateChange(scope.row)"> |
| | | @change="() => handleInlineTaxRateChange(scope.row)" |
| | | :disabled="isReviewedEdit"> |
| | | <el-option label="1" |
| | | value="1" /> |
| | | <el-option label="3" |
| | |
| | | <el-table-column label="含税总价(元)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | min-width="120" /> |
| | | min-width="100" /> |
| | | <el-table-column label="不含税总价(元)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | min-width="120" /> |
| | | min-width="100" /> |
| | | <el-table-column label="加工要求" |
| | | prop="processRequirement" |
| | | min-width="100" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.processRequirement" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.processRequirement ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="发票类型" |
| | | prop="invoiceType" |
| | | min-width="120"> |
| | | min-width="90"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.invoiceType" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 100%"> |
| | | style="width: 100%" |
| | | :disabled="isReviewedEdit"> |
| | | <el-option label="增普票" |
| | | value="增普票" /> |
| | | <el-option label="增专票" |
| | |
| | | <span v-else>{{ scope.row.invoiceType ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="加工要求" |
| | | prop="processRequirement" |
| | | min-width="160" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.processRequirement" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" /> |
| | | <span v-else>{{ scope.row.processRequirement ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="备注" |
| | | prop="remark" |
| | | min-width="140" |
| | | min-width="100" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.remark" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" /> |
| | | style="width: 100%" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.remark ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="楼层编号" |
| | | prop="floorCode" |
| | | min-width="250" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.floorCode" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" /> |
| | | <span v-else>{{ scope.row.floorCode ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="重箱" |
| | | prop="heavyBox" |
| | | min-width="100"> |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.heavyBox" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" /> |
| | | style="width: 100%" |
| | | :disabled="isReviewedEdit" /> |
| | | <span v-else>{{ scope.row.heavyBox ?? "" }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" |
| | | label="操作" |
| | | min-width="220" |
| | | min-width="150" |
| | | align="center" |
| | | v-if="operationType !== 'view'"> |
| | | <template #default="scope"> |
| | |
| | | style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;"> |
| | | {{ item.processName }} |
| | | </el-tag> |
| | | <el-input-number v-model="item.quantity" |
| | | <el-input-number :controls="false" v-model="item.quantity" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="0" |
| | |
| | | </el-popover> |
| | | </template> |
| | | <template v-else> |
| | | <el-button link |
| | | <!-- 已审核模式:只能修改单价 --> |
| | | <el-button v-if="isReviewedEdit" |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | @click="editProductInline(scope.row, scope.$index)"> |
| | | 编辑 |
| | | @click="editPriceOnly(scope.row)"> |
| | | 修改单价 |
| | | </el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row) || hasEditingProductRow()" |
| | | @click="copyProductInline(scope.row, scope.$index)"> |
| | | 复制新建 |
| | | </el-button> |
| | | <!-- 未审核模式:完整编辑 --> |
| | | <template v-if="!isReviewedEdit"> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | @click="editProductInline(scope.row, scope.$index)"> |
| | | 编辑 |
| | | </el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | @click="copyProductInline(scope.row, scope.$index)"> |
| | | 复制 |
| | | </el-button> |
| | | </template> |
| | | <el-popover :width="560" |
| | | trigger="click" |
| | | :hide-after="0" |
| | |
| | | <el-button type="primary" |
| | | plain |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit" |
| | | @click="startAddOtherAmountForRow(scope.row)"> |
| | | 新增 |
| | | </el-button> |
| | |
| | | clearable |
| | | placeholder="请选择额外加工项目" |
| | | style="width: 100%;" |
| | | :disabled="isProductShipped(scope.row)"> |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit"> |
| | | <el-option v-for="item in otherAmountSelectOptions" |
| | | :key="item.id" |
| | | :label="item.processName" |
| | |
| | | </el-select> |
| | | <div style="display:flex; justify-content:flex-end; gap: 8px;"> |
| | | <el-button size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit" |
| | | @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null"> |
| | | 取消 |
| | | </el-button> |
| | | <el-button type="primary" |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row) || scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''" |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit || scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''" |
| | | @click="confirmAddOtherAmountForRow(scope.row)"> |
| | | 确认添加 |
| | | </el-button> |
| | |
| | | style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;"> |
| | | {{ item.processName }} |
| | | </el-tag> |
| | | <el-input-number v-model="item.quantity" |
| | | <el-input-number :controls="false" v-model="item.quantity" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="0" |
| | | style="width: 120px;" |
| | | placeholder="数量" |
| | | :disabled="operationType === 'view' || isProductShipped(scope.row)" |
| | | :disabled="operationType === 'view' || isProductShipped(scope.row) || isReviewedEdit" |
| | | @change="handleOtherAmountQuantityChange(scope.row)" /> |
| | | <el-button type="danger" |
| | | link |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | :disabled="isProductShipped(scope.row) || isReviewedEdit" |
| | | @click="removeOtherAmountAtForRow(scope.row, idx)"> |
| | | 删除 |
| | | </el-button> |
| | |
| | | clearable |
| | | type="textarea" |
| | | :rows="2" |
| | | :disabled="operationType === 'view'" /> |
| | | :disabled="operationType === 'view' || isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | clearable |
| | | type="textarea" |
| | | :rows="2" |
| | | :disabled="operationType === 'view'" /> |
| | | :disabled="operationType === 'view' || isReviewedEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | :on-success="handleUploadSuccess" |
| | | :on-remove="handleRemove"> |
| | | <el-button type="primary" |
| | | v-if="operationType !== 'view'">上传</el-button> |
| | | v-if="operationType !== 'view' || isCompletedOrder">上传</el-button> |
| | | <template #tip |
| | | v-if="operationType !== 'view'"> |
| | | v-if="operationType !== 'view' || isCompletedOrder"> |
| | | <div class="el-upload__tip"> |
| | | 文件格式支持 |
| | | doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="厚度:" |
| | | prop="thickness"> |
| | | <el-input-number v-model="productForm.thickness" |
| | | <el-input-number :controls="false" v-model="productForm.thickness" |
| | | :min="0" |
| | | :step="0.000000000000001" |
| | | :precision="15" |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="含税单价(元):" |
| | | prop="taxInclusiveUnitPrice"> |
| | | <el-input-number :step="0.01" |
| | | <el-input-number :controls="false" :step="0.01" |
| | | :min="0" |
| | | v-model="productForm.taxInclusiveUnitPrice" |
| | | style="width: 100%" |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="数量:" |
| | | prop="quantity"> |
| | | <el-input-number :step="0.1" |
| | | <el-input-number :controls="false" :step="0.1" |
| | | :min="0" |
| | | v-model="productForm.quantity" |
| | | placeholder="请输入" |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="宽(mm):" |
| | | prop="width"> |
| | | <el-input-number v-model="productForm.width" |
| | | <el-input-number :controls="false" v-model="productForm.width" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="高(mm):" |
| | | prop="height"> |
| | | <el-input-number v-model="productForm.height" |
| | | <el-input-number :controls="false" v-model="productForm.height" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="周长(cm):" |
| | | prop="perimeter"> |
| | | <el-input-number v-model="productForm.perimeter" |
| | | <el-input-number :controls="false" v-model="productForm.perimeter" |
| | | :min="0" |
| | | :step="0.01" |
| | | :precision="2" |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="实际单片面积(㎡):" |
| | | prop="actualPieceArea"> |
| | | <el-input-number v-model="productForm.actualPieceArea" |
| | | <el-input-number :controls="false" v-model="productForm.actualPieceArea" |
| | | :min="0" |
| | | :step="0.0001" |
| | | :precision="4" |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="实际总面积(㎡):" |
| | | prop="actualTotalArea"> |
| | | <el-input-number v-model="productForm.actualTotalArea" |
| | | <el-input-number :controls="false" v-model="productForm.actualTotalArea" |
| | | :min="0" |
| | | :step="0.0001" |
| | | :precision="4" |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="结算单片面积(㎡):" |
| | | prop="settlePieceArea"> |
| | | <el-input-number v-model="productForm.settlePieceArea" |
| | | <el-input-number :controls="false" v-model="productForm.settlePieceArea" |
| | | :min="0" |
| | | :step="0.0001" |
| | | :precision="4" |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="结算总面积(㎡):" |
| | | prop="settleTotalArea"> |
| | | <el-input-number v-model="productForm.settleTotalArea" |
| | | <el-input-number :controls="false" v-model="productForm.settleTotalArea" |
| | | :min="0" |
| | | :step="0.0001" |
| | | :precision="4" |
| | |
| | | <el-col :span="8"> |
| | | <el-form-item label="结算总面积(㎡):" |
| | | prop="settleTotalArea"> |
| | | <el-input-number v-model="productForm.settleTotalArea" |
| | | <el-input-number :controls="false" v-model="productForm.settleTotalArea" |
| | | :min="0" |
| | | :step="0.0001" |
| | | :precision="4" |
| | |
| | | </el-tag> |
| | | </div> |
| | | <div style="flex: 1;"> |
| | | <el-input-number v-model="item.quantity" |
| | | <el-input-number :controls="false" v-model="item.quantity" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="0" |
| | |
| | | </el-button> |
| | | </div> |
| | | </el-dialog> |
| | | <!-- 反审核弹窗 --> |
| | | <el-dialog v-model="reverseAuditDialogVisible" |
| | | title="反审核确认" |
| | | width="500px" |
| | | :close-on-click-modal="false" |
| | | class="reverse-audit-dialog"> |
| | | <el-form label-width="110px" |
| | | label-position="left"> |
| | | <el-form-item label="反审核类型"> |
| | | <el-radio-group v-model="reverseAuditForm.counterReviewType"> |
| | | <el-radio :label="1"> |
| | | <span>作废订单</span> |
| | | <span style="color: #909399; font-size: 12px; margin-left: 4px;">(不生成新订单)</span> |
| | | </el-radio> |
| | | <el-radio :label="2"> |
| | | <span>重新生成</span> |
| | | <span style="color: #909399; font-size: 12px; margin-left: 4px;">(跳转到新增页面编辑)</span> |
| | | </el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="反审核原因"> |
| | | <el-input v-model="reverseAuditForm.counterReviewDesc" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请输入反审核原因" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="reverse-audit-warning"> |
| | | <el-icon style="color: #E6A23C; margin-right: 4px;"><WarningFilled /></el-icon> |
| | | 注意:反审核将自动作废该订单对应的所有入库、出库、发货单据 |
| | | </div> |
| | | <template #footer> |
| | | <el-button @click="reverseAuditDialogVisible = false">取消</el-button> |
| | | <el-button type="danger" |
| | | @click="confirmReverseAudit" |
| | | :disabled="!reverseAuditForm.counterReviewType || !reverseAuditForm.counterReviewDesc.trim()">确认反审核</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { getToken } from "@/utils/auth"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { onMounted, ref, getCurrentInstance, watch, nextTick } from "vue"; |
| | | import { onMounted, ref, reactive, getCurrentInstance, watch, nextTick, computed } from "vue"; |
| | | import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | | import { ArrowDown } from "@element-plus/icons-vue"; |
| | | import { ArrowDown, WarningFilled } from "@element-plus/icons-vue"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import useAppStore from "@/store/modules/app"; |
| | | import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import FileListDialog from "@/components/Dialog/FileListDialog.vue"; |
| | |
| | | import OtherAmountMaintenanceButton from "./components/OtherAmountMaintenanceButton.vue"; |
| | | import ProcessFlowMaintenanceButton from "./components/ProcessFlowMaintenanceButton.vue"; |
| | | import ProcessFlowConfigSelectDialog from "./components/ProcessFlowConfigSelectDialog.vue"; |
| | | import ReverseAuditHistory from "../reverseAuditHistory/index.vue"; |
| | | import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; |
| | | import { |
| | | ledgerListPage, |
| | |
| | | getSalesInvoices, |
| | | getSalesLabel, |
| | | salesStock, |
| | | counterReview, |
| | | markOrderCompleted, |
| | | incrementPrintCount, |
| | | } from "@/api/salesManagement/salesLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | |
| | | import { printSalesDeliveryNote } from "./components/salesDeliveryPrint.js"; |
| | | import { printSalesLabel } from "./components/salesLabelPrint.js"; |
| | | import QRCode from "qrcode"; |
| | | import { useRoute, useRouter } from "vue-router"; |
| | | // import { salesLedgerProductSetProcessFlowConfig } from "@/api/salesManagement/salesProcessFlowConfig.js"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const route = useRoute(); |
| | | const router = useRouter(); |
| | | const tableData = ref([]); |
| | | const productData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const productSelectedRows = ref([]); |
| | | const pinnedProductTemplate = ref(null); |
| | | const userList = ref([]); |
| | | const userListApprove = ref([]); |
| | | const customerOption = ref([]); |
| | |
| | | }); |
| | | const total = ref(0); |
| | | const fileList = ref([]); |
| | | const processRouteExportDateRange = ref([]); |
| | | |
| | | // 工艺路线配置选择弹窗(绑定到台账产品) |
| | | const processFlowSelectDialogVisible = ref(false); |
| | | const processFlowSelectLedgerRow = ref(null); |
| | | const processFlowSelectDefaultRouteId = ref(null); |
| | | const processFlowSelectDefaultRecordList = ref([]); |
| | | const processFlowSelectBoundRouteId = ref(null); |
| | | const processFlowSelectBoundRouteName = ref(""); |
| | | |
| | |
| | | const ledgerQrDialogVisible = ref(false); |
| | | const ledgerQrCompositeUrl = ref(""); |
| | | const ledgerQrDownloadBaseName = ref(""); |
| | | const pendingAddPrefillDetail = ref(null); |
| | | const pageMode = computed(() => String(route.query.mode || "")); |
| | | const isFormPageMode = computed(() => |
| | | pageMode.value === "add" || pageMode.value === "edit" |
| | | ); |
| | | const pageFormTitle = computed(() => |
| | | pageMode.value === "edit" ? "编辑销售台账" : "新增销售台账" |
| | | ); |
| | | const pageFormSubtitle = computed(() => |
| | | pageMode.value === "edit" |
| | | ? "编辑完成后将自动返回销售台账列表" |
| | | : "新增完成后将自动返回销售台账列表" |
| | | ); |
| | | |
| | | const appStore = useAppStore(); |
| | | watch(isFormPageMode, (val) => { |
| | | if (val) { |
| | | appStore.closeSideBar({ withoutAnimation: false }); |
| | | } else { |
| | | if (!appStore.sidebar.opened) { |
| | | appStore.toggleSideBar(false); |
| | | } |
| | | } |
| | | }); |
| | | onMounted(() => { |
| | | if (isFormPageMode.value) { |
| | | appStore.closeSideBar({ withoutAnimation: false }); |
| | | } |
| | | }); |
| | | import { onUnmounted } from "vue"; |
| | | onUnmounted(() => { |
| | | if (!appStore.sidebar.opened) { |
| | | appStore.toggleSideBar(false); |
| | | } |
| | | }); |
| | | |
| | | const sanitizeLedgerQrFilename = s => |
| | | String(s) |
| | |
| | | const ctx = canvas.getContext("2d"); |
| | | canvas.width = Math.max(QR_SIZE + horizontalPad * 2, 280); |
| | | ctx.font = `${fontSize}px "Microsoft YaHei", "PingFang SC", sans-serif`; |
| | | const lines = wrapLedgerQrTextLines(ctx, label, canvas.width - horizontalPad * 2); |
| | | const lines = wrapLedgerQrTextLines( |
| | | ctx, |
| | | label, |
| | | canvas.width - horizontalPad * 2 |
| | | ); |
| | | const textBlockHeight = lines.length * lineHeight; |
| | | canvas.height = padTop + QR_SIZE + gapAfterQr + textBlockHeight + bottomPad; |
| | | canvas.height = |
| | | padTop + QR_SIZE + gapAfterQr + textBlockHeight + bottomPad; |
| | | |
| | | ctx.fillStyle = "#ffffff"; |
| | | ctx.fillRect(0, 0, canvas.width, canvas.height); |
| | |
| | | // 用户信息表单弹框数据 |
| | | const operationType = ref(""); |
| | | const dialogFormVisible = ref(false); |
| | | // 已完成订单标记:view 模式但附件上传仍可用 |
| | | const isCompletedOrder = ref(false); |
| | | // 已审核订单编辑模式:只能修改单价,其他字段禁用 |
| | | const isReviewedEdit = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | customerName: "", // 客户名称 |
| | |
| | | entryDate: null, // 录入日期 |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | deliveryStatus: undefined, // 发货状态:1未发货 2审批中 3审批失败 4已发货 |
| | | deliveryStatus: undefined, // 发货状态:1未发货 2审批中 3审批不通过 4审批通过 5已发货 6部分发货 |
| | | stockStatus: undefined, // 入库状态:0未入库 1部分入库 2已入库 |
| | | reviewStatus: undefined, // 审核状态:0待审核 1已审核 2已反审 |
| | | orderStatus: undefined, // 订单状态:0进行中 1已完成 |
| | | }, |
| | | form: { |
| | | salesContractNo: "", |
| | |
| | | maintenanceTime: "", |
| | | productData: [], |
| | | executionDate: "", |
| | | reviewStatus: undefined, |
| | | stockStatus: undefined, |
| | | }, |
| | | rules: { |
| | | salesman: [{ required: true, message: "请选择", trigger: "change" }], |
| | |
| | | return (productData.value || []).some(r => r && r.__editing); |
| | | }; |
| | | |
| | | const buildEmptyInlineProductRow = () => ({ |
| | | const isPlaceholderRowDirty = row => { |
| | | if (!row || !row.__placeholder) return false; |
| | | const fieldsToCheck = [ |
| | | "width", |
| | | "height", |
| | | "quantity", |
| | | "settlePieceArea", |
| | | "taxInclusiveUnitPrice", |
| | | "taxRate", |
| | | "invoiceType", |
| | | "floorCode", |
| | | "processRequirement", |
| | | "remark", |
| | | "heavyBox", |
| | | ]; |
| | | return fieldsToCheck.some(key => { |
| | | const value = row[key]; |
| | | return value !== null && value !== undefined && value !== ""; |
| | | }); |
| | | }; |
| | | |
| | | const getPlaceholderRowIndex = () => |
| | | (productData.value || []).findIndex(r => r && r.__placeholder); |
| | | |
| | | const discardPlaceholderRowIfPristine = () => { |
| | | const index = getPlaceholderRowIndex(); |
| | | if (index === -1) return true; |
| | | const row = productData.value[index]; |
| | | if (isPlaceholderRowDirty(row)) { |
| | | proxy.$modal.msgWarning("请先保存或取消当前待录入行"); |
| | | return false; |
| | | } |
| | | productData.value.splice(index, 1); |
| | | if (editingProductRow.value === row) { |
| | | editingProductRow.value = null; |
| | | } |
| | | return true; |
| | | }; |
| | | |
| | | const buildEmptyInlineProductRow = (prefill = {}) => ({ |
| | | id: null, |
| | | __tempKey: `__temp_${Date.now()}_${Math.random().toString(16).slice(2)}`, |
| | | __editing: true, |
| | | __isNew: true, |
| | | __productCategoryId: null, |
| | | productCategory: "", |
| | | productModelId: null, |
| | | specificationModel: "", |
| | | thickness: null, |
| | | __placeholder: true, |
| | | __productCategoryId: prefill.__productCategoryId ?? null, |
| | | productCategory: prefill.productCategory ?? "", |
| | | productModelId: prefill.productModelId ?? null, |
| | | specificationModel: prefill.specificationModel ?? "", |
| | | thickness: prefill.thickness ?? null, |
| | | quantity: null, |
| | | taxInclusiveUnitPrice: null, |
| | | taxRate: "", |
| | |
| | | heavyBox: "", |
| | | }); |
| | | |
| | | const getPinnedProductPrefill = async () => { |
| | | await getProductOptions(); |
| | | if (!pinnedProductTemplate.value) { |
| | | const models = await modelList({}); |
| | | modelOptions.value = models || []; |
| | | return {}; |
| | | } |
| | | if (pinnedProductTemplate.value.__productCategoryId) { |
| | | const models = await modelList({ |
| | | id: pinnedProductTemplate.value.__productCategoryId, |
| | | }); |
| | | modelOptions.value = models || []; |
| | | } else { |
| | | const models = await modelList({}); |
| | | modelOptions.value = models || []; |
| | | } |
| | | return { ...pinnedProductTemplate.value }; |
| | | }; |
| | | |
| | | const appendEditablePlaceholderRow = async () => { |
| | | if (operationType.value === "view" || isReviewedEdit.value) return; |
| | | const existingPlaceholder = (productData.value || []).find( |
| | | r => r && r.__placeholder |
| | | ); |
| | | if (existingPlaceholder) { |
| | | if (!existingPlaceholder.__editing) existingPlaceholder.__editing = true; |
| | | editingProductRow.value = existingPlaceholder; |
| | | productForm.value = existingPlaceholder; |
| | | return; |
| | | } |
| | | if (hasEditingProductRow()) return; |
| | | await getProductOptions(); |
| | | await fetchOtherAmountSelectOptions(true); |
| | | const prefill = await getPinnedProductPrefill(); |
| | | const row = buildEmptyInlineProductRow(prefill); |
| | | productData.value.push(row); |
| | | editingProductRow.value = row; |
| | | productForm.value = row; |
| | | }; |
| | | |
| | | const pinSelectedProductRow = async () => { |
| | | if (operationType.value === "view" || isReviewedEdit.value) return; |
| | | if (!productSelectedRows.value || productSelectedRows.value.length !== 1) { |
| | | proxy.$modal.msgWarning("请选择一条产品数据进行固定"); |
| | | return; |
| | | } |
| | | const selectedRow = productSelectedRows.value[0]; |
| | | const categoryId = |
| | | selectedRow.__productCategoryId ?? |
| | | findNodeIdByLabel(productOptions.value, selectedRow.productCategory) ?? |
| | | null; |
| | | if (!selectedRow.productCategory || !selectedRow.specificationModel) { |
| | | proxy.$modal.msgWarning("请先选择完整的产品大类和规格型号再固定"); |
| | | return; |
| | | } |
| | | if (!categoryId || !selectedRow.productModelId) { |
| | | proxy.$modal.msgWarning("请先保存当前产品后再固定"); |
| | | return; |
| | | } |
| | | pinnedProductTemplate.value = { |
| | | __productCategoryId: categoryId, |
| | | productCategory: selectedRow.productCategory ?? "", |
| | | productModelId: selectedRow.productModelId ?? null, |
| | | specificationModel: selectedRow.specificationModel ?? "", |
| | | thickness: |
| | | selectedRow.thickness !== null && |
| | | selectedRow.thickness !== undefined && |
| | | selectedRow.thickness !== "" |
| | | ? Number(selectedRow.thickness) |
| | | : null, |
| | | }; |
| | | proxy.$modal.msgSuccess("固定成功,后续会自动带出该产品和规格型号"); |
| | | const editingRow = (productData.value || []).find(r => r?.__editing); |
| | | if ( |
| | | editingRow && |
| | | !editingRow.productCategory && |
| | | !editingRow.productModelId && |
| | | !editingRow.specificationModel |
| | | ) { |
| | | Object.assign(editingRow, await getPinnedProductPrefill()); |
| | | } |
| | | }; |
| | | |
| | | const addProductInline = async () => { |
| | | if (operationType.value === "view") return; |
| | | if (hasEditingProductRow()) { |
| | | proxy.$modal.msgWarning("请先保存或取消当前编辑行"); |
| | | return; |
| | | } |
| | | await getProductOptions(); |
| | | await fetchOtherAmountSelectOptions(true); |
| | | const row = buildEmptyInlineProductRow(); |
| | | productData.value.push(row); |
| | | editingProductRow.value = row; |
| | | // 让现有的计算/其他金额逻辑复用当前行 |
| | | productForm.value = row; |
| | | await appendEditablePlaceholderRow(); |
| | | }; |
| | | |
| | | const copyProductInline = async row => { |
| | |
| | | proxy.$modal.msgWarning("已发货或审核通过的产品不能复制"); |
| | | return; |
| | | } |
| | | if (hasEditingProductRow()) { |
| | | const hasBlockingEditingRow = (productData.value || []).some( |
| | | item => item && item.__editing && !item.__placeholder |
| | | ); |
| | | if (hasBlockingEditingRow) { |
| | | proxy.$modal.msgWarning("请先保存或取消当前编辑行"); |
| | | return; |
| | | } |
| | | if (!discardPlaceholderRowIfPristine()) return; |
| | | await getProductOptions(); |
| | | await fetchOtherAmountSelectOptions(true); |
| | | |
| | | const copied = buildEmptyInlineProductRow(); |
| | | copied.__placeholder = false; |
| | | copied.__productCategoryId = |
| | | row.__productCategoryId ?? |
| | | findNodeIdByLabel(productOptions.value, row.productCategory) ?? |
| | |
| | | copied.productModelId = row.productModelId ?? null; |
| | | copied.specificationModel = row.specificationModel ?? ""; |
| | | copied.thickness = |
| | | row.thickness !== null && row.thickness !== undefined && row.thickness !== "" |
| | | row.thickness !== null && |
| | | row.thickness !== undefined && |
| | | row.thickness !== "" |
| | | ? Number(row.thickness) |
| | | : null; |
| | | copied.floorCode = row?.floorCode ?? row?.floor_code ?? ""; |
| | | |
| | | // 复制新建仅带出产品大类与规格型号,其他数字字段全部留空,避免出现 0.00 |
| | | const srcUnit = row?.taxInclusiveUnitPrice; |
| | | const unitNum = |
| | | srcUnit !== null && srcUnit !== undefined && srcUnit !== "" |
| | | ? Number(srcUnit) |
| | | : NaN; |
| | | copied.taxInclusiveUnitPrice = Number.isFinite(unitNum) ? unitNum : null; |
| | | |
| | | // 复制新建带出:产品大类、规格型号、厚度、楼层编号、单价;其余数量/面积/总价等留空,避免出现 0.00 |
| | | copied.quantity = null; |
| | | copied.taxInclusiveUnitPrice = null; |
| | | copied.taxInclusiveTotalPrice = null; |
| | | copied.taxExclusiveTotalPrice = null; |
| | | copied.width = null; |
| | |
| | | productForm.value = copied; |
| | | }; |
| | | |
| | | /** 已审核订单:仅修改含税单价 */ |
| | | const editPriceOnly = (row) => { |
| | | if (operationType.value === "view") return; |
| | | if (!row) return; |
| | | stopOtherEditingRows(); |
| | | row.__editing = true; |
| | | row.__priceOnly = true; // 标记只修改单价 |
| | | }; |
| | | |
| | | const editProductInline = async (row, index) => { |
| | | if (operationType.value === "view") return; |
| | | if (!row) return; |
| | |
| | | proxy.$modal.msgWarning("已发货或审核通过的产品不能编辑"); |
| | | return; |
| | | } |
| | | if (!discardPlaceholderRowIfPristine()) return; |
| | | stopOtherEditingRows(); |
| | | await getProductOptions(); |
| | | await fetchOtherAmountSelectOptions(true); |
| | |
| | | proxy.$modal.msgWarning("面积必须大于0"); |
| | | return false; |
| | | } |
| | | if (row.taxInclusiveUnitPrice <= 0) { |
| | | proxy.$modal.msgWarning("含税单价必须大于0"); |
| | | return false; |
| | | } |
| | | if (!row.productModelId) { |
| | | proxy.$modal.msgWarning("请选择规格型号"); |
| | | return false; |
| | |
| | | if (model?.model) row.specificationModel = model.model; |
| | | |
| | | if (operationType.value === "edit") { |
| | | row.__placeholder = false; |
| | | // 台账已存在:走原接口保存到后端,再回拉刷新 |
| | | const payload = { ...row, salesLedgerId: currentId.value, type: 1 }; |
| | | delete payload.__backup; |
| | | delete payload.__editing; |
| | | delete payload.__isNew; |
| | | delete payload.__placeholder; |
| | | delete payload.__productCategoryId; |
| | | delete payload.__tempKey; |
| | | await addOrUpdateSalesLedgerProduct(payload); |
| | |
| | | productData.value = res.productData; |
| | | } |
| | | ); |
| | | stopOtherEditingRows(); |
| | | await appendEditablePlaceholderRow(); |
| | | } else { |
| | | // 新增台账:仅在本地 productData 生效,最终随台账一起提交 |
| | | row.__isNew = false; |
| | | row.__placeholder = false; |
| | | row.__editing = false; |
| | | delete row.__backup; |
| | | stopOtherEditingRows(); |
| | | await appendEditablePlaceholderRow(); |
| | | } |
| | | |
| | | stopOtherEditingRows(); |
| | | }; |
| | | |
| | | const cancelProductInline = (row, index) => { |
| | | const cancelProductInline = async (row, index) => { |
| | | if (!row) return; |
| | | if (row.__isNew) { |
| | | if (row.__placeholder) { |
| | | productData.value.splice(index, 1); |
| | | } else if (row.__backup) { |
| | | const restored = JSON.parse(JSON.stringify(row.__backup)); |
| | |
| | | Object.assign(row, restored); |
| | | row.id = keepId; |
| | | row.__editing = false; |
| | | row.__placeholder = false; |
| | | delete row.__backup; |
| | | } |
| | | stopOtherEditingRows(); |
| | | await appendEditablePlaceholderRow(); |
| | | }; |
| | | |
| | | const openOtherAmountInline = async row => { |
| | |
| | | notShipped: "/sales/ledger/salesHistory/notShippingImport", |
| | | shipped: "/sales/ledger/salesHistory/shippingImport", |
| | | }; |
| | | const HISTORY_IMPORT_TEMPLATE_URL_MAP = { |
| | | notShipped: "/sales/ledger/salesHistory/notShippingImportTemplate", |
| | | shipped: "/sales/ledger/salesHistory/shippingImportTemplate", |
| | | }; |
| | | const HISTORY_IMPORT_TEMPLATE_FILE_NAME_MAP = { |
| | | notShipped: "销售发货历史数据导入模板-未发货.xlsx", |
| | | shipped: "销售发货历史数据导入模板-已发货.xlsx", |
| | | }; |
| | | const currentImportCommand = ref("default"); |
| | | const activeStatusTab = ref("all"); |
| | | const salesLedgerStatusTabs = [ |
| | | { key: "all", label: "全部" }, |
| | | { key: "pendingReview", label: "未审核" }, |
| | | { key: "reviewed", label: "已审核" }, |
| | | { key: "reverseReviewed", label: "反审核" }, |
| | | { key: "stocked", label: "已入库" }, |
| | | { key: "delivered", label: "已发货" }, |
| | | { key: "completed", label: "已完成" }, |
| | | ]; |
| | | |
| | | const resetStatusFilters = () => { |
| | | searchForm.reviewStatus = undefined; |
| | | searchForm.stockStatus = undefined; |
| | | searchForm.deliveryStatus = undefined; |
| | | searchForm.orderStatus = undefined; |
| | | }; |
| | | |
| | | const handleStatusTabChange = tabKey => { |
| | | activeStatusTab.value = tabKey; |
| | | resetStatusFilters(); |
| | | switch (tabKey) { |
| | | case "all": |
| | | break; |
| | | case "pendingReview": |
| | | searchForm.reviewStatus = 0; |
| | | break; |
| | | case "reviewed": |
| | | searchForm.reviewStatus = 1; |
| | | break; |
| | | case "reverseReviewed": |
| | | searchForm.reviewStatus = 2; |
| | | break; |
| | | case "stocked": |
| | | searchForm.stockStatus = 2; |
| | | break; |
| | | case "delivered": |
| | | searchForm.deliveryStatus = 5; |
| | | break; |
| | | case "completed": |
| | | searchForm.orderStatus = 1; |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | const changeDaterange = value => { |
| | | if (value) { |
| | | searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | |
| | | searchForm.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | |
| | | /** 批量按钮禁用判断:根据选中行的审核状态和订单状态控制按钮可用性 |
| | | * 未审核(0):只能审核、删除、编辑 |
| | | * 已审核(1):可以反审、入库、发货、导出、打印、工艺路线 |
| | | * 已完成(orderStatus=1):所有操作按钮禁用 |
| | | * 未选中任何行时所有批量按钮禁用 |
| | | */ |
| | | const isBatchButtonDisabled = (action) => { |
| | | if (selectedRows.value.length === 0) return true; |
| | | const hasCompleted = selectedRows.value.some(r => Number(r.orderStatus) === 1); |
| | | // 已完成订单:所有操作按钮禁用 |
| | | if (hasCompleted && action !== 'markCompleted') return true; |
| | | const statuses = selectedRows.value.map(r => Number(r.reviewStatus)); |
| | | const allUnreviewed = statuses.every(s => s === 0); |
| | | const allReviewed = statuses.every(s => s === 1); |
| | | switch (action) { |
| | | case 'audit': |
| | | return !allUnreviewed; |
| | | case 'reverseAudit': |
| | | return !allReviewed; |
| | | case 'stock': |
| | | return !allReviewed; |
| | | case 'delivery': |
| | | return !allReviewed; |
| | | case 'export': |
| | | return !allReviewed; |
| | | case 'delete': |
| | | return !allUnreviewed; |
| | | case 'print': |
| | | return !allReviewed; |
| | | case 'markCompleted': |
| | | // 只有已审核且未完成的订单才能标记完成 |
| | | return !allReviewed || hasCompleted; |
| | | default: |
| | | return false; |
| | | } |
| | | }; |
| | | |
| | | // 查询列表 |
| | |
| | | expandedRowKeys.value = []; |
| | | getList(); |
| | | }; |
| | | |
| | | /** 审核按钮操作 */ |
| | | const handleAudit = async () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("请选择要审核的数据"); |
| | | return; |
| | | } |
| | | const canNotAudit = selectedRows.value.filter( |
| | | row => Number(row.reviewStatus) !== 0 |
| | | ); |
| | | if (canNotAudit.length > 0) { |
| | | proxy.$modal.msgWarning("选中的数据中包含非待审核项,请重新选择"); |
| | | return; |
| | | } |
| | | |
| | | // 录入人不能审核该条数据 |
| | | const isEntryPerson = selectedRows.value.some( |
| | | row => String(row.entryPerson) === String(userStore.id) |
| | | ); |
| | | if (isEntryPerson) { |
| | | proxy.$modal.msgWarning("录入人不能审核自己提交的数据"); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | await ElMessageBox.confirm("是否确认审核选中的销售台账?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }); |
| | | for (const row of selectedRows.value) { |
| | | await addOrUpdateSalesLedger({ ...row, reviewStatus: 1 }); |
| | | } |
| | | proxy.$modal.msgSuccess("审核成功"); |
| | | getList(); |
| | | } catch (error) { |
| | | console.log(error); |
| | | } |
| | | }; |
| | | |
| | | /** 反审核弹窗状态 */ |
| | | const reverseAuditDialogVisible = ref(false); |
| | | const reverseAuditForm = reactive({ |
| | | counterReviewType: null, |
| | | counterReviewDesc: "", |
| | | }); |
| | | |
| | | /** 反审按钮操作 — 打开弹窗 */ |
| | | const handleReverseAudit = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("请选择要反审核的数据"); |
| | | return; |
| | | } |
| | | const canNotReverse = selectedRows.value.filter( |
| | | row => Number(row.reviewStatus) !== 1 |
| | | ); |
| | | if (canNotReverse.length > 0) { |
| | | proxy.$modal.msgWarning("选中的数据中包含非已审核项,请重新选择"); |
| | | return; |
| | | } |
| | | // 重置弹窗表单 |
| | | reverseAuditForm.counterReviewType = null; |
| | | reverseAuditForm.counterReviewDesc = ""; |
| | | reverseAuditDialogVisible.value = true; |
| | | }; |
| | | |
| | | /** 反审核确认操作 */ |
| | | const confirmReverseAudit = async () => { |
| | | if (!reverseAuditForm.counterReviewType) { |
| | | proxy.$modal.msgWarning("请选择反审核类型"); |
| | | return; |
| | | } |
| | | if (!reverseAuditForm.counterReviewDesc.trim()) { |
| | | proxy.$modal.msgWarning("请输入反审核原因"); |
| | | return; |
| | | } |
| | | try { |
| | | const ids = selectedRows.value.map(row => row.id); |
| | | const res = await counterReview({ |
| | | ids, |
| | | counterReviewType: reverseAuditForm.counterReviewType, |
| | | counterReviewDesc: reverseAuditForm.counterReviewDesc, |
| | | }); |
| | | reverseAuditDialogVisible.value = false; |
| | | proxy.$modal.msgSuccess("反审核成功"); |
| | | |
| | | // 重新生成类型:跳转到新增台账页面(预填数据) |
| | | if (reverseAuditForm.counterReviewType === 2 && res.newLedgerIds && res.newLedgerIds.length > 0) { |
| | | // 单条反审时跳转到新增页面编辑新台账 |
| | | if (res.newLedgerIds.length === 1) { |
| | | const newId = res.newLedgerIds[0]; |
| | | getSalesLedgerWithProducts({ id: newId, type: 1 }).then(detail => { |
| | | enterAddPage(detail); |
| | | }); |
| | | } |
| | | } |
| | | getList(); |
| | | } catch (error) { |
| | | console.log(error); |
| | | } |
| | | }; |
| | | |
| | | /** 标记完成操作 */ |
| | | const handleMarkCompleted = async () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("请选择要标记完成的数据"); |
| | | return; |
| | | } |
| | | const cannotComplete = selectedRows.value.filter( |
| | | row => Number(row.reviewStatus) !== 1 || Number(row.orderStatus) === 1 |
| | | ); |
| | | if (cannotComplete.length > 0) { |
| | | proxy.$modal.msgWarning("选中的数据中包含非已审核或已完成的项,请重新选择"); |
| | | return; |
| | | } |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | "标记完成后订单将变为只读,只能查看和上传下载附件,不可撤销。是否确认?", |
| | | "提示", |
| | | { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } |
| | | ); |
| | | const ids = selectedRows.value.map(row => row.id); |
| | | await markOrderCompleted({ ids }); |
| | | proxy.$modal.msgSuccess("标记完成成功"); |
| | | getList(); |
| | | } catch { |
| | | // 用户取消 |
| | | } |
| | | }; |
| | | |
| | | /** 用预填数据打开新增台账页面(重新生成场景) */ |
| | | const openFormWithPreFill = async (detail, keepPageMode = false) => { |
| | | operationType.value = "add"; |
| | | form.value = {}; |
| | | productData.value = []; |
| | | selectedQuotation.value = null; |
| | | fileList.value = []; |
| | | pinnedProductTemplate.value = null; |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | customerList().then(res => { |
| | | customerOption.value = res; |
| | | }); |
| | | // 预填原台账数据到新增表单 |
| | | form.value.customerId = detail.customerId; |
| | | form.value.customerName = detail.customerName; |
| | | form.value.projectName = detail.projectName; |
| | | form.value.salesman = detail.salesman; |
| | | form.value.entryPerson = Number(userStore.id); |
| | | form.value.entryDate = getCurrentDate(); |
| | | form.value.executionDate = detail.executionDate || getCurrentDate(); |
| | | form.value.deliveryDate = detail.deliveryDate; |
| | | form.value.paymentMethod = detail.paymentMethod; |
| | | form.value.contractAmount = detail.contractAmount; |
| | | form.value.remarks = detail.remarks; |
| | | form.value.customerRemarks = detail.customerRemarks ?? detail.customer_remarks ?? ""; |
| | | productData.value = detail.productData || []; |
| | | form.value.deliveryDate = dayjs(form.value.entryDate).add(7, "day").format("YYYY-MM-DD"); |
| | | await appendEditablePlaceholderRow(); |
| | | dialogFormVisible.value = !keepPageMode; |
| | | }; |
| | | |
| | | const paginationChange = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | |
| | | tableLoading.value = true; |
| | | const { entryDate, ...rest } = searchForm; |
| | | // 将范围日期字段传递给后端 |
| | | const params = { ...rest, ...page }; |
| | | const params = { ...rest, ...page, reviewStatusList: [0, 1, 2] }; |
| | | // 移除录入日期的默认值设置,只保留范围日期字段 |
| | | delete params.entryDate; |
| | | // 查询客户名称与新增保持一致:先选 customerId,再映射为 customerName 查询 |
| | |
| | | delete params.customerName; |
| | | } |
| | | } |
| | | const widthValue = |
| | | params.width != null ? String(params.width).trim() : ""; |
| | | const widthValue = params.width != null ? String(params.width).trim() : ""; |
| | | if (widthValue) { |
| | | params.width = widthValue; |
| | | } else { |
| | | delete params.width; |
| | | } |
| | | const heightValue = |
| | | params.height != null ? String(params.height).trim() : ""; |
| | | const heightValue = params.height != null ? String(params.height).trim() : ""; |
| | | if (heightValue) { |
| | | params.height = heightValue; |
| | | } else { |
| | |
| | | return; |
| | | } |
| | | const row = selectedRows.value[0] || {}; |
| | | if (Number(row.reviewStatus) !== 1) { |
| | | ElMessage.warning("只有已审核的台账才能进行入库操作"); |
| | | return; |
| | | } |
| | | const id = row?.id; |
| | | if (!id) { |
| | | ElMessage.warning("所选数据缺少id,无法入库"); |
| | |
| | | const res = await productList({ salesLedgerId: id, type: 1 }); |
| | | stockProductList.value = []; |
| | | stockProductList.value = |
| | | res.data.filter(item => item.productStockStatus == 0 || item.productStockStatus == 1) || []; |
| | | res.data.filter( |
| | | item => item.productStockStatus == 0 || item.productStockStatus == 1 |
| | | ) || []; |
| | | } catch (e) { |
| | | proxy?.$modal?.msgError?.("获取产品或审批人失败"); |
| | | } finally { |
| | |
| | | |
| | | proxy?.$modal?.loading?.("正在入库,请稍候..."); |
| | | try { |
| | | const approveUserIds = stockApproverNodes.value.map(node => node.userId).join(","); |
| | | const approveUserIds = stockApproverNodes.value |
| | | .map(node => node.userId) |
| | | .join(","); |
| | | const approveUserName = stockApproverNodes.value |
| | | .map(node => stockApproverOptions.value.find(item => String(item.userId) === String(node.userId))?.userName) |
| | | .map( |
| | | node => |
| | | stockApproverOptions.value.find( |
| | | item => String(item.userId) === String(node.userId) |
| | | )?.userName |
| | | ) |
| | | .filter(Boolean) |
| | | .join(","); |
| | | await salesStock({ |
| | |
| | | // 打开“工艺路线配置”选择弹窗(必须显式选择) |
| | | const openProcessFlowSelect = async ledgerRow => { |
| | | if (!ledgerRow) return; |
| | | if (!ledgerRow.isEdit) return; |
| | | if (Number(ledgerRow.reviewStatus) !== 1) { |
| | | proxy.$modal.msgWarning("只有已审核的台账才能选择工艺路线"); |
| | | return; |
| | | } |
| | | // if (!ledgerRow.isEdit) return; |
| | | |
| | | processFlowSelectLedgerRow.value = ledgerRow; |
| | | processFlowSelectDefaultRouteId.value = null; |
| | | processFlowSelectDefaultRecordList.value = []; |
| | | processFlowSelectBoundRouteId.value = null; |
| | | processFlowSelectBoundRouteName.value = ""; |
| | | |
| | |
| | | const boundId = info?.processRouteId ?? info?.routeId ?? info?.id ?? null; |
| | | const boundName = |
| | | info?.processRouteName ?? info?.routeName ?? info?.name ?? ""; |
| | | const recordList = Array.isArray(info?.recordList) ? info.recordList : []; |
| | | processFlowSelectBoundRouteId.value = boundId; |
| | | processFlowSelectBoundRouteName.value = boundName; |
| | | processFlowSelectDefaultRouteId.value = boundId; |
| | | processFlowSelectDefaultRecordList.value = recordList; |
| | | } catch (e) { |
| | | // 查询失败时按未绑定处理,不阻塞弹窗 |
| | | processFlowSelectBoundRouteId.value = null; |
| | | processFlowSelectBoundRouteName.value = ""; |
| | | processFlowSelectDefaultRouteId.value = null; |
| | | processFlowSelectDefaultRecordList.value = []; |
| | | } |
| | | |
| | | processFlowSelectDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 绑定工艺路线到当前台账数据 |
| | | const handleProcessFlowSelectConfirm = async routeId => { |
| | | const handleProcessFlowSelectConfirm = async payload => { |
| | | const ledgerRow = processFlowSelectLedgerRow.value; |
| | | if (!ledgerRow?.id) return; |
| | | |
| | | const finalRouteId = routeId ?? null; |
| | | const finalRouteId = payload?.routeId ?? payload ?? null; |
| | | const recordList = Array.isArray(payload?.recordList) ? payload.recordList : []; |
| | | if (!finalRouteId) return; |
| | | |
| | | const oldRouteId = processFlowSelectBoundRouteId.value; |
| | |
| | | await saleProcessBind({ |
| | | salesLedgerId: ledgerRow.id, |
| | | processRouteId: finalRouteId, |
| | | recordList, |
| | | }); |
| | | |
| | | proxy?.$modal?.msgSuccess?.("工艺路线绑定成功"); |
| | |
| | | }; |
| | | // 获取tree子数据 |
| | | const getModels = value => { |
| | | // 产品大类变化时,重置规格型号与厚度,避免旧值残留 |
| | | productForm.value.productModelId = null; |
| | | productForm.value.specificationModel = ""; |
| | | productForm.value.thickness = null; |
| | | |
| | | if (!value) { |
| | | productForm.value.productCategory = ""; |
| | | modelOptions.value = []; |
| | | modelList({}).then(res => { |
| | | modelOptions.value = res || []; |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | productForm.value.productCategory = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res || []; |
| | | const currentModelId = productForm.value.productModelId; |
| | | if (currentModelId) { |
| | | const isValid = modelOptions.value.some(item => item.id === currentModelId); |
| | | if (!isValid) { |
| | | productForm.value.productModelId = null; |
| | | productForm.value.specificationModel = ""; |
| | | productForm.value.thickness = null; |
| | | } |
| | | } else { |
| | | productForm.value.productModelId = null; |
| | | productForm.value.specificationModel = ""; |
| | | productForm.value.thickness = null; |
| | | } |
| | | }); |
| | | }; |
| | | const getProductModel = value => { |
| | |
| | | modelThickness === "" |
| | | ? null |
| | | : Number(modelThickness); |
| | | |
| | | const categoryId = selectedModel?.productId; |
| | | if (categoryId && productForm.value.__productCategoryId !== categoryId) { |
| | | productForm.value.__productCategoryId = categoryId; |
| | | productForm.value.productCategory = findNodeById(productOptions.value, categoryId); |
| | | } |
| | | } else { |
| | | productForm.value.specificationModel = null; |
| | | productForm.value.thickness = null; |
| | |
| | | "taxExclusiveTotalPrice", |
| | | ]); |
| | | }; |
| | | const summarizeProductTable = param => { |
| | | return proxy.summarizeTable(param, [ |
| | | "quantity", |
| | | "settlePieceArea", |
| | | "actualTotalArea", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | ]); |
| | | }; |
| | | |
| | | const initAddFormState = async () => { |
| | | operationType.value = "add"; |
| | | isCompletedOrder.value = false; |
| | | isReviewedEdit.value = false; |
| | | form.value = {}; |
| | | productData.value = []; |
| | | fileList.value = []; |
| | | selectedQuotation.value = null; |
| | | pinnedProductTemplate.value = null; |
| | | |
| | | const userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | customerList().then(res => { |
| | | customerOption.value = res; |
| | | }); |
| | | |
| | | form.value.entryPerson = userStore.id; |
| | | form.value.entryDate = getCurrentDate(); |
| | | form.value.executionDate = getCurrentDate(); |
| | | form.value.customerRemarks = ""; |
| | | form.value.deliveryDate = dayjs(form.value.entryDate) |
| | | .add(7, "day") |
| | | .format("YYYY-MM-DD"); |
| | | await appendEditablePlaceholderRow(); |
| | | }; |
| | | |
| | | const initEditFormState = async rowId => { |
| | | operationType.value = "edit"; |
| | | isCompletedOrder.value = false; |
| | | form.value = {}; |
| | | productData.value = []; |
| | | fileList.value = []; |
| | | selectedQuotation.value = null; |
| | | pinnedProductTemplate.value = null; |
| | | |
| | | const userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | customerList().then(res => { |
| | | customerOption.value = res; |
| | | }); |
| | | |
| | | currentId.value = rowId; |
| | | const res = await getSalesLedgerWithProducts({ id: rowId, type: 1 }); |
| | | form.value = { ...res }; |
| | | form.value.entryPerson = Number(res.entryPerson); |
| | | form.value.customerRemarks = |
| | | res?.customerRemarks ?? res?.customer_remarks ?? ""; |
| | | productData.value = form.value.productData || []; |
| | | fileList.value = form.value.salesLedgerFiles || []; |
| | | isReviewedEdit.value = Number(res?.reviewStatus) === 1; |
| | | await appendEditablePlaceholderRow(); |
| | | }; |
| | | |
| | | const enterAddPage = async detail => { |
| | | pendingAddPrefillDetail.value = detail || null; |
| | | const query = { ...route.query, mode: "add" }; |
| | | await router.push({ path: route.path, query }); |
| | | }; |
| | | |
| | | const enterEditPage = async row => { |
| | | const query = { ...route.query, mode: "edit", id: row?.id }; |
| | | await router.push({ path: route.path, query }); |
| | | }; |
| | | |
| | | const exitFormPage = async (shouldRefresh = false) => { |
| | | const query = { ...route.query }; |
| | | delete query.mode; |
| | | delete query.id; |
| | | pendingAddPrefillDetail.value = null; |
| | | await router.push({ path: route.path, query }); |
| | | closeDia(false); |
| | | if (shouldRefresh) { |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | watch( |
| | | pageMode, |
| | | async enabled => { |
| | | if (enabled === "add") { |
| | | await initAddFormState(); |
| | | if (pendingAddPrefillDetail.value) { |
| | | await openFormWithPreFill(pendingAddPrefillDetail.value, true); |
| | | pendingAddPrefillDetail.value = null; |
| | | } |
| | | return; |
| | | } |
| | | if (enabled === "edit" && route.query.id) { |
| | | await initEditFormState(route.query.id); |
| | | } |
| | | }, |
| | | { immediate: true } |
| | | ); |
| | | |
| | | // 打开弹框 |
| | | const openForm = async (type, row) => { |
| | | operationType.value = type; |
| | | if (type === "add") { |
| | | await enterAddPage(); |
| | | return; |
| | | } |
| | | if (type === "edit") { |
| | | await enterEditPage(row); |
| | | return; |
| | | } |
| | | // 已完成订单强制为只读模式,但附件上传仍可用 |
| | | const isCompleted = Number(row?.orderStatus) === 1; |
| | | const effectiveType = isCompleted ? 'view' : type; |
| | | operationType.value = effectiveType; |
| | | isCompletedOrder.value = isCompleted; |
| | | form.value = {}; |
| | | productData.value = []; |
| | | selectedQuotation.value = null; |
| | | // 已审核订单编辑时,标记只能修改单价(已完成订单已强制view,此处不会再命中) |
| | | isReviewedEdit.value = effectiveType === "edit" && Number(row?.reviewStatus) === 1; |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | customerList().then(res => { |
| | |
| | | console.log("productData.value--", productData.value); |
| | | // 行内编辑未保存时不允许提交,避免脏数据/临时字段进入后端 |
| | | const hasEditingRow = (productData.value || []).some( |
| | | r => r && r.__editing |
| | | r => r && r.__editing && !r.__placeholder |
| | | ); |
| | | if (hasEditingRow) { |
| | | proxy.$modal.msgWarning("产品信息存在未保存的编辑行,请先保存或取消"); |
| | | return; |
| | | } |
| | | if (productData.value !== null && productData.value.length > 0) { |
| | | const cleanedProducts = (productData.value || []).map(p => { |
| | | const cleanedProducts = (productData.value || []) |
| | | .filter(p => p && !p.__placeholder) |
| | | .map(p => { |
| | | if (!p || typeof p !== "object") return p; |
| | | const { |
| | | __editing, |
| | | __isNew, |
| | | __placeholder, |
| | | __backup, |
| | | __productCategoryId, |
| | | __tempKey, |
| | |
| | | rest.quantity = Number(rest.quantity ?? 0) || 0; |
| | | return rest; |
| | | }); |
| | | if (cleanedProducts.length === 0) { |
| | | proxy.$modal.msgWarning("请添加产品信息"); |
| | | return; |
| | | } |
| | | form.value.productData = proxy.HaveJson(cleanedProducts); |
| | | } else { |
| | | proxy.$modal.msgWarning("请添加产品信息"); |
| | |
| | | } |
| | | form.value.tempFileIds = tempFileIds; |
| | | form.value.type = 1; |
| | | form.value.reviewStatus = form.value.reviewStatus ?? 0; // 默认审核状态为待审核 |
| | | form.value.stockStatus = 0; // 默认入库状态为未入库 |
| | | const submitPayload = { ...form.value }; |
| | | delete submitPayload.paymentMethod; |
| | | addOrUpdateSalesLedger(submitPayload).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | if (isFormPageMode.value) { |
| | | exitFormPage(true); |
| | | return; |
| | | } |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | |
| | | }); |
| | | }; |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | const closeDia = (resetFormRef = true) => { |
| | | if (resetFormRef) { |
| | | proxy.resetForm("formRef"); |
| | | } |
| | | dialogFormVisible.value = false; |
| | | isCompletedOrder.value = false; |
| | | isReviewedEdit.value = false; |
| | | if (!isFormPageMode.value) { |
| | | fileList.value = []; |
| | | } |
| | | }; |
| | | |
| | | const productIndex = ref(0); |
| | |
| | | productData.value.splice(index, 1); |
| | | } |
| | | }); |
| | | appendEditablePlaceholderRow(); |
| | | } else { |
| | | const placeholderRows = productSelectedRows.value.filter( |
| | | item => item?.__placeholder |
| | | ); |
| | | if (placeholderRows.length > 0) { |
| | | placeholderRows.forEach(selectedRow => { |
| | | const index = productData.value.findIndex(product => { |
| | | if (!product || !selectedRow) return false; |
| | | return ( |
| | | product.__tempKey && |
| | | selectedRow.__tempKey && |
| | | String(product.__tempKey) === String(selectedRow.__tempKey) |
| | | ); |
| | | }); |
| | | if (index !== -1) { |
| | | productData.value.splice(index, 1); |
| | | } |
| | | }); |
| | | appendEditablePlaceholderRow(); |
| | | } |
| | | let ids = []; |
| | | if (productSelectedRows.value.length > 0) { |
| | | ids = productSelectedRows.value.map(item => item.id); |
| | | const persistedRows = productSelectedRows.value.filter( |
| | | item => !item?.__placeholder && item?.id !== null && item?.id !== undefined |
| | | ); |
| | | if (persistedRows.length > 0) { |
| | | ids = persistedRows.map(item => item.id); |
| | | } else { |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", { |
| | | confirmButtonText: "确认", |
| | |
| | | }; |
| | | // 导入 |
| | | const handleImport = () => { |
| | | currentImportCommand.value = "default"; |
| | | openImportDialog("导入销售台账", "/sales/ledger/import"); |
| | | }; |
| | | // 历史迁移 |
| | | const handleHistoryImportCommand = command => { |
| | | const url = HISTORY_IMPORT_URL_MAP[command]; |
| | | if (!url) return; |
| | | const title = command === "shipped" ? "历史迁移-已发货" : "历史迁移-未发货"; |
| | | currentImportCommand.value = command; |
| | | const title = command === "shipped" ? "历史迁移-已出库" : "历史迁移-未出库"; |
| | | openImportDialog(title, url); |
| | | }; |
| | | |
| | | // 下载导入模板 |
| | | const downloadTemplate = () => { |
| | | const command = currentImportCommand.value; |
| | | if (command && command !== "default") { |
| | | const templateUrl = HISTORY_IMPORT_TEMPLATE_URL_MAP[command]; |
| | | const fileName = HISTORY_IMPORT_TEMPLATE_FILE_NAME_MAP[command]; |
| | | if (templateUrl) { |
| | | proxy.download( |
| | | templateUrl, |
| | | {}, |
| | | fileName || "销售发货历史数据导入模板.xlsx" |
| | | ); |
| | | return; |
| | | } |
| | | } |
| | | proxy.download("/sales/ledger/exportTemplate", {}, "销售台账导入模板.xlsx"); |
| | | }; |
| | | const onClose = () => { |
| | |
| | | proxy.$refs["importUploadRef"].submit(); |
| | | }; |
| | | |
| | | // 导出 |
| | | // 导出(按当前查询条件导出) |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/sales/ledger/export", {}, "销售台账.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | | }); |
| | | // 构建查询参数(与 getList 保持一致) |
| | | const { entryDate, ...rest } = searchForm; |
| | | const params = { ...rest }; |
| | | |
| | | // 处理录入日期范围 |
| | | if (entryDate && entryDate.length === 2) { |
| | | params.entryDateStart = entryDate[0]; |
| | | params.entryDateEnd = entryDate[1]; |
| | | } |
| | | |
| | | // 处理客户名称查询 |
| | | const selectedCustomer = (customerOption.value || []).find( |
| | | item => String(item?.id ?? "") === String(params.customerId ?? "") |
| | | ); |
| | | if (selectedCustomer?.customerName) { |
| | | params.customerName = String(selectedCustomer.customerName).trim(); |
| | | } |
| | | delete params.customerId; |
| | | |
| | | // 处理产品宽高查询参数 |
| | | const widthValue = params.width != null ? String(params.width).trim() : ""; |
| | | const heightValue = params.height != null ? String(params.height).trim() : ""; |
| | | if (!widthValue) delete params.width; |
| | | if (!heightValue) delete params.height; |
| | | |
| | | proxy.download("/sales/ledger/exportWithProducts", params, "销售台账.xlsx"); |
| | | }; |
| | | |
| | | const exportProcessRouteDialogVisible = ref(false); |
| | | |
| | | const handleExportProcessRoute = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy?.$modal?.msgWarning?.("请选择要导出的销售台账"); |
| | | return; |
| | | } |
| | | const salesLedgerIds = selectedRows.value |
| | | .map(item => item.id) |
| | | .filter(id => id !== null && id !== undefined && id !== ""); |
| | | if (salesLedgerIds.length === 0) { |
| | | proxy?.$modal?.msgWarning?.("请选择要导出的销售台账"); |
| | | return; |
| | | } |
| | | exportProcessRouteDialogVisible.value = true; |
| | | }; |
| | | |
| | | const confirmExportProcessRoute = () => { |
| | | const salesLedgerIds = selectedRows.value |
| | | .map(item => item.id) |
| | | .filter(id => id !== null && id !== undefined && id !== ""); |
| | | |
| | | const params = { |
| | | salesLedgerIds: salesLedgerIds.join(","), |
| | | }; |
| | | if ( |
| | | Array.isArray(processRouteExportDateRange.value) && |
| | | processRouteExportDateRange.value.length === 2 |
| | | ) { |
| | | params.completedTimeStart = processRouteExportDateRange.value[0]; |
| | | params.completedTimeEnd = processRouteExportDateRange.value[1]; |
| | | } |
| | | |
| | | proxy.download( |
| | | "/sales/ledger/exportProcessRoute", |
| | | params, |
| | | "销售台账工艺路线导出.xlsx" |
| | | ); |
| | | exportProcessRouteDialogVisible.value = false; |
| | | }; |
| | | /** 判断单个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */ |
| | | const isProductShipped = product => { |
| | |
| | | return; |
| | | } |
| | | const ids = selectedRows.value.map(item => item.id); |
| | | |
| | | // 检查是否有已审核的台账 |
| | | const audited = selectedRows.value.filter( |
| | | row => Number(row.reviewStatus) === 1 |
| | | ); |
| | | if (audited.length > 0) { |
| | | proxy.$modal.msgWarning("选中的数据中包含已审核项,不能删除"); |
| | | return; |
| | | } |
| | | |
| | | // 检查是否有已进行发货或发货完成的销售订单,若有则不允许删除 |
| | | const cannotDeleteNames = []; |
| | |
| | | command !== "salesDeliveryNote" |
| | | ) |
| | | return; |
| | | |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("请至少选择一条销售台账数据进行打印"); |
| | | return; |
| | | } |
| | | const hasUnapproved = selectedRows.value.some( |
| | | row => Number(row.reviewStatus) !== 1 |
| | | ); |
| | | if (hasUnapproved) { |
| | | proxy.$modal.msgWarning("选中的数据中包含未审核项,无法打印"); |
| | | return; |
| | | } |
| | | |
| | | if (command === "salesDeliveryNote") { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("请至少选择一条销售台账数据进行打印"); |
| | | return; |
| | | } |
| | | const customerNames = Array.from( |
| | | new Set( |
| | | selectedRows.value.map(item => String(item?.customerName ?? "").trim()) |
| | |
| | | const res = await getSalesInvoices(selectedIds); |
| | | const salesInvoiceData = res?.data ?? {}; |
| | | await printSalesDeliveryNote(salesInvoiceData, selectedRow, selectedIds); |
| | | // 打印成功后递增单据打印次数 |
| | | selectedIds.forEach(id => { |
| | | incrementPrintCount({ id, printType: 'document' }).catch(() => {}); |
| | | }); |
| | | } catch (error) { |
| | | console.error("打印销售发货单失败:", error); |
| | | proxy.$modal.msgError("打印失败,请稍后重试"); |
| | |
| | | const res = await getSalesOrder(selectedId); |
| | | const salesOrderData = res?.data ?? {}; |
| | | printSalesOrder(salesOrderData); |
| | | // 打印成功后递增单据打印次数 |
| | | incrementPrintCount({ id: selectedId, printType: 'document' }).catch(() => {}); |
| | | } else { |
| | | const res = await getProcessCard(selectedId); |
| | | const processCardData = res?.data ?? {}; |
| | | // 补齐二维码所需的台账标识(后端数据有时不带 id) |
| | | if (processCardData && typeof processCardData === "object") { |
| | | processCardData.salesLedgerId = processCardData.salesLedgerId ?? selectedId; |
| | | processCardData.salesLedgerId = |
| | | processCardData.salesLedgerId ?? selectedId; |
| | | processCardData.salesContractNo = |
| | | (processCardData.salesContractNo ?? "").trim() || |
| | | String(selectedRow?.salesContractNo ?? "").trim(); |
| | |
| | | return; |
| | | } |
| | | await printFinishedProcessCard(processCardData); |
| | | // 打印成功后递增单据打印次数 |
| | | incrementPrintCount({ id: selectedId, printType: 'document' }).catch(() => {}); |
| | | } else { |
| | | await printFinishedProcessCard(processCardData); |
| | | // 打印成功后递增单据打印次数 |
| | | incrementPrintCount({ id: selectedId, printType: 'document' }).catch(() => {}); |
| | | } |
| | | } |
| | | } catch (error) { |
| | |
| | | return; |
| | | } |
| | | |
| | | const selectedId = selectedRows.value[0]?.id; |
| | | const selectedRow = selectedRows.value[0]; |
| | | if (Number(selectedRow?.reviewStatus) !== 1) { |
| | | proxy.$modal.msgWarning("只有已审核的台账才能进行标签打印"); |
| | | return; |
| | | } |
| | | const selectedId = selectedRow?.id; |
| | | if (!selectedId) { |
| | | proxy.$modal.msgWarning("当前选择数据缺少ID,无法打印标签"); |
| | | return; |
| | |
| | | return; |
| | | } |
| | | printSalesLabel(labelList); |
| | | // 打印成功后递增标签打印次数 |
| | | incrementPrintCount({ id: selectedId, printType: 'label' }).catch(() => {}); |
| | | } catch (error) { |
| | | console.error("打印标签失败:", error); |
| | | proxy.$modal.msgError("打印标签失败,请稍后重试"); |
| | |
| | | return false; |
| | | } |
| | | |
| | | // 如果后端返回了台账级发货状态(deliveryStatus) |
| | | // 1=已发货,则禁止再次发货 |
| | | // 台账级发货状态(deliveryStatus):2审批中、5已发货 时不可再发起本行发货;6部分发货仍可按明细继续发 |
| | | const deliveryStatus = row.deliveryStatus; |
| | | if ( |
| | | deliveryStatus !== null && |
| | | deliveryStatus !== undefined && |
| | | String(deliveryStatus).trim() !== "" |
| | | ) { |
| | | if (Number(deliveryStatus) === 1) return false; |
| | | const ds = Number(deliveryStatus); |
| | | if (ds === 2 || ds === 5) return false; |
| | | } |
| | | |
| | | // 获取发货状态 |
| | |
| | | return; |
| | | } |
| | | |
| | | // 只允许【未发货/审批失败】进入发货流程 |
| | | // 允许:1未发货、3审批不通过、4审批通过、6部分发货;不允许:2审批中、5已发货 |
| | | const statusItem = selectedRows.value[0].deliveryStatus; |
| | | const ledgerAllowsDelivery = s => [1, 3, 4, 6].includes(Number(s)); |
| | | let isTrue = true; |
| | | selectedRows.value.forEach(row => { |
| | | if (row.deliveryStatus != 1 && row.deliveryStatus != 3) { |
| | | proxy.$modal.msgWarning("仅未发货或审批失败的台账可以发货"); |
| | | if (!ledgerAllowsDelivery(row.deliveryStatus)) { |
| | | proxy.$modal.msgWarning( |
| | | "仅未发货、审批不通过、审批通过或部分发货的台账可以发货" |
| | | ); |
| | | isTrue = false; |
| | | return; |
| | | } |
| | |
| | | return; |
| | | } |
| | | |
| | | // 已发货台账:弹窗提醒,不能再次发货(4 视为已发货) |
| | | // 已全部发货(5)的台账:弹窗提醒,不能再次发货 |
| | | const shippedLedgers = selectedRows.value.filter( |
| | | r => Number(r.deliveryStatus) === 4 |
| | | r => Number(r.deliveryStatus) === 5 |
| | | ); |
| | | if (shippedLedgers.length === selectedRows.value.length) { |
| | | try { |
| | |
| | | try { |
| | | const targets = []; |
| | | for (const ledger of selectedRows.value) { |
| | | //如果已经是“审批中(2)”或“已发货(4)”,则跳过,不允许重复操作 |
| | | // 审批中(2)、已全部发货(5) 跳过;部分发货(6) 等仍收集可发明细 |
| | | const status = Number(ledger.deliveryStatus); |
| | | if (status === 2 || status === 4) { |
| | | if (status === 2 || status === 5) { |
| | | console.warn( |
| | | `台账编号 ${ledger.salesContractNo} 状态为 ${status},跳过发货` |
| | | ); |
| | |
| | | |
| | | // 打开发货弹框(单条) |
| | | const openDeliveryForm = async row => { |
| | | // 只允许【未发货/审批失败】发货;已发货/审批中不允许 |
| | | const status = Number(row.deliveryStatus); |
| | | if (status !== 1 && status !== 3) { |
| | | proxy.$modal.msgWarning("只有发货状态为未发货或审批失败的记录才可以发货"); |
| | | if (![1, 3, 4, 6].includes(status)) { |
| | | proxy.$modal.msgWarning( |
| | | "只有发货状态为未发货、审批不通过、审批通过或部分发货的记录才可以发货" |
| | | ); |
| | | return; |
| | | } |
| | | |
| | |
| | | const run = async () => { |
| | | for (const salesLedgerId of uniqueLedgerIds) { |
| | | await addShippingInfo({ |
| | | scanOutbound: false, |
| | | salesLedgerId, |
| | | type: deliveryForm.value.type, |
| | | approveUserIds, |
| | |
| | | currentFactoryName.value = res.user.currentFactoryName; |
| | | }; |
| | | onMounted(() => { |
| | | getList(); |
| | | handleStatusTabChange(activeStatusTab.value); |
| | | customerList().then(res => { |
| | | customerOption.value = res; |
| | | }); |
| | |
| | | margin-top: unset; |
| | | } |
| | | |
| | | .sales-ledger-status-bar { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | padding: 12px 14px; |
| | | margin-bottom: 14px; |
| | | background: linear-gradient(180deg, #f9fbff 0%, #f2f6ff 100%); |
| | | border: 1px solid #dbe6ff; |
| | | border-radius: 12px; |
| | | } |
| | | |
| | | .sales-ledger-status-tab { |
| | | min-width: 88px; |
| | | height: 34px; |
| | | padding: 0 14px; |
| | | border: 1px solid #d7def0; |
| | | border-radius: 9px; |
| | | background: #fff; |
| | | color: #4b5567; |
| | | font-size: 13px; |
| | | cursor: pointer; |
| | | transition: all 0.18s ease; |
| | | } |
| | | |
| | | .sales-ledger-status-tab:hover { |
| | | border-color: #8fb3ff; |
| | | color: #1d4ed8; |
| | | } |
| | | |
| | | .sales-ledger-status-tab.is-active { |
| | | border-color: #1d4ed8; |
| | | background: linear-gradient(135deg, #2f67f6 0%, #1d4ed8 100%); |
| | | color: #fff; |
| | | box-shadow: 0 8px 18px rgba(29, 78, 216, 0.16); |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .sales-ledger-toolbar { |
| | | align-items: flex-start; |
| | | gap: 14px; |
| | | padding: 10px 12px; |
| | | background: #fff; |
| | | border: 1px solid #e7edf7; |
| | | border-radius: 12px; |
| | | } |
| | | |
| | | .sales-ledger-toolbar-actions { |
| | | display: flex; |
| | | flex: 1; |
| | | flex-wrap: wrap; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .sales-ledger-toolbar-group { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | padding: 4px; |
| | | background: #f7f9fc; |
| | | border: 1px solid #edf1f7; |
| | | border-radius: 10px; |
| | | } |
| | | |
| | | .sales-ledger-toolbar-group--muted { |
| | | background: #fbfcfe; |
| | | } |
| | | |
| | | .sales-ledger-page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | gap: 16px; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .sales-ledger-page-header-content { |
| | | flex: 1; |
| | | } |
| | | |
| | | .sales-ledger-page-title { |
| | | font-size: 22px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | line-height: 1.3; |
| | | } |
| | | |
| | | .sales-ledger-page-back { |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .sales-ledger-page-subtitle { |
| | | margin-top: 6px; |
| | | font-size: 13px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .sales-ledger-page-form { |
| | | padding: 20px; |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .sales-ledger-page-actions { |
| | | display: flex; |
| | | justify-content: center; |
| | | gap: 16px; |
| | | margin-top: 24px; |
| | | } |
| | | |
| | | .compact-product-table { |
| | | :deep(.el-table__cell) { |
| | | padding: 4px 0; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | :deep(.cell) { |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | :deep(.el-input__wrapper) { |
| | | padding: 0 8px; |
| | | } |
| | | |
| | | :deep(.el-input-number .el-input__wrapper) { |
| | | padding-left: 8px; |
| | | padding-right: 8px; |
| | | } |
| | | |
| | | :deep(.el-input-number), |
| | | :deep(.el-select), |
| | | :deep(.el-input), |
| | | :deep(.el-tree-select) { |
| | | font-size: 12px; |
| | | } |
| | | |
| | | :deep(.el-input-number .el-input__inner) { |
| | | height: 28px; |
| | | font-size: 12px; |
| | | text-align: left; |
| | | } |
| | | |
| | | :deep(.el-select .el-input__inner), |
| | | :deep(.el-input .el-input__inner) { |
| | | height: 28px; |
| | | } |
| | | |
| | | :deep(.el-button--small), |
| | | :deep(.el-button.is-link) { |
| | | font-size: 12px; |
| | | padding: 2px 4px; |
| | | } |
| | | } |
| | | |
| | | .ledger-qr-dialog { |
| | |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .sales-ledger-status-tab { |
| | | min-width: unset; |
| | | flex: 1 1 calc(50% - 8px); |
| | | } |
| | | |
| | | .sales-ledger-toolbar, |
| | | .sales-ledger-page-header { |
| | | flex-direction: column; |
| | | align-items: stretch; |
| | | } |
| | | |
| | | .sales-ledger-toolbar-actions { |
| | | width: 100%; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .approver-node-item { |
| | | flex: 0 0 100%; |
| | | } |
| | | } |
| | | |
| | | .reverse-audit-warning { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | padding: 10px 12px; |
| | | margin-top: 8px; |
| | | background-color: #fdf6ec; |
| | | border: 1px solid #faecd8; |
| | | border-radius: 4px; |
| | | color: #e6a23c; |
| | | font-size: 13px; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .reverse-audit-dialog .el-radio { |
| | | display: flex; |
| | | align-items: center; |
| | | height: 36px; |
| | | margin-right: 0; |
| | | margin-bottom: 8px; |
| | | } |
| | | </style> |