@pisell/private-materials 3.4.156 → 3.4.158

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 (70) hide show
  1. package/build/lowcode/assets-daily.json +11 -11
  2. package/build/lowcode/assets-dev.json +2 -2
  3. package/build/lowcode/assets-prod.json +11 -11
  4. package/build/lowcode/meta.js +1 -1
  5. package/build/lowcode/render/default/view.css +1 -1
  6. package/build/lowcode/render/default/view.js +14 -14
  7. package/build/lowcode/view.css +1 -1
  8. package/build/lowcode/view.js +14 -14
  9. package/es/index.d.ts +7 -1
  10. package/es/index.js +5 -1
  11. package/es/plus/inventory/BindingModal.js +94 -0
  12. package/es/plus/inventory/InventoryItemManagement.d.ts +7 -0
  13. package/es/plus/inventory/InventoryItemManagement.js +204 -0
  14. package/es/plus/inventory/InventoryLocationManagement.d.ts +7 -0
  15. package/es/plus/inventory/InventoryLocationManagement.js +296 -0
  16. package/es/plus/inventory/InventoryUnitManagement.d.ts +7 -0
  17. package/es/plus/inventory/InventoryUnitManagement.js +144 -0
  18. package/es/plus/inventory/ItemDrawer.js +266 -0
  19. package/es/plus/inventory/RecordDialog.js +141 -0
  20. package/es/plus/inventory/StockList.js +132 -0
  21. package/es/plus/inventory/api.js +66 -0
  22. package/es/plus/inventory/components.js +172 -0
  23. package/es/plus/inventory/context.js +118 -0
  24. package/es/plus/inventory/forms.js +46 -0
  25. package/es/plus/inventory/hooks.js +235 -0
  26. package/es/plus/inventory/index.less +559 -0
  27. package/es/plus/inventory/locales.js +277 -0
  28. package/es/plus/inventory/types.d.ts +10 -0
  29. package/es/plus/inventory/utils.js +24 -0
  30. package/es/plus/pisellSupersetDashboard/README.md +291 -0
  31. package/es/plus/pisellSupersetDashboard/index.d.ts +15 -0
  32. package/es/plus/pisellSupersetDashboard/index.js +187 -0
  33. package/es/plus/pisellSupersetDashboard/index.less +83 -0
  34. package/es/plus/pisellSupersetDashboard/locales.js +36 -0
  35. package/es/plus/pisellSupersetDashboard/serve.js +51 -0
  36. package/es/plus/pisellSupersetDashboard/types.d.ts +17 -0
  37. package/es/plus/skuOptionsSelection/store/createStore.js +2 -2
  38. package/lib/index.d.ts +7 -1
  39. package/lib/index.js +134 -126
  40. package/lib/plus/inventory/BindingModal.js +96 -0
  41. package/lib/plus/inventory/InventoryItemManagement.d.ts +7 -0
  42. package/lib/plus/inventory/InventoryItemManagement.js +206 -0
  43. package/lib/plus/inventory/InventoryLocationManagement.d.ts +7 -0
  44. package/lib/plus/inventory/InventoryLocationManagement.js +298 -0
  45. package/lib/plus/inventory/InventoryUnitManagement.d.ts +7 -0
  46. package/lib/plus/inventory/InventoryUnitManagement.js +146 -0
  47. package/lib/plus/inventory/ItemDrawer.js +268 -0
  48. package/lib/plus/inventory/RecordDialog.js +143 -0
  49. package/lib/plus/inventory/StockList.js +134 -0
  50. package/lib/plus/inventory/api.js +66 -0
  51. package/lib/plus/inventory/components.js +181 -0
  52. package/lib/plus/inventory/context.js +121 -0
  53. package/lib/plus/inventory/forms.js +50 -0
  54. package/lib/plus/inventory/hooks.js +240 -0
  55. package/lib/plus/inventory/index.less +559 -0
  56. package/lib/plus/inventory/locales.js +277 -0
  57. package/lib/plus/inventory/types.d.ts +10 -0
  58. package/lib/plus/inventory/utils.js +30 -0
  59. package/lib/plus/pisellSupersetDashboard/README.md +291 -0
  60. package/lib/plus/pisellSupersetDashboard/index.d.ts +15 -0
  61. package/lib/plus/pisellSupersetDashboard/index.js +189 -0
  62. package/lib/plus/pisellSupersetDashboard/index.less +83 -0
  63. package/lib/plus/pisellSupersetDashboard/locales.js +36 -0
  64. package/lib/plus/pisellSupersetDashboard/serve.js +52 -0
  65. package/lib/plus/pisellSupersetDashboard/types.d.ts +17 -0
  66. package/lowcode/inventory-item-management/meta.ts +20 -0
  67. package/lowcode/inventory-location-management/meta.ts +20 -0
  68. package/lowcode/inventory-unit-management/meta.ts +20 -0
  69. package/lowcode/pisell-superset-dashboard/meta.ts +75 -0
  70. package/package.json +5 -3
