更新记录

1.0.0(2026-10-27)

首个版本:流式读取 xlsx/xlsm(内存与行数无关)、取值还原(共享字符串/内联字符串/富文本/公式缓存值/布尔/日期含 1900 闰年偏差/数值浮点尾巴)、表头别名映射、逐行校验(必填/类型/长度/范围/枚举/唯一)、错误行 CSV 回执(行号为 Excel 真实行号)、统一错误码;UTS→Kotlin 编译通过,75 项核心逻辑测试通过(含 openpyxl 产出文件、共享字符串合成包、1/2/3/7/61 字节切块流式一致性、3001 行大表)。


平台兼容性

uni-app

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× √ - - √ × √ - -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

企业报表 Excel 导入(映射 · 校验)

让员工把 Excel 台账直接导进 App:读表 → 对字段 → 逐行校验 → 错误行回执,一条链路走完。

这个插件解决什么问题

做过 App 端导入的都遇到过这几件事:

  • 前端库解 xlsx,几万行就把页面卡死,最后只能规定"最多导入 5000 条";
  • 客户表格的表头年年变:今年叫「料号」,明年叫「物料编码」,代码里写死的列序一改就崩;
  • 日期读出来是 46296 这种序列号,金额读出来是 15.500000000000002;
  • 校验只报一句"导入失败",用户不知道是哪一行哪一列错了,最后还是要人工对表;
  • 表格中间有空行,报错行号整体偏移,用户按行号去改,改错了地方。

本插件针对的正是这五件事:

老问题 本插件的做法
大表卡死 工作表按 16K 字符分块喂进解析器,行数据边解析边交回,内存占用与总行数无关
表头漂移 字段与表头按「别名 → 完全相等 → 前缀 → 包含」四级匹配,同一列不会被两个字段抢走
日期变数字 按样式表识别日期格式,统一还原成 yyyy-MM-dd[ HH:mm:ss],含 1900 闰年历史偏差处理
报错说不清 每个错误带 Excel 真实行号、字段中文名、原值、中文原因,可一键导出 CSV 回执
行号偏移 行号取自 row 节点的 r 属性,表格中间空行、跳行都不会错位

适用场景

  • 入库单 / 发货单 / 盘点表的批量导入:仓管在电脑上填表,业务员用手机导进系统;
  • 客户、供应商、物料主数据的初始化与增量维护;
  • 现场盘点结果回传:离线填表,回办公室导入;
  • 任何"甲方给一张 Excel,要求 App 能读"的项目。

主要能力

  • 流式读取:maxRows 控制读取上限,超限返回 truncated: true,不会静默截断;
  • 取值还原:共享字符串、内联字符串、富文本、公式缓存值、布尔、日期、数值浮点尾巴;
  • 表头映射:字段定义带别名数组,mapColumns 返回每个字段命中第几列,缺列时列出缺失字段;
  • 逐行校验:必填、整数 / 数字 / 日期 / 布尔 / 枚举、最大长度、最小值 / 最大值、跨行唯一;
  • 错误回执:errorsToCsv 生成带 BOM 的 CSV,Excel 双击不乱码;
  • 零三方依赖:手写 OOXML 解析 + java.util.zip,不引 POI、不引 sheetjs,不与其它插件抢同名类。

快速上手

import { readWorkbook, importSheet, writeErrorReport } from '@/uni_modules/szhzy-xlsx-import'

// 1. 先看有哪些工作表(多表时让用户选)
const wb = readWorkbook({ path: '/sdcard/Android/data/<包名>/files/入库单.xlsx' })
// wb.sheets → [{ name: '入库明细', index: 0 }, { name: '说明', index: 1 }]

// 2. 读表 + 映射 + 校验,一次调用拿回全部结果
const res = importSheet({
  path: '/sdcard/Android/data/<包名>/files/入库单.xlsx',
  sheet: '入库明细',
  headerRow: 1,
  fields: [
    { key: 'code',  label: '物料编码', aliases: ['料号', '物料号'], required: true, type: 'text', maxLength: 20, unique: true },
    { key: 'qty',   label: '数量',     aliases: ['入库数量'],       required: true, type: 'int', min: 1 },
    { key: 'price', label: '单价',     aliases: ['含税单价'],       required: true, type: 'number', min: 0 },
    { key: 'date',  label: '入库日期', aliases: ['到货日期'],       required: true, type: 'date' }
  ]
})

