| | |
| | | <template> |
| | | <view> |
| | | <cu-custom :bgColor="NavBarColor" :isBack="true" backRouterName="index"> |
| | | <block slot="backText">返回</block> |
| | | <block slot="content">扫一扫</block> |
| | | </cu-custom> |
| | | <view class="scanCode"> |
| | | <mumu-get-qrcode :continue="true" @success='qrcodeSucess' @error="qrcodeError" |
| | | :definition="true"></mumu-get-qrcode> |
| | | </view> |
| | | </view> |
| | | <view class="container"> |
| | | <cu-custom :bgColor="NavBarColor" :isBack="true" backRouterName="index"> |
| | | <block slot="backText">返回</block> |
| | | <block slot="content">移库单打印</block> |
| | | </cu-custom> |
| | | |
| | | <view class="print-area"> |
| | | <button @click="previewTransferOrder" class="btn test" :loading="isLoading"> |
| | | {{ isLoading ? '生成中...' : '预览移库单' }} |
| | | </button> |
| | | </view> |
| | | |
| | | <view class="status"> |
| | | 预览状态: {{ isPreview ? '已生成预览' : '未生成' }} |
| | | </view> |
| | | <view class="log" v-if="logList.length > 0"> |
| | | <text class="log-title">操作日志:</text> |
| | | <view v-for="(log, index) in logList" :key="index" class="log-item"> |
| | | {{ log }} |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mumuGetQrcode from '@/uni_modules/mumu-getQrcode/components/mumu-getQrcode/mumu-getQrcode.vue'; |
| | | export default { |
| | | name: 'TransferOrderPrinter', |
| | | data() { |
| | | return { |
| | | NavBarColor: '#007AFF', |
| | | isPreview: false, |
| | | logList: [], |
| | | isLoading: false, |
| | | // 移库单数据(实际项目中可从接口获取) |
| | | orderData: { |
| | | productionOrderNo: '112379', |
| | | productModel: 'G-639', |
| | | customerName: '东方日产', |
| | | materialNo: '120047854', |
| | | customerModel: '4200-51354', |
| | | productionBatch: '25159847', |
| | | factory: '双林新火炬工厂', |
| | | quantity: '73', |
| | | docCode: 'XHJ.QM.QMS009E', |
| | | qrCodeText: 'C2506080024' |
| | | } |
| | | }; |
| | | }, |
| | | methods: { |
| | | // 添加操作日志 |
| | | addLog(message) { |
| | | const time = new Date().toLocaleTimeString(); |
| | | this.logList.unshift(`[${time}] ${message}`); |
| | | if (this.logList.length > 20) this.logList.pop(); |
| | | }, |
| | | |
| | | export default { |
| | | components: { |
| | | mumuGetQrcode // 注册 |
| | | }, |
| | | name: 'spare', |
| | | data() { |
| | | return { |
| | | NavBarColor: this.NavBarColor, |
| | | hasNavigated: false // 标记是否已经跳转 |
| | | }; |
| | | }, |
| | | // 使用在线API生成二维码(无Canvas依赖) |
| | | async generateQrCode(content) { |
| | | try { |
| | | this.addLog('开始生成二维码...'); |
| | | |
| | | // 方案1: 使用Base64编码内容直接生成二维码(推荐) |
| | | const base64Content = encodeURIComponent(content); |
| | | // 该链接会根据内容生成二维码,无需后端支持 |
| | | return `https://api.qrserver.com/v1/create-qr-code/?data=${base64Content}&size=120x120`; |
| | | |
| | | // 方案2: 备用API(若方案1不可用) |
| | | // return `https://chart.googleapis.com/chart?chs=120x120&cht=qr&chl=${base64Content}`; |
| | | } catch (error) { |
| | | this.addLog(`二维码生成失败: ${error.message}`); |
| | | throw error; |
| | | } |
| | | }, |
| | | |
| | | methods: { |
| | | navigateToDeviceDetails(equipmentId) { |
| | | this.hasNavigated = true; // 标记已经跳转 |
| | | uni.redirectTo({ |
| | | url: `/pages/device/deviceWebDeils/deviceWebDeils?equipmentId=${encodeURIComponent(equipmentId)}`, |
| | | success: () => { |
| | | console.log('Navigated to device details'); |
| | | }, |
| | | fail: (err) => { |
| | | console.error('Navigation failed:', err); |
| | | } |
| | | }); |
| | | }, |
| | | qrcodeSucess(data) { // 扫码成功 |
| | | setTimeout(() => { |
| | | let equipmentId; |
| | | try { |
| | | // 兼容性处理:优先使用URL和URLSearchParams,如果不可用则使用字符串解析 |
| | | if ('URL' in window && 'URLSearchParams' in window) { |
| | | const url = new URL(data); |
| | | const params = new URLSearchParams(url.hash.slice(1).split('?')[1]); |
| | | equipmentId = params.get('equipmentId'); |
| | | } else { |
| | | // 字符串解析方式 |
| | | const hashIndex = data.indexOf('#'); |
| | | if (hashIndex !== -1) { |
| | | const hashPart = data.substring(hashIndex + 1); |
| | | const paramIndex = hashPart.indexOf('?'); |
| | | if (paramIndex !== -1) { |
| | | const paramStr = hashPart.substring(paramIndex + 1); |
| | | const pairs = paramStr.split('&'); |
| | | for (let pair of pairs) { |
| | | const [key, value] = pair.split('='); |
| | | if (key === 'equipmentId') { |
| | | equipmentId = value; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | // 生成移库单HTML并预览 |
| | | async generateTransferHTML() { |
| | | try { |
| | | this.isLoading = true; |
| | | this.addLog('开始生成移库单...'); |
| | | |
| | | if (equipmentId) { |
| | | // 登录成功且获取到equipmentId,跳转到设备详情页面 |
| | | this.navigateToDeviceDetails(equipmentId); |
| | | } else { |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: "二维码中未找到有效的equipmentId", |
| | | showCancel: false |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | uni.showModal({ |
| | | title: '错误', |
| | | content: "解析URL出错,请检查二维码内容", |
| | | showCancel: false |
| | | }); |
| | | } |
| | | }, 200); // 增加100毫秒的延迟 |
| | | }, |
| | | qrcodeError(err) { // 扫码失败 |
| | | uni.showModal({ |
| | | title: '摄像头授权失败', |
| | | content: '摄像头授权失败,请检测当前浏览器是否有摄像头权限。', |
| | | success: () => { |
| | | uni.navigateBack({}); // 返回到上一页 |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | // 生成二维码内容 |
| | | const qrContent = `生产订单号:${this.orderData.productionOrderNo},产品型号:${this.orderData.productModel},客户名称:${this.orderData.customerName},物料号:${this.orderData.materialNo},客户型号:${this.orderData.customerModel},生产批号:${this.orderData.productionBatch},生产分厂:${this.orderData.factory},数量:${this.orderData.quantity}`; |
| | | |
| | | // 调用在线API生成二维码 |
| | | const qrDataUrl = await this.generateQrCode(qrContent); |
| | | this.addLog('二维码生成成功'); |
| | | |
| | | // 处理logo图片(使用本地资源) |
| | | const logoUrl = '/static/index_logo.png'; |
| | | |
| | | // 生成移库单HTML内容 |
| | | const htmlContent = ` |
| | | <!DOCTYPE html> |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="UTF-8"> |
| | | <title>移库单</title> |
| | | <style> |
| | | @media print { |
| | | @page { |
| | | size: A4 landscape; |
| | | margin: 15mm; |
| | | } |
| | | body { |
| | | -webkit-print-color-adjust: exact; |
| | | } |
| | | } |
| | | body { |
| | | margin: 0; |
| | | padding: 0; |
| | | font-family: "Microsoft YaHei", "SimHei", sans-serif; |
| | | } |
| | | .transfer-order { |
| | | width: 800px; |
| | | height: 550px; |
| | | border: 2px solid #000; |
| | | box-sizing: border-box; |
| | | margin: 20px auto; |
| | | padding: 15px; |
| | | } |
| | | .header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 10px 0; |
| | | border-bottom: 2px solid #000; |
| | | margin-bottom: 15px; |
| | | } |
| | | .logo img { |
| | | width: 180px; |
| | | height: auto; |
| | | } |
| | | .doc-info { |
| | | text-align: right; |
| | | } |
| | | .doc-code { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | margin-bottom: 5px; |
| | | } |
| | | .doc-name { |
| | | font-size: 28px; |
| | | font-weight: bold; |
| | | text-align: center; |
| | | margin: 15px 0; |
| | | } |
| | | .divider { |
| | | border-top: 2px solid #000; |
| | | width: 200px; |
| | | margin: 0 auto 15px; |
| | | } |
| | | table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | table-layout: fixed; |
| | | } |
| | | table, th, td { |
| | | border: 1px solid #000; |
| | | padding: 10px; |
| | | font-size: 15px; |
| | | word-wrap: break-word; |
| | | } |
| | | .label-container { |
| | | display: flex; |
| | | gap: 15px; |
| | | flex-wrap: wrap; |
| | | padding: 5px 0; |
| | | } |
| | | .status-label { |
| | | display: inline-block; |
| | | padding: 8px 15px; |
| | | background-color: #f0f0f0; |
| | | border: 1px solid #ccc; |
| | | border-radius: 4px; |
| | | font-size: 14px; |
| | | } |
| | | table tr td:first-child { |
| | | width: 120px; |
| | | } |
| | | table tr td[colspan="4"] { |
| | | width: calc(100% - 120px); |
| | | } |
| | | .qrcode-area { |
| | | text-align: center; |
| | | vertical-align: middle; |
| | | } |
| | | .qrcode-area img { |
| | | width: 150px; |
| | | height: 150px; |
| | | display: block; |
| | | margin: 0 auto; |
| | | } |
| | | .qrcode-text { |
| | | margin-top: 10px; |
| | | text-align: center; |
| | | font-size: 14px; |
| | | } |
| | | </style> |
| | | </head> |
| | | <body> |
| | | <div class="transfer-order"> |
| | | <div class="header"> |
| | | <div class="logo"> |
| | | <img src="${logoUrl}" alt="企业logo"> |
| | | </div> |
| | | <div class="doc-info"> |
| | | <div class="doc-code">${this.orderData.docCode}</div> |
| | | </div> |
| | | </div> |
| | | <div class="doc-name">移库单</div> |
| | | <div class="divider"></div> |
| | | <table> |
| | | <tr> |
| | | <td>生产订单号</td> |
| | | <td>${this.orderData.productionOrderNo}</td> |
| | | <td>产品型号</td> |
| | | <td>${this.orderData.productModel}</td> |
| | | <td rowspan="4" class="qrcode-area"> |
| | | <img src="${qrDataUrl}" alt="二维码"> |
| | | <div class="qrcode-text">${this.orderData.qrCodeText}</div> |
| | | </td> |
| | | </tr> |
| | | <tr> |
| | | <td>客户名称</td> |
| | | <td>${this.orderData.customerName}</td> |
| | | <td>物料号</td> |
| | | <td>${this.orderData.materialNo}</td> |
| | | </tr> |
| | | <tr> |
| | | <td>客户型号</td> |
| | | <td>${this.orderData.customerModel}</td> |
| | | <td>生产批号</td> |
| | | <td>${this.orderData.productionBatch}</td> |
| | | </tr> |
| | | <tr> |
| | | <td>生产分厂</td> |
| | | <td>${this.orderData.factory}</td> |
| | | <td>数量</td> |
| | | <td>${this.orderData.quantity}</td> |
| | | </tr> |
| | | <tr> |
| | | <td>检验状态</td> |
| | | <td colspan="4"> |
| | | <div class="label-container"> |
| | | <span class="status-label">合格</span> |
| | | <span class="status-label">已检验</span> |
| | | </div> |
| | | </td> |
| | | </tr> |
| | | </table> |
| | | </div> |
| | | </body> |
| | | </html> |
| | | `; |
| | | |
| | | // 生成Blob链接并跳转预览 |
| | | const blob = new Blob([htmlContent], { type: 'text/html' }); |
| | | const previewUrl = URL.createObjectURL(blob); |
| | | |
| | | uni.navigateTo({ |
| | | url: `/pages/print-preview/print-preview?previewUrl=${encodeURIComponent(previewUrl)}`, |
| | | success: () => { |
| | | this.isPreview = true; |
| | | this.addLog('移库单预览生成成功'); |
| | | }, |
| | | fail: (err) => { |
| | | this.addLog(`跳转预览失败: ${err.errMsg}`); |
| | | } |
| | | }); |
| | | } catch (error) { |
| | | this.addLog(`生成失败: ${error.message}`); |
| | | uni.showToast({ title: '生成失败', icon: 'none' }); |
| | | } finally { |
| | | this.isLoading = false; |
| | | } |
| | | }, |
| | | |
| | | // 触发预览 |
| | | previewTransferOrder() { |
| | | if (this.isLoading) return; |
| | | this.generateTransferHTML(); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style> |
| | | .scanCode { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | z-index: 99; |
| | | height: 100%; |
| | | width: 100%; |
| | | background-color: rgba(0, 0, 0, 0.7); |
| | | } |
| | | |
| | | .reader-box { |
| | | position: fixed; |
| | | top: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background-color: rgba(0, 0, 0, 0.5); |
| | | } |
| | | |
| | | .reader { |
| | | width: 540rpx; |
| | | height: 540rpx; |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%); |
| | | } |
| | | <style scoped> |
| | | .print-area { |
| | | padding: 20rpx; |
| | | } |
| | | .btn { |
| | | width: 100%; |
| | | padding: 20rpx; |
| | | margin-bottom: 15rpx; |
| | | border-radius: 8rpx; |
| | | color: #fff; |
| | | font-size: 28rpx; |
| | | background-color: #FF9500; |
| | | } |
| | | .status { |
| | | padding: 20rpx; |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | } |
| | | .log { |
| | | margin: 10rpx; |
| | | padding: 20rpx; |
| | | background-color: #f5f5f5; |
| | | border-radius: 10rpx; |
| | | } |
| | | .log-title { |
| | | font-weight: bold; |
| | | display: block; |
| | | margin-bottom: 10rpx; |
| | | color: #333; |
| | | } |
| | | .log-item { |
| | | font-size: 26rpx; |
| | | color: #666; |
| | | margin-bottom: 5rpx; |
| | | word-break: break-all; |
| | | } |
| | | </style> |