aldehyde 0.2.528 → 0.2.530

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 (29) hide show
  1. package/lib/controls/direct-file-view/index.d.ts.map +1 -1
  2. package/lib/controls/direct-file-view/index.js +16 -4
  3. package/lib/controls/direct-file-view/index.js.map +1 -1
  4. package/lib/lowcode-components/index.d.ts +2 -0
  5. package/lib/lowcode-components/index.d.ts.map +1 -1
  6. package/lib/lowcode-components/index.js +2 -1
  7. package/lib/lowcode-components/index.js.map +1 -1
  8. package/lib/lowcode-components/lowcode-view/component/assets.d.ts.map +1 -1
  9. package/lib/lowcode-components/lowcode-view/component/assets.js +8 -0
  10. package/lib/lowcode-components/lowcode-view/component/assets.js.map +1 -1
  11. package/lib/lowcode-components/ranking-list/index.d.ts +46 -0
  12. package/lib/lowcode-components/ranking-list/index.d.ts.map +1 -0
  13. package/lib/lowcode-components/ranking-list/index.js +290 -0
  14. package/lib/lowcode-components/ranking-list/index.js.map +1 -0
  15. package/lib/routable/ttmpl-table-route.d.ts.map +1 -1
  16. package/lib/routable/ttmpl-table-route.js.map +1 -1
  17. package/lib/table/column/column-builder.d.ts.map +1 -1
  18. package/lib/table/column/column-builder.js +5 -4
  19. package/lib/table/column/column-builder.js.map +1 -1
  20. package/lib/tmpl/interface.d.ts +1 -0
  21. package/lib/tmpl/interface.d.ts.map +1 -1
  22. package/package.json +1 -1
  23. package/src/aldehyde/controls/direct-file-view/index.tsx +14 -4
  24. package/src/aldehyde/lowcode-components/index.ts +3 -1
  25. package/src/aldehyde/lowcode-components/lowcode-view/component/assets.ts +8 -0
  26. package/src/aldehyde/lowcode-components/ranking-list/index.tsx +434 -0
  27. package/src/aldehyde/routable/ttmpl-table-route.tsx +0 -1
  28. package/src/aldehyde/table/column/column-builder.tsx +5 -4
  29. package/src/aldehyde/tmpl/interface.tsx +1 -1
