提交
This commit is contained in:
1 parent
70bdd820ce
commit
bede140d12
988 files changed
+692310
-775910
No files matched your search
@@ -0,0 +1,39 @@
|
||||
/** 主体危化品能力:产品是否允许选择 */
|
||||
|
||||
export function productAllowedByHazard(
|
||||
isToxic: number | undefined,
|
||||
isExplosive: number | undefined,
|
||||
allowToxic: number,
|
||||
allowExplosive: number
|
||||
): boolean {
|
||||
const toxic = Number(isToxic || 0) === 1
|
||||
const explosive = Number(isExplosive || 0) === 1
|
||||
if (!toxic && !explosive) return true
|
||||
if (toxic && explosive) return allowToxic === 1 && allowExplosive === 1
|
||||
if (toxic) return allowToxic === 1
|
||||
return allowExplosive === 1
|
||||
}
|
||||
|
||||
export function removeProductsNotAllowedByHazard<
|
||||
T extends {
|
||||
productId?: string | number
|
||||
productName?: string
|
||||
isToxic?: number
|
||||
isExplosive?: number
|
||||
}
|
||||
>(
|
||||
products: T[],
|
||||
allowToxic: number,
|
||||
allowExplosive: number
|
||||
): { kept: T[]; removedNames: string[] } {
|
||||
const kept: T[] = []
|
||||
const removedNames: string[] = []
|
||||
for (const p of products) {
|
||||
if (productAllowedByHazard(p.isToxic, p.isExplosive, allowToxic, allowExplosive)) {
|
||||
kept.push(p)
|
||||
} else {
|
||||
removedNames.push(p.productName || String(p.productId || ''))
|
||||
}
|
||||
}
|
||||
return { kept, removedNames }
|
||||
}
|
||||
@@ -4,12 +4,14 @@ import { applyPdfStableTableCellBorders } from '@/utils/pl-slip-pdf-render'
|
||||
/** 入库单打印默认公司信息(可在富文本中修改) */
|
||||
export const INBOUND_SLIP_DEFAULTS = {
|
||||
companyName: '安庆市山峡化工有限公司',
|
||||
receivingUnit: '安庆市山峡化工有限公司',
|
||||
companyAddress: '安庆市高新区霞虹路5号',
|
||||
companyPhone: '0556-5999710',
|
||||
contactPhone: '方总13395560066'
|
||||
} as const
|
||||
|
||||
/** 旧模板曾把送货单位写死为本公司名,保存还原时一并识别 */
|
||||
const LEGACY_RECEIVING_UNIT = '安庆市山峡化工有限公司'
|
||||
|
||||
const DOC_FONT = "SimSun, 'Songti SC', serif"
|
||||
const FONT_TITLE = 'font-size:26px;font-weight:bold;'
|
||||
const FONT_BODY = 'font-size:20px;'
|
||||
@@ -29,17 +31,18 @@ const INFO_COLGROUP = `<colgroup>
|
||||
<col span="7" style="width:14.28%;" />
|
||||
</colgroup>`
|
||||
/**
|
||||
* 物料表列宽:加宽编号/名称,收窄规格/单位;前 5 列合计供条款区使用。
|
||||
* 物料编号 / 物料名称 / 规格 / 单位 / 数量 / 产品规格 / 实收数量
|
||||
* 物料表列宽:单据编号 + 加宽编号/名称,收窄规格/单位。
|
||||
* 单据编号 / 物料编号 / 物料名称 / 规格 / 单位 / 数量 / 产品规格 / 实收数量
|
||||
*/
|
||||
const PRODUCT_COLGROUP = `<colgroup>
|
||||
<col style="width:18%;" />
|
||||
<col style="width:24%;" />
|
||||
<col style="width:14%;" />
|
||||
<col style="width:14%;" />
|
||||
<col style="width:20%;" />
|
||||
<col style="width:8%;" />
|
||||
<col style="width:6%;" />
|
||||
<col style="width:12%;" />
|
||||
<col style="width:16%;" />
|
||||
<col style="width:16%;" />
|
||||
<col style="width:10%;" />
|
||||
<col style="width:14%;" />
|
||||
<col style="width:14%;" />
|
||||
</colgroup>`
|
||||
|
||||
/** 物料网格 padding(编辑器预览) */
|
||||
@@ -62,16 +65,12 @@ const PRODUCT_GRID_CELL = `padding:${PRODUCT_GRID_CELL_PADDING};box-sizing:borde
|
||||
const PRODUCT_CELL = `padding:4px 6px;border:${PRODUCT_TABLE_BORDER} !important;text-align:center;${FONT_BODY}line-height:1.35;background-color:#fff !important;${PRODUCT_CELL_WRAP}`
|
||||
const PRODUCT_TH = `padding:${PRODUCT_GRID_TH_PADDING};box-sizing:border-box;border:${PRODUCT_GRID_BORDER} !important;text-align:center;${FONT_BODY}line-height:normal;font-weight:bold;background-color:#fff !important;${PRODUCT_CELL_WRAP}`
|
||||
const PRODUCT_TABLE_CELL_LEFT = `padding:10px 6px 14px 6px;border:${PRODUCT_TABLE_BORDER} !important;text-align:left;${FONT_BODY}line-height:1.35;vertical-align:top;background-color:#fff !important;`
|
||||
/** 条款单元格:行高随内容,避免被右侧印章区 min-height 撑出底部空白 */
|
||||
/** 条款单元格样式(兼容已保存模板归一化) */
|
||||
const PRODUCT_TERMS_CELL_LEFT = `padding:10px 6px 14px 6px;border:${PRODUCT_TABLE_BORDER} !important;text-align:left;${FONT_BODY}line-height:1.35;vertical-align:top;background-color:#fff !important;`
|
||||
const TERMS_TEXT_STYLE = 'line-height:1.35;'
|
||||
const SEAL_BLOCK_LINE_STYLE = 'line-height:1.35;'
|
||||
/** 入库单印章:public/sxshzyz.png,定位在地址单元格右上角(相对原位置向上 66px) */
|
||||
/** 入库单印章:public/sxshzyz.png(兼容已保存模板的红章开关) */
|
||||
const INBOUND_SLIP_SEAL_IMAGE_SRC = '/sxshzyz.png'
|
||||
const INBOUND_SLIP_SEAL_TOP = -66
|
||||
const INBOUND_SLIP_SEAL_IMAGE_STYLE = `position:absolute;right:4px;top:${INBOUND_SLIP_SEAL_TOP}px;width:140px;height:140px;opacity:0.85;z-index:10;`
|
||||
const HANDWRITE_BLANK_INNER = ' '
|
||||
const HANDWRITE_BLANK = `<span class="inbound-slip-handwrite-blank">(${HANDWRITE_BLANK_INNER})</span>`
|
||||
const PLAIN_CELL = `border:none !important;padding:${TABLE_ROW_PADDING};${FONT_BODY}line-height:1.8;vertical-align:top;background:transparent !important;`
|
||||
const NO_BORDER = `${PLAIN_CELL} vertical-align:bottom;`
|
||||
|
||||
@@ -87,9 +86,9 @@ const INFO_VALUE_INLINE_LEFT = `flex:1;min-width:0;box-sizing:border-box;border-
|
||||
const INFO_VALUE_INLINE_CENTER = `flex:1;min-width:0;box-sizing:border-box;border-bottom:${INFO_UNDERLINE} !important;padding:0 2px 2px;text-align:center;min-height:1.3em;`
|
||||
const INFO_CELL_LEFT = `${NO_BORDER}text-align:left;`
|
||||
const INFO_CELL_RIGHT = `${NO_BORDER}text-align:left;`
|
||||
const INFO_GAP_BEFORE_PRODUCT = 24
|
||||
const INFO_GAP_BEFORE_PRODUCT = 12
|
||||
const GAP_BEFORE_PRODUCT_CELL = `border:none !important;padding:0 !important;line-height:0 !important;font-size:0 !important;background:transparent !important;height:${INFO_GAP_BEFORE_PRODUCT}px !important;min-height:${INFO_GAP_BEFORE_PRODUCT}px !important;`
|
||||
const INFO_GAP_BEFORE_SIGN = 24
|
||||
const INFO_GAP_BEFORE_SIGN = 12
|
||||
const GAP_BEFORE_SIGN_CELL = `border:none !important;padding:0 !important;line-height:0 !important;font-size:0 !important;background:transparent !important;height:${INFO_GAP_BEFORE_SIGN}px !important;min-height:${INFO_GAP_BEFORE_SIGN}px !important;`
|
||||
const SIDE_LABELS_COL_WIDTH = 36
|
||||
|
||||
@@ -99,7 +98,7 @@ function buildInboundSlipSignItem(label: string, valueContent: string, minWidth:
|
||||
|
||||
function buildInboundSlipSignRowHtml(): string {
|
||||
return `<tr class="inbound-slip-sign-row">
|
||||
<td colspan="7" class="inbound-slip-sign-row-cell" style="border:none !important;padding:${TABLE_ROW_PADDING};text-align:center;${FONT_SIGN}background:transparent !important;">
|
||||
<td colspan="8" class="inbound-slip-sign-row-cell" style="border:none !important;padding:${TABLE_ROW_PADDING};text-align:center;${FONT_SIGN}background:transparent !important;">
|
||||
<div class="inbound-slip-sign-inner">
|
||||
${buildInboundSlipSignItem('仓库:', ' ', '80px')}
|
||||
${buildInboundSlipSignItem('审核:', ' ', '80px')}
|
||||
@@ -149,6 +148,10 @@ function formatQty(value?: number | string | null): string {
|
||||
return num.toFixed(3)
|
||||
}
|
||||
|
||||
function buildDeliveryUnit(plan: Plan): string {
|
||||
return plan.customerName?.trim() || plan.destinationName?.trim() || ''
|
||||
}
|
||||
|
||||
function formatInboundSlipProductGridCellContent(content: string): string {
|
||||
const plain = content
|
||||
.trim()
|
||||
@@ -161,15 +164,22 @@ function formatInboundSlipProductGridCellContent(content: string): string {
|
||||
return plain || ' '
|
||||
}
|
||||
|
||||
function buildProductRows(products: PlanProduct[] = [], ctx?: InboundSlipRenderContext): string {
|
||||
function buildProductRows(
|
||||
products: PlanProduct[] = [],
|
||||
planNo = '',
|
||||
ctx?: InboundSlipRenderContext
|
||||
): string {
|
||||
if (!products.length) {
|
||||
return `<tr><td class="inbound-slip-product-cell" colspan="7" style="${PRODUCT_CELL}">暂无产品信息</td></tr>`
|
||||
return `<tr><td class="inbound-slip-product-cell" colspan="8" style="${PRODUCT_CELL}">暂无产品信息</td></tr>`
|
||||
}
|
||||
|
||||
const planNoText = formatInboundSlipProductGridCellContent(planNo)
|
||||
|
||||
const dataRows = products
|
||||
.map((product) => {
|
||||
const qty = product.inboundQty ?? product.inboundEstimateQty ?? product.quantity ?? ''
|
||||
return `<tr>
|
||||
<td class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_GRID_CELL}">${planNoText}</td>
|
||||
<td class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_GRID_CELL}">${formatInboundSlipProductGridCellContent(resolveSlipProductCode(product, ctx))}</td>
|
||||
<td class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_GRID_CELL}">${formatInboundSlipProductGridCellContent(product.productName || '')}</td>
|
||||
<td class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_GRID_CELL}">${formatInboundSlipProductGridCellContent(product.model || '')}</td>
|
||||
@@ -451,25 +461,8 @@ export function buildInboundSlipCompanySealHtml(): string {
|
||||
return `<img src="${INBOUND_SLIP_SEAL_IMAGE_SRC}" alt="印章" style="${INBOUND_SLIP_SEAL_IMAGE_STYLE}" />`
|
||||
}
|
||||
|
||||
const SEAL_BLOCK = `<div class="inbound-slip-seal-block" style="position:relative;padding:0;${FONT_BODY}${SEAL_BLOCK_LINE_STYLE}">
|
||||
<!-- 印章图片 -->
|
||||
{{companySeal}}
|
||||
<div class="inbound-slip-seal-lines" style="position:relative;z-index:1;${SEAL_BLOCK_LINE_STYLE}margin:0;padding:0;">
|
||||
地址:{{companyAddress}}<br />
|
||||
送货单位固话:{{companyPhone}}<br />
|
||||
联系人电话:{{contactPhone}}
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
const SIDE_LABEL_CELL = `border:none !important;padding:0 !important;vertical-align:top;text-align:center;${FONT_SIDE}line-height:1.35;background:transparent !important;`
|
||||
|
||||
function buildInboundSlipTermsHtml(): string {
|
||||
return `<div class="inbound-slip-terms-text" style="${TERMS_TEXT_STYLE}">
|
||||
1.本送货单如购销双方未签订合同,视为有效;<br />
|
||||
2.本次送货含桶${HANDWRITE_BLANK}个,回收${HANDWRITE_BLANK}个,结存${HANDWRITE_BLANK}个;
|
||||
</div>`
|
||||
}
|
||||
|
||||
/** 右侧联次:单列纵向,带圈序号 + 每字一行依次向下排列 */
|
||||
export function buildInboundSlipSideLabelsHtml(): string {
|
||||
return `<table class="inbound-slip-side-labels" style="border:none !important;border-collapse:collapse;margin:0 auto;width:auto;">
|
||||
@@ -502,24 +495,10 @@ export function generateInboundSlipTemplate(_plan?: Plan): string {
|
||||
<tr>
|
||||
<td colspan="4" style="${INFO_CELL_LEFT}">
|
||||
<div class="inbound-slip-info-row" style="${INFO_ROW}">
|
||||
<span class="inbound-slip-info-label" style="${INFO_LABEL}">收货单位:</span>
|
||||
<span class="inbound-slip-info-label" style="${INFO_LABEL}">送货单位:</span>
|
||||
<span class="inbound-slip-info-value-left" style="${INFO_VALUE_INLINE_LEFT}">{{receivingUnit}} </span>
|
||||
</div>
|
||||
</td>
|
||||
<td colspan="3" style="${INFO_CELL_RIGHT}">
|
||||
<div class="inbound-slip-info-row" style="${INFO_ROW}">
|
||||
<span class="inbound-slip-info-label" style="${INFO_LABEL}">单据编号:</span>
|
||||
<span class="inbound-slip-info-value-center" style="${INFO_VALUE_INLINE_CENTER}">{{planNo}} </span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" style="${INFO_CELL_LEFT}">
|
||||
<div class="inbound-slip-info-row" style="${INFO_ROW}">
|
||||
<span class="inbound-slip-info-label" style="${INFO_LABEL}">收货联系方式:</span>
|
||||
<span class="inbound-slip-info-value-left" style="${INFO_VALUE_INLINE_LEFT}">{{receiveContact}} </span>
|
||||
</div>
|
||||
</td>
|
||||
<td colspan="3" style="${INFO_CELL_RIGHT}">
|
||||
<div class="inbound-slip-info-row" style="${INFO_ROW}">
|
||||
<span class="inbound-slip-info-label" style="${INFO_LABEL}">日 期:</span>
|
||||
@@ -531,10 +510,11 @@ export function generateInboundSlipTemplate(_plan?: Plan): string {
|
||||
|
||||
<div class="inbound-slip-section-gap inbound-slip-gap-before-product" style="${GAP_BEFORE_PRODUCT_CELL}"> </div>
|
||||
|
||||
<!-- 2. 物料明细表(含备注、条款+印章;可拖表头/明细列宽行高) -->
|
||||
<!-- 2. 物料明细表(含备注;可拖表头/明细列宽行高) -->
|
||||
<table class="inbound-slip-product-table" style="width:100%;border-collapse:collapse;table-layout:fixed;border:none !important;">
|
||||
${PRODUCT_COLGROUP}
|
||||
<tr class="inbound-slip-product-header">
|
||||
<th class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_TH}">单据编号</th>
|
||||
<th class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_TH}">物料编号</th>
|
||||
<th class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_TH}">物料名称</th>
|
||||
<th class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_TH}">规格</th>
|
||||
@@ -545,18 +525,10 @@ export function generateInboundSlipTemplate(_plan?: Plan): string {
|
||||
</tr>
|
||||
{{productTable}}
|
||||
<tr>
|
||||
<td colspan="7" class="inbound-slip-product-cell" style="${PRODUCT_TABLE_CELL_LEFT}">
|
||||
<td colspan="8" class="inbound-slip-product-cell" style="${PRODUCT_TABLE_CELL_LEFT}">
|
||||
备注:{{remark}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="5" class="inbound-slip-product-cell inbound-slip-terms-cell" style="${PRODUCT_TERMS_CELL_LEFT}">
|
||||
${buildInboundSlipTermsHtml()}
|
||||
</td>
|
||||
<td colspan="2" class="inbound-slip-product-cell" style="${PRODUCT_TABLE_CELL_LEFT}">
|
||||
${SEAL_BLOCK}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="inbound-slip-section-gap inbound-slip-gap-before-sign" style="${GAP_BEFORE_SIGN_CELL}"> </div>
|
||||
@@ -655,7 +627,7 @@ function patchInboundSlipProductGridCellAttrs(attrs: string): string {
|
||||
function normalizeInboundSlipProductGridCell(html: string): string {
|
||||
if (!html.includes('inbound-slip-doc') || !html.includes('inbound-slip-product-cell')) return html
|
||||
|
||||
let result = html.replace(/<tr([^>]*)>\s*(<th[\s\S]*?<\/th>\s*){7}\s*<\/tr>/g, (rowMatch) => {
|
||||
let result = html.replace(/<tr([^>]*)>\s*(<th[\s\S]*?<\/th>\s*){8}\s*<\/tr>/g, (rowMatch) => {
|
||||
if (!rowMatch.includes('物料编号') || rowMatch.includes('colspan')) return rowMatch
|
||||
let row = rowMatch.includes('inbound-slip-product-header')
|
||||
? rowMatch
|
||||
@@ -665,7 +637,7 @@ function normalizeInboundSlipProductGridCell(html: string): string {
|
||||
})
|
||||
})
|
||||
|
||||
result = result.replace(/<tr([^>]*)>\s*(<td(?![^>]*colspan)[^>]*>[\s\S]*?<\/td>\s*){7}\s*<\/tr>/g, (rowMatch) => {
|
||||
result = result.replace(/<tr([^>]*)>\s*(<td(?![^>]*colspan)[^>]*>[\s\S]*?<\/td>\s*){8}\s*<\/tr>/g, (rowMatch) => {
|
||||
if (
|
||||
rowMatch.includes('备注') ||
|
||||
rowMatch.includes('1.本送货单') ||
|
||||
@@ -862,9 +834,10 @@ function normalizeInboundSlipProductTableFooter(html: string): string {
|
||||
|
||||
let result = html
|
||||
|
||||
result = result.replace(/<td([^>]*colspan="7"[^>]*)>(\s*)备注:/g, (_match, attrs, space) => {
|
||||
result = result.replace(/<td([^>]*colspan="[78]"[^>]*)>(\s*)备注:/g, (_match, attrs, space) => {
|
||||
const withSpan = String(attrs).replace(/colspan="[78]"/, 'colspan="8"')
|
||||
const patched = patchProductFooterCellAttrs(
|
||||
attrs,
|
||||
withSpan,
|
||||
'inbound-slip-product-cell',
|
||||
PRODUCT_TABLE_CELL_LEFT
|
||||
)
|
||||
@@ -949,33 +922,44 @@ function normalizeInboundSlipProductColWidths(html: string): string {
|
||||
)
|
||||
}
|
||||
|
||||
/** 条款区两行间距与括号内手写空白(兼容已保存模板) */
|
||||
/** 移除条款+地址印章行(兼容已保存模板) */
|
||||
function normalizeInboundSlipTermsText(html: string): string {
|
||||
if (!html.includes('inbound-slip-doc') || !html.includes('1.本送货单')) {
|
||||
if (!html.includes('inbound-slip-doc')) return html
|
||||
if (
|
||||
!html.includes('1.本送货单') &&
|
||||
!html.includes('inbound-slip-terms-text') &&
|
||||
!html.includes('inbound-slip-seal-block')
|
||||
) {
|
||||
return html
|
||||
}
|
||||
|
||||
const termsHtml = buildInboundSlipTermsHtml()
|
||||
|
||||
if (html.includes('inbound-slip-terms-text')) {
|
||||
return html.replace(/<div class="inbound-slip-terms-text"[\s\S]*?<\/div>/, termsHtml.trim())
|
||||
}
|
||||
|
||||
return html.replace(
|
||||
/1\.本送货单如购销双方未签订合同,视为有效;[\s\S]*?结存\([^)]*\)个;/,
|
||||
termsHtml.trim()
|
||||
// 条款+印章整行删除(同一 tr 内左右两格)
|
||||
let result = html.replace(
|
||||
/<tr[^>]*>\s*<td[^>]*colspan="[45]"[^>]*>[\s\S]*?(?:inbound-slip-terms-text|1\.本送货单)[\s\S]*?<\/tr>\s*/gi,
|
||||
''
|
||||
)
|
||||
// 兜底:独立印章行
|
||||
result = result.replace(
|
||||
/<tr[^>]*>\s*<td[^>]*>[\s\S]*?inbound-slip-seal-block[\s\S]*?<\/tr>\s*/gi,
|
||||
''
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
/** 刷新已有间距行的 inline 样式(TinyMCE 保存后可能丢失) */
|
||||
function refreshInboundSlipGapBeforeProduct(html: string): string {
|
||||
return html.replace(
|
||||
/<tr class="inbound-slip-gap-before-product">[\s\S]*?<\/tr>/,
|
||||
`<tr class="inbound-slip-gap-before-product">\n <td colspan="7" class="inbound-slip-gap-before-product-cell" style="${GAP_BEFORE_PRODUCT_CELL}"> </td>\n </tr>`
|
||||
)
|
||||
return html
|
||||
.replace(
|
||||
/<tr class="inbound-slip-gap-before-product">[\s\S]*?<\/tr>/,
|
||||
`<tr class="inbound-slip-gap-before-product">\n <td colspan="8" class="inbound-slip-gap-before-product-cell" style="${GAP_BEFORE_PRODUCT_CELL}"> </td>\n </tr>`
|
||||
)
|
||||
.replace(
|
||||
/<div class="inbound-slip-section-gap inbound-slip-gap-before-product"[^>]*>[\s\S]*?<\/div>/,
|
||||
`<div class="inbound-slip-section-gap inbound-slip-gap-before-product" style="${GAP_BEFORE_PRODUCT_CELL}"> </div>`
|
||||
)
|
||||
}
|
||||
|
||||
/** 在收货联系方式行与物料表之间插入间距行(兼容已保存模板) */
|
||||
/** 在信息区与物料表之间插入间距(兼容已保存模板) */
|
||||
function normalizeInboundSlipGapBeforeProduct(html: string): string {
|
||||
if (!html.includes('inbound-slip-doc')) {
|
||||
return html
|
||||
@@ -985,16 +969,21 @@ function normalizeInboundSlipGapBeforeProduct(html: string): string {
|
||||
return refreshInboundSlipGapBeforeProduct(html)
|
||||
}
|
||||
|
||||
const gapRow = `<tr class="inbound-slip-gap-before-product"><td colspan="7" class="inbound-slip-gap-before-product-cell" style="${GAP_BEFORE_PRODUCT_CELL}"> </td></tr>\n `
|
||||
const gapRow = `<tr class="inbound-slip-gap-before-product"><td colspan="8" class="inbound-slip-gap-before-product-cell" style="${GAP_BEFORE_PRODUCT_CELL}"> </td></tr>\n `
|
||||
const productHeader = `(?:单据编号|物料编号)`
|
||||
|
||||
const withComment = html.replace(
|
||||
/(<span class="inbound-slip-info-label"[^>]*>日(?: )?期:<\/span>[\s\S]*?<\/tr>\s*)(<!--\s*中:产品明细表[\s\S]*?<tr>\s*<th[^>]*>物料编号)/,
|
||||
new RegExp(
|
||||
`(<span class="inbound-slip-info-label"[^>]*>日(?: )?期:</span>[\\s\\S]*?</tr>\\s*)(<!--\\s*中:产品明细表[\\s\\S]*?<tr>\\s*<th[^>]*>${productHeader})`
|
||||
),
|
||||
`$1${gapRow}$2`
|
||||
)
|
||||
if (withComment !== html) return withComment
|
||||
|
||||
const withHeader = html.replace(
|
||||
/(<span class="inbound-slip-info-label"[^>]*>日(?: )?期:<\/span>[\s\S]*?<\/tr>\s*)(<tr>\s*<th[^>]*>物料编号)/,
|
||||
new RegExp(
|
||||
`(<span class="inbound-slip-info-label"[^>]*>日(?: )?期:</span>[\\s\\S]*?</tr>\\s*)(<tr>\\s*<th[^>]*>${productHeader})`
|
||||
),
|
||||
`$1${gapRow}$2`
|
||||
)
|
||||
if (withHeader !== html) return withHeader
|
||||
@@ -1007,10 +996,15 @@ function normalizeInboundSlipGapBeforeProduct(html: string): string {
|
||||
|
||||
/** 刷新签字区上方间距行 inline 样式 */
|
||||
function refreshInboundSlipGapBeforeSign(html: string): string {
|
||||
return html.replace(
|
||||
/<tr class="inbound-slip-gap-before-sign">[\s\S]*?<\/tr>/,
|
||||
`<tr class="inbound-slip-gap-before-sign">\n <td colspan="7" class="inbound-slip-gap-before-sign-cell" style="${GAP_BEFORE_SIGN_CELL}"> </td>\n </tr>`
|
||||
)
|
||||
return html
|
||||
.replace(
|
||||
/<tr class="inbound-slip-gap-before-sign">[\s\S]*?<\/tr>/,
|
||||
`<tr class="inbound-slip-gap-before-sign">\n <td colspan="8" class="inbound-slip-gap-before-sign-cell" style="${GAP_BEFORE_SIGN_CELL}"> </td>\n </tr>`
|
||||
)
|
||||
.replace(
|
||||
/<div class="inbound-slip-section-gap inbound-slip-gap-before-sign"[^>]*>[\s\S]*?<\/div>/,
|
||||
`<div class="inbound-slip-section-gap inbound-slip-gap-before-sign" style="${GAP_BEFORE_SIGN_CELL}"> </div>`
|
||||
)
|
||||
}
|
||||
|
||||
/** 在物料表与签字区之间插入间距行(兼容已保存模板) */
|
||||
@@ -1021,16 +1015,16 @@ function normalizeInboundSlipGapBeforeSign(html: string): string {
|
||||
return refreshInboundSlipGapBeforeSign(html)
|
||||
}
|
||||
|
||||
const gapRow = `<tr class="inbound-slip-gap-before-sign"><td colspan="7" class="inbound-slip-gap-before-sign-cell" style="${GAP_BEFORE_SIGN_CELL}"> </td></tr>\n\n `
|
||||
const gapRow = `<tr class="inbound-slip-gap-before-sign"><td colspan="8" class="inbound-slip-gap-before-sign-cell" style="${GAP_BEFORE_SIGN_CELL}"> </td></tr>\n\n `
|
||||
|
||||
const withComment = html.replace(
|
||||
/(<\/tr>\s*)(<!--\s*下:签字区[\s\S]*?<tr>\s*<td colspan="7"[^>]*>\s*仓库:)/,
|
||||
/(<\/tr>\s*)(<!--\s*下:签字区[\s\S]*?<tr>\s*<td colspan="[78]"[^>]*>\s*仓库:)/,
|
||||
`$1${gapRow}$2`
|
||||
)
|
||||
if (withComment !== html) return withComment
|
||||
|
||||
return html.replace(
|
||||
/(inbound-slip-seal-block[\s\S]*?<\/tr>\s*)(<tr>\s*<td colspan="7"[^>]*>\s*仓库:)/,
|
||||
/(inbound-slip-seal-block[\s\S]*?<\/tr>\s*)(<tr>\s*<td colspan="[78]"[^>]*>\s*仓库:)/,
|
||||
`$1${gapRow}$2`
|
||||
)
|
||||
}
|
||||
@@ -1065,7 +1059,7 @@ function normalizeInboundSlipSignRow(html: string): string {
|
||||
if (!html.includes('inbound-slip-doc') || !html.includes('仓库:')) return html
|
||||
|
||||
let result = html.replace(
|
||||
/<tr([^>]*)>\s*<td colspan="7"([^>]*)>([\s\S]*?仓库:[\s\S]*?司机\/送货人\(附车牌\)[^<]*[\s\S]*?)<\/td>\s*<\/tr>/g,
|
||||
/<tr([^>]*)>\s*<td colspan="[78]"([^>]*)>([\s\S]*?仓库:[\s\S]*?司机\/送货人\(附车牌\)[^<]*[\s\S]*?)<\/td>\s*<\/tr>/g,
|
||||
(match, trAttrs, tdAttrs, content) => {
|
||||
if (!content.includes('inbound-slip-sign-value') || content.includes('inbound-slip-sign-inner')) {
|
||||
return match
|
||||
@@ -1097,7 +1091,7 @@ function normalizeInboundSlipSignRow(html: string): string {
|
||||
nextTdAttrs = `${nextTdAttrs} class="inbound-slip-sign-row-cell"`
|
||||
}
|
||||
|
||||
return `<tr${trClassAttr}>\n <td colspan="7"${nextTdAttrs}>\n <div class="inbound-slip-sign-inner">${wrappedContent.trim()}</div>\n </td>\n </tr>`
|
||||
return `<tr${trClassAttr}>\n <td colspan="8"${nextTdAttrs}>\n <div class="inbound-slip-sign-inner">${wrappedContent.trim()}</div>\n </td>\n </tr>`
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1109,6 +1103,82 @@ function normalizeInboundSlipSignRow(html: string): string {
|
||||
return normalizeInboundSlipSignLabelMarkup(result)
|
||||
}
|
||||
|
||||
/** 信息区:送货单位 + 日期(兼容旧「收货单位/单据编号/收货联系方式」布局) */
|
||||
function normalizeInboundSlipInfoLayout(html: string): string {
|
||||
if (!html.includes('inbound-slip-info-table')) return html
|
||||
|
||||
const infoMatch = html.match(/<table class="inbound-slip-info-table"[^>]*>[\s\S]*?<\/table>/i)
|
||||
if (!infoMatch) return html
|
||||
|
||||
const infoHtml = infoMatch[0]
|
||||
const needsMigrate =
|
||||
infoHtml.includes('收货单位:') ||
|
||||
infoHtml.includes('收货联系方式:') ||
|
||||
infoHtml.includes('单据编号:') ||
|
||||
!infoHtml.includes('送货单位:') ||
|
||||
(infoHtml.match(/<tr[\s>]/g) || []).length > 1
|
||||
|
||||
if (!needsMigrate) return html
|
||||
|
||||
const newInfoTable = `<table class="inbound-slip-info-table" style="width:100%;border-collapse:collapse;table-layout:fixed;border:none !important;">
|
||||
${INFO_COLGROUP}
|
||||
<tr>
|
||||
<td colspan="4" style="${INFO_CELL_LEFT}">
|
||||
<div class="inbound-slip-info-row" style="${INFO_ROW}">
|
||||
<span class="inbound-slip-info-label" style="${INFO_LABEL}">送货单位:</span>
|
||||
<span class="inbound-slip-info-value-left" style="${INFO_VALUE_INLINE_LEFT}">{{receivingUnit}} </span>
|
||||
</div>
|
||||
</td>
|
||||
<td colspan="3" style="${INFO_CELL_RIGHT}">
|
||||
<div class="inbound-slip-info-row" style="${INFO_ROW}">
|
||||
<span class="inbound-slip-info-label" style="${INFO_LABEL}">日 期:</span>
|
||||
<span class="inbound-slip-info-value-center" style="${INFO_VALUE_INLINE_CENTER}">{{planDateChinese}} </span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>`
|
||||
|
||||
return html.replace(infoMatch[0], newInfoTable)
|
||||
}
|
||||
|
||||
/** 物料表:表头补「单据编号」列,并把旧 7 列数据行升为 8 列(兼容已保存模板) */
|
||||
function normalizeInboundSlipProductPlanNoColumn(html: string): string {
|
||||
if (!html.includes('inbound-slip-product-table')) return html
|
||||
|
||||
let result = html
|
||||
|
||||
if (!/>单据编号<\/th>/.test(result)) {
|
||||
result = result.replace(
|
||||
/(<tr[^>]*class="[^"]*inbound-slip-product-header[^"]*"[^>]*>\s*)(<th[^>]*>物料编号<\/th>)/,
|
||||
`$1<th class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_TH}">单据编号</th>\n $2`
|
||||
)
|
||||
result = result.replace(
|
||||
/(<tr>\s*)(<th[^>]*>物料编号<\/th>)/,
|
||||
`$1<th class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_TH}">单据编号</th>\n $2`
|
||||
)
|
||||
}
|
||||
|
||||
// 旧 7 列明细行:在首格前插入单据编号占位
|
||||
result = result.replace(
|
||||
/<tr([^>]*)>\s*((?:<td(?![^>]*colspan)[^>]*>[\s\S]*?<\/td>\s*){7})<\/tr>/g,
|
||||
(rowMatch, trAttrs, cells) => {
|
||||
if (
|
||||
rowMatch.includes('备注') ||
|
||||
rowMatch.includes('1.本送货单') ||
|
||||
rowMatch.includes('仓库:') ||
|
||||
rowMatch.includes('暂无产品信息') ||
|
||||
rowMatch.includes('单据编号')
|
||||
) {
|
||||
return rowMatch
|
||||
}
|
||||
return `<tr${trAttrs}>\n <td class="inbound-slip-product-cell inbound-slip-product-grid-cell" style="${PRODUCT_GRID_CELL}">{{planNo}}</td>\n ${cells}</tr>`
|
||||
}
|
||||
)
|
||||
|
||||
result = result.replace(/colspan="7"/g, 'colspan="8"')
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 旧版一体 layout-table / 曾拆出的 footer-table → 方案A:
|
||||
* 信息表 + 物料表(含备注条款) + 签字表。
|
||||
@@ -1143,6 +1213,7 @@ function migrateInboundSlipSplitTables(html: string): string {
|
||||
(_full, inner: string) => {
|
||||
const headerMatch =
|
||||
inner.match(/<tr[^>]*class="[^"]*inbound-slip-product-header[^"]*"[\s\S]*?<\/tr>/i) ||
|
||||
inner.match(/<tr>\s*<th[^>]*>单据编号[\s\S]*?<\/tr>/i) ||
|
||||
inner.match(/<tr>\s*<th[^>]*>物料编号[\s\S]*?<\/tr>/i)
|
||||
if (!headerMatch) return _full
|
||||
|
||||
@@ -1287,12 +1358,12 @@ export function replaceInboundSlipPlaceholders(
|
||||
|
||||
const replacements: Record<string, string> = {
|
||||
'{{companyName}}': INBOUND_SLIP_DEFAULTS.companyName,
|
||||
'{{receivingUnit}}': INBOUND_SLIP_DEFAULTS.receivingUnit,
|
||||
'{{receivingUnit}}': buildDeliveryUnit(plan),
|
||||
'{{planNo}}': plan.planNo || '',
|
||||
'{{receiveContact}}': '',
|
||||
'{{driverInfo}}': '',
|
||||
'{{planDateChinese}}': formatPlanDateChinese(plan.planDate),
|
||||
'{{productTable}}': buildProductRows(plan.products, ctx),
|
||||
'{{productTable}}': buildProductRows(plan.products, plan.planNo || '', ctx),
|
||||
'{{remark}}': plan.remark || '',
|
||||
'{{companyAddress}}': INBOUND_SLIP_DEFAULTS.companyAddress,
|
||||
'{{companyPhone}}': INBOUND_SLIP_DEFAULTS.companyPhone,
|
||||
@@ -1310,7 +1381,13 @@ export function replaceInboundSlipPlaceholders(
|
||||
normalizeInboundSlipProductTableFooter(
|
||||
normalizeInboundSlipProductColWidths(
|
||||
normalizeInboundSlipProductGridCell(
|
||||
normalizeInboundSlipGapBeforeProduct(normalizeInboundSlipSideLabelsPlaceholder(htmlContent))
|
||||
normalizeInboundSlipProductPlanNoColumn(
|
||||
normalizeInboundSlipInfoLayout(
|
||||
normalizeInboundSlipGapBeforeProduct(
|
||||
normalizeInboundSlipSideLabelsPlaceholder(htmlContent)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -1330,9 +1407,10 @@ export function replaceInboundSlipPlaceholders(
|
||||
export function restoreInboundSlipPlaceholders(template: string, plan: Plan): string {
|
||||
let result = template
|
||||
|
||||
const deliveryUnit = buildDeliveryUnit(plan)
|
||||
const dynamicReplacements: Record<string, string> = {
|
||||
'{{companyName}}': INBOUND_SLIP_DEFAULTS.companyName,
|
||||
'{{receivingUnit}}': INBOUND_SLIP_DEFAULTS.receivingUnit,
|
||||
'{{receivingUnit}}': deliveryUnit,
|
||||
'{{planNo}}': plan.planNo || '',
|
||||
'{{planDateChinese}}': formatPlanDateChinese(plan.planDate),
|
||||
'{{remark}}': plan.remark || '',
|
||||
@@ -1347,9 +1425,14 @@ export function restoreInboundSlipPlaceholders(template: string, plan: Plan): st
|
||||
result = result.replace(new RegExp(escaped, 'g'), placeholder)
|
||||
}
|
||||
|
||||
// 旧模板写死的本公司名 → 送货单位占位符
|
||||
if (deliveryUnit !== LEGACY_RECEIVING_UNIT) {
|
||||
result = result.replace(new RegExp(LEGACY_RECEIVING_UNIT.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '{{receivingUnit}}')
|
||||
}
|
||||
|
||||
// 产品明细行还原为占位符(表头「实收数量」与「备注」行之间)
|
||||
result = result.replace(
|
||||
/(<th[^>]*>实收数量<\/th>\s*<\/tr>\s*)([\s\S]*?)(\s*<tr>\s*<td colspan="7"[^>]*>\s*备注:)/,
|
||||
/(<th[^>]*>实收数量<\/th>\s*<\/tr>\s*)([\s\S]*?)(\s*<tr>\s*<td colspan="[78]"[^>]*>\s*备注:)/,
|
||||
'$1{{productTable}}$3'
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/** 运输单据打印弹窗:红章显隐(本次弹窗有效) */
|
||||
|
||||
export const SLIP_SEAL_IMG_RE = /<img[^>]*alt=["']印章["'][^>]*>/gi
|
||||
|
||||
export function stripSlipSealImages(html: string): string {
|
||||
return html.replace(SLIP_SEAL_IMG_RE, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* 将公司章插回单据 HTML。
|
||||
* 优先落在「印章图片」注释后;否则插入指定 seal-block 容器开头。
|
||||
*/
|
||||
export function injectSlipSealImages(
|
||||
html: string,
|
||||
sealHtml: string,
|
||||
sealBlockClass: string
|
||||
): string {
|
||||
let result = stripSlipSealImages(html)
|
||||
if (!sealHtml) return result
|
||||
|
||||
if (/<!--\s*印章图片[^>]*-->/.test(result)) {
|
||||
let sealCommentIndex = 0
|
||||
return result.replace(/<!--\s*印章图片[^>]*-->/g, (match) => {
|
||||
sealCommentIndex += 1
|
||||
if (sealCommentIndex === 1) return `${match}\n ${sealHtml}`
|
||||
return match
|
||||
})
|
||||
}
|
||||
|
||||
if (sealBlockClass && result.includes(sealBlockClass)) {
|
||||
const escaped = sealBlockClass.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
return result.replace(
|
||||
new RegExp(`(<div class="${escaped}"[^>]*>)`),
|
||||
`$1\n <!-- 印章图片 -->\n ${sealHtml}\n `
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -87,9 +87,9 @@ const INFO_VALUE_INLINE_LEFT = `flex:1;min-width:0;box-sizing:border-box;border-
|
||||
const INFO_VALUE_INLINE_CENTER = `flex:1;min-width:0;box-sizing:border-box;border-bottom:${INFO_UNDERLINE} !important;padding:0 2px 2px;text-align:center;min-height:1.3em;`
|
||||
const INFO_CELL_LEFT = `${NO_BORDER}text-align:left;`
|
||||
const INFO_CELL_RIGHT = `${NO_BORDER}text-align:left;`
|
||||
const INFO_GAP_BEFORE_PRODUCT = 24
|
||||
const INFO_GAP_BEFORE_PRODUCT = 12
|
||||
const GAP_BEFORE_PRODUCT_CELL = `border:none !important;padding:0 !important;line-height:0 !important;font-size:0 !important;background:transparent !important;height:${INFO_GAP_BEFORE_PRODUCT}px !important;min-height:${INFO_GAP_BEFORE_PRODUCT}px !important;`
|
||||
const INFO_GAP_BEFORE_SIGN = 24
|
||||
const INFO_GAP_BEFORE_SIGN = 12
|
||||
const GAP_BEFORE_SIGN_CELL = `border:none !important;padding:0 !important;line-height:0 !important;font-size:0 !important;background:transparent !important;height:${INFO_GAP_BEFORE_SIGN}px !important;min-height:${INFO_GAP_BEFORE_SIGN}px !important;`
|
||||
const SIDE_LABELS_COL_WIDTH = 36
|
||||
|
||||
@@ -1434,10 +1434,15 @@ function normalizeSubpackSlipRemoveTermsRow(html: string): string {
|
||||
|
||||
/** 刷新已有间距行的 inline 样式(TinyMCE 保存后可能丢失) */
|
||||
function refreshSubpackSlipGapBeforeProduct(html: string): string {
|
||||
return html.replace(
|
||||
/<tr class="subpack-slip-gap-before-product">[\s\S]*?<\/tr>/,
|
||||
`<tr class="subpack-slip-gap-before-product">\n <td colspan="7" class="subpack-slip-gap-before-product-cell" style="${GAP_BEFORE_PRODUCT_CELL}"> </td>\n </tr>`
|
||||
)
|
||||
return html
|
||||
.replace(
|
||||
/<tr class="subpack-slip-gap-before-product">[\s\S]*?<\/tr>/,
|
||||
`<tr class="subpack-slip-gap-before-product">\n <td colspan="7" class="subpack-slip-gap-before-product-cell" style="${GAP_BEFORE_PRODUCT_CELL}"> </td>\n </tr>`
|
||||
)
|
||||
.replace(
|
||||
/<div class="subpack-slip-section-gap subpack-slip-gap-before-product"[^>]*>[\s\S]*?<\/div>/,
|
||||
`<div class="subpack-slip-section-gap subpack-slip-gap-before-product" style="${GAP_BEFORE_PRODUCT_CELL}"> </div>`
|
||||
)
|
||||
}
|
||||
|
||||
/** 在收货联系方式行与物料表之间插入间距行(兼容已保存模板) */
|
||||
@@ -1472,10 +1477,15 @@ function normalizeSubpackSlipGapBeforeProduct(html: string): string {
|
||||
|
||||
/** 刷新签字区上方间距行 inline 样式 */
|
||||
function refreshSubpackSlipGapBeforeSign(html: string): string {
|
||||
return html.replace(
|
||||
/<tr class="subpack-slip-gap-before-sign">[\s\S]*?<\/tr>/,
|
||||
`<tr class="subpack-slip-gap-before-sign">\n <td colspan="7" class="subpack-slip-gap-before-sign-cell" style="${GAP_BEFORE_SIGN_CELL}"> </td>\n </tr>`
|
||||
)
|
||||
return html
|
||||
.replace(
|
||||
/<tr class="subpack-slip-gap-before-sign">[\s\S]*?<\/tr>/,
|
||||
`<tr class="subpack-slip-gap-before-sign">\n <td colspan="7" class="subpack-slip-gap-before-sign-cell" style="${GAP_BEFORE_SIGN_CELL}"> </td>\n </tr>`
|
||||
)
|
||||
.replace(
|
||||
/<div class="subpack-slip-section-gap subpack-slip-gap-before-sign"[^>]*>[\s\S]*?<\/div>/,
|
||||
`<div class="subpack-slip-section-gap subpack-slip-gap-before-sign" style="${GAP_BEFORE_SIGN_CELL}"> </div>`
|
||||
)
|
||||
}
|
||||
|
||||
/** 在物料表与签字区之间插入间距行(兼容已保存模板) */
|
||||
|
||||
Reference in new issue
Block a user