293 lines
7.0 KiB
Vue
293 lines
7.0 KiB
Vue
<template>
|
||
<view v-if="show" class="pound-qr-mask" @click="close">
|
||
<view class="pound-qr-panel" @click.stop>
|
||
<view class="pound-qr-panel__title">{{ title }}</view>
|
||
<view v-if="loading" class="pound-qr-panel__loading">加载中...</view>
|
||
<view v-else-if="row" class="pound-slip-capture-inline">
|
||
<view class="capture-card">
|
||
<view class="capture-row capture-row-plate">
|
||
<view class="vehicle-plate">
|
||
<view class="vehicle-plate-dot vehicle-plate-dot-lt" />
|
||
<view class="vehicle-plate-dot vehicle-plate-dot-lb" />
|
||
<text class="vehicle-plate-text">{{ row.vehicleNo || '未填车牌' }}</text>
|
||
<view class="vehicle-plate-dot vehicle-plate-dot-rt" />
|
||
<view class="vehicle-plate-dot vehicle-plate-dot-rb" />
|
||
</view>
|
||
</view>
|
||
<view class="capture-row capture-row-large">
|
||
<text class="label">往来单位:</text>
|
||
<text class="value">{{ row.partnerCompany || '--' }}</text>
|
||
</view>
|
||
<view class="capture-row capture-row-large">
|
||
<text class="label">物料名称:</text>
|
||
<text class="value">{{ productLine }}</text>
|
||
</view>
|
||
<view v-if="remarksText" class="capture-row">
|
||
<text class="label">备注:</text>
|
||
<text class="value">{{ remarksText }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="capture-card capture-qrcode">
|
||
<view class="capture-id">
|
||
{{ row.date || '' }}
|
||
{{ row.businessType === '出库' ? '出库单ID:' : '入库单ID:' }}{{ row.id }}
|
||
</view>
|
||
<view class="capture-qr-wrap">
|
||
<image v-if="qrPath" class="capture-qr-img" :src="qrPath" mode="aspectFit" />
|
||
</view>
|
||
<view class="capture-desc">出入库单标识(扫码核验)</view>
|
||
</view>
|
||
</view>
|
||
<!-- 隐藏 canvas,供 uqrcodejs 绘制 -->
|
||
<canvas
|
||
canvas-id="poundQrCanvas"
|
||
id="poundQrCanvas"
|
||
class="pound-qr-canvas"
|
||
:style="{ width: canvasSize + 'px', height: canvasSize + 'px' }"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, getCurrentInstance, ref, watch } from 'vue'
|
||
import { inventoryApi } from '@/pages/db/api/db'
|
||
import {
|
||
formatPoundQrProductLine,
|
||
normalizePoundQrRemarks,
|
||
poundQrDialogTitle
|
||
} from '@/utils/dbPoundQr'
|
||
import { makeQrTempFile } from '@/utils/makeQrTempFile'
|
||
|
||
const props = defineProps({
|
||
show: Boolean,
|
||
inventoryId: { type: String, default: '' },
|
||
/** 列表行快照(可选);仍会按需 get 补全 */
|
||
snapshot: { type: Object, default: null }
|
||
})
|
||
const emit = defineEmits(['close', 'update:show'])
|
||
|
||
const loading = ref(false)
|
||
const row = ref(null)
|
||
const qrPath = ref('')
|
||
let loadSeq = 0
|
||
const canvasSize = 200
|
||
const instance = getCurrentInstance()
|
||
|
||
const title = computed(() => poundQrDialogTitle(row.value?.businessType))
|
||
const productLine = computed(() =>
|
||
row.value ? formatPoundQrProductLine(row.value, '吨') : ''
|
||
)
|
||
const remarksText = computed(() => (row.value ? normalizePoundQrRemarks(row.value) : ''))
|
||
|
||
function close() {
|
||
emit('update:show', false)
|
||
emit('close')
|
||
}
|
||
|
||
function reset() {
|
||
loadSeq += 1
|
||
row.value = null
|
||
qrPath.value = ''
|
||
loading.value = false
|
||
}
|
||
|
||
async function load() {
|
||
const seq = ++loadSeq
|
||
const id = String(props.inventoryId || '').trim()
|
||
if (!id) {
|
||
uni.showToast({ title: '缺少单据ID', icon: 'none' })
|
||
close()
|
||
return
|
||
}
|
||
loading.value = true
|
||
row.value = null
|
||
qrPath.value = ''
|
||
try {
|
||
let data = props.snapshot && props.snapshot.id === id ? { ...props.snapshot } : null
|
||
if (!data || data.quantity == null || !data.partnerCompany) {
|
||
const res = await inventoryApi.get(id)
|
||
data = res?.data ?? res
|
||
}
|
||
if (!data || !data.id) throw new Error('单据不存在')
|
||
if (seq !== loadSeq) return
|
||
row.value = data
|
||
await new Promise((r) => setTimeout(r, 50))
|
||
const proxy = instance?.proxy
|
||
const path = await makeQrTempFile({
|
||
text: data.id,
|
||
canvasId: 'poundQrCanvas',
|
||
componentInstance: proxy,
|
||
size: canvasSize
|
||
})
|
||
if (seq !== loadSeq) return
|
||
qrPath.value = path
|
||
} catch (e) {
|
||
if (seq !== loadSeq) return
|
||
uni.showToast({ title: e?.message || '加载二维码失败', icon: 'none' })
|
||
close()
|
||
} finally {
|
||
if (seq === loadSeq) loading.value = false
|
||
}
|
||
}
|
||
|
||
watch(
|
||
() => [props.show, props.inventoryId],
|
||
([show]) => {
|
||
if (show) load()
|
||
else reset()
|
||
}
|
||
)
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.pound-qr-mask {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 1100;
|
||
background: rgba(0, 0, 0, 0.92);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 24px 16px;
|
||
box-sizing: border-box;
|
||
}
|
||
.pound-qr-panel {
|
||
width: 100%;
|
||
max-width: 360px;
|
||
max-height: 90vh;
|
||
overflow: auto;
|
||
background: #f5f7fa;
|
||
border-radius: 12px;
|
||
position: relative;
|
||
}
|
||
.pound-qr-panel__title {
|
||
text-align: center;
|
||
font-size: 17px;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
padding: 14px 12px 6px;
|
||
}
|
||
.pound-qr-panel__loading {
|
||
text-align: center;
|
||
color: #9ca3af;
|
||
padding: 40px 0;
|
||
font-size: 14px;
|
||
}
|
||
.pound-qr-canvas {
|
||
position: fixed;
|
||
left: -9999px;
|
||
top: 0;
|
||
width: 200px;
|
||
height: 200px;
|
||
}
|
||
|
||
.pound-slip-capture-inline {
|
||
background: #f5f7fa;
|
||
padding: 4px 8px 12px;
|
||
margin: 0 6px 10px;
|
||
}
|
||
.capture-card {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
margin: 10px;
|
||
padding: 6px 8px;
|
||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||
}
|
||
.capture-row {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
margin-bottom: 4px;
|
||
font-size: 13px;
|
||
}
|
||
.capture-row .label {
|
||
color: #1e293b;
|
||
min-width: 82px;
|
||
font-size: 12px;
|
||
font-weight: 400;
|
||
}
|
||
.capture-row .value {
|
||
color: #1e293b;
|
||
flex: 1;
|
||
font-size: 13px;
|
||
}
|
||
.capture-row-large .value {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
}
|
||
.capture-row-plate {
|
||
width: 100%;
|
||
margin-bottom: 8px;
|
||
}
|
||
.vehicle-plate {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
padding: 10px 32px;
|
||
box-sizing: border-box;
|
||
background: #f5d000;
|
||
border: 3px solid #fff;
|
||
border-radius: 8px;
|
||
box-shadow: 0 0 0 2px #fff;
|
||
}
|
||
.vehicle-plate-text {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #000;
|
||
letter-spacing: 1px;
|
||
}
|
||
.vehicle-plate-dot {
|
||
position: absolute;
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background: #fff;
|
||
}
|
||
.vehicle-plate-dot-lt {
|
||
left: 6px;
|
||
top: 8px;
|
||
}
|
||
.vehicle-plate-dot-lb {
|
||
left: 6px;
|
||
bottom: 8px;
|
||
}
|
||
.vehicle-plate-dot-rt {
|
||
right: 6px;
|
||
top: 8px;
|
||
}
|
||
.vehicle-plate-dot-rb {
|
||
right: 6px;
|
||
bottom: 8px;
|
||
}
|
||
.capture-qrcode {
|
||
text-align: center;
|
||
}
|
||
.capture-id {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
margin-bottom: 6px;
|
||
word-break: break-all;
|
||
padding: 0 4px;
|
||
}
|
||
.capture-qr-wrap {
|
||
padding: 8px 0;
|
||
min-height: 200px;
|
||
}
|
||
.capture-qr-img {
|
||
width: 200px;
|
||
height: 200px;
|
||
display: inline-block;
|
||
}
|
||
.capture-desc {
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
color: #64748b;
|
||
margin-top: 4px;
|
||
padding-bottom: 8px;
|
||
}
|
||
</style>
|