feat(pl): 运费结算审批分流与车辆挂车号等 PL 能力完善
拆分运费审批/出款页面,补充结算发票附件与菜单 SQL,并同步前后端模型与车辆相关展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
66d5a8714b
commit
98aec80d71
37 files changed
+1362
-195
No files matched your search
@@ -6,11 +6,16 @@ const SITE_INSTRUCTION = '说明:请打开链接上传现场附件并填写备
|
||||
|
||||
type SiteSummaryRecord = {
|
||||
productName?: string
|
||||
productText?: string
|
||||
batchNo?: string
|
||||
planNo?: string
|
||||
siteUrl?: string
|
||||
}
|
||||
|
||||
function formatSiteSummaryProductLine(record: SiteSummaryRecord) {
|
||||
return record.productText || record.productName || '—'
|
||||
}
|
||||
|
||||
export function buildSiteSummaryFromInventoryLinks(options: {
|
||||
planNo?: string
|
||||
licensePlate?: string
|
||||
@@ -33,13 +38,13 @@ export function buildSiteSummaryFromInventoryLinks(options: {
|
||||
|
||||
if (validRecords.length === 1) {
|
||||
const record = validRecords[0]
|
||||
lines.push(`产品:${record.productName || '—'}`)
|
||||
lines.push(`产品:${formatSiteSummaryProductLine(record)}`)
|
||||
if (record.batchNo) lines.push(`出入库单号:${record.batchNo}`)
|
||||
lines.push(`现场链接:${record.siteUrl}`)
|
||||
} else {
|
||||
lines.push('')
|
||||
validRecords.forEach((record, index) => {
|
||||
lines.push(`【${index + 1}】${record.productName || '产品'}`)
|
||||
lines.push(`【${index + 1}】${formatSiteSummaryProductLine(record)}`)
|
||||
if (record.batchNo) lines.push(`出入库单号:${record.batchNo}`)
|
||||
lines.push(`现场链接:${record.siteUrl}`)
|
||||
if (index < validRecords.length - 1) lines.push('')
|
||||
|
||||
@@ -6,6 +6,24 @@ export type AttachmentDownloadItem = {
|
||||
fileName?: string
|
||||
}
|
||||
|
||||
/** 将 JSON 数组或逗号分隔的附件 URL 统一为逗号分隔字符串 */
|
||||
export function normalizeAttachmentUrls(raw?: string | null): string {
|
||||
if (!raw) return ''
|
||||
const trimmed = String(raw).trim()
|
||||
if (!trimmed) return ''
|
||||
if (trimmed.startsWith('[')) {
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed)
|
||||
if (Array.isArray(parsed)) {
|
||||
return parsed.map((url) => String(url).trim()).filter(Boolean).join(',')
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
|
||||
function triggerAnchorDownload(href: string, fileName: string) {
|
||||
const a = document.createElement('a')
|
||||
a.href = href
|
||||
|
||||
@@ -51,6 +51,14 @@ export function getPlanProductQuantity(product: {
|
||||
return String(qty)
|
||||
}
|
||||
|
||||
export function getPlanProductPlannedQuantity(product: {
|
||||
quantity?: number | string | null
|
||||
}) {
|
||||
const qty = product.quantity
|
||||
if (qty === undefined || qty === null || qty === '') return '—'
|
||||
return String(qty)
|
||||
}
|
||||
|
||||
export function formatPlanProductListText(
|
||||
product: {
|
||||
productName?: string
|
||||
@@ -62,11 +70,14 @@ export function formatPlanProductListText(
|
||||
inboundEstimateQty?: number | string | null
|
||||
outboundEstimateQty?: number | string | null
|
||||
},
|
||||
getUnitLabel?: (unit: string) => string
|
||||
getUnitLabel?: (unit: string) => string,
|
||||
options?: { usePlannedQuantity?: boolean }
|
||||
) {
|
||||
const name = product.productName || '—'
|
||||
const spec = getPlanProductSpec(product)
|
||||
const qty = getPlanProductQuantity(product)
|
||||
const qty = options?.usePlannedQuantity
|
||||
? getPlanProductPlannedQuantity(product)
|
||||
: getPlanProductQuantity(product)
|
||||
const unitRaw = product.unit || ''
|
||||
const unit = unitRaw ? (getUnitLabel ? getUnitLabel(unitRaw) || unitRaw : unitRaw) : ''
|
||||
|
||||
|
||||
Reference in new issue
Block a user