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.
- package/README.md +257 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
# cm-reporting
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/cm-reporting)
|
|
4
|
+
[](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
|
-
##
|
|
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
|
-
|
|
37
|
-
- 工具:`parseSnapshot()` / `stringifySnapshot()`
|
|
83
|
+
### CMReporting Ref
|
|
38
84
|
|
|
39
|
-
|
|
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
|
-
|
|
46
|
-
const ref = useRef<CMReportingRef>(null)
|
|
91
|
+
const ref = useRef<CMReportingRef>(null)
|
|
47
92
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
148
|
+
```tsx
|
|
149
|
+
import { parseSnapshot } from 'cm-reporting'
|
|
60
150
|
|
|
61
|
-
|
|
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
|
-
|
|
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
|
|
85
|
-
//
|
|
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
|
-
|
|
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={
|
|
95
|
-
<CMReporting ref={ref} templateType="cmrt" versionId="6.5"
|
|
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
|
-
|
|
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
|
|
324
|
+
[PolyForm Noncommercial 1.0.0](https://github.com/cirscn/cm-reporting/blob/main/LICENSE)
|
|
108
325
|
|
|
109
|
-
|
|
110
|
-
- 如需商业使用,请联系版权所有者获取单独商业授权。
|
|
326
|
+
> **注意**:本项目仅允许非商业用途。如需商业使用,请联系版权所有者获取单独商业授权。
|