@@ -0,0 +1,277 @@
1
+ import { _objectSpread2 } from "../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
2
+ //#region src/plus/inventory/locales.ts
3
+ const en = {
4
+ items: "Inventory items",
5
+ units: "Item units",
6
+ locations: "Storage locations",
7
+ item: "Item",
8
+ unit: "Unit",
9
+ location: "Location",
10
+ shelf: "Shelf",
11
+ space: "Space",
12
+ allLocations: "All locations",
13
+ name: "Name",
14
+ status: "Status",
15
+ sort: "Sort order",
16
+ active: "Active",
17
+ inactive: "Inactive",
18
+ allStatuses: "All statuses",
19
+ allUnits: "All units",
20
+ add: "Add",
21
+ edit: "Edit",
22
+ view: "View",
23
+ remove: "Delete",
24
+ save: "Save",
25
+ cancel: "Cancel",
26
+ close: "Close",
27
+ refresh: "Refresh",
28
+ retry: "Retry",
29
+ actions: "Actions",
30
+ updated: "Updated",
31
+ search: "Search by name",
32
+ searchItems: "Search name / SKU / barcode",
33
+ searchHint: "A numeric search of 10 or more digits matches the barcode exactly.",
34
+ sku: "SKU",
35
+ barcode: "Barcode",
36
+ cover: "Cover URL",
37
+ note: "Note",
38
+ stockMode: "Stock mode",
39
+ allStockModes: "All stock modes",
40
+ tracked: "Tracked",
41
+ unlimited: "Unlimited",
42
+ quantity: "Physical stock",
43
+ available: "Available stock",
44
+ totalQuantity: "Total physical stock",
45
+ totalAvailable: "Total available stock",
46
+ stockSummary: "Stock summary",
47
+ basic: "Basic information",
48
+ bindings: "Space bindings",
49
+ bind: "Bind space",
50
+ unbind: "Unbind",
51
+ path: "Location / shelf / space",
52
+ stockReadOnly: "Stock quantities are read-only. Binding a space does not receive or adjust stock.",
53
+ unbindBlocked: "Both physical and available stock must be zero before unbinding.",
54
+ bindHint: "Select a location, shelf and space. An existing binding is kept without changing its quantities.",
55
+ saveFirst: "Save the item before binding spaces.",
56
+ empty: "No records",
57
+ chooseLocation: "Select a location",
58
+ chooseShelf: "Select a shelf",
59
+ chooseSpace: "Select a space",
60
+ loadMore: "Load more",
61
+ loading: "Loading…",
62
+ noUnit: "No unit",
63
+ missing: "Record unavailable",
64
+ required: "This field is required",
65
+ originalRequired: "Enter the original name",
66
+ invalidUrl: "Enter a valid absolute HTTP or HTTPS URL",
67
+ sortHint: "Higher values appear first",
68
+ nonnegative: "Enter a non-negative integer",
69
+ success: "Saved",
70
+ deleted: "Deleted",
71
+ bound: "Space bound",
72
+ unbound: "Space unbound",
73
+ failed: "The request failed. Please try again.",
74
+ noRequest: "The host request service is unavailable.",
75
+ invalidResponse: "The server returned an invalid response.",
76
+ deleteTitle: "Delete this record?",
77
+ deleteHint: "Linked records can prevent deletion. This action does not delete children or remove stock bindings.",
78
+ unbindTitle: "Unbind this space?",
79
+ unbindHint: "Only the empty stock binding will be removed.",
80
+ unsavedTitle: "Discard unsaved changes?",
81
+ unsavedHint: "Your changes have not been saved.",
82
+ discard: "Discard",
83
+ referencedStock: "Items stored here",
84
+ hierarchyHint: "Choose a location or shelf to manage its children.",
85
+ itemHint: "Manage items and the spaces where they are stored.",
86
+ unitHint: "Maintain the units used by inventory items.",
87
+ parentHint: "Changing the parent updates the storage path. It does not change stock quantities.",
88
+ statusHint: "Status changes do not cascade to children or remove existing bindings.",
89
+ count: "Total",
90
+ expand: "Expand shelves",
91
+ collapse: "Collapse shelves",
92
+ viewChildren: "Open children",
93
+ editBasicFirst: "Save or cancel basic information changes before managing bindings."
94
+ };
95
+ const zhCN = {
96
+ items: "货品管理",
97
+ units: "货品单位管理",
98
+ locations: "仓储位置管理",
99
+ item: "货品",
100
+ unit: "单位",
101
+ location: "地点",
102
+ shelf: "货架",
103
+ space: "货位",
104
+ allLocations: "全部地点",
105
+ name: "名称",
106
+ status: "状态",
107
+ sort: "排序",
108
+ active: "启用",
109
+ inactive: "停用",
110
+ allStatuses: "全部状态",
111
+ allUnits: "全部单位",
112
+ add: "新增",
113
+ edit: "编辑",
114
+ view: "查看",
115
+ remove: "删除",
116
+ save: "保存",
117
+ cancel: "取消",
118
+ close: "关闭",
119
+ refresh: "刷新",
120
+ retry: "重试",
121
+ actions: "操作",
122
+ updated: "更新时间",
123
+ search: "搜索名称",
124
+ searchItems: "搜索名称 / SKU / 条码",
125
+ searchHint: "输入 10 位及以上纯数字时,按条码精确查询。",
126
+ sku: "SKU",
127
+ barcode: "条码",
128
+ cover: "封面 URL",
129
+ note: "备注",
130
+ stockMode: "库存模式",
131
+ allStockModes: "全部库存模式",
132
+ tracked: "追踪库存",
133
+ unlimited: "不限库存",
134
+ quantity: "真实库存",
135
+ available: "可用库存",
136
+ totalQuantity: "真实库存合计",
137
+ totalAvailable: "可用库存合计",
138
+ stockSummary: "库存汇总",
139
+ basic: "基本信息",
140
+ bindings: "货位绑定",
141
+ bind: "绑定货位",
142
+ unbind: "解绑",
143
+ path: "地点 / 货架 / 货位",
144
+ stockReadOnly: "库存数量只读,绑定货位不会入库或调整库存。",
145
+ unbindBlocked: "真实库存和可用库存必须同时为零才能解绑。",
146
+ bindHint: "依次选择地点、货架和货位。重复绑定会保留已有记录,不改变库存数量。",
147
+ saveFirst: "请先保存货品,再绑定货位。",
148
+ empty: "暂无记录",
149
+ chooseLocation: "请选择地点",
150
+ chooseShelf: "请选择货架",
151
+ chooseSpace: "请选择货位",
152
+ loadMore: "加载更多",
153
+ loading: "加载中…",
154
+ noUnit: "无单位",
155
+ missing: "记录不存在或已失效",
156
+ required: "请填写此项",
157
+ originalRequired: "请填写原始名称",
158
+ invalidUrl: "请输入有效的 HTTP 或 HTTPS 完整地址",
159
+ sortHint: "数值越大越靠前",
160
+ nonnegative: "请输入非负整数",
161
+ success: "保存成功",
162
+ deleted: "删除成功",
163
+ bound: "绑定成功",
164
+ unbound: "解绑成功",
165
+ failed: "请求失败,请重试。",
166
+ noRequest: "宿主请求服务不可用。",
167
+ invalidResponse: "接口返回格式异常。",
168
+ deleteTitle: "确定删除此记录?",
169
+ deleteHint: "存在引用时可能无法删除。本操作不会删除下级或解除库存关联。",
170
+ unbindTitle: "确定解除此货位绑定?",
171
+ unbindHint: "仅移除这条空库存关联。",
172
+ unsavedTitle: "放弃未保存的修改?",
173
+ unsavedHint: "当前修改尚未保存。",
174
+ discard: "放弃修改",
175
+ referencedStock: "此货位的货品",
176
+ hierarchyHint: "选择地点或货架,维护其下级记录。",
177
+ itemHint: "维护货品资料及其存放货位。",
178
+ unitHint: "维护库存货品使用的计量单位。",
179
+ parentHint: "修改归属将更新仓储路径,不改变库存数量。",
180
+ statusHint: "启停状态不会向下级联动,也不会解除已有绑定。",
181
+ count: "共",
182
+ expand: "展开货架",
183
+ collapse: "收起货架",
184
+ viewChildren: "进入下级",
185
+ editBasicFirst: "请先保存或取消基本信息修改,再维护货位绑定。"
186
+ };
187
+ const zhHK = _objectSpread2(_objectSpread2({}, zhCN), {}, {
188
+ items: "貨品管理",
189
+ units: "貨品單位管理",
190
+ locations: "倉儲位置管理",
191
+ item: "貨品",
192
+ unit: "單位",
193
+ shelf: "貨架",
194
+ space: "貨位",
195
+ allLocations: "全部地點",
196
+ location: "地點",
197
+ name: "名稱",
198
+ status: "狀態",
199
+ active: "啟用",
200
+ inactive: "停用",
201
+ allStatuses: "全部狀態",
202
+ allUnits: "全部單位",
203
+ edit: "編輯",
204
+ view: "查看",
205
+ remove: "刪除",
206
+ save: "儲存",
207
+ close: "關閉",
208
+ refresh: "重新整理",
209
+ actions: "操作",
210
+ updated: "更新時間",
211
+ search: "搜尋名稱",
212
+ searchItems: "搜尋名稱 / SKU / 條碼",
213
+ searchHint: "輸入 10 位及以上純數字時,按條碼精確查詢。",
214
+ barcode: "條碼",
215
+ cover: "封面 URL",
216
+ note: "備註",
217
+ stockMode: "庫存模式",
218
+ allStockModes: "全部庫存模式",
219
+ tracked: "追蹤庫存",
220
+ unlimited: "不限庫存",
221
+ quantity: "實際庫存",
222
+ available: "可用庫存",
223
+ totalQuantity: "實際庫存合計",
224
+ totalAvailable: "可用庫存合計",
225
+ stockSummary: "庫存彙總",
226
+ basic: "基本資訊",
227
+ bindings: "貨位綁定",
228
+ bind: "綁定貨位",
229
+ unbind: "解除綁定",
230
+ path: "地點 / 貨架 / 貨位",
231
+ stockReadOnly: "庫存數量唯讀,綁定貨位不會入庫或調整庫存。",
232
+ unbindBlocked: "實際庫存和可用庫存必須同時為零才能解除綁定。",
233
+ bindHint: "依次選擇地點、貨架和貨位。重複綁定會保留已有記錄,不改變庫存數量。",
234
+ saveFirst: "請先儲存貨品,再綁定貨位。",
235
+ empty: "暫無記錄",
236
+ chooseLocation: "請選擇地點",
237
+ chooseShelf: "請選擇貨架",
238
+ chooseSpace: "請選擇貨位",
239
+ loadMore: "載入更多",
240
+ loading: "載入中…",
241
+ noUnit: "無單位",
242
+ missing: "記錄不存在或已失效",
243
+ required: "請填寫此項",
244
+ originalRequired: "請填寫原始名稱",
245
+ invalidUrl: "請輸入有效的 HTTP 或 HTTPS 完整地址",
246
+ sortHint: "數值越大越靠前",
247
+ nonnegative: "請輸入非負整數",
248
+ success: "儲存成功",
249
+ deleted: "刪除成功",
250
+ bound: "綁定成功",
251
+ unbound: "已解除綁定",
252
+ failed: "請求失敗,請重試。",
253
+ noRequest: "宿主請求服務不可用。",
254
+ invalidResponse: "介面返回格式異常。",
255
+ deleteTitle: "確定刪除此記錄?",
256
+ deleteHint: "存在引用時可能無法刪除。本操作不會刪除下級或解除庫存關聯。",
257
+ unbindTitle: "確定解除此貨位綁定?",
258
+ unbindHint: "僅移除這條空庫存關聯。",
259
+ unsavedTitle: "放棄未儲存的修改?",
260
+ unsavedHint: "目前修改尚未儲存。",
261
+ discard: "放棄修改",
262
+ referencedStock: "此貨位的貨品",
263
+ hierarchyHint: "選擇地點或貨架,維護其下級記錄。",
264
+ itemHint: "維護貨品資料及其存放貨位。",
265
+ unitHint: "維護庫存貨品使用的計量單位。",
266
+ parentHint: "修改歸屬將更新倉儲路徑,不改變庫存數量。",
267
+ statusHint: "啟停狀態不會向下級聯動,也不會解除已有綁定。",
268
+ expand: "展開貨架",
269
+ collapse: "收起貨架",
270
+ viewChildren: "進入下級",
271
+ editBasicFirst: "請先儲存或取消基本資訊修改,再維護貨位綁定。",
272
+ retry: "重試",
273
+ count: "共"
274
+ });
275
+ const getInventoryTexts = (locale) => /^(zh-HK|zh-TW|zh-Hant)/i.test(locale) ? zhHK : /^zh/i.test(locale) ? zhCN : en;
276
+ //#endregion
277
+ export { getInventoryTexts };
@@ -0,0 +1,10 @@
1
+ import { CSSProperties } from "react";
2
+
3
+ //#region src/plus/inventory/types.d.ts
4
+ interface InventoryPageProps {
5
+ className?: string;
6
+ style?: CSSProperties;
7
+ __id?: string;
8
+ }
9
+ //#endregion
10
+ export { InventoryPageProps };
@@ -0,0 +1,24 @@
1
+ import Decimal from "decimal.js";
2
+ //#region src/plus/inventory/utils.ts
3
+ function inventoryName(value, locale) {
4
+ const translated = value === null || value === void 0 ? void 0 : value[locale];
5
+ if (typeof translated === "string" && translated.trim()) return translated;
6
+ return typeof (value === null || value === void 0 ? void 0 : value.original) === "string" && value.original.trim() ? value.original : "—";
7
+ }
8
+ function inventoryQuantity(value) {
9
+ if (typeof value !== "string" || !/^-?\d+(\.\d+)?$/.test(value)) return "—";
10
+ const result = value.includes(".") ? value.replace(/0+$/, "").replace(/\.$/, "") : value;
11
+ return /^-?0$/.test(result) ? "0" : result;
12
+ }
13
+ function canUnbind(stock) {
14
+ return [stock.quantity, stock.available_quantity].every((value) => {
15
+ if (typeof value !== "string" || !/^-?\d+(\.\d+)?$/.test(value)) return false;
16
+ return new Decimal(value).isZero();
17
+ });
18
+ }
19
+ function mergeInventoryRecords(previous, next) {
20
+ return Array.from(new Map([...previous, ...next].map((record) => [record.id, record])).values());
21
+ }
22
+ const optionalText = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || null;
23
+ //#endregion
24
+ export { canUnbind, inventoryName, inventoryQuantity, mergeInventoryRecords, optionalText };
@@ -0,0 +1,291 @@
1
+ # PisellSupersetDashboard
2
+
3
+ 用于 SaaS 页面和低代码页面的 Superset 报表组件。宿主提供报表嵌入 UUID、UI 配置和已有请求上下文,组件负责获取会话、初始化 SDK、令牌续期、加载展示、失败重试及资源清理。
4
+
5
+ 业务接口固定为 `POST /shop/report/superset/embed`。组件使用 `@superset-ui/embedded-sdk@0.4.0` 创建 iframe,不自行拼接带令牌的报表 URL。
6
+
7
+ ## 1. 文件结构与职责
8
+
9
+ | 文件 | 职责 |
10
+ | ---------------------------------------------------- | ---------------------------------------------------- |
11
+ | `index.tsx` | 组件入口、状态管理、SDK 初始化与清理、加载和错误交互 |
12
+ | `types.ts` | 公开 Props,以及内部请求客户端和会话类型 |
13
+ | `serve.ts` | 业务接口调用、响应校验和报表异常类型 |
14
+ | `locales.ts` | 英文、简体中文、繁体中文文案 |
15
+ | `index.less` | 容器尺寸、iframe、加载图标和错误遮罩样式 |
16
+ | `index.test.tsx` | 组件行为、续期、超时和异步竞态测试 |
17
+ | `serve.test.ts` | 请求契约、业务错误和响应字段校验测试 |
18
+ | `../../../lowcode/pisell-superset-dashboard/meta.ts` | 低代码物料描述、属性编辑器及默认片段 |
19
+
20
+ 包入口 `packages/private-materials/src/index.ts` 导出 `PisellSupersetDashboard` 和 `PisellSupersetDashboardProps`。组件目录同时提供默认导出与具名导出。
21
+
22
+ ## 2. 基础使用
23
+
24
+ 以下组件应渲染在宿主已有的引擎上下文 Provider 内:
25
+
26
+ ```tsx
27
+ import {
28
+ PisellSupersetDashboard,
29
+ type PisellSupersetDashboardProps,
30
+ } from '@pisell/private-materials';
31
+
32
+ /** 宿主页面保存的报表配置;示例 UUID 需在目标环境核实。 */
33
+ const reportConfig: PisellSupersetDashboardProps['proCodeProps'] = {
34
+ dashboardId: '6c442e22-dccf-48bd-b159-7569d497ab56',
35
+ dashboardUiConfig: {
36
+ filters: { visible: true, expanded: false },
37
+ hideTitle: true,
38
+ hideChartControls: true,
39
+ },
40
+ };
41
+
42
+ /** 已位于宿主引擎上下文内的报表页面。 */
43
+ export function ReportPage() {
44
+ return (
45
+ <PisellSupersetDashboard
46
+ proCodeProps={reportConfig}
47
+ style={{ height: 'calc(100vh - 120px)', minHeight: 320 }}
48
+ />
49
+ );
50
+ }
51
+ ```
52
+
53
+ 组件接收的完整页面属性结构:
54
+
55
+ ```json
56
+ {
57
+ "proCodeProps": {
58
+ "dashboardId": "6c442e22-dccf-48bd-b159-7569d497ab56",
59
+ "dashboardUiConfig": {
60
+ "filters": { "visible": true, "expanded": false },
61
+ "hideTitle": true,
62
+ "hideChartControls": true
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ `dashboardId` 来自 Superset Dashboard 的嵌入配置,必须使用嵌入 UUID。普通数字 Dashboard ID 和完整报表 URL 均不适用。
69
+
70
+ ## 3. Props 与类型
71
+
72
+ ```ts
73
+ interface PisellSupersetDashboardProps {
74
+ /** 宿主页面下发的报表配置。 */
75
+ proCodeProps?: {
76
+ /** 报表嵌入 UUID。 */
77
+ dashboardId: string;
78
+ /** 完整透传给 SDK 的 UI 配置。 */
79
+ dashboardUiConfig?: Record<string, any>;
80
+ };
81
+ /** 根容器附加样式类名。 */
82
+ className?: string;
83
+ /** 根容器样式,可覆盖默认宽高。 */
84
+ style?: React.CSSProperties;
85
+ }
86
+ ```
87
+
88
+ | 属性 | 类型 | 行为 |
89
+ | -------------------------------- | --------------------------- | -------------------------------------------- |
90
+ | `proCodeProps` | 对象,可选 | 缺失时显示配置提示,不发请求 |
91
+ | `proCodeProps.dashboardId` | `string` | 非空时请求后端;UUID 格式由后端最终校验 |
92
+ | `proCodeProps.dashboardUiConfig` | `Record<string, any>`,可选 | 原样传给 SDK;未提供时沿用 SDK 默认行为 |
93
+ | `className` | `string`,可选 | 追加到组件根容器 |
94
+ | `style` | `React.CSSProperties`,可选 | 覆盖默认宽 `100%`、高 `75vh`、最小高 `520px` |
95
+
96
+ 组件不合并默认 UI 配置、不筛除扩展字段,也不把 UI 配置提交给业务接口。具体字段是否生效取决于当前 SDK 和 Superset 服务端的支持情况。
97
+
98
+ 父组件传入内容相同的新配置对象不会重建报表。嵌套字段变化会触发重新初始化;使用深拷贝快照,也能识别原对象被修改后发生重新渲染的情况。仅修改对象而没有触发 React 渲染,不会自动触发更新。
99
+
100
+ 当前没有额外的事件 Props、命令式方法、`request`、`token` 或 Superset 域名 Props。点击内置重试按钮即可重新初始化。
101
+
102
+ ## 4. 宿主上下文与请求方式
103
+
104
+ 组件从以下位置获取已有服务:
105
+
106
+ ```ts
107
+ /** 宿主引擎上下文。 */
108
+ const context = useEngineContext();
109
+ /** 宿主请求客户端,需至少提供 post 方法。 */
110
+ const client = context?.appHelper?.utils?.request;
111
+ ```
112
+
113
+ 宿主需要通过现有引擎 Context Provider 提供 `appHelper.utils.request`。普通 proCode 页面也需要该上下文,单独渲染组件不会自动获得业务请求服务。上下文缺失时组件显示提示,不尝试从共享单例或浏览器存储寻找备用客户端。
114
+
115
+ 请求客户端负责:
116
+
117
+ - 选择当前环境的业务 API baseURL,并支持 `isShopApi: true`。
118
+ - 注入已选店铺的有效 Bearer Token,处理登录 Token 更新和登录失效。
119
+ - 支持第三个参数中的 `AbortSignal`,以及 `fullResult: true`。
120
+ - 返回已解包的业务响应主体,而非原始 `AxiosResponse`。
121
+
122
+ 组件直接将当前客户端传给 `getSupersetSession`,不调用 `request.setRequest()`。这样不会覆盖组件包的共享客户端配置。客户端引用独立不代表内部登录态被固定,切店顺序仍须由宿主保证。
123
+
124
+ 直接使用客户端也不会自动注入共享包装层的 `customToast`;本组件通过错误遮罩展示异常,宿主自己的拦截器和错误处理仍会执行。
125
+
126
+ ## 5. 接口契约与 SDK 映射
127
+
128
+ 每次会话请求均为:
129
+
130
+ ```ts
131
+ client.post(
132
+ '/shop/report/superset/embed',
133
+ { dashboard_id: dashboardId },
134
+ { isShopApi: true, fullResult: true, signal }
135
+ );
136
+ ```
137
+
138
+ 请求体只提交 `dashboard_id`。店铺身份来自宿主登录态,`shop_id`、RLS、服务账号凭据、UI 配置和 Superset 域名均不由组件提交。
139
+
140
+ 成功业务响应示例:
141
+
142
+ ```json
143
+ {
144
+ "status": true,
145
+ "code": 200,
146
+ "message": "",
147
+ "data": {
148
+ "guest_token": "当前店铺的短期访客令牌",
149
+ "superset_domain": "https://bi.mypisell.com",
150
+ "dashboard_id": "6c442e22-dccf-48bd-b159-7569d497ab56"
151
+ }
152
+ }
153
+ ```
154
+
155
+ | 数据来源 | SDK 参数或用途 |
156
+ | -------------------------------- | --------------------------------------------------- |
157
+ | `data.dashboard_id` | `embedDashboard.id`,采用实际响应值 |
158
+ | `data.superset_domain` | `embedDashboard.supersetDomain` |
159
+ | `data.guest_token` | `fetchGuestToken` 返回的字符串 |
160
+ | `proCodeProps.dashboardUiConfig` | `embedDashboard.dashboardUiConfig` |
161
+ | 本轮独立 DOM 节点 | `embedDashboard.mountPoint` |
162
+ | 固定来源策略 | `referrerPolicy: 'strict-origin-when-cross-origin'` |
163
+
164
+ 接口适配层同时要求 `status === true`、`code === 200`,并检查三个会话字段都是非空字符串。HTTP 200 不能单独作为成功依据。业务失败时优先展示 `message`;没有可用消息时使用国际化兜底文案。
165
+
166
+ `SupersetReportError` 是内部异常类型,包含只读 `textKey` 和继承的 `message`。宿主拒绝的请求不会在接口层被改写,组件在上层统一处理其展示。
167
+
168
+ ## 6. 初始化与令牌续期
169
+
170
+ ```mermaid
171
+ flowchart TD
172
+ A[组件挂载或配置变化] --> B{报表 ID 和客户端可用?}
173
+ B -- 否 --> C[显示配置错误]
174
+ B -- 是 --> D[显示加载状态并申请会话]
175
+ D --> E[使用响应域名和 ID 初始化 SDK]
176
+ E --> F[首次取令牌复用本轮已有令牌]
177
+ F --> G[SDK 初始化完成,隐藏加载状态]
178
+ G --> H[SDK 后续调用取令牌回调]
179
+ H --> I[重新请求业务接口]
180
+ I --> G
181
+ D -- 失败或初始化超时 --> J[清理当前会话并显示错误]
182
+ E -- 初始化失败 --> J
183
+ I -- 续期失败 --> J
184
+ J --> K[用户点击重新加载]
185
+ K --> A
186
+ ```
187
+
188
+ 每轮初始化维护以下内部状态:
189
+
190
+ | 标识 | 含义 |
191
+ | --------------------------------- | ------------------------------------------ |
192
+ | `INITIALIZATION_TIMEOUT` | 当前为 `60000` 毫秒,即 60 秒初始化超时 |
193
+ | `configSnapshot` / `stableConfig` | 比较 UI 配置内容,避免等值对象触发重复挂载 |
194
+ | `host` / `mountPoint` | React 容器和本轮 SDK 专属节点 |
195
+ | `attempt` | 手动重试序号 |
196
+ | `loading` / `error` | 加载状态及可展示错误 |
197
+ | `controller` | 本轮业务请求的取消控制器 |
198
+ | `stopped` | 本轮会话是否已失效 |
199
+ | `instance` | 已完成初始化的 SDK 实例 |
200
+ | `firstToken` | 仅允许首次回调消费一次的令牌 |
201
+ | `timer` | 首次初始化的超时计时器 |
202
+
203
+ 首次业务请求成功后,`firstToken` 供 SDK 第一次调用时返回,随后立即清空。后续每次 SDK 续期回调都重新调用同一业务接口,返回新的 `guest_token` 字符串。组件不自行高频轮询,也不写死 Guest Token 的有效期。
204
+
205
+ 60 秒计时覆盖首次业务请求和 SDK 初始化,初始化完成即清除;它不限制图表数据加载时间,也不是令牌有效期或后续所有请求的统一超时时间。请求本身的超时仍由宿主客户端处理,SDK 的内部取令牌超时遵循所用版本的实现。
206
+
207
+ ## 7. 清理、竞态与切店
208
+
209
+ | 内部方法 | 职责 |
210
+ | -------------------- | ------------------------------------------------ |
211
+ | `getSupersetSession` | 调用业务接口并校验响应 |
212
+ | `getSession` | 在请求前后检查会话有效性,拒绝迟到响应 |
213
+ | `assertActive` | 阻止失效会话继续请求或返回令牌 |
214
+ | `dispose` | 标记失效、取消请求、清空令牌、卸载实例并移除节点 |
215
+ | `fail` | 清理失败会话,并在当前有效页面显示错误 |
216
+
217
+ 组件卸载、报表 ID 变化、UI 配置内容变化、客户端引用变化或手动重试时,都会先结束旧会话。
218
+
219
+ 每轮使用独立的 DOM 节点和取消控制器。即便请求客户端未真正中止网络请求,响应返回后也会检查 `stopped`。SDK 创建期间如果发生卸载或超时,迟到的 SDK 实例会立即卸载,不会保存到新会话,也不会清空新报表容器。
220
+
221
+ 切店或退出登录必须由宿主按以下顺序执行:
222
+
223
+ 1. 先卸载当前报表组件,结束旧实例及续期回调。
224
+ 2. 完成店铺登录态切换或退出登录。
225
+ 3. 新登录上下文准备好后,再渲染目标报表。
226
+
227
+ 宿主可使用 `key={currentShopId}` 区分报表实例,但在登录态更新后才更换 `key` 不能替代上述卸载顺序。不要通过路由保活、隐藏 DOM 或预渲染持续保留旧报表。组件不会主动监听外部存储中的店铺或 Token 变化。
228
+
229
+ Guest Token 只用于当前会话,不写入 URL、日志或浏览器持久存储,不跨店铺或报表复用。
230
+
231
+ ## 8. 加载、失败与样式
232
+
233
+ 加载状态由 Ant Design `Spin` 和 `LoadingOutlined` 组成,图标为 28px,置于 56px 的浅灰圆角底框内,下方展示加载文案。图标颜色沿用 Ant Design 主题。系统设置减少动画时关闭旋转,保留图标和文案。
234
+
235
+ 根容器通过 `aria-busy` 表示初始化状态,加载区使用 `role="status"`,错误区使用 `role="alert"`;装饰图标使用 `aria-hidden` 避免重复播报。
236
+
237
+ 默认容器宽度为 `100%`,高度为 `75vh`,最小高度为 `520px`。使用更小高度时,应同时覆盖 `minHeight`。iframe 填满容器,无边框。
238
+
239
+ | 场景 | 组件行为 |
240
+ | ------------------------- | -------------------------------- |
241
+ | 缺少报表 ID 或请求客户端 | 不发请求,显示配置提示 |
242
+ | HTTP 成功但业务失败 | 显示业务消息或兜底文案 |
243
+ | 成功响应缺少会话字段 | 显示报表配置不完整 |
244
+ | 首次请求或 SDK 初始化失败 | 清理本轮资源,显示重新加载按钮 |
245
+ | 初始化超过 60 秒 | 标记超时,取消请求,清理挂载节点 |
246
+ | 续期请求失败 | 停止当前实例,等待手动重新加载 |
247
+ | 主动卸载或被新会话替代 | 静默清理,不展示旧会话错误 |
248
+
249
+ 加载提示消失只表示 SDK 初始化完成,不表示每张图表的数据都加载成功。跨域 iframe 中的图表错误不在组件的直接检测范围内。
250
+
251
+ `locales.ts` 提供 `en`、`zh-CN`、`zh-HK` 文案,显示语言来自 `context.engine.props.locale`,未提供时使用 `en`。后端返回的错误消息按原文展示。
252
+
253
+ ## 9. 低代码与构建
254
+
255
+ 低代码物料名称和包导出名称均为 `PisellSupersetDashboard`。属性面板使用 `ObjectSetter` 编辑 `proCodeProps`,其中 `dashboardId` 使用字符串编辑器,`dashboardUiConfig` 使用 JSON 编辑器。
256
+
257
+ 默认物料片段将报表 ID 留空,预置筛选器可见且收起、隐藏标题和图表操作项;这些设置只属于片段默认值,组件运行时不会强制添加。
258
+
259
+ SDK 固定版本为 `0.4.0`,其发布文件包含 Webpack 4 不支持的现代语法。组件包 `build.plugin.js` 已将该 SDK 纳入已有 Babel 转译规则。开发环境建议使用 Node.js 18;现有低代码构建插件在 Node.js 22 下存在类型文件加载兼容问题。
260
+
261
+ 在仓库根目录执行:
262
+
263
+ ```bash
264
+ # 运行组件与接口测试。
265
+ pnpm --filter @pisell/private-materials exec vitest run src/plus/pisellSupersetDashboard --maxWorkers=1 --minWorkers=1
266
+
267
+ # 生成组件包 ESM/CJS 产物和类型声明。
268
+ pnpm --filter @pisell/private-materials build
269
+
270
+ # 使用 Node.js 18 执行现有 Webpack 4 低代码构建。
271
+ NODE_OPTIONS=--openssl-legacy-provider pnpm --filter @pisell/private-materials lowcode:build
272
+ ```
273
+
274
+ 自动测试覆盖配置透传、响应映射、首次令牌复用、续期失败、手动重试、配置变化、初始化超时、卸载后的迟到响应及多实例隔离。测试使用模拟客户端和 SDK,不会签发真实令牌。
275
+
276
+ ## 10. 联调与常见问题
277
+
278
+ | 现象 | 核对事项 |
279
+ | ------------------------ | ----------------------------------------------------------------------- |
280
+ | 提示宿主未提供请求客户端 | 确认当前页面处于引擎 Provider 内,并提供 `appHelper.utils.request.post` |
281
+ | 提示缺少报表嵌入 ID | 检查配置是否放在 `proCodeProps.dashboardId` |
282
+ | 后端提示 UUID 不合法 | 确认使用嵌入配置生成的 UUID |
283
+ | 接口成功但提示配置不完整 | 检查客户端解包层级,以及三个会话字段是否齐全 |
284
+ | iframe 空白或拒绝嵌入 | 检查返回域名、嵌入允许来源、Referer、CSP 和容器尺寸 |
285
+ | 图表显示但无数据 | 检查 Superset 数据集权限、RLS 和目标店铺数据 |
286
+ | 切店后仍看到旧内容 | 检查是否先卸载旧组件,以及路由是否保活 |
287
+ | 重复挂载或重复申请令牌 | 检查报表配置、客户端引用和 React key 是否持续变化 |
288
+
289
+ Superset 需要启用嵌入能力并允许实际前端来源;业务 API 跨域访问还需正确配置 CORS。服务账号、RLS、数据集权限和店铺隔离由后端及 Superset 配置负责。
290
+
291
+ 正式接入后仍需验证真实报表展示、长时间打开时的续期、离开页面后的清理,以及 A/B 店铺的数据隔离。组件包导出和物料注册不等于宿主已接通 `superset_dashboard` 页面,该页面映射需要由宿主完成。
@@ -0,0 +1,15 @@
1
+ import { PisellSupersetDashboardProps } from "./types.js";
2
+ import React from "react";
3
+ //#region src/plus/pisellSupersetDashboard/index.d.ts
4
+ /**
5
+ * Superset 报表组件,负责请求会话、挂载报表、续期、状态展示和资源清理。
6
+ * 切店或退出登录时,宿主必须先卸载旧组件,再更新登录态并重新挂载。
7
+ * @param props 页面报表配置、附加样式类名和根容器样式,详见组件属性类型。
8
+ */
9
+ declare function PisellSupersetDashboard({
10
+ proCodeProps,
11
+ className,
12
+ style
13
+ }: PisellSupersetDashboardProps): React.JSX.Element;
14
+ //#endregion
15
+ export { PisellSupersetDashboard };