| | |
| | | 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> |
| | |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | label="操作" |
| | | width="200" |
| | | width="280" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | |
| | | <el-button link |
| | | type="primary" |
| | | @click="downLoadFile(scope.row)">附件</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="openLedgerQrDialog(scope.row)">二维码</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | :key="item.id" |
| | | :label="item.customerName" |
| | | :value="item.id"> |
| | | {{ item.customerName + (item.taxpayerIdentificationNumber ? "——" + item.taxpayerIdentificationNumber : "") }} |
| | | {{ item.customerName + (item.taxpayerIdentificationNumber ? "——" + item.taxpayerIdentificationNumber : "") }} |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | |
| | | <FormDialog v-model="importUpload.open" |
| | | :title="importUpload.title" |
| | | :width="'600px'" |
| | | @close="importUpload.open = false" |
| | | :loading="importUpload.isUploading" |
| | | @close="onClose" |
| | | @confirm="submitImportFile" |
| | | @cancel="importUpload.open = false"> |
| | | @cancel="onClose"> |
| | | <el-upload ref="importUploadRef" |
| | | :limit="1" |
| | | accept=".xlsx,.xls" |
| | |
| | | :on-error="importUpload.onError" |
| | | :on-progress="importUpload.onProgress" |
| | | :on-change="importUpload.onChange" |
| | | :on-exceed="importUpload.onExceed" |
| | | :auto-upload="false" |
| | | drag> |
| | | <i class="el-icon-upload"></i> |
| | |
| | | <el-table-column prop="quantity" |
| | | label="数量" |
| | | width="100" /> |
| | | <el-table-column prop="stockedQuantity" |
| | | label="已入库数量" |
| | | width="120" |
| | | align="center" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="floorCode" |
| | | label="楼层编号" |
| | | show-overflow-tooltip /> |
| | |
| | | 确认入库 |
| | | </el-button> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog v-model="ledgerQrDialogVisible" |
| | | title="销售订单二维码" |
| | | width="360px" |
| | | draggable |
| | | :close-on-click-modal="false"> |
| | | <div class="ledger-qr-dialog"> |
| | | <img v-if="ledgerQrCompositeUrl" |
| | | :src="ledgerQrCompositeUrl" |
| | | alt="销售订单二维码" |
| | | class="ledger-qr-composite-img" /> |
| | | <el-button type="primary" |
| | | class="ledger-qr-save-btn" |
| | | :disabled="!ledgerQrCompositeUrl" |
| | | @click="downloadLedgerQrCode"> |
| | | 保存图片 |
| | | </el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | |
| | | import { printSalesOrder } from "./components/salesOrderPrint.js"; |
| | | import { printSalesDeliveryNote } from "./components/salesDeliveryPrint.js"; |
| | | import { printSalesLabel } from "./components/salesLabelPrint.js"; |
| | | import QRCode from "qrcode"; |
| | | // import { salesLedgerProductSetProcessFlowConfig } from "@/api/salesManagement/salesProcessFlowConfig.js"; |
| | | |
| | | const userStore = useUserStore(); |
| | |
| | | const selectedStockProductIds = ref([]); |
| | | const stockLoading = ref(false); |
| | | const currentStockLedgerId = ref(null); |
| | | |
| | | const ledgerQrDialogVisible = ref(false); |
| | | const ledgerQrCompositeUrl = ref(""); |
| | | const ledgerQrDownloadBaseName = ref(""); |
| | | |
| | | const sanitizeLedgerQrFilename = s => |
| | | String(s) |
| | | .replace(/[\\/:*?"<>|]/g, "_") |
| | | .trim() |
| | | .slice(0, 80) || "ledger"; |
| | | |
| | | const wrapLedgerQrTextLines = (ctx, text, maxWidth) => { |
| | | const chars = [...text]; |
| | | const lines = []; |
| | | let line = ""; |
| | | for (const ch of chars) { |
| | | const test = line + ch; |
| | | if (ctx.measureText(test).width > maxWidth && line.length) { |
| | | lines.push(line); |
| | | line = ch; |
| | | } else { |
| | | line = test; |
| | | } |
| | | } |
| | | if (line) lines.push(line); |
| | | return lines; |
| | | }; |
| | | |
| | | const buildLedgerQrCompositeDataUrl = row => |
| | | new Promise((resolve, reject) => { |
| | | const payload = JSON.stringify({ id: row.id }); |
| | | QRCode.toDataURL(payload, { width: 220, margin: 2 }) |
| | | .then(qrDataUrl => { |
| | | const contract = (row.salesContractNo ?? "").trim() || "—"; |
| | | const img = new Image(); |
| | | img.onload = () => { |
| | | const QR_SIZE = 220; |
| | | const padTop = 16; |
| | | const gapAfterQr = 14; |
| | | const bottomPad = 48; |
| | | const horizontalPad = 20; |
| | | const lineHeight = 20; |
| | | const fontSize = 14; |
| | | const label = `销售合同号:${contract}`; |
| | | |
| | | const canvas = document.createElement("canvas"); |
| | | 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 textBlockHeight = lines.length * lineHeight; |
| | | canvas.height = padTop + QR_SIZE + gapAfterQr + textBlockHeight + bottomPad; |
| | | |
| | | ctx.fillStyle = "#ffffff"; |
| | | ctx.fillRect(0, 0, canvas.width, canvas.height); |
| | | |
| | | const qrX = (canvas.width - QR_SIZE) / 2; |
| | | ctx.drawImage(img, qrX, padTop, QR_SIZE, QR_SIZE); |
| | | |
| | | ctx.fillStyle = "#606266"; |
| | | ctx.font = `${fontSize}px "Microsoft YaHei", "PingFang SC", sans-serif`; |
| | | ctx.textAlign = "center"; |
| | | ctx.textBaseline = "top"; |
| | | const textY0 = padTop + QR_SIZE + gapAfterQr; |
| | | lines.forEach((ln, i) => { |
| | | ctx.fillText(ln, canvas.width / 2, textY0 + i * lineHeight); |
| | | }); |
| | | |
| | | const baseName = sanitizeLedgerQrFilename( |
| | | contract !== "—" ? contract : String(row.id) |
| | | ); |
| | | resolve({ dataUrl: canvas.toDataURL("image/png"), baseName }); |
| | | }; |
| | | img.onerror = () => reject(new Error("二维码图片加载失败")); |
| | | img.src = qrDataUrl; |
| | | }) |
| | | .catch(reject); |
| | | }); |
| | | |
| | | const openLedgerQrDialog = async row => { |
| | | if (row?.id === undefined || row?.id === null || row?.id === "") { |
| | | ElMessage.warning("无法生成二维码:缺少台账 ID"); |
| | | return; |
| | | } |
| | | ledgerQrCompositeUrl.value = ""; |
| | | ledgerQrDownloadBaseName.value = ""; |
| | | try { |
| | | const { dataUrl, baseName } = await buildLedgerQrCompositeDataUrl(row); |
| | | ledgerQrCompositeUrl.value = dataUrl; |
| | | ledgerQrDownloadBaseName.value = baseName; |
| | | ledgerQrDialogVisible.value = true; |
| | | } catch { |
| | | ElMessage.error("二维码生成失败"); |
| | | } |
| | | }; |
| | | |
| | | const downloadLedgerQrCode = () => { |
| | | if (!ledgerQrCompositeUrl.value) return; |
| | | const a = document.createElement("a"); |
| | | a.href = ledgerQrCompositeUrl.value; |
| | | a.download = `销售销售订单二维码-${ledgerQrDownloadBaseName.value}.png`; |
| | | a.click(); |
| | | }; |
| | | |
| | | // 用户信息表单弹框数据 |
| | | const operationType = ref(""); |
| | |
| | | }, |
| | | onChange: (file, fileList) => { |
| | | console.log("文件状态改变", file, fileList); |
| | | }, |
| | | onExceed: (files, fileList) => { |
| | | if (importUploadRef.value) { |
| | | importUploadRef.value.clearFiles(); |
| | | const file = files[0]; |
| | | importUploadRef.value.handleStart(file); |
| | | } |
| | | }, |
| | | onProgress: (event, file, fileList) => { |
| | | console.log("上传中...", event.percent); |
| | |
| | | const downloadTemplate = () => { |
| | | proxy.download("/sales/ledger/exportTemplate", {}, "销售台账导入模板.xlsx"); |
| | | }; |
| | | const onClose = () => { |
| | | importUpload.open = false; |
| | | if (importUploadRef.value) { |
| | | importUploadRef.value.clearFiles(); |
| | | } |
| | | }; |
| | | |
| | | // 提交导入文件 |
| | | const submitImportFile = () => { |
| | |
| | | } else { |
| | | const res = await getProcessCard(selectedId); |
| | | const processCardData = res?.data ?? {}; |
| | | printFinishedProcessCard(processCardData); |
| | | const routeNodes = processCardData?.routeNodes; |
| | | const isProcessRouteEmpty = |
| | | !Array.isArray(routeNodes) || routeNodes.length === 0; |
| | | if (isProcessRouteEmpty) { |
| | | proxy.$modal.closeLoading(); |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | "当前订单未绑定工艺路线也没有设置默认的工艺路线,是否仍要打印?", |
| | | "提示", |
| | | { |
| | | confirmButtonText: "打印", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | } |
| | | ); |
| | | } catch { |
| | | return; |
| | | } |
| | | printFinishedProcessCard(processCardData); |
| | | } else { |
| | | printFinishedProcessCard(processCardData); |
| | | } |
| | | } |
| | | } catch (error) { |
| | | console.error( |
| | |
| | | const isTrue = true; |
| | | selectedRows.value.forEach(row => { |
| | | if (row.deliveryStatus != 1 && row.deliveryStatus != 3) { |
| | | proxy.$modal.msgWarning("仅未发货或审批失败的台账可以合并发货"); |
| | | proxy.$modal.msgWarning("仅未发货或审批失败的台账可以发货"); |
| | | isTrue = false; |
| | | return; |
| | | } |
| | |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .ledger-qr-dialog { |
| | | text-align: center; |
| | | padding-bottom: 8px; |
| | | } |
| | | |
| | | .ledger-qr-composite-img { |
| | | max-width: 100%; |
| | | height: auto; |
| | | display: block; |
| | | margin: 0 auto 28px; |
| | | } |
| | | |
| | | .ledger-qr-save-btn { |
| | | margin-bottom: 12px; |
| | | } |
| | | </style> |