Files
shanxia2.0/docs/superpowers/specs/2026-08-20-crm-invoice-paste-recognize-design.md
T
2026-08-21 09:35:11 +08:00

6.3 KiB
Raw Blame History

CRM 客户开票信息 · 粘贴快速识别 · 设计说明

Demo:docs/demo-crm-invoice-paste-recognize.html(方案 A)
日期:2026-08-20
状态:已实现(待联调验收)

1. 背景与目标

客户管理详情弹窗右侧「开票信息」新增/编辑时,业务常从微信/邮件粘贴整段「开票资料」文本,再手工逐项填入,效率低且易错。

目标: 在新增/编辑弹窗顶部提供「粘贴识别」区,用纯前端规则解析常见开票资料文本,并将结果填入对应表单字段;用户可再微调后保存。不依赖后端、不调用大模型。

非目标:

  • 不新增数据库字段(银行行号、邮编、邮寄地址等仍无独立列)
  • 不接阿里云 / OCR / 大模型
  • 不改开票信息保存 API 与权限

2. 已确认决策

项 结论
入口布局 方案 A:新增/编辑弹窗表单顶部「粘贴识别」区
识别方式 纯前端规则(标签别名 + 行解析),零成本、离线
回填策略 识别后覆盖对应字段(已有值一并覆盖)
无表单字段的信息 写入 remark(银行行号、邮编、发票邮寄信息等)
备注合并 识别产生的备注内容追加到 remark 末尾;若与已有内容重复则不重复追加
范围 仅 web-admin 客户详情 → InvoiceInfo.vue;以及客户新增弹窗 CustomerFormDialog.vue 底部开票信息(二级粘贴弹窗)

3. 交互设计

组件:web-admin/src/views/crm/customer/components/InvoiceInfo.vue

在现有 el-form 顶部、.form-grid-2 字段区之前增加粘贴区:

el-dialog「添加/修改开票信息」
  └─ el-form
       ├─ 粘贴识别面板(虚线边框 + primary-bg)
       │    ├─ 标题「粘贴识别」+ 简短提示
       │    ├─ el-input type=textarea(粘贴原文)
       │    ├─ [清空] [识别并填入]
       │    └─ 结果提示文案(成功/部分成功/失败)
       └─ 原有字段栅格(单位名称、税号…)

行为:

  1. 打开新增/编辑弹窗时,粘贴区默认空(不强制预填样例,避免误覆盖业务数据)。
  2. 点击「识别并填入」→ 调用解析工具 → 覆盖已映射字段;未映射信息写入/追加 remark。
  3. 点击「清空」仅清空粘贴框与提示,不清空表单字段。
  4. 关闭弹窗时重置粘贴区与表单(沿用现有 reset)。
  5. 识别失败或零字段:ElMessage.warning,不改表单。
  6. 部分成功:照常覆盖已识别字段,并用消息列出未识别的关键项(如缺单位名称)。

视觉对齐 Fresh Mint:主色 #10b981 / #059669 / #ecfdf5;按钮带图标(DocumentCopy / Delete / Close / Check 等既有习惯)。

4. 解析规则

新增工具:web-admin/src/utils/crm-invoice-paste-recognize.ts

参考既有模式:web-admin/src/utils/pl-paste-recognize.ts(纯函数、导出样例常量、返回 warnings)。

4.1 输入输出

interface CrmInvoicePasteResult {
  companyName?: string
  taxNo?: string
  companyAddress?: string
  legalRepresentative?: string
  agent?: string
  phoneFax?: string
  bankName?: string
  bankAccount?: string
  /** 无独立字段的附加信息,供写入 remark */
  remarkParts: string[]
  warnings: string[]
  /** 成功写入表单的字段数(不含 remarkParts) */
  filledCount: number
}

4.2 标签别名 → 字段

字段 标签别名(行首,支持全/半角冒号、标签内空格)
companyName 公司名称、单位名称、单位名、名称
taxNo 纳税人识别号、纳税识别号、统一社会信用代码、税号
companyAddress 单位地址、地址(见 4.3 邮寄段排除)
phoneFax 电话/传真、电话传真、传真、电话
bankName 银行开户行、开户银行、开户行
bankAccount 银行账号、账号、帐号(去除内部空格)
legalRepresentative 法定代表人、法人代表、法人
agent 代理人、委托代理人

「地址及电话」单独规则:整行取值后,若末尾匹配固话/手机则拆到 phoneFax,其余为 companyAddress。

4.3 段落与冲突

  • 若存在「发票邮寄地址」标题,其后块内的「姓名 / 电话 / 地址」不写入 companyName / phoneFax / companyAddress,而是汇总进 remarkParts(如 邮寄:汪璇 / 188… / 地址…)。
  • 「开票资料」等总标题行忽略。
  • 银行行号 / 联行号 / 行号 → remarkParts:银行行号:…(去空格)。
  • 邮编 → remarkParts:邮编:…。

4.4 验收样例(必须通过)

样例 1(安庆山峡): 公司名称、税号、开户行、帐号、地址、电话/传真正确回填;银行行号、邮编、邮寄信息进 remark。

样例 2(江峰生物): 单位名、税号、地址及电话拆分、开户行、带空格账号去空格、电话正确;银行行号进 remark。

单测:web-admin/src/utils/crm-invoice-paste-recognize.test.ts(与现有 vitest 风格一致,如 pl-cert-slots.test.ts)。

5. 数据流

用户粘贴文本
  → parseCrmInvoicePaste(text)
  → 覆盖 form 对应键(仅当解析值非空)
  → remark:将 remarkParts 用「;」拼接后追加到现有 remark
  → ElMessage 提示 filledCount / warnings
  → 用户点确定 → 现有 /web/crm/invoice-info/add|update(不变)

覆盖规则细节:某字段解析值为空字符串时,不清空该表单字段(只覆盖「识别到了」的字段)。

6. 风险与后续

风险 应对
新标签写法未覆盖 补别名;UI 提示可手动改
无标签散文文本 识别失败,手动填写
长期需行号等独立字段 另开变更加列;本设计不扩表

可选后续(本变更不做):规则失败时降级调大模型。

7. 实现清单(实现阶段)

  1. 新增 crm-invoice-paste-recognize.ts + 单测(含两段业务样例)
  2. 改 InvoiceInfo.vue:顶部粘贴区 + 识别回填
  3. 保留/更新 Demo:docs/demo-crm-invoice-paste-recognize.html(与线上规则一致即可)
  4. 更新 graphify(web-admin)

不改 backend、不改 SQL、不改 OpenSpec 基线(纯前端体验增强)。