chore: sync remaining local plan, form mapping and graphify cache

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-07-28 10:05:03 +08:00
1 parent 44a57b86ca
commit dd4aa734ee
28 files changed
+3215 -5

No files matched your search

@@ -76,11 +76,121 @@ TS
Expected: `pl-batch-print: ok`
(实现文件内容见原 Step 2 代码块;可删去 `pl-batch-print.selftest.mjs` 双写方案,只保留 `.ts`。)
(Step 2 写出 `.ts` 后再跑本命令;顺序可先 Step 2 再 Step 1。)
- [ ] **Step 2: 实现 `web-admin/src/utils/pl-batch-print.ts`**
(完整代码同上一版 Step 2 中的 TypeScript 模块。)
```ts
// web-admin/src/utils/pl-batch-print.ts
import type { Plan, PlanProduct } from '@/api/pl'
import type { PlPrintSlipCommand } from '@/utils/pl-display'
import { canPrintSlipCommand } from '@/utils/pl-display'
export type SlipPlanProduct = PlanProduct & { slipPlanNo?: string }
export function normalizeCustomerName(name?: string | null) {
return String(name || '').trim()
}
export function canBatchPrintSlip(
plans: Array<Pick<Plan, 'planType' | 'customerName' | 'planDate'>>,
command: PlPrintSlipCommand
): { ok: boolean; reason?: string } {
if (!plans?.length) return { ok: false, reason: '请先勾选运输计划' }
if (plans.length === 1) {
if (!canPrintSlipCommand(plans[0].planType, command)) {
return { ok: false, reason: '当前单据类型不支持打印该单据' }
}
return { ok: true }
}
if (command === 'inbound') {
if (!plans.every((p) => p.planType === 'inbound')) {
return { ok: false, reason: '合打入库单要求:均为入库类计划' }
}
const c = normalizeCustomerName(plans[0].customerName)
const d = plans[0].planDate
if (!c || !d || !plans.every((p) => normalizeCustomerName(p.customerName) === c && p.planDate === d)) {
return {
ok: false,
reason: '合打入库单要求:客户名称一致且计划日期一致,且均为入库类计划'
}
}
return { ok: true }
}
if (command === 'delivery') {
if (!plans.every((p) => p.planType === 'outbound')) {
return { ok: false, reason: '合打送(提)货单要求:均为出库类计划' }
}
const c = normalizeCustomerName(plans[0].customerName)
const d = plans[0].planDate
if (!c || !d || !plans.every((p) => normalizeCustomerName(p.customerName) === c && p.planDate === d)) {
return {
ok: false,
reason: '合打送(提)货单要求:客户名称一致且计划日期一致,且均为出库类计划'
}
}
return { ok: true }
}
if (command === 'subpack') {
if (!plans.every((p) => canPrintSlipCommand(p.planType, 'subpack'))) {
return { ok: false, reason: '分装单仅支持出库类计划' }
}
return { ok: true }
}
return { ok: false, reason: '未知单据类型' }
}
export function mergeBatchPrintRemarks(plans: Array<Pick<Plan, 'planNo' | 'remark'>>) {
return plans
.map((p) => {
const text = String(p.remark || '').trim()
if (!text) return ''
return `${p.planNo}:${text}`
})
.filter(Boolean)
.join('\n')
}
/** 备注写入 HTML 模版时换行 */
export function formatRemarkForSlipHtml(remark?: string | null) {
return String(remark || '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '<br />')
}
/** 合打视图:首条为抬头基准;产品展开并打 slipPlanNo;清空车辆;备注合并 */
export function buildBatchPrintPlanView(plans: Plan[]): Plan {
if (!plans.length) throw new Error('plans empty')
const base = { ...plans[0] }
const products: SlipPlanProduct[] = []
for (const p of plans) {
for (const prod of p.products || []) {
products.push({ ...prod, slipPlanNo: p.planNo })
}
}
base.products = products
base.remark = mergeBatchPrintRemarks(plans)
base.licensePlate = undefined
base.vehicleId = undefined
base.driverId = undefined
base.driverName = undefined
base.trailerNo = undefined
base.personnelSummary = undefined
return base
}
export function normalizePlanIds(planIdOrIds: string | string[]): string[] {
const ids = Array.isArray(planIdOrIds) ? planIdOrIds : [planIdOrIds]
return ids.map((id) => String(id || '').trim()).filter(Boolean)
}
```
- [ ] **Step 3: 再跑 Step 1 命令,确认 PASS**