b39501291c
- 取消 el-tooltip 包裹,改用纯 el-button 实现带图标的展开收起按钮 - 按 avue-crud 规范控制按钮显示,新增 searchIndex (默认 4)和 searchItemCount 控制逻辑 - 搜索项数量未超出 searchIndex 时隐藏展开收起按钮 - 相关组件支付模块多处应用此统一方案(bill-ledger-search、bill-payment-search、invoice-application、invoice-receipt、receipt-claim-record、receipt-flow) - 维护展开状态对应的箭头图标和文案为“收起”与“展开” refactor(business): 优化路线变更内容渲染与样式 - 替换旧的 formatRouteChangeContent 为更详细的 parseRouteChangeContent,支持多分组与字段显示 - 路线变更内容支持多层次分组、字段标签、改变前后值对比及未变更标识展示 - 取消全局列溢出提示以避免多行内容被截断,针对路线变更内容列显式禁用溢出提示并设置白色换行样式 - 在 business-crud-page、waybill-manage-page、loading-manage 相关页面和组件中统一应用该解析与渲染方案 refactor(option): 调整 measurement-unit 配置以提升表格列展示 - 移除 menuWidth 配置,采用 menuFixed 固定右侧模式 - 为创建人和更新人列添加 minWidth 110,时间列添加 width 180,避免内容被截断 - 表格列数组顺序调整,确保搜索与展示保持一致 refactor(payment & receipt-flow): 统一支付及回执模块工具栏和表格行为 - 按照统一规范和用户反馈,调整多处页面的搜索栏操作区和表格列配置 - 引入 TableHeaderTools 组件和相关状态传递,实现搜索显隐与表格尺寸切换统一 - 优化 avue-crud 表格组件的交互一致性和视觉表现 refactor(utils): 重构路线变更内容解析函数 - 新增 parseRouteChangeContent 以支持复杂路线变更文本的解析与分组 - 识别运单号、字段标签、变更前后值,支持多种箭头符号及调整描述 - 用法推广至相关业务页面,支持更加直观的路线变更展示界面
134 lines
4.5 KiB
JavaScript
134 lines
4.5 KiB
JavaScript
/**
|
||
* 路线变更记录内容解析
|
||
*
|
||
* 存储格式(单行):
|
||
* YD-20260904-0022:发货地 A → B;到货地 C → D;YD-20260904-0025:发货地 E → F;到货地 G → H
|
||
* 路线顺序:A → B → C 调整为 B → A → C
|
||
*
|
||
* 解析结果(分组,供变更记录列表按「分组标题 - 字段 - 变更前/后」渲染):
|
||
* [
|
||
* {
|
||
* title: 'YD-20260904-0022',
|
||
* text: '',
|
||
* fields: [{ label: '发货地', before: 'A', after: 'B', single: 'B', changed: true, unchanged: false }],
|
||
* },
|
||
* { title: '', text: '无法识别的原文', fields: [] },
|
||
* ]
|
||
*
|
||
* 说明:仅用于展示层,不改变已存储的 content 文本(历史数据同样适用)。
|
||
*/
|
||
// 长标签必须排在短标签前(「发货地址」优先于「发货地」),否则会被前缀吃掉
|
||
const FIELD_LABELS = ['发货地址', '到货地址', '发货地', '到货地', '装货地', '卸货地', '收货地'];
|
||
const FIELD_LABEL_PATTERN = new RegExp(`^(${FIELD_LABELS.join('|')})\\s*([\\s\\S]*)$`);
|
||
// 变更前 / 变更后分隔符:→、➔、➜、->、-->、–>、—>
|
||
const ARROW_PATTERN = /\s*(?:→|➔|➜|[\-–—]{1,2}\s*>)\s*/;
|
||
// 运单地址之外的变更描述,例如「A → B → C 调整为 B → A → C」
|
||
const ADJUST_PATTERN = /^([\s\S]*?)\s调整为\s*([\s\S]*)$/;
|
||
const EMPTY_TEXT = ['', '-', '--', '—', 'null', 'undefined'];
|
||
// 冒号前内容超过该长度时不视为分组标题(运单号通常长于该值)
|
||
const TITLE_MAX_LENGTH = 12;
|
||
|
||
const normalizeValue = value => {
|
||
const text = String(value === undefined || value === null ? '' : value).trim();
|
||
return EMPTY_TEXT.includes(text.toLowerCase()) ? '-' : text;
|
||
};
|
||
|
||
const buildField = (label, rawValue) => {
|
||
const value = String(rawValue === undefined || rawValue === null ? '' : rawValue).trim();
|
||
const adjustMatched = value.match(ADJUST_PATTERN);
|
||
let before = value;
|
||
let after = '';
|
||
let hasPair = false;
|
||
|
||
if (adjustMatched) {
|
||
before = adjustMatched[1];
|
||
after = adjustMatched[2];
|
||
hasPair = true;
|
||
} else {
|
||
const parts = value.split(ARROW_PATTERN);
|
||
if (parts.length > 1) {
|
||
before = parts[0];
|
||
after = parts[1];
|
||
hasPair = true;
|
||
}
|
||
}
|
||
|
||
const beforeText = normalizeValue(before);
|
||
const afterText = normalizeValue(after);
|
||
|
||
return {
|
||
label,
|
||
before: beforeText,
|
||
after: afterText,
|
||
// 单值展示(未拆分出前后值时使用)
|
||
single: hasPair ? afterText : beforeText,
|
||
changed: hasPair && beforeText !== afterText,
|
||
unchanged: hasPair && beforeText === afterText,
|
||
};
|
||
};
|
||
|
||
const parseSegment = segment => {
|
||
// 形如「发货地 A → B」(前面只有「;」分隔,无分组标题)→ 归入上一条运单
|
||
const leadingMatched = segment.match(FIELD_LABEL_PATTERN);
|
||
if (leadingMatched && leadingMatched[2].trim()) {
|
||
return {
|
||
type: 'field',
|
||
title: '',
|
||
text: '',
|
||
fields: [buildField(leadingMatched[1], leadingMatched[2])],
|
||
};
|
||
}
|
||
|
||
const colonIndex = segment.search(/[::]/);
|
||
if (colonIndex === -1) {
|
||
return { type: 'text', title: '', text: segment, fields: [] };
|
||
}
|
||
|
||
const head = segment.slice(0, colonIndex).trim();
|
||
const tail = segment.slice(colonIndex + 1).trim();
|
||
const fieldMatched = tail.match(FIELD_LABEL_PATTERN);
|
||
|
||
// 形如「YD-20260904-0022:发货地 A → B」
|
||
if (fieldMatched && fieldMatched[2].trim()) {
|
||
return {
|
||
type: 'waybill',
|
||
title: head,
|
||
text: '',
|
||
fields: [buildField(fieldMatched[1], fieldMatched[2])],
|
||
};
|
||
}
|
||
|
||
// 形如「路线顺序:A → B → C 调整为 B → A → C」
|
||
if (head && head.length <= TITLE_MAX_LENGTH) {
|
||
return { type: 'group', title: head, text: '', fields: [buildField('', tail)] };
|
||
}
|
||
|
||
return { type: 'text', title: '', text: segment, fields: [] };
|
||
};
|
||
|
||
export const parseRouteChangeContent = content => {
|
||
if (content === undefined || content === null) return [];
|
||
const text = String(content).replace(/\r\n?/g, '\n').trim();
|
||
if (!text) return [];
|
||
|
||
const segments = text
|
||
.split(/[;;\n]+/)
|
||
.map(item => item.trim())
|
||
.filter(Boolean);
|
||
|
||
const groups = [];
|
||
|
||
segments.forEach(segment => {
|
||
const group = parseSegment(segment);
|
||
const last = groups[groups.length - 1];
|
||
// 同一运单下的「到货地…」等无标题字段,合并进上一个运单分组
|
||
if (group.type === 'field' && last && (last.type === 'waybill' || last.type === 'field')) {
|
||
last.fields.push(...group.fields);
|
||
return;
|
||
}
|
||
groups.push(group);
|
||
});
|
||
|
||
return groups;
|
||
};
|