@@ -0,0 +1,434 @@
1
+
2
+ import React, { ForwardedRef, forwardRef, useEffect, useImperativeHandle, useState, useRef, useMemo } from 'react';
3
+ import { Listy, Spin, Progress, Tooltip, Empty } from 'antd';
4
+ import { generate } from '@ant-design/colors';
5
+ import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons';
6
+ import { useLocale } from "../../locale/useLocale";
7
+ import HydrocarbonService from "../../tmpl/hcservice-v3";
8
+ import { DataConfigProps } from "../data";
9
+
10
+ const medalColor = ['#ffd76a', '#cfd8ea', '#e8a86e'];
11
+ const medalText = ['🥇', '🥈', '🥉'];
12
+
13
+ const demoData = [
14
+ { name: '综采一队', value: 4.2, rate: -3.1 },
15
+ { name: '综采二队', value: 3.1, rate: -1.2 },
16
+ { name: '洗煤厂', value: 2.6, rate: 0.8 },
17
+ { name: '运输工区', value: 2.4, rate: 1.5 },
18
+ { name: '通风工区', value: 2.3, rate: 2.4 },
19
+ { name: '掘进工区', value: 2.3, rate: -0.6 },
20
+ ];
21
+
22
+ /**
23
+ * 处理数据格式约定:
24
+ * v1:名称
25
+ * v2:值
26
+ * v3:环比
27
+ * v4\v5...:其他数据弹窗显示
28
+ * d1:进度条最大值(图表未配置最大值时,使用d1值为最大值)
29
+ */
30
+
31
+ const handleData = (data: { config: { [key: string]: string }, value: { [key: string]: any }[] }[]) => {
32
+ const temConfig = data?.[0]?.config || {}; // 列配置(字段中文名等)
33
+ const temValue = data?.[0]?.value || [];
34
+ const temData = temValue.map((row: { [key: string]: any }) => {
35
+ const [id, temName] = String(row?.v1 ?? '').split('@R@'); // v1 格式:id@R@名称
36
+ // v4、v5... 其他字段,用于弹窗展示
37
+ const extras = Object.keys(row || {})
38
+ .filter(key => !['v1', 'v2', 'v3'].includes(key) && !key.includes('-'))
39
+ .map(key => ({ key, label: temConfig[key] || key, value: row[key], color: row[`${key}-color`] }))
40
+ .filter(item => item.value !== null && item.value !== undefined && item.value !== '')
41
+ .sort((a, b) => { // v 开头在前(v4、v5...),其他前缀(d1...)在后,同前缀按数字升序
42
+ const getSort = (key: string) => {
43
+ const prefix = (key.match(/^[a-zA-Z]+/) || [''])[0];
44
+ return { group: prefix === 'v' ? 0 : 1, prefix, num: Number(key.replace(/\D/g, '')) || 0 };
45
+ };
46
+ const x = getSort(a.key);
47
+ const y = getSort(b.key);
48
+ return x.group - y.group || x.prefix.localeCompare(y.prefix) || x.num - y.num;
49
+ });
50
+ return {
51
+ id, // 名称对应的id
52
+ name: temName ?? id, // v1:名称
53
+ value: row?.v2 ?? undefined, // v2:值
54
+ rate: row?.v3 ?? undefined, // v3:环比
55
+ maxValue: row?.d1 ?? undefined, // d1(维度1):进度条最大值
56
+ extras, // v4、v5...:其他数据
57
+ };
58
+ });
59
+ // 按值倒序排列
60
+ return temData.sort((a, b) => (Number(b.value) || 0) - (Number(a.value) || 0));
61
+ }
62
+
63
+ export interface ComponentStyle {
64
+ primaryColor?: string; // 主色
65
+ order?: { // 排序配置
66
+ show?: boolean; // 是否显示
67
+ showMedal?: boolean; // 是否显示勋章
68
+ };
69
+ rate?: {
70
+ show?: boolean; // 是否显示环比
71
+ fontSize?: number; // 字体大小
72
+ };
73
+ value?: {
74
+ show?: boolean; // 是否显示值
75
+ fontSize?: number; // 字体大小
76
+ maxData?: number; // 最大数据值,用于进度条
77
+ };
78
+ name?: {
79
+ show?: boolean; // 是否显示名称
80
+ fontSize?: number; // 字体大小
81
+ };
82
+ progress?: { // 进度条配置
83
+ show?: boolean; // 是否显示进度条
84
+ size?: number; // 尺寸(高度)
85
+ };
86
+ showTooltip?: boolean; // 是否显示提示
87
+ }
88
+
89
+ export interface ComponentProps {
90
+ style?: ComponentStyle;
91
+ base?: { width: number, height: number };
92
+ data?: DataConfigProps;
93
+ isDesignMode?: boolean; // 是否编辑模式
94
+ initSearchParams?: { [key: string]: any }; // 全局初始化外部参数,例:传参包含设备id,则在查询时带上设备id
95
+ }
96
+
97
+ export interface ComponentRef {
98
+ updateConfig: (newConfig: ComponentProps) => void;
99
+ destroy: () => void;
100
+ }
101
+
102
+ const Index = forwardRef((props: ComponentProps, ref: ForwardedRef<ComponentRef>) => {
103
+ const { style, data: propsData, isDesignMode, base: propsBase } = props;
104
+ const [config, setConfig] = useState<ComponentStyle>(style || {});
105
+ const [dataConfig, setDataConfig] = useState<DataConfigProps>({ ...propsData }); // 数据源配置
106
+ const [size, setSize] = useState<{ width: number, height: number }>(propsBase);
107
+ const [data, setData] = useState<{ [key: string]: any }[]>();
108
+ const [searchParams, setSearchParams] = useState<{ [key: string]: any }>();
109
+ const [initSearchParams, setInitSearchParams] = useState<{ [key: string]: any }>();
110
+ const [loading, setLoading] = useState<boolean>(false);
111
+ const pollingIntervalRef = useRef<number | NodeJS.Timeout>(null);
112
+ const { translate } = useLocale();
113
+
114
+ // 组件销毁
115
+ const handleDestroy = () => {
116
+ pollingIntervalRef.current && clearInterval(pollingIntervalRef.current);
117
+ };
118
+
119
+ // 全局初始化请求数据处理
120
+ const handleInitData = (val: DataConfigProps) => {
121
+ if (val.sourceType === "sourceId" && val.isInit && val.initData) {
122
+ const { config, value } = val.initData;
123
+ const temData = handleData(value);
124
+ setData(temData);
125
+ if (config?.requestMode === "polling") {
126
+ pollingIntervalRef.current = setInterval(() => { getData() }, config?.pollingInterval || 60000);
127
+ }
128
+ }
129
+ }
130
+
131
+ // ws订阅数据更新
132
+ const handleWSData = (val: DataConfigProps) => {
133
+ if (val.sourceType === "sourceId" && val.wsData) {
134
+ const { value } = val.wsData;
135
+ const temData = handleData(value);
136
+ setData(temData);
137
+ }
138
+ }
139
+
140
+ useImperativeHandle(ref, () => ({ // 暴露到外部func
141
+ updateConfig: (newConfig) => { // 组件更新配置
142
+ const { base, style, data: newDataConfig, initSearchParams: newISParams } = newConfig;
143
+ setConfig({ ...(style || {}) });
144
+ setSize({ width: base.width, height: base.height });
145
+ setDataConfig(newDataConfig);
146
+ handleInitData(newDataConfig);
147
+ setSearchParams(newDataConfig.searchParams);
148
+ setInitSearchParams(newISParams);
149
+ handleWSData(newDataConfig);
150
+ },
151
+ destroy: handleDestroy
152
+ }));
153
+
154
+ // 请求数据
155
+ const getData = async (params?: object) => {
156
+ setLoading(true);
157
+ const { data } = await HydrocarbonService.requestChartData(null, dataConfig?.sourceId, params) || {};
158
+ const { config, value } = data || {};
159
+ const temData = handleData(value);
160
+ setData(temData);
161
+ setLoading(false);
162
+ return config;
163
+ }
164
+
165
+ // 初始化请求数据
166
+ const getInitData = async (params?: object) => {
167
+ pollingIntervalRef.current && clearInterval(pollingIntervalRef.current);
168
+ const config = await getData(params);
169
+ if (config?.requestMode === "polling") {
170
+ pollingIntervalRef.current = setInterval(() => getData(params), config?.pollingInterval || 60000);
171
+ }
172
+ }
173
+
174
+ useEffect(() => {
175
+ return () => {
176
+ handleDestroy();
177
+ }
178
+ }, []);
179
+
180
+ // 静态数据处理
181
+ useEffect(() => {
182
+ if (dataConfig?.sourceType === 'staticData') {
183
+ const { value } = dataConfig.staticData || {};
184
+ setData(value);
185
+ }
186
+ }, [dataConfig?.sourceType, dataConfig?.staticData]);
187
+
188
+ /**
189
+ * 数据源数据请求
190
+ * 1、编辑态不请求数据
191
+ */
192
+ useEffect(() => {
193
+ if (!isDesignMode && dataConfig?.sourceType === 'sourceId' && dataConfig?.sourceId && !dataConfig?.isInit && dataConfig?.isDefRequest !== false) {
194
+ if (dataConfig?.isInitParams) { // 组件数据源接收外部参数
195
+ initSearchParams && getInitData(initSearchParams);
196
+ } else {
197
+ getInitData();
198
+ }
199
+ }
200
+ }, [dataConfig?.sourceType, dataConfig?.sourceId, initSearchParams]);
201
+
202
+ // 搜索参数变化时重新请求数据
203
+ useEffect(() => {
204
+ if (searchParams) {
205
+ const params = { ...(initSearchParams || {}), ...searchParams };
206
+ if (dataConfig?.isInitParams) { // 数据源接收外部参数,则参数存在再请求
207
+ initSearchParams && getInitData(params);
208
+ } else {
209
+ getInitData(params);
210
+ }
211
+ }
212
+ }, [searchParams, initSearchParams]);
213
+
214
+ // 渲染环比变化
215
+ const renderChg = (val: number, fontSize: number = 12) => {
216
+ let temData: any = { color: "#8f9bae", icon: "-", value: "" };
217
+ if (val > 0) {
218
+ temData = { color: "#4fe3c1", icon: <CaretUpOutlined />, value: val.toFixed(2) };
219
+ } else if (val < 0) {
220
+ temData = { color: "#f27c8c", icon: <CaretDownOutlined />, value: Math.abs(val).toFixed(2) };
221
+ }
222
+ return <span style={{ color: temData.color, fontSize }}>{temData.icon} {temData.value}</span>;
223
+ };
224
+
225
+ const renderItem = (item, i) => {
226
+ const { primaryColor = '#1677ff', order, rate, value, name, progress, showTooltip = true } = config;
227
+ const palette = generate(primaryColor); // 主题色映射
228
+ const isTop = i < 3; // 是否为前3名
229
+ const showOrder = order?.show !== false; // 是否显示排序
230
+ const showMedal = isTop && order?.showMedal !== false; // 前三名是否显示勋章
231
+ const showRate = rate?.show !== false; // 是否显示环比
232
+ const showValue = value?.show !== false; // 是否显示值
233
+ const showName = name?.show !== false; // 是否显示名称
234
+ const showProgress = progress?.show !== false; // 是否显示进度条
235
+ // 进度条最大值-优先使用图表设置的最大值,图表未配置最大值时,使用d1值为最大值
236
+ const val = Math.min(100, (item.value / (value.maxData || Number(item.maxValue || 100))) * 100); // 进度条数值
237
+ // 列表卡片样式
238
+ const listItemStyle: React.CSSProperties = {
239
+ cursor: 'pointer',
240
+ padding: '10px 12px',
241
+ marginBottom: 8,
242
+ borderRadius: 8,
243
+ background: `${palette[5]}50`,
244
+ border: `1px solid ${palette[1]}`,
245
+ borderLeft: `3px solid ${isTop ? medalColor[i] : palette[3]}`,
246
+ backdropFilter: 'blur(10px)',
247
+ transition: 'all .15s',
248
+ };
249
+
250
+ const renderList = () => (
251
+ <div style={listItemStyle}>
252
+ <div style={{ display: 'flex', alignItems: 'center', gap: showOrder ? 10 : 0 }}>
253
+ {showOrder && (
254
+ <div
255
+ style={{
256
+ borderRadius: 7, minWidth: "30px", minHeight: "30px", aspectRatio: "1/1",
257
+ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: "4px",
258
+ fontWeight: 700, fontSize: isTop ? progress.size * 1.5 : progress.size,
259
+ background: isTop ? `${medalColor[i]}22` : 'rgba(6,225,253,.12)',
260
+ color: isTop ? medalColor[i] : palette[2],
261
+ }}
262
+ >
263
+ {showMedal ? medalText[i] : i + 1}
264
+ </div>
265
+ )}
266
+ <div style={{ flex: 1, minWidth: 0 }}>
267
+ {(showName || showRate) && (
268
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
269
+ {showName ? (
270
+ <span style={{ fontSize: name?.fontSize || 15, fontWeight: 700, color: palette[2] }}>{item.name}</span>
271
+ ) : <span />}
272
+ {showRate && (
273
+ <span style={{ fontSize: rate?.fontSize || 11, color: palette[2] }}>
274
+ {translate("${环比}")} {renderChg(item.rate, rate?.fontSize || 12)}
275
+ </span>
276
+ )}
277
+ </div>
278
+ )}
279
+ {(showProgress || showValue) && (
280
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
281
+ {showProgress && (
282
+ <Progress
283
+ percent={val}
284
+ showInfo={false}
285
+ strokeColor={{ from: palette[5], to: palette[1] }}
286
+ railColor="rgba(255,255,255,.08)"
287
+ size={progress?.size ? { height: progress.size } : undefined}
288
+ style={{ flex: 1, margin: 0 }}
289
+ />
290
+ )}
291
+ {showValue && (
292
+ <span
293
+ style={{
294
+ fontSize: value?.fontSize || 16, fontWeight: 700,
295
+ color: palette[3],
296
+ maxWidth: "20%", textAlign: 'right',
297
+ textShadow: `0 0 8px ${palette[3]}32`,
298
+ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
299
+ }}
300
+ >
301
+ {item.value}
302
+ </span>
303
+ )}
304
+ </div>
305
+ )}
306
+ </div>
307
+ </div>
308
+ </div>
309
+ );
310
+
311
+ // tooltip 卡片:风格与列表一致,布局为标题 + 明细行
312
+ const renderTooltip = () => (
313
+ <div
314
+ style={{
315
+ minWidth: 180,
316
+ padding: '10px 12px',
317
+ borderRadius: 8,
318
+ background: `${palette[9]}cc`,
319
+ backdropFilter: 'blur(10px)',
320
+ }}
321
+ >
322
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
323
+ {showOrder && (
324
+ <span
325
+ style={{
326
+ width: 20, height: 20, borderRadius: 5, flexShrink: 0,
327
+ display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
328
+ fontSize: 12, fontWeight: 700,
329
+ background: isTop ? `${medalColor[i]}22` : 'rgba(6,225,253,.12)',
330
+ color: isTop ? medalColor[i] : palette[2],
331
+ }}
332
+ >
333
+ {showMedal ? medalText[i] : i + 1}
334
+ </span>
335
+ )}
336
+ {showName && (
337
+ <span
338
+ style={{
339
+ flex: 1, minWidth: 0, fontWeight: 700,
340
+ fontSize: (name?.fontSize || 15) - 1, color: palette[0],
341
+ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
342
+ }}
343
+ >
344
+ {item.name}
345
+ </span>
346
+ )}
347
+ {showRate && (
348
+ <span
349
+ style={{
350
+ fontWeight: 700, fontSize: (value?.fontSize || 16) - 2,
351
+ color: palette[3], textShadow: `0 0 8px ${palette[3]}32`,
352
+ }}
353
+ >
354
+ {renderChg(item.rate, 12)}
355
+ </span>
356
+ )}
357
+ </div>
358
+ {/* v4、v5... 其他数据 */}
359
+ {!!item.extras?.length && (
360
+ <div
361
+ style={{
362
+ display: 'grid',
363
+ gridTemplateColumns: 'auto 1fr',
364
+ alignItems: 'center',
365
+ columnGap: 12, rowGap: 4,
366
+ }}
367
+ >
368
+ {item.extras.map((extra: { key: string, label: string, value: any, color?: string }) => (
369
+ <React.Fragment key={extra.key}>
370
+ <span style={{ fontSize: 11, color: palette[1], whiteSpace: 'nowrap' }}>{extra.label}</span>
371
+ <span
372
+ style={{
373
+ fontSize: 12, textAlign: 'right',
374
+ color: extra.color || palette[2],
375
+ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
376
+ }}
377
+ >
378
+ {extra.value}
379
+ </span>
380
+ </React.Fragment>
381
+ ))}
382
+ </div>
383
+ )}
384
+ </div>
385
+ );
386
+
387
+ return showTooltip ? (
388
+ <Tooltip
389
+ title={renderTooltip()}
390
+ placement="right"
391
+ arrow={false}
392
+ styles={{
393
+ root: { maxWidth: 'none' },
394
+ container: { padding: 0, background: 'transparent', boxShadow: 'none' },
395
+ }}
396
+ >
397
+ {renderList()}
398
+ </Tooltip>
399
+ ) : renderList();
400
+ }
401
+
402
+ // 编辑态且非静态数据源时展示默认数据
403
+ const listData = useMemo(() => {
404
+ const isDemo = isDesignMode && dataConfig?.sourceType !== 'staticData';
405
+ return isDemo ? demoData : (data || []);
406
+ }, [isDesignMode, dataConfig, data]);
407
+
408
+ return (
409
+ <Spin spinning={!isDesignMode && loading}>
410
+ {listData.length ? (
411
+ <Listy
412
+ styles={{
413
+ root: { background: 'transparent', width: '100%', height: '100%' },
414
+ item: { padding: 0, border: 'none', background: 'transparent' },
415
+ }}
416
+ height={size?.height}
417
+ rowKey={(item) => `${item.name}-${item.value}`}
418
+ items={listData}
419
+ itemRender={renderItem}
420
+ />
421
+ ) : (
422
+ <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
423
+ <Empty
424
+ image={Empty.PRESENTED_IMAGE_SIMPLE}
425
+ description={translate("${暂无数据}")}
426
+ styles={{ description: { color: config?.primaryColor } }}
427
+ />
428
+ </div>
429
+ )}
430
+ </Spin>
431
+ );
432
+ });
433
+
434
+ export default Index;
@@ -68,7 +68,6 @@ const Index = (props: Props) => {
68
68
  const ettmplConfig: EttmplConfig = await HCDataSource.requestEttmplConfig(serverKey, sourceId);
69
69
  const ettmplSourceId_ = ettmplConfig?.sourceId || "";
70
70
  const ettmplLtmplConfig: LtmplConfigRes = await HCDataSource.requestEttmplLtmplConfig(serverKey, ettmplSourceId_, ettmplSourceId_);
71
-
72
71
  const ltmplConfig = ettmplLtmplConfig?.ltmplConfig;
73
72
  setLtmplConfig(ltmplConfig);
74
73
  setEttmplConfig(ettmplConfig);
@@ -581,8 +581,9 @@ export default {
581
581
 
582
582
  // 展开树行操作添加按钮
583
583
  if (rowRactionAddConfigs?.length && doAddRaction && !readOnly) {
584
- if (rowRactionAddConfigs?.length > 1) {
585
- const temItems = rowRactionAddConfigs.map(item => ({ key: item.id, label: <Tooltip title={`${translate("${添加}")}${translate("${" + item.title + "}")}`}><div onClick={() => doAddRaction(item.id, record.code)}><PlusOutlined style={{ marginRight: "6px" }} /><span>{item.title}</span></div></Tooltip> }));
584
+ const temConfigs = rowRactionAddConfigs.filter(item => ActionUtils.isShow(item.addableActionConfig?.preposes, [record], undefined)); // 过滤前置条件
585
+ if (temConfigs?.length > 1) {
586
+ const temItems = temConfigs.map(item => ({ key: item.id, label: <Tooltip title={`${translate("${添加}")}${translate("${" + item.title + "}")}`}><div onClick={() => doAddRaction(item.id, record.code)}><PlusOutlined style={{ marginRight: "6px" }} /><span>{item.title}</span></div></Tooltip> }));
586
587
  actionButtons.push(<Dropdown menu={{ items: temItems }}>
587
588
  <Button
588
589
  size="small"
@@ -590,8 +591,8 @@ export default {
590
591
  icon={<PlusOutlined />}
591
592
  />
592
593
  </Dropdown>);
593
- } else {
594
- const tItem = rowRactionAddConfigs[0];
594
+ } else if (temConfigs[0]) {
595
+ const tItem = temConfigs[0];
595
596
  actionButtons.push(
596
597
  <Tooltip title={`${translate("${添加}")}${translate("${" + tItem.title + "}")}`}>
597
598
  <Button
@@ -723,7 +723,7 @@ export interface LtmplConfig extends SelectConfig {
723
723
  tableLayout?: "auto" | "fixed"; // 表格布局(自适应列宽/固定列宽)
724
724
  buttonAuditViewAction?: ActionConfig; // 审计查看按钮
725
725
  buttonAuditEditAction?: ActionConfig; // 审计编辑按钮
726
- rowRactionAddConfigs?: { id: string, order: number, tip: string, title: string }[]; // 展开树行操作添加按钮配置
726
+ rowRactionAddConfigs?: { id: string, order: number, tip: string, title: string, addableActionConfig: ActionConfig }[]; // 展开树行操作添加按钮配置
727
727
  }
728
728
 
729
729
  export interface EnumItem {