feat(web-admin): add mobile print preview for transport plan slips.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-09-14 16:47:12 +08:00
1 parent bc7a1c225a
commit 866a87f747
2 files changed
+266 -9

No files matched your search

+139 -9
View File
@@ -1,18 +1,148 @@
<template>
<MpWebviewShell>
<p class="mp-wv-placeholder">打印预览加载中…</p>
<div v-if="forbidden" class="mp-wv-empty">无权限</div>
<div v-else-if="errorText" class="mp-wv-empty">{{ errorText }}</div>
<template v-else>
<header class="mp-wv-header">
<div class="mp-wv-header__row">
<button class="mp-wv-header__back" type="button" @click="goBack">← 详情</button>
</div>
<h1>打印单据</h1>
</header>
<p class="mp-print-hint">长按预览区可保存图片;WebView 不支持系统打印。</p>
<div class="mp-wv-chips">
<button
v-for="item in kindOptions"
:key="item.value"
type="button"
class="mp-wv-chip"
:class="{ 'is-active': kind === item.value }"
@click="selectKind(item.value)"
>
{{ item.label }}
</button>
</div>
<div v-if="kind === 'delivery' && customers.length > 1" class="mp-wv-toolbar">
<el-select
v-model="customerId"
placeholder="选择送货客户"
style="width: 100%"
@change="reloadPreview"
>
<el-option
v-for="item in customers"
:key="item.customerId"
:label="item.customerName"
:value="item.customerId"
/>
</el-select>
</div>
<div v-if="loading" class="mp-wv-empty">正在生成单据…</div>
<div v-else-if="blockReason" class="mp-wv-empty">{{ blockReason }}</div>
<div v-else class="mp-print-preview" v-html="html"></div>
</template>
</MpWebviewShell>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import MpWebviewShell from '@/layouts/MpWebviewShell.vue'
</script>
import { planApi, type Plan } from '@/api/pl'
import { canBatchPrintSlip } from '@/utils/pl-batch-print'
import { listDeliveryPrintCustomers } from '@/utils/pl-delivery-slip-print'
import { buildMpPlanPrintHtml, type MpPlanPrintKind } from '@/utils/mp-pl-plan-print'
import { HttpError } from '@/utils/http/error'
import { ApiStatus } from '@/utils/http/status'
import '@/styles/mp-webview.scss'
<style scoped>
.mp-wv-placeholder {
margin: 24px 0 0;
font-size: 14px;
color: #6b7b74;
text-align: center;
const kindOptions: { label: string; value: MpPlanPrintKind }[] = [
{ label: '入库单', value: 'inbound' },
{ label: '送(提)货单', value: 'delivery' },
{ label: '分装单', value: 'subpack' },
{ label: '空白入库单', value: 'blank-inbound' },
{ label: '空白送(提)货单', value: 'blank-delivery' },
{ label: '空白分装单', value: 'blank-subpack' }
]
const route = useRoute()
const router = useRouter()
const plan = ref<Plan | null>(null)
const kind = ref<MpPlanPrintKind>('inbound')
const html = ref('')
const loading = ref(false)
const forbidden = ref(false)
const errorText = ref('')
const blockReason = ref('')
const customerId = ref('')
const customers = ref<{ customerId: string; customerName: string }[]>([])
function goBack() {
if (window.history.length > 1) {
router.back()
return
}
const id = String(route.params.id || '')
void router.replace(id ? `/m/pl/plan/${id}` : '/m/pl/plan')
}
</style>
function slipCommand(current: MpPlanPrintKind) {
if (current === 'inbound' || current === 'delivery' || current === 'subpack') return current
return null
}
async function reloadPreview() {
const current = kind.value
const command = slipCommand(current)
if (command && plan.value) {
const check = canBatchPrintSlip([plan.value], command)
if (!check.ok) {
html.value = ''
blockReason.value = check.reason || '无法打印'
return
}
}
blockReason.value = ''
loading.value = true
try {
html.value = await buildMpPlanPrintHtml(current, plan.value as Plan, {
customerId: customerId.value
})
} catch (error) {
html.value = ''
blockReason.value = error instanceof Error ? error.message : '加载单据失败'
} finally {
loading.value = false
}
}
function selectKind(next: MpPlanPrintKind) {
kind.value = next
void reloadPreview()
}
onMounted(async () => {
const id = String(route.params.id || '')
if (!id) {
errorText.value = '缺少计划 ID'
return
}
try {
const res = await planApi.get(id)
plan.value = ((res as { data?: Plan }).data ?? res) as Plan
customers.value = listDeliveryPrintCustomers(plan.value)
if (customers.value.length === 1) customerId.value = customers.value[0].customerId
await reloadPreview()
} catch (error) {
if (error instanceof HttpError && error.code === ApiStatus.forbidden) {
forbidden.value = true
return
}
errorText.value = error instanceof Error ? error.message : '加载失败'
}
})
</script>