cm-reporting 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +257 -41
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,25 @@
1
1
  # cm-reporting
2
2
 
3
- 基于 React + Ant Design 的冲突矿产报告(RMI 模板)开箱即用嵌入式应用组件库,支持 CMRT / EMRT / CRT / AMRT 的全版本覆盖。
3
+ [![npm version](https://img.shields.io/npm/v/cm-reporting.svg)](https://www.npmjs.com/package/cm-reporting)
4
+ [![License: PolyForm Noncommercial](https://img.shields.io/badge/License-PolyForm%20Noncommercial%201.0.0-orange.svg)](https://github.com/cirscn/cm-reporting/blob/main/LICENSE)
5
+
6
+ 基于 React + Ant Design 的冲突矿产报告(RMI 模板)开箱即用嵌入式组件库。
7
+
8
+ - CMRT / EMRT / CRT / AMRT 全模板、全版本覆盖
9
+ - 中英文国际化
10
+ - JSON Snapshot 导入/导出
11
+ - Excel 导出(基于 RMI 原始模板最小 patch,保留 DV / 公式 / 格式)
12
+ - Integrations 扩展点(宿主接管冶炼厂/产品选择)
13
+ - 旧版 cirs-gpm JSON 互转适配器
14
+
15
+ ## 支持的模板
16
+
17
+ | 模板 | 版本 | 说明 |
18
+ |------|------|------|
19
+ | CMRT | 6.01, 6.1, 6.22, 6.31, 6.4, 6.5 | Conflict Minerals Reporting Template |
20
+ | EMRT | 1.1, 1.11, 1.2, 1.3, 2.0, 2.1 | Extended Minerals Reporting Template |
21
+ | CRT | 2.2, 2.21 | Cobalt Reporting Template |
22
+ | AMRT | 1.1, 1.2, 1.3 | Additional Minerals Reporting Template |
4
23
 
5
24
  ## 安装
6
25
 
@@ -10,7 +29,19 @@ npm install cm-reporting
10
29
  pnpm add cm-reporting
11
30
  ```
12
31
 
13
- ## 使用(开箱即用)
32
+ **Peer dependencies**(需宿主自行安装):
33
+
34
+ ```bash
35
+ npm install react react-dom antd @ant-design/icons
36
+ ```
37
+
38
+ | Peer | 版本要求 |
39
+ |------|----------|
40
+ | `react` / `react-dom` | ^18.0.0 \|\| ^19.0.0 |
41
+ | `antd` | ^5.0.0 \|\| ^6.0.0 |
42
+ | `@ant-design/icons` | ^5.0.0 \|\| ^6.0.0 |
43
+
44
+ ## 快速开始
14
45
 
15
46
  ```tsx
16
47
  import { CMReporting } from 'cm-reporting'
@@ -23,7 +54,7 @@ export function App() {
23
54
  versionId="6.5"
24
55
  locale="zh-CN"
25
56
  onSnapshotChange={(snapshot) => {
26
- // 全量 JSON:建议宿主自行节流 + 落库/上报
57
+ // 全量 JSON 快照:建议宿主自行节流后落库
27
58
  console.log(snapshot)
28
59
  }}
29
60
  />
@@ -31,80 +62,265 @@ export function App() {
31
62
  }
32
63
  ```
33
64
 
34
- ## JSON(全量快照)
65
+ ## API
66
+
67
+ ### CMReporting Props
68
+
69
+ | Prop | 类型 | 必须 | 说明 |
70
+ |------|------|:----:|------|
71
+ | `templateType` | `TemplateType` | ✅ | 模板类型:`'cmrt' \| 'emrt' \| 'crt' \| 'amrt'` |
72
+ | `versionId` | `string` | ✅ | 模板版本号,如 `'6.5'`、`'2.1'` |
73
+ | `locale` | `Locale` | - | 语言:`'en-US' \| 'zh-CN'`,默认 `'en-US'` |
74
+ | `onLocaleChange` | `(locale: Locale) => void` | - | 语言变化回调 |
75
+ | `theme` | `object` | - | Ant Design 主题 token 覆盖 |
76
+ | `cssVariables` | `object` | - | CSS 变量覆盖 |
77
+ | `maxContentWidth` | `number` | - | 内容区最大宽度(不设则撑满父容器) |
78
+ | `integrations` | `CMReportingIntegrations` | - | 外部选择/回写扩展点,见 [Integrations](#integrations) |
79
+ | `initialSnapshot` | `ReportSnapshotV1` | - | 初始快照(用于编辑旧报告) |
80
+ | `onSnapshotChange` | `(snapshot: ReportSnapshotV1) => void` | - | 任意字段变化时回调全量快照 |
81
+ | `fallback` | `ReactNode` | - | 加载态内容(Suspense fallback) |
35
82
 
36
- - 数据契约:`ReportSnapshotV1`(含 `schemaVersion/templateType/versionId/data`)
37
- - 工具:`parseSnapshot()` / `stringifySnapshot()`
83
+ ### CMReporting Ref
38
84
 
39
- 组件 ref 方式(推荐用于“导出/保存/回填”):
85
+ 通过 `ref` 进行编程式操作:
40
86
 
41
87
  ```tsx
42
- import { CMReporting, type CMReportingRef } from 'cm-reporting'
43
88
  import { useRef } from 'react'
89
+ import { CMReporting, type CMReportingRef } from 'cm-reporting'
44
90
 
45
- export function App() {
46
- const ref = useRef<CMReportingRef>(null)
91
+ const ref = useRef<CMReportingRef>(null)
47
92
 
48
- return (
49
- <>
50
- <button onClick={() => console.log(ref.current?.getSnapshot())}>Export JSON</button>
51
- <CMReporting ref={ref} templateType="cmrt" versionId="6.5" locale="en-US" />
52
- </>
53
- )
93
+ // 获取当前快照
94
+ ref.current?.getSnapshot()
95
+
96
+ // 回填快照(templateType/versionId 必须匹配)
97
+ ref.current?.setSnapshot(snapshot)
98
+
99
+ // 导出 JSON 字符串
100
+ ref.current?.exportJson()
101
+
102
+ // 导出 Excel(需传入模板 xlsx ArrayBuffer)
103
+ await ref.current?.exportExcel({ templateXlsx })
104
+
105
+ // 触发全量校验
106
+ await ref.current?.validate()
107
+ ```
108
+
109
+ | 方法 | 返回值 | 说明 |
110
+ |------|--------|------|
111
+ | `getSnapshot()` | `ReportSnapshotV1` | 获取当前全量快照 |
112
+ | `setSnapshot(snapshot)` | `void` | 回填快照 |
113
+ | `exportJson()` | `string` | 导出快照 JSON 字符串 |
114
+ | `exportExcel(input)` | `Promise<Blob>` | 导出 Excel |
115
+ | `validate()` | `Promise<boolean>` | 触发全量校验 |
116
+
117
+ ### useCMReporting Hook
118
+
119
+ 在 `CMReporting` 子组件树内使用,提供与 ref 相同的编程式 API:
120
+
121
+ ```tsx
122
+ import { useCMReporting } from 'cm-reporting'
123
+
124
+ function MyComponent() {
125
+ const api = useCMReporting()
126
+ const snapshot = api.snapshot // 响应式,随表单变化自动更新
54
127
  }
55
128
  ```
56
129
 
57
- ## Excel(基于模板赋值导出)
130
+ ## JSON Snapshot
131
+
132
+ 数据契约为 `ReportSnapshotV1`,包含 `schemaVersion`、`templateType`、`versionId`、`data`。
133
+
134
+ ### 导出
135
+
136
+ ```tsx
137
+ import { stringifySnapshot } from 'cm-reporting'
138
+
139
+ // 通过 ref
140
+ const json = ref.current?.exportJson()
141
+
142
+ // 或手动序列化
143
+ const json = stringifySnapshot(ref.current?.getSnapshot())
144
+ ```
145
+
146
+ ### 导入
58
147
 
59
- 严格要求:打开 Excel 后视觉/校验(DV)/公式行为一致,因此导出采用“基于原始 RMI 模板最小差异赋值后导出”的策略。
148
+ ```tsx
149
+ import { parseSnapshot } from 'cm-reporting'
60
150
 
61
- 对外 API:
151
+ // 解析(含 Zod 校验)
152
+ const snapshot = parseSnapshot(JSON.parse(jsonString))
153
+
154
+ // 回填
155
+ ref.current?.setSnapshot(snapshot)
156
+ ```
157
+
158
+ ### 初始化编辑旧报告
159
+
160
+ ```tsx
161
+ <CMReporting
162
+ templateType={snapshot.templateType}
163
+ versionId={snapshot.versionId}
164
+ initialSnapshot={snapshot}
165
+ />
166
+ ```
62
167
 
63
- - `exportToExcel({ templateXlsx, snapshot })`
64
- - `ref.exportExcel({ templateXlsx })`
168
+ ## Excel 导出
65
169
 
66
- 示例(浏览器下载):
170
+ 采用"基于原始 RMI 模板最小差异赋值"策略,严格保留模板的 DV、格式、公式、隐藏 sheet,仅填入用户数据。
67
171
 
68
172
  ```tsx
69
173
  import { CMReporting, type CMReportingRef } from 'cm-reporting'
70
174
  import { useRef } from 'react'
71
175
 
72
- async function downloadBlob(blob: Blob, filename: string) {
73
- const url = URL.createObjectURL(blob)
74
- const a = document.createElement('a')
75
- a.href = url
76
- a.download = filename
77
- a.click()
78
- URL.revokeObjectURL(url)
79
- }
80
-
81
176
  export function App() {
82
177
  const ref = useRef<CMReportingRef>(null)
83
178
 
84
- const onExportExcel = async () => {
85
- // 模板来源由宿主决定:可从你们的静态资源/CDN,或从包导出的 templates 复制到你的 public。
179
+ const onExport = async () => {
180
+ // 模板来源由宿主决定:可从 CDN、静态资源,或从包导出的 templates 目录获取
86
181
  const res = await fetch('/templates/RMI_CMRT_6.5.xlsx')
87
182
  const templateXlsx = await res.arrayBuffer()
88
183
  const blob = await ref.current!.exportExcel({ templateXlsx })
89
- await downloadBlob(blob, 'cmrt-6.5.xlsx')
184
+
185
+ const url = URL.createObjectURL(blob)
186
+ const a = document.createElement('a')
187
+ a.href = url
188
+ a.download = 'cmrt-6.5.xlsx'
189
+ a.click()
190
+ URL.revokeObjectURL(url)
90
191
  }
91
192
 
92
193
  return (
93
194
  <>
94
- <button onClick={onExportExcel}>Export Excel</button>
95
- <CMReporting ref={ref} templateType="cmrt" versionId="6.5" locale="en-US" />
195
+ <button onClick={onExport}>Export Excel</button>
196
+ <CMReporting ref={ref} templateType="cmrt" versionId="6.5" />
96
197
  </>
97
198
  )
98
199
  }
99
200
  ```
100
201
 
101
- ## 模板文件
202
+ 也可以脱离组件直接调用:
203
+
204
+ ```tsx
205
+ import { exportToExcel } from 'cm-reporting'
206
+
207
+ const blob = await exportToExcel({ templateXlsx, snapshot })
208
+ ```
209
+
210
+ ### 模板文件
211
+
212
+ 包内导出 RMI 原始模板文件,可复制到宿主静态资源目录:
213
+
214
+ ```
215
+ cm-reporting/templates/RMI_CMRT_6.5.xlsx
216
+ cm-reporting/templates/RMI_EMRT_2.1.xlsx
217
+ ...
218
+ ```
219
+
220
+ ## Integrations
221
+
222
+ Integrations 允许宿主接管冶炼厂/产品的"选择"交互,典型场景:从宿主自有数据库弹窗选择后回写到表单。
223
+
224
+ ```tsx
225
+ <CMReporting
226
+ integrations={{
227
+ smelterList: {
228
+ lookupMode: 'external',
229
+ onPickSmelterForRow: async (ctx) => {
230
+ // ctx.rowId, ctx.metal, ctx.row — 当前行信息
231
+ // 返回 { items: [partial] } 或 null(取消)
232
+ },
233
+ rowClassName: (record, index) => {
234
+ // 自定义行样式(宿主提供 CSS)
235
+ return record.listed === 'Non-Listed' ? 'row-unlisted' : ''
236
+ },
237
+ },
238
+ productList: {
239
+ addMode: 'external-only',
240
+ label: '从系统选择产品',
241
+ onPickProducts: async (ctx) => {
242
+ // ctx.currentRows — 当前已有行
243
+ return { items: [{ productNumber: 'P-001', productName: 'xxx' }] }
244
+ },
245
+ },
246
+ }}
247
+ />
248
+ ```
249
+
250
+ 宿主的实现通常使用 **Promise + ref** 模式暂存 `resolve`,在 Modal 确认时调用:
251
+
252
+ ```tsx
253
+ const resolveRef = useRef<((result: ExternalPickResult) => void) | null>(null)
254
+
255
+ const onPickProducts = async (ctx: ProductPickContext) => {
256
+ setModalOpen(true)
257
+ return new Promise<ExternalPickResult<Partial<ProductRow>>>((resolve) => {
258
+ resolveRef.current = resolve
259
+ })
260
+ }
261
+
262
+ // Modal 确认 → resolveRef.current?.({ items: selectedItems })
263
+ // Modal 取消 → resolveRef.current?.(null)
264
+ ```
265
+
266
+ ## Legacy Adapter
267
+
268
+ 用于旧版 cirs-gpm JSON 与内部 Snapshot 的互转。
269
+
270
+ ```tsx
271
+ import { cirsGpmLegacyAdapter } from 'cm-reporting'
272
+ ```
273
+
274
+ ### 导入
275
+
276
+ ```tsx
277
+ const { snapshot, ctx } = cirsGpmLegacyAdapter.toInternal(legacyJson)
278
+ // snapshot → 用于 CMReporting
279
+ // ctx → 保存用于精确回写
280
+ ```
281
+
282
+ ### 导出
283
+
284
+ ```tsx
285
+ // 精确回写(Roundtrip):需要导入时保存的 ctx
286
+ const legacy = cirsGpmLegacyAdapter.toExternal(snapshot, ctx)
287
+
288
+ // 宽松导出(Loose):无需 ctx,仅保证 schema 兼容
289
+ const legacy = cirsGpmLegacyAdapter.toExternalLoose(snapshot)
290
+ ```
102
291
 
103
- 包内导出静态模板文件:`cm-reporting/templates/*`(用于宿主自持有/下载/二次分发)。
292
+ | 场景 | 方法 | 精确度 | 要求 |
293
+ |------|------|--------|------|
294
+ | 导入后再导出 | `toExternal(snapshot, ctx)` | byte-level roundtrip | 需保留 `ctx` |
295
+ | 全新报告导出 | `toExternalLoose(snapshot)` | schema 兼容 | 无需 `ctx` |
296
+
297
+ ## TypeScript
298
+
299
+ 所有公开类型均从包入口统一导入:
300
+
301
+ ```tsx
302
+ import type {
303
+ // 核心
304
+ Locale, TemplateType,
305
+ // 组件
306
+ CMReportingProps, CMReportingRef, CMReportingApi,
307
+ // 表格行
308
+ SmelterRow, ProductRow, MineRow, MineralsScopeRow,
309
+ // Snapshot
310
+ ReportSnapshotV1,
311
+ // Integrations
312
+ CMReportingIntegrations, SmelterListIntegration, ProductListIntegration,
313
+ SmelterPickContext, SmelterRowPickContext, ProductPickContext,
314
+ ExternalPickResult, ExternalAddMode, SmelterLookupMode,
315
+ // Excel
316
+ ExportExcelInput,
317
+ // Legacy
318
+ CirsGpmLegacyReport, CirsGpmLegacyRoundtripContext,
319
+ } from 'cm-reporting'
320
+ ```
104
321
 
105
322
  ## License
106
323
 
107
- PolyForm-Noncommercial-1.0.0
324
+ [PolyForm Noncommercial 1.0.0](https://github.com/cirscn/cm-reporting/blob/main/LICENSE)
108
325
 
109
- - 禁止商业用途(Noncommercial only)。
110
- - 如需商业使用,请联系版权所有者获取单独商业授权。
326
+ > **注意**:本项目仅允许非商业用途。如需商业使用,请联系版权所有者获取单独商业授权。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cm-reporting",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Conflict Minerals Reporting Template Components",
5
5
  "license": "PolyForm-Noncommercial-1.0.0",
6
6
  "repository": {