diff --git a/web-admin/src/utils/mp-pl-plan-print.ts b/web-admin/src/utils/mp-pl-plan-print.ts new file mode 100644 index 000000000..590f0f3a1 --- /dev/null +++ b/web-admin/src/utils/mp-pl-plan-print.ts @@ -0,0 +1,127 @@ +import { fetchProductDetail } from '@/api/crm/product' +import { planApi, type Plan } from '@/api/pl' +import { useDictStore } from '@/store/modules/dict' +import { useUserStore } from '@/store/modules/user' +import { applyDeliveryCustomerToPlan } from '@/utils/pl-delivery-slip-print' +import { + generateDeliverySlipTemplate, + renderBlankDeliverySlipHtml, + replaceDeliverySlipPlaceholders +} from '@/utils/pl-delivery-slip-template' +import { + generateInboundSlipTemplate, + renderBlankInboundSlipHtml, + replaceInboundSlipPlaceholders +} from '@/utils/pl-inbound-slip-template' +import { + formatSubpackSlipDateIso, + generateSubpackSlipTemplate, + renderBlankSubpackSlipHtml, + replaceSubpackSlipPlaceholders +} from '@/utils/pl-subpack-slip-template' + +export type MpPlanPrintKind = + | 'inbound' + | 'delivery' + | 'subpack' + | 'blank-inbound' + | 'blank-delivery' + | 'blank-subpack' + +async function loadProductCodeContext(plan: Plan) { + const dictStore = useDictStore() + await dictStore.loadAllDict() + const productIds = [ + ...new Set( + (plan.products || []).map((item) => String(item.productId || '').trim()).filter(Boolean) + ) + ] + const productCodeById: Record = {} + await Promise.all( + productIds.map(async (productId) => { + try { + const res = await fetchProductDetail({ id: productId }) + const data = (res as { data?: { productNo?: string }; productNo?: string }).data ?? res + const productNo = String((data as { productNo?: string }).productNo || '').trim() + if (productNo) productCodeById[productId] = productNo + } catch { + // 单个产品查询失败时保留计划明细中的冗余字段 + } + }) + ) + return { + productCodeById, + getUnitLabel: (unitCode: string) => dictStore.getLabel('crm_product_unit', unitCode) || unitCode + } +} + +async function loadSavedTemplate( + loader: (id: string) => Promise, + id: string, + fallback: () => string +) { + try { + const templateRes = await loader(id) + const saved = (templateRes as { data?: string }).data ?? templateRes + if (typeof saved === 'string' && saved.trim()) return saved + } catch { + // 无模板时用默认 + } + return fallback() +} + +export async function buildMpPlanPrintHtml( + kind: MpPlanPrintKind, + plan: Plan, + options?: { customerId?: string } +): Promise { + if (kind === 'blank-inbound') return renderBlankInboundSlipHtml() + if (kind === 'blank-delivery') return renderBlankDeliverySlipHtml() + if (kind === 'blank-subpack') return renderBlankSubpackSlipHtml() + + const id = String(plan.id || '').trim() + if (!id) throw new Error('无法获取计划ID') + + if (kind === 'inbound') { + const htmlContent = await loadSavedTemplate( + (planId) => planApi.getInboundSlipTemplate(planId), + id, + () => generateInboundSlipTemplate(plan) + ) + const ctx = await loadProductCodeContext(plan) + return replaceInboundSlipPlaceholders(htmlContent, plan, ctx) + } + + if (kind === 'delivery') { + let view = plan + const customerId = String(options?.customerId || '').trim() + if (customerId) view = applyDeliveryCustomerToPlan(plan, customerId) + const htmlContent = await loadSavedTemplate( + (planId) => planApi.getDeliverySlipTemplate(planId), + id, + () => generateDeliverySlipTemplate(view) + ) + const ctx = await loadProductCodeContext(view) + return replaceDeliverySlipPlaceholders(htmlContent, view, ctx) + } + + const slipNoRes = await planApi.ensureSubpackSlipNo(id) + const subpackSlipNo = String((slipNoRes as { data?: string }).data ?? slipNoRes ?? '').trim() + const htmlContent = await loadSavedTemplate( + (planId) => planApi.getSubpackSlipTemplate(planId), + id, + () => generateSubpackSlipTemplate(plan) + ) + const dictStore = useDictStore() + const userStore = useUserStore() + await dictStore.loadAllDict() + const userInfo = userStore.getUserInfo as { realName?: string; userName?: string } | null + const loginUserName = String(userInfo?.realName || userInfo?.userName || '').trim() + return replaceSubpackSlipPlaceholders(htmlContent, plan, { + getUnitLabel: (unitCode: string) => dictStore.getLabel('crm_product_unit', unitCode) || unitCode, + subpackSlipNo, + orderDate: formatSubpackSlipDateIso(), + orderUser: loginUserName, + preparerUser: loginUserName + }) +} diff --git a/web-admin/src/views/m/pl/plan/print.vue b/web-admin/src/views/m/pl/plan/print.vue index 2418ca936..50f93ea08 100644 --- a/web-admin/src/views/m/pl/plan/print.vue +++ b/web-admin/src/views/m/pl/plan/print.vue @@ -1,18 +1,148 @@ + 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' - + + 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 : '加载失败' + } + }) +