| | |
| | | <el-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="销售合同号:"> |
| | | <el-form-item label="销售单号:"> |
| | | <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | |
| | | width="100px" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | |
| | | <el-tag v-if="scope.row.approveStatus === 1 && (!scope.row.shippingDate || !scope.row.shippingCarNumber)" |
| | | type="success">充足</el-tag> |
| | | <el-tag v-else-if="scope.row.approveStatus === 0 && (scope.row.shippingDate || scope.row.shippingCarNumber)" |
| | | type="success">已出库</el-tag> |
| | | <!-- 已发货显示已出库 --> |
| | | <el-tag v-if="scope.row.shippingDate" type="success">已出库</el-tag> |
| | | <!-- 未发货且库存充足 --> |
| | | <el-tag v-else-if="scope.row.approveStatus === 1" type="success">充足</el-tag> |
| | | <!-- 未发货且库存不足 --> |
| | | <el-tag v-else type="danger">不足</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | <!--操作--> |
| | | <el-table-column Width="60px" label="操作" align="center"> |
| | | <el-table-column width="100px" label="操作" align="center"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | :disabled="!canShip(scope.row)" |
| | | @click="openDeliveryForm(scope.row)"> |
| | | 发货 |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="序号" type="index" width="60" /> |
| | | <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip /> |
| | | <el-table-column label="销售单号" prop="salesContractNo" width="180" show-overflow-tooltip /> |
| | | <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip /> |
| | | <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip /> |
| | |
| | | <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="交付日期" prop="deliveryDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="备注" prop="remarks" width="200" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="操作" min-width="100" align="center"> |
| | | <el-table-column fixed="right" label="操作" width="130" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">编辑</el-button> |
| | | <!-- <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详情</el-button>--> |
| | | <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button> |
| | | <!-- <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>--> |
| | | <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">编辑</el-button> |
| | | <el-button link type="primary" @click="downLoadFile(scope.row)">附件</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="销售合同号:" prop="salesContractNo"> |
| | | <el-form-item label="销售单号:" prop="salesContractNo"> |
| | | <el-input v-model="form.salesContractNo" placeholder="自动生成" clearable disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | <el-table-column fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" |
| | | <el-button link type="primary" size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button> |
| | | </template> |
| | |
| | | <el-button type="primary" @click="fetchQuotationList">搜索</el-button> |
| | | <el-button @click="resetQuotationSearch">重置</el-button> |
| | | </div> |
| | | |
| | | |
| | | <el-table |
| | | :data="quotationList" |
| | | border |
| | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | |
| | | <pagination |
| | | v-show="quotationPage.total > 0" |
| | | :total="quotationPage.total" |
| | |
| | | :limit="quotationPage.size" |
| | | @pagination="quotationPaginationChange" |
| | | /> |
| | | |
| | | |
| | | <template #footer> |
| | | <el-button @click="quotationDialogVisible = false">关闭</el-button> |
| | | </template> |
| | |
| | | <div class="header"> |
| | | <div class="document-title">零售发货单</div> |
| | | </div> |
| | | |
| | | |
| | | <div class="info-section"> |
| | | <div class="info-row"> |
| | | <div> |
| | |
| | | <span class="value">{{ item.salesContractNo }}</span> |
| | | </div> |
| | | </div> |
| | | |
| | | |
| | | <div class="table-section"> |
| | | <table class="product-table"> |
| | | <thead> |
| | |
| | | </tfoot> |
| | | </table> |
| | | </div> |
| | | |
| | | |
| | | <div class="footer-section"> |
| | | <div class="footer-row"> |
| | | <div class="footer-item"> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="发货日期:" prop="shippingDate"> |
| | | <el-date-picker |
| | | style="width: 100%" |
| | | v-model="deliveryForm.shippingDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="请选择发货日期" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24" v-if="deliveryForm.type === '货车'"> |
| | | <el-form-item label="发货车牌号:" prop="shippingCarNumber"> |
| | | <el-input |
| | | v-model="deliveryForm.shippingCarNumber" |
| | | placeholder="请输入发货车牌号" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="24" v-else> |
| | | <el-form-item label="快递公司:" prop="expressCompany"> |
| | | <el-input |
| | | v-model="deliveryForm.expressCompany" |
| | | placeholder="请输入快递公司" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30" v-if="deliveryForm.type === '快递'"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="快递单号:" prop="expressNumber"> |
| | | <el-input |
| | | v-model="deliveryForm.expressNumber" |
| | | placeholder="请输入快递单号" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="发货图片:"> |
| | | <el-upload |
| | | v-model:file-list="deliveryFileList" |
| | | :action="upload.url" |
| | | multiple |
| | | ref="deliveryFileUpload" |
| | | auto-upload |
| | | :headers="upload.headers" |
| | | :data="{ type: 9 }" |
| | | :on-success="handleDeliveryUploadSuccess" |
| | | :on-remove="handleDeliveryRemove" |
| | | list-type="picture-card" |
| | | :limit="9" |
| | | accept="image/png,image/jpeg,image/jpg" |
| | | > |
| | | <el-icon class="avatar-uploader-icon"><Plus /></el-icon> |
| | | <template #tip> |
| | | <div class="el-upload__tip"> |
| | | 支持 jpg、jpeg、png 格式,最多上传 9 张,单张大小不超过 10MB |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 审批人选择(仿协同审批里的审批人节点选择) --> |
| | | <el-row> |
| | |
| | | <div> |
| | | <el-button |
| | | type="danger" |
| | | size="small" |
| | | @click="removeApproverNode(index)" |
| | | v-if="approverNodes.length > 1" |
| | | >删除</el-button> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitDelivery">确认发货</el-button> |
| | | <el-button type="success" @click="submitOneClickDelivery">一键发货</el-button> |
| | | <el-button type="primary" @click="submitDelivery">提交审批</el-button> |
| | | <el-button @click="closeDeliveryDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 批量一键发货弹框 --> |
| | | <el-dialog |
| | | v-model="batchDeliveryFormVisible" |
| | | title="批量一键发货" |
| | | width="40%" |
| | | @close="closeBatchDeliveryDia" |
| | | > |
| | | <el-form :model="batchDeliveryForm" label-width="120px" label-position="top" :rules="batchDeliveryRules" ref="batchDeliveryFormRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="发货类型:" prop="type"> |
| | | <el-select |
| | | v-model="batchDeliveryForm.type" |
| | | placeholder="请选择发货类型" |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="货车" value="货车" /> |
| | | <el-option label="快递" value="快递" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="发货日期:" prop="shippingDate"> |
| | | <el-date-picker |
| | | style="width: 100%" |
| | | v-model="batchDeliveryForm.shippingDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="请选择发货日期" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24" v-if="batchDeliveryForm.type === '货车'"> |
| | | <el-form-item label="发货车牌号:"> |
| | | <el-input |
| | | v-model="batchDeliveryForm.shippingCarNumber" |
| | | placeholder="请输入发货车牌号" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="24" v-else> |
| | | <el-form-item label="快递公司:"> |
| | | <el-input |
| | | v-model="batchDeliveryForm.expressCompany" |
| | | placeholder="请输入快递公司" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30" v-if="batchDeliveryForm.type === '快递'"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="快递单号:"> |
| | | <el-input |
| | | v-model="batchDeliveryForm.expressNumber" |
| | | placeholder="请输入快递单号" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-alert |
| | | title="提示:将对该销售台账下所有未发货的产品进行一键发货,自动审批通过并出库" |
| | | type="warning" |
| | | :closable="false" |
| | | show-icon |
| | | style="margin-top: 10px;" |
| | | /> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="success" @click="submitBatchDelivery">确认批量发货</el-button> |
| | | <el-button @click="closeBatchDeliveryDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | <script setup> |
| | | import { getToken } from "@/utils/auth"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import {onMounted, ref, getCurrentInstance} from "vue"; |
| | | import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js"; |
| | | import {onMounted, ref, getCurrentInstance, h} from "vue"; |
| | | import { ElButton } from "element-plus"; |
| | | import { addShippingInfo, oneClickShipping, batchOneClickShipping } from "@/api/salesManagement/deliveryLedger.js"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | | import { UploadFilled, Download } from "@element-plus/icons-vue"; |
| | | import useUserStore from "@/store/modules/user"; |
| | |
| | | // 发货相关 |
| | | const deliveryFormVisible = ref(false); |
| | | const currentDeliveryRow = ref(null); |
| | | |
| | | // 批量一键发货相关 |
| | | const batchDeliveryFormVisible = ref(false); |
| | | const currentBatchDeliveryRow = ref(null); |
| | | const batchDeliveryFormData = reactive({ |
| | | batchDeliveryForm: { |
| | | type: "货车", |
| | | shippingDate: "", |
| | | shippingCarNumber: "", |
| | | expressCompany: "", |
| | | expressNumber: "", |
| | | }, |
| | | batchDeliveryRules: { |
| | | type: [ |
| | | { required: true, message: "请选择发货类型", trigger: "change" } |
| | | ], |
| | | shippingDate: [{ required: true, message: "请选择发货日期", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { batchDeliveryForm, batchDeliveryRules } = toRefs(batchDeliveryFormData); |
| | | const deliveryFileList = ref([]); |
| | | const deliveryFormData = reactive({ |
| | | deliveryForm: { |
| | | type: "货车", // 货车, 快递 |
| | | shippingDate: "", |
| | | shippingCarNumber: "", |
| | | expressCompany: "", |
| | | expressNumber: "", |
| | | }, |
| | | deliveryRules: { |
| | | type: [ |
| | | { required: true, message: "请选择发货类型", trigger: "change" } |
| | | ] |
| | | ], |
| | | shippingDate: [{ required: true, message: "请选择发货日期", trigger: "change" }], |
| | | shippingCarNumber: [ |
| | | { validator: (_, value, callback) => validateShippingCarNumber(value, callback), trigger: "blur" } |
| | | ], |
| | | expressCompany: [ |
| | | { validator: (_, value, callback) => validateExpressCompany(value, callback), trigger: "blur" } |
| | | ], |
| | | }, |
| | | }); |
| | | const { deliveryForm, deliveryRules } = toRefs(deliveryFormData); |
| | | |
| | | // 发货类型校验:货车时车牌非必填,快递时要求快递公司 |
| | | const validateShippingCarNumber = (value, callback) => { |
| | | // 取消车牌必填限制 |
| | | callback(); |
| | | }; |
| | | const validateExpressCompany = (value, callback) => { |
| | | if (deliveryForm.value.type === "快递") { |
| | | if (!value) return callback(new Error("请输入快递公司")); |
| | | } |
| | | callback(); |
| | | }; |
| | | |
| | | // 发货审批人节点(仿协同审批 infoFormDia.vue) |
| | | const approverNodes = ref([{ id: 1, userId: null }]); |
| | |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | |
| | | const expandChange = (row, expandedRows) => { |
| | | if (expandedRows.length > 0) { |
| | | expandedRowKeys.value = []; |
| | | // 设置当前展开行,用于一键发货 |
| | | currentExpandedRow.value = row; |
| | | try { |
| | | productList({ salesLedgerId: row.id, type: 1 }).then((res) => { |
| | | const index = tableData.value.findIndex((item) => item.id === row.id); |
| | |
| | | } |
| | | } else { |
| | | expandedRowKeys.value = []; |
| | | currentExpandedRow.value = null; |
| | | } |
| | | }; |
| | | |
| | |
| | | "taxExclusiveTotalPrice", |
| | | ]); |
| | | }; |
| | | // 子表合计方法 |
| | | |
| | | // 当前展开行的销售台账数据(用于一键发货) |
| | | const currentExpandedRow = ref(null); |
| | | // 当前展开行是否有未发货产品 |
| | | const hasUnshippedProducts = ref(false); |
| | | |
| | | // 子表合计方法 - 最后一列显示一键发货按钮 |
| | | const summarizeChildrenTable = (param) => { |
| | | return proxy.summarizeTable(param, [ |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | ]); |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | |
| | | // 检查是否有未发货的产品 |
| | | const unshipped = data.filter(item => { |
| | | const status = item.shippingStatus; |
| | | return !item.shippingDate && (!status || status === '待发货' || status === '审核拒绝'); |
| | | }); |
| | | hasUnshippedProducts.value = unshipped.length > 0; |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "合计"; |
| | | return; |
| | | } |
| | | |
| | | const prop = column.property; |
| | | const summaryProps = ["taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice"]; |
| | | |
| | | if (summaryProps.includes(prop)) { |
| | | const values = data.map((item) => Number(item[prop])); |
| | | if (!values.every(isNaN)) { |
| | | const sum = values.reduce((acc, val) => (!isNaN(val) ? acc + val : acc), 0); |
| | | sums[index] = parseFloat(sum).toFixed(2); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | } else if (index === columns.length - 1 && currentExpandedRow.value && hasUnshippedProducts.value) { |
| | | // 最后一列(操作列)渲染一键发货按钮,仅当有未发货产品时显示 |
| | | sums[index] = h( |
| | | ElButton, |
| | | { |
| | | type: 'success', |
| | | size: 'small', |
| | | onClick: () => openBatchDeliveryForm(currentExpandedRow.value) |
| | | }, |
| | | { default: () => '一键发货' } |
| | | ); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | |
| | | return sums; |
| | | }; |
| | | // 打开弹框 |
| | | const openForm = async (type, row) => { |
| | |
| | | const applyQuotation = (row) => { |
| | | if (!row) return; |
| | | selectedQuotation.value = row; |
| | | |
| | | |
| | | // 业务员 |
| | | form.value.salesman = (row.salesperson || "").trim(); |
| | | |
| | | |
| | | // 客户名称 -> customerId |
| | | const qCustomerName = String(row.customer || "").trim(); |
| | | const customer = (customerOption.value || []).find((c) => { |
| | |
| | | // 如果找不到,保留原值(允许用户手动选择/不打断已有输入) |
| | | form.value.customerId = form.value.customerId || ""; |
| | | } |
| | | |
| | | |
| | | // 产品信息映射:报价 products -> 台账 productData |
| | | const products = Array.isArray(row.products) ? row.products : []; |
| | | productData.value = products.map((p) => { |
| | |
| | | invoiceType: "增普票", |
| | | }; |
| | | }); |
| | | |
| | | |
| | | quotationDialogVisible.value = false; |
| | | }; |
| | | function changs(val) { |
| | |
| | | addOrUpdateSalesLedger(form.value).then((res) => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | expandedRowKeys.value = []; |
| | | getList(); |
| | | }); |
| | | } |
| | |
| | | proxy.$modal.msgWarning("已发货或审核通过的产品不能编辑"); |
| | | return; |
| | | } |
| | | |
| | | |
| | | productOperationType.value = type; |
| | | productForm.value = {}; |
| | | proxy.resetForm("productFormRef"); |
| | |
| | | proxy.$modal.msgWarning("请选择数据"); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // 检查是否有已发货或审核通过的产品 |
| | | const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row)); |
| | | if (shippedProducts.length > 0) { |
| | | proxy.$modal.msgWarning("已发货或审核通过的产品不能删除"); |
| | | return; |
| | | } |
| | | |
| | | |
| | | if (operationType.value === "add") { |
| | | productSelectedRows.value.forEach((selectedRow) => { |
| | | const index = productData.value.findIndex( |
| | |
| | | proxy.$modal.msgWarning("请选择要打印的数据"); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // 显示加载状态 |
| | | proxy.$modal.loading("正在获取产品数据,请稍候..."); |
| | | |
| | | |
| | | try { |
| | | // 为每个选中的销售台账记录查询对应的产品数据 |
| | | const printDataWithProducts = []; |
| | | |
| | | |
| | | for (const row of selectedRows.value) { |
| | | try { |
| | | // 调用productList接口查询产品数据 |
| | | const productRes = await productList({ salesLedgerId: row.id, type: 1 }); |
| | | |
| | | |
| | | // 将产品数据整合到销售台账记录中 |
| | | const rowWithProducts = { |
| | | ...row, |
| | | products: productRes.data || [] |
| | | }; |
| | | |
| | | |
| | | printDataWithProducts.push(rowWithProducts); |
| | | } catch (error) { |
| | | console.error(`获取销售台账 ${row.id} 的产品数据失败:`, error); |
| | |
| | | }); |
| | | } |
| | | } |
| | | |
| | | |
| | | printData.value = printDataWithProducts; |
| | | console.log('打印数据(包含产品):', printData.value); |
| | | printPreviewVisible.value = true; |
| | | |
| | | |
| | | } catch (error) { |
| | | console.error('获取产品数据失败:', error); |
| | | proxy.$modal.msgError("获取产品数据失败,请重试"); |
| | |
| | | const executePrint = () => { |
| | | console.log('开始执行打印,数据条数:', printData.value.length); |
| | | console.log('打印数据:', printData.value); |
| | | |
| | | |
| | | // 创建一个新的打印窗口 |
| | | const printWindow = window.open('', '_blank', 'width=800,height=600'); |
| | | |
| | | |
| | | // 构建打印内容 |
| | | let printContent = ` |
| | | <!DOCTYPE html> |
| | |
| | | </head> |
| | | <body> |
| | | `; |
| | | |
| | | |
| | | // 为每条数据生成打印页面 |
| | | printData.value.forEach((item, index) => { |
| | | printContent += ` |
| | |
| | | <div class="header"> |
| | | <div class="document-title">零售发货单</div> |
| | | </div> |
| | | |
| | | |
| | | <div class="info-section"> |
| | | <div class="info-row"> |
| | | <div> |
| | |
| | | </div> |
| | | `; |
| | | }); |
| | | |
| | | |
| | | printContent += ` |
| | | </body> |
| | | </html> |
| | | `; |
| | | |
| | | |
| | | // 写入内容到新窗口 |
| | | printWindow.document.write(printContent); |
| | | printWindow.document.close(); |
| | | |
| | | |
| | | // 等待内容加载完成后打印 |
| | | printWindow.onload = () => { |
| | | setTimeout(() => { |
| | |
| | | // 根据含税总价计算含税单价和数量 |
| | | const calculateFromTotalPrice = () => { |
| | | if (isCalculating.value) return; |
| | | |
| | | |
| | | const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice); |
| | | const quantity = parseFloat(productForm.value.quantity); |
| | | |
| | | |
| | | if (!totalPrice || !quantity || quantity <= 0) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | isCalculating.value = true; |
| | | |
| | | |
| | | // 计算含税单价 = 含税总价 / 数量 |
| | | productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2); |
| | | |
| | | |
| | | // 如果有税率,计算不含税总价 |
| | | if (productForm.value.taxRate) { |
| | | productForm.value.taxExclusiveTotalPrice = |
| | |
| | | productForm.value.taxRate |
| | | ); |
| | | } |
| | | |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | |
| | |
| | | return; |
| | | } |
| | | if (isCalculating.value) return; |
| | | |
| | | |
| | | const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice); |
| | | const quantity = parseFloat(productForm.value.quantity); |
| | | const taxRate = parseFloat(productForm.value.taxRate); |
| | | |
| | | |
| | | if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | isCalculating.value = true; |
| | | |
| | | |
| | | // 先计算含税总价 = 不含税总价 / (1 - 税率/100) |
| | | const taxRateDecimal = taxRate / 100; |
| | | const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal); |
| | | productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2); |
| | | |
| | | |
| | | // 计算含税单价 = 含税总价 / 数量 |
| | | productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2); |
| | | |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | |
| | |
| | | return; |
| | | } |
| | | if (isCalculating.value) return; |
| | | |
| | | |
| | | const quantity = parseFloat(productForm.value.quantity); |
| | | const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice); |
| | | |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | isCalculating.value = true; |
| | | |
| | | |
| | | // 计算含税总价 |
| | | productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | |
| | | |
| | | // 如果有税率,计算不含税总价 |
| | | if (productForm.value.taxRate) { |
| | | productForm.value.taxExclusiveTotalPrice = |
| | |
| | | productForm.value.taxRate |
| | | ); |
| | | } |
| | | |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | |
| | |
| | | return; |
| | | } |
| | | if (isCalculating.value) return; |
| | | |
| | | |
| | | const quantity = parseFloat(productForm.value.quantity); |
| | | const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice); |
| | | |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | isCalculating.value = true; |
| | | |
| | | |
| | | // 计算含税总价 |
| | | productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | |
| | | |
| | | // 如果有税率,计算不含税总价 |
| | | if (productForm.value.taxRate) { |
| | | productForm.value.taxExclusiveTotalPrice = |
| | |
| | | productForm.value.taxRate |
| | | ); |
| | | } |
| | | |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | |
| | |
| | | return; |
| | | } |
| | | if (isCalculating.value) return; |
| | | |
| | | |
| | | const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice); |
| | | const taxRate = parseFloat(productForm.value.taxRate); |
| | | |
| | | |
| | | if (!inclusiveTotalPrice || !taxRate) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | isCalculating.value = true; |
| | | |
| | | |
| | | // 计算不含税总价 |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | proxy.calculateTaxExclusiveTotalPrice( |
| | | inclusiveTotalPrice, |
| | | taxRate |
| | | ); |
| | | |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | /** |
| | |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return '已发货'; |
| | | } |
| | | |
| | | |
| | | // 获取发货状态字段 |
| | | const status = row.shippingStatus; |
| | | |
| | | |
| | | // 如果状态为空或未定义,默认为"待发货" |
| | | if (status === null || status === undefined || status === '') { |
| | | return '待发货'; |
| | | } |
| | | |
| | | |
| | | // 状态是字符串 |
| | | const statusStr = String(status).trim(); |
| | | const statusTextMap = { |
| | |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return 'success'; |
| | | } |
| | | |
| | | |
| | | // 获取发货状态字段 |
| | | const status = row.shippingStatus; |
| | | |
| | | |
| | | // 如果状态为空或未定义,默认为灰色(待发货) |
| | | if (status === null || status === undefined || status === '') { |
| | | return 'info'; |
| | | } |
| | | |
| | | |
| | | // 状态是字符串 |
| | | const statusStr = String(status).trim(); |
| | | const typeTextMap = { |
| | |
| | | if (row.approveStatus !== 1) { |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // 获取发货状态 |
| | | const shippingStatus = row.shippingStatus; |
| | | |
| | | |
| | | // 如果已发货(有发货日期或车牌号),不能再次发货 |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // 发货状态必须是"待发货"或"审核拒绝" |
| | | const statusStr = shippingStatus ? String(shippingStatus).trim() : ''; |
| | | return statusStr === '待发货' || statusStr === '审核拒绝'; |
| | |
| | | proxy.$modal.msgWarning("只有在产品状态是充足,发货状态是待发货或审核拒绝的时候才可以发货"); |
| | | return; |
| | | } |
| | | |
| | | |
| | | currentDeliveryRow.value = row; |
| | | deliveryForm.value = { |
| | | type: "货车", |
| | | shippingDate: getCurrentDate(), |
| | | shippingCarNumber: "", |
| | | expressCompany: "", |
| | | expressNumber: "", |
| | | }; |
| | | deliveryFileList.value = []; |
| | | // 重置审批人节点(默认一个空节点) |
| | | approverNodes.value = [{ id: 1, userId: null }]; |
| | | nextApproverId = 2; |
| | | deliveryFormVisible.value = true; |
| | | }; |
| | | |
| | | // 发货图片上传成功回调 |
| | | const handleDeliveryUploadSuccess = (res, file, uploadFiles) => { |
| | | if (res.code === 200) { |
| | | file.tempId = res.data.tempId; |
| | | proxy.$modal.msgSuccess("上传成功"); |
| | | } else { |
| | | proxy.$modal.msgError(res.msg); |
| | | } |
| | | }; |
| | | |
| | | // 移除发货图片 |
| | | const handleDeliveryRemove = (file) => { |
| | | // 文件移除时自动从 fileList 中删除,无需额外处理 |
| | | }; |
| | | |
| | | // 提交发货表单 |
| | |
| | | // 保存当前展开的行ID,以便发货后重新加载子表格数据 |
| | | const currentExpandedKeys = [...expandedRowKeys.value]; |
| | | const salesLedgerId = currentDeliveryRow.value.salesLedgerId; |
| | | |
| | | // 获取上传图片的临时ID |
| | | let tempFileIds = []; |
| | | if (deliveryFileList.value !== null && deliveryFileList.value.length > 0) { |
| | | tempFileIds = deliveryFileList.value.map((item) => item.tempId); |
| | | } |
| | | |
| | | addShippingInfo({ |
| | | salesLedgerId: salesLedgerId, |
| | | salesLedgerProductId: currentDeliveryRow.value.id, |
| | | type: deliveryForm.value.type, |
| | | shippingDate: deliveryForm.value.shippingDate, |
| | | shippingCarNumber: deliveryForm.value.type === "货车" ? deliveryForm.value.shippingCarNumber : "", |
| | | expressCompany: deliveryForm.value.type === "快递" ? deliveryForm.value.expressCompany : "", |
| | | expressNumber: deliveryForm.value.type === "快递" ? deliveryForm.value.expressNumber : "", |
| | | tempFileIds: tempFileIds, |
| | | approveUserIds, |
| | | }) |
| | | .then(() => { |
| | |
| | | }); |
| | | }; |
| | | |
| | | // 一键发货 - 自动审批通过并出库 |
| | | const submitOneClickDelivery = () => { |
| | | proxy.$refs["deliveryFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | // 保存当前展开的行ID,以便发货后重新加载子表格数据 |
| | | const currentExpandedKeys = [...expandedRowKeys.value]; |
| | | const salesLedgerId = currentDeliveryRow.value.salesLedgerId; |
| | | |
| | | // 获取上传图片的临时ID |
| | | let tempFileIds = []; |
| | | if (deliveryFileList.value !== null && deliveryFileList.value.length > 0) { |
| | | tempFileIds = deliveryFileList.value.map((item) => item.tempId); |
| | | } |
| | | |
| | | oneClickShipping({ |
| | | salesLedgerId: salesLedgerId, |
| | | salesLedgerProductId: currentDeliveryRow.value.id, |
| | | type: deliveryForm.value.type, |
| | | shippingDate: deliveryForm.value.shippingDate, |
| | | shippingCarNumber: deliveryForm.value.type === "货车" ? deliveryForm.value.shippingCarNumber : "", |
| | | expressCompany: deliveryForm.value.type === "快递" ? deliveryForm.value.expressCompany : "", |
| | | expressNumber: deliveryForm.value.type === "快递" ? deliveryForm.value.expressNumber : "", |
| | | tempFileIds: tempFileIds, |
| | | }) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("一键发货成功"); |
| | | closeDeliveryDia(); |
| | | // 刷新主表数据 |
| | | getList().then(() => { |
| | | // 如果之前有展开的行,重新加载这些行的子表格数据 |
| | | if (currentExpandedKeys.length > 0) { |
| | | const loadPromises = currentExpandedKeys.map(ledgerId => { |
| | | return productList({ salesLedgerId: ledgerId, type: 1 }).then((res) => { |
| | | const index = tableData.value.findIndex((item) => item.id === ledgerId); |
| | | if (index > -1) { |
| | | tableData.value[index].children = res.data; |
| | | } |
| | | }); |
| | | }); |
| | | Promise.all(loadPromises).then(() => { |
| | | expandedRowKeys.value = currentExpandedKeys; |
| | | }); |
| | | } |
| | | }); |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 关闭发货弹框 |
| | | const closeDeliveryDia = () => { |
| | | proxy.resetForm("deliveryFormRef"); |
| | | deliveryFileList.value = []; |
| | | deliveryFormVisible.value = false; |
| | | currentDeliveryRow.value = null; |
| | | }; |
| | | |
| | | // 打开批量一键发货弹框 |
| | | const openBatchDeliveryForm = (row) => { |
| | | currentBatchDeliveryRow.value = row; |
| | | batchDeliveryForm.value = { |
| | | type: "货车", |
| | | shippingDate: getCurrentDate(), |
| | | shippingCarNumber: "", |
| | | expressCompany: "", |
| | | expressNumber: "", |
| | | }; |
| | | batchDeliveryFormVisible.value = true; |
| | | }; |
| | | |
| | | // 关闭批量发货弹框 |
| | | const closeBatchDeliveryDia = () => { |
| | | proxy.resetForm("batchDeliveryFormRef"); |
| | | batchDeliveryFormVisible.value = false; |
| | | currentBatchDeliveryRow.value = null; |
| | | }; |
| | | |
| | | // 提交批量一键发货 |
| | | const submitBatchDelivery = () => { |
| | | proxy.$refs["batchDeliveryFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | // 保存当前展开的行ID |
| | | const currentExpandedKeys = [...expandedRowKeys.value]; |
| | | const salesLedgerId = currentBatchDeliveryRow.value.id; |
| | | |
| | | batchOneClickShipping({ |
| | | salesLedgerId: salesLedgerId, |
| | | type: batchDeliveryForm.value.type, |
| | | shippingDate: batchDeliveryForm.value.shippingDate, |
| | | shippingCarNumber: batchDeliveryForm.value.type === "货车" ? batchDeliveryForm.value.shippingCarNumber : "", |
| | | expressCompany: batchDeliveryForm.value.type === "快递" ? batchDeliveryForm.value.expressCompany : "", |
| | | expressNumber: batchDeliveryForm.value.type === "快递" ? batchDeliveryForm.value.expressNumber : "", |
| | | }) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("批量发货成功"); |
| | | closeBatchDeliveryDia(); |
| | | // 刷新主表数据 |
| | | getList().then(() => { |
| | | // 如果之前有展开的行,重新加载这些行的子表格数据 |
| | | if (currentExpandedKeys.length > 0) { |
| | | const loadPromises = currentExpandedKeys.map(ledgerId => { |
| | | return productList({ salesLedgerId: ledgerId, type: 1 }).then((res) => { |
| | | const index = tableData.value.findIndex((item) => item.id === ledgerId); |
| | | if (index > -1) { |
| | | tableData.value[index].children = res.data; |
| | | } |
| | | }); |
| | | }); |
| | | Promise.all(loadPromises).then(() => { |
| | | expandedRowKeys.value = currentExpandedKeys; |
| | | }); |
| | | } |
| | | }); |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const currentFactoryName = ref(""); |
| | | const getCurrentFactoryName = async () => { |
| | | let res = await userStore.getInfo(); |
| | |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | ::v-deep .yellow { |
| | | :deep(.yellow) { |
| | | background-color: #FAF0DE; |
| | | } |
| | | |
| | | ::v-deep .pink { |
| | | :deep(.pink) { |
| | | background-color: #FAE1DE; |
| | | } |
| | | |
| | | ::v-deep .red { |
| | | :deep(.red) { |
| | | background-color: #FAE1DE; |
| | | } |
| | | |
| | | ::v-deep .purple{ |
| | | :deep(.purple){ |
| | | background-color: #F4DEFA; |
| | | } |
| | | |
| | |
| | | padding: 15px; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | text-align: center; |
| | | |
| | | |
| | | .el-button { |
| | | margin: 0 10px; |
| | | } |
| | | } |
| | | |
| | | |
| | | .print-preview-content { |
| | | padding: 20px; |
| | | background-color: #f5f5f5; |
| | |
| | | .header { |
| | | text-align: center; |
| | | margin-bottom: 8px; |
| | | |
| | | |
| | | .company-name { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | |
| | | .document-title { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | |
| | | |
| | | .info-row { |
| | | line-height: 20px; |
| | | |
| | | |
| | | .label { |
| | | font-weight: bold; |
| | | width: 60px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | |
| | | .value { |
| | | margin-right: 20px; |
| | | min-width: 80px; |
| | |
| | | .table-section { |
| | | margin-bottom: 4px; |
| | | flex: 1; |
| | | |
| | | |
| | | .product-table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | border: 1px solid #000; |
| | | |
| | | |
| | | th, td { |
| | | border: 1px solid #000; |
| | | padding: 6px; |
| | |
| | | font-size: 14px; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | |
| | | th { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | |
| | | .total-label { |
| | | text-align: right; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | |
| | | .total-value { |
| | | font-weight: bold; |
| | | } |
| | |
| | | margin-bottom: 3px; |
| | | line-height: 20px; |
| | | justify-content: space-between; |
| | | |
| | | |
| | | .footer-item { |
| | | display: flex; |
| | | margin-right: 20px; |
| | | |
| | | |
| | | .label { |
| | | font-weight: bold; |
| | | width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | |
| | | .value { |
| | | min-width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | |
| | | &.address-item { |
| | | .address-value { |
| | | min-width: 200px; |
| | |
| | | .app-container { |
| | | display: none; |
| | | } |
| | | |
| | | |
| | | .print-page { |
| | | box-shadow: none; |
| | | margin: 0; |