// res.headers        → ['物料编码', '物料名称', '数量', '单价', '入库日期', '备注']
// res.rows           → 二维数组,已按"人看到的文本"还原
// res.rowNumbers     → 每行对应的 Excel 真实行号
// res.mapping.indexes→ 每个字段命中的列下标,-1 表示未找到
// res.errors         → [{ row: 5, key: 'qty', label: '数量', value: '一百二十', code: 'TYPE', message: '数量必须是整数' }]
// res.validCount / res.errorCount / res.truncated

// 3. 有错误就把回执导出来,直接发给填表人
if (res.errorCount > 0) {
  const out = writeErrorReport({ errors: res.errors, prefix: '入库单导入错误', stamp: '20261027-0912' })
  // out.path → 生成的文件路径
}

只要原始数据、不做映射校验时,importSheet 不传 fields 即可,返回的 rows 就是纯二维文本。

API

方法 说明
readWorkbook({ path }) 读工作表清单:{ ok, sheets: [{ name, index }] }
importSheet({ path, sheet?, headerRow?, maxRows?, fields?, previewRows? }) 读表 + 映射 + 校验,统一信封
writeErrorReport({ errors, dir?, path?, prefix?, stamp? }) 错误行写成 CSV 回执
getPrivateDir() 应用外部私有目录(读写都不需要存储权限)
getCodeText(code) / getVersion() / getDefaultMaxRows() 错误码中文说明、版本号、默认读取上限

统一错误码:ARG_INVALID、ZIP_INVALID、PART_MISSING、XML_INVALID、SHEET_NOT_FOUND、HEADER_ROW_INVALID、FIELD_NOT_MATCHED、IO_FAILED。 校验错误码:REQUIRED、TYPE、LENGTH、RANGE、ENUM、DUPLICATE。

常见问题

支持 .xls 吗? 不支持。.xls 是 97-2003 的二进制格式,与 xlsx 完全两套结构。请让填表人用 Excel/WPS 另存为 .xlsx(2007 及以上),插件会在 .xls 上直接返回 ARG_INVALID,不会静默读出错数据。

公式单元格读到什么? 读 Excel 保存时写入的缓存值(也就是打开文件时看到的结果)。如果第三方工具生成的 xlsx 只写了公式没写缓存值,这里会是空——Excel 手工另存一次就会补上。

能读多大的表? 默认上限 5000 行(getDefaultMaxRows()),可通过 maxRows 调整。行数上限的作用是防止误选百万行的表把设备内存吃满;超过上限会返回 truncated: true,不会静默截断。

需要存储权限吗? 不需要。读取应用私有目录、应用外部私有目录(getPrivateDir())都不声明任何权限。读取其它位置的文件时,由调用方先用系统文件选择器取得授权,把路径交给插件。

购买前必读

  • 仅支持 Android App(uni-app Vue3 / Vue2)。iOS、鸿蒙、小程序、H5 端不支持,代码里也没有对应实现。
  • 需要 HBuilderX 3.99 及以上,Android 6.0 及以上。
  • UTS 插件不在标准基座内,真机调试请使用自定义基座。
  • 读取在调用线程执行。十万行级别的表建议先用小 maxRows 做预览,确认无误再全量读取。
  • 本插件只做"读 + 校验",不做文件选择、不做上传、不做数据库写入——这些请配合你现有的文件选择器与接口层使用。

验证状态(如实说明)

项目 状态
UTS → Kotlin 编译 通过(HBuilderX 5.26,产物 index.kt 2120 行)
核心逻辑测试 75 项全部通过(Node 真实执行,含 openpyxl 产出文件的往返验证、共享字符串/公式缓存/日期样式的合成包验证、1/2/3/7/61 字节切块流式一致性验证、3001 行大表、跳号行编号)
真机运行验证 未执行。发布当晚 HBuilderX 命令行运行会话启动即退出(Stopped running),标准基座未切换到本工程,无法在真机上跑通读取链路
真机截图 未取得。市场截图为 H5 构建产物在浏览器中的真实渲染,页面已标注示例数据

也就是说:表格解析与校验逻辑是被真实测试覆盖的,Android 平台层的 zip 读取与文件流在真机上还没有跑过。 如果你在真机上遇到 ZIP_INVALID 或 IO_FAILED,请把文件与错误码反馈给作者,优先修。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无。读取应用私有目录或应用外部私有目录不需要任何声明权限;读取其它位置的文件时,由调用方先通过系统文件选择器取得授权,插件不申请存储权限。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集、不上传任何数据,不访问网络。被读取的表格只在本机内存中解析,解析结果只返回给调用方,不会写往任何外部位置;错误回执 CSV 由调用方指定路径,默认写在应用私有目录。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。