更新记录
1.0.0(2026-10-29)
首个版本:两张 CSV/TSV 按主键对齐,输出新增/删除/变更三类差异;差异按字段逐条列,带字段名与旧值→新值;数值容差(12.5 与 12.50 视为同一金额,0.01 以内不算差异);忽略列支持双向包含匹配;主键定位三级(精确/忽略大小写/包含),重复与空主键单独计数;只有一边有的列单独列出;引号与转义切分、逗号与制表符自动识别;差异 CSV 导出固定 5 列带 BOM;条目上限可配。UTS→Kotlin 编译通过(index.kt 983 行、57 个函数,生成 classes.dex 39,756 字节),71 项回归测试全部通过;真机未执行(本轮无可用设备)。
平台兼容性
uni-app
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| × | √ | × | × | √ | × | √ | × | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | - | × | × | - | × | × |
szhzy-sheet-diff
拿两张 CSV / TSV,按主键对齐,找出「只在旧表有、只在新表有、两边都有但字段变了」。 差异按字段逐条列出,数值列可设容差,结果可导出成差异 CSV。纯本地计算,不联网、不上传。
适用场景:供应商报价单两版核对、月度对账明细比上一期、ERP 导出与现场表格核对、改价前后的物料清单对照。
验证状态
| 项 | 结果 |
|---|---|
| UTS → Kotlin 编译 | 通过(编译成功,无 error;index.kt 983 行 / 57 个函数,生成 classes.dex 39,756 字节) |
| 纯逻辑回归测试 | 71 项,失败 0(dev-tools-uts/verify/sheet-diff/run-tests.mjs) |
| 真机运行 | 未执行(本轮无可用设备,adb devices 为空) |
| iOS / 鸿蒙 | 不支持,仅 Android |
真机未验证的部分:Android 平台层的文件读取(
content://授权 URI 转缓存)与差异 CSV 写出。 比对逻辑(切分、主键定位、容差、三类差异、统计、CSV 输出)已全部在 Node 下真实执行。
它不做什么
- 不读 xlsx / xls。Excel 请先用
szhzy-xlsx-export导出成 CSV,或用 WPS / Excel「另存为 CSV」。 - 不做模糊匹配。主键必须能对上,
P-1001与P1001是两个键。名字打错了会报成「一删一增」。 - 不是数据库 diff。不做字段类型变更、索引变更这类结构比对。
- 不支持多表 / 多 sheet。一次两张表,多张就分多次比。
- 不合并三方。只做两方差异,不做 base/ours/theirs 三路合并。
快速开始
import { compareText, compareFiles, previewTable } from '@/uni_modules/szhzy-sheet-diff'
// 1) 先看看两张表长什么样(列名对不对、列数是否一致)
const a = previewTable({ path: pathA })
console.log(a.headers, a.rows, a.cols)
// 2) 直接比对两段文本
const r = compareText({
textA: csvOld,
textB: csvNew,
key: '编号', // 不传则取第一列
tolerance: 0.01, // 数值容差,0.01 表示差一分钱不算差异
ignoreFields: ['备注'] // 不参与比较的列
})
console.log(r.summary) // { added, removed, changed, changedRows, same, duplicateKeys, emptyKeys, diffTotal }
console.log(r.items[0]) // { kind, title, key, field, oldValue, newValue }
// 3) 比对两个文件,并把差异清单写成 CSV
const f = compareFiles({
pathA: fileOld, // 系统选择器给的路径或 content:// 授权 URI
pathB: fileNew,
key: '编号',
writeCsv: true,
fileName: '报价差异-2026-10.csv'
})
console.log(f.path, f.bytes, f.summary)
同一份示例数据(两张报价单)的真实输出:
变更 P-1001 数量 500 → 600
变更 P-1001 交期 2026-10-15 → 2026-10-18
变更 P-1003 单价 23.00 → 23.50
变更 P-1006 交期 2026-10-22 → 2026-10-24
新增 P-1007 → 接线端子 | 2P | 2000 | 0.32 | 2026-10-25
删除 P-1004 铝基板 1.2M | 1.0mm | 600 | 6.20 | 2026-10-20 →
汇总:新增 1、删除 1、变更 4(涉及 3 行)、完全一致 2。
API
所有方法返回统一信封 { ok, code, message, ... },不抛异常。
| 方法 | 入参 | 返回要点 |
|---|---|---|
compareText |
{ textA, textB, key?, tolerance?, ignoreFields?, maxItems? } |
summary、items[]、columns、onlyInA、onlyInB、truncated |
compareFiles |
同上 + { pathA, pathB, dir?, fileName?, bom?, writeCsv? } |
同上 + path(差异 CSV 路径)、bytes |
previewTable |
{ path } 或 { text } |
headers、rows、cols、delimiter、sample(前 5 行) |
findColumn |
{ headers, name } |
index、matched,用于让用户点选主键列 |
toCsv |
{ textA, textB, key?, delimiter? } |
csv 文本 + summary(不落盘) |
kinds |
— | 三种变更类型 |
getOutputDir |
— | 默认输出目录(应用外部私有目录) |
getVersion |
— | 插件版本 |
参数
| 参数 | 默认 | 说明 |
|---|---|---|
key |
第一列表头 | 主键列名;精确匹配 → 忽略大小写 → 包含匹配 |
tolerance |
0 |
数值容差。两边都是数字时按差额比较,0.01 表示一分钱以内不算差异 |
ignoreFields |
[] |
不参与比较的列,支持包含匹配(配「备注说明」也能命中列名「备注」) |
maxItems |
2000 |
差异条目上限,超出置 truncated: true,但 summary 仍按全量统计 |
writeCsv |
false |
是否把差异清单写成文件 |
bom |
true |
写 UTF-8 BOM,Excel 双击不乱码 |
差异条目字段
| 字段 | 说明 |
|---|---|
kind |
added / removed / changed |
title |
中文名:新增 / 删除 / 变更 |
key |
主键值 |
field |
变更的字段名;新增与删除为空 |
oldValue / newValue |
旧值 / 新值;新增只有 newValue,删除只有 oldValue |
汇总字段
| 字段 | 说明 |
|---|---|
added / removed |
只在 B / 只在 A 的行数 |
changed |
字段级变更条数(一行改两列算 2) |
changedRows |
有变更的行数 |
same |
完全一致的行数 |
duplicateKeys |
主键重复的行数(按首次出现为准) |
emptyKeys |
主键为空被跳过的行数 |
diffTotal |
added + removed + changed |
差异 CSV 结构
固定 5 列,任何下游工具都能直接吃:
变更类型,主键,字段,旧值,新值
变更,P-1001,数量,500,600
变更,P-1003,单价,23.00,23.50
新增,P-1007,,,接线端子 | 2P | 2000 | 0.32 | 2026-10-25
删除,P-1004,,铝基板 1.2M | 1.0mm | 600 | 6.20 | 2026-10-20,
新增 / 删除行的「旧值/新值」里放的是整行内容(不含主键),用 | 分隔,方便一眼看出这行是什么。
错误码
| code | 含义 | 处理建议 |
|---|---|---|
EMPTY |
没传 textA/textB/pathA/pathB | 检查参数 |
NO_HEADER |
至少一张表解析不出表头 | 确认是 CSV/TSV 且首行是列名 |
KEY_NOT_FOUND |
主键列找不到 | 报错信息里会列出两边实际列名 |
TOO_LARGE |
单表超过 20000 行 | 先分片 |
FILE_NOT_FOUND |
文件不存在 | 确认选择器返回的路径 |
READ_FAILED |
读文件失败 | content:// 授权可能已过期,重新选 |
WRITE_FAILED |
写差异表失败 | 检查 dir 是否可写 |
演示工程
demo-sheet-diff/ 两个页面:
- 对账差异核对:两版报价单(6 行 vs 6 行)比对,顶部四个数字(新增 1 / 删除 1 / 变更 4 / 一致 2),下面按字段逐条列差异,旧值划线、新值绿色;
- 差异表导出:选两张表 → 比对 → 写出
diff-report.csv,附 CSV 预览与写出结果。
预览环境(H5)用固定示例数据走通页面流程,页面标注「示例数据」;装到 Android 后同一次操作读的是真实文件。
隐私说明
- 不联网、不上传、不写日志、不采集统计。
- 两张表只在内存中比对,插件不自行缓存。
- 输出文件默认写应用外部私有目录(
getExternalFilesDir),不需要存储权限。
授权
购买授权后可在自己的产品(含为客户定制的项目)中不限项目数、不限安装量使用。
源码授权另允许阅读与二次开发,但不得把插件本身(含修改版)再次分发或销售。详见 license.md。

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 13
赞赏 0
下载 12723595
赞赏 1958
赞赏
京公网安备:11010802035340号