neo-cmp-cli 1.12.12 → 1.13.0

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 (52) hide show
  1. package/README.md +2 -1
  2. package/dist/index2.js +1 -1
  3. package/dist/module/neoInitByCopy.js +1 -1
  4. package/dist/package.json.js +1 -1
  5. package/package.json +1 -1
  6. package/template/antd-custom-cmp-template/package.json +1 -1
  7. package/template/asset-manage-template/package.json +1 -1
  8. package/template/echarts-custom-cmp-template/package.json +1 -1
  9. package/template/empty-custom-cmp-template/package.json +1 -1
  10. package/template/map-custom-cmp-template/package.json +1 -1
  11. package/template/neo-bi-cmps/.prettierrc.js +12 -0
  12. package/template/neo-bi-cmps/@types/neo-ui-common.d.ts +36 -0
  13. package/template/neo-bi-cmps/README.md +99 -0
  14. package/template/neo-bi-cmps/commitlint.config.js +59 -0
  15. package/template/neo-bi-cmps/neo.config.js +124 -0
  16. package/template/neo-bi-cmps/package.json +62 -0
  17. package/template/neo-bi-cmps/public/css/base.css +283 -0
  18. package/template/neo-bi-cmps/public/scripts/app/bluebird.js +6679 -0
  19. package/template/neo-bi-cmps/public/template.html +13 -0
  20. package/template/neo-bi-cmps/src/assets/css/common.scss +127 -0
  21. package/template/neo-bi-cmps/src/assets/css/mixin.scss +47 -0
  22. package/template/neo-bi-cmps/src/assets/img/AIBtn.gif +0 -0
  23. package/template/neo-bi-cmps/src/assets/img/NeoCRM.jpg +0 -0
  24. package/template/neo-bi-cmps/src/assets/img/aiLogo.png +0 -0
  25. package/template/neo-bi-cmps/src/assets/img/card-list.svg +1 -0
  26. package/template/neo-bi-cmps/src/assets/img/contact-form.svg +1 -0
  27. package/template/neo-bi-cmps/src/assets/img/custom-form.svg +1 -0
  28. package/template/neo-bi-cmps/src/assets/img/custom-widget.svg +1 -0
  29. package/template/neo-bi-cmps/src/assets/img/data-list.svg +1 -0
  30. package/template/neo-bi-cmps/src/assets/img/detail.svg +1 -0
  31. package/template/neo-bi-cmps/src/assets/img/favicon.png +0 -0
  32. package/template/neo-bi-cmps/src/assets/img/map.svg +1 -0
  33. package/template/neo-bi-cmps/src/assets/img/search.svg +1 -0
  34. package/template/neo-bi-cmps/src/assets/img/table.svg +1 -0
  35. package/template/neo-bi-cmps/src/components/targetNumber__c/README.md +100 -0
  36. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/configSchema.ts +253 -0
  37. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.scss +76 -0
  38. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.tsx +148 -0
  39. package/template/neo-bi-cmps/src/components/targetNumber__c/index.tsx +440 -0
  40. package/template/neo-bi-cmps/src/components/targetNumber__c/model.ts +128 -0
  41. package/template/neo-bi-cmps/src/components/targetNumber__c/style.scss +173 -0
  42. package/template/neo-bi-cmps/src/utils/axiosFetcher.ts +37 -0
  43. package/template/neo-bi-cmps/src/utils/queryObjectData.ts +76 -0
  44. package/template/neo-bi-cmps/src/utils/xobjects.ts +162 -0
  45. package/template/neo-bi-cmps/tsconfig.json +40 -0
  46. package/template/neo-custom-cmp-template/package.json +1 -1
  47. package/template/neo-h5-cmps/package.json +1 -1
  48. package/template/neo-order-cmps/package.json +1 -1
  49. package/template/neo-web-cmps/package.json +1 -1
  50. package/template/react-custom-cmp-template/package.json +1 -1
  51. package/template/react-ts-custom-cmp-template/package.json +1 -1
  52. package/template/vue2-custom-cmp-template/package.json +1 -1
@@ -0,0 +1,76 @@
1
+ .properties-panel-custom-style-config {
2
+ .custom-style-config-input-container {
3
+ display: flex;
4
+ flex-direction: row !important;
5
+ align-items: center;
6
+ gap: 0;
7
+
8
+ .custom-style-config-input {
9
+ flex: 1;
10
+ height: 35px;
11
+ padding: 4px 11px;
12
+ border: 1px solid #d9d9d9;
13
+ // border-right: none;
14
+ border-radius: 2px 0 0 2px;
15
+ background: var(--background-light);
16
+ color: #262626;
17
+ transition: all 0.2s ease;
18
+ cursor: not-allowed;
19
+
20
+ &::placeholder {
21
+ color: #aaaaaa;
22
+ }
23
+
24
+ &:focus {
25
+ box-shadow: none;
26
+ border-color: #d9d9d9;
27
+ }
28
+
29
+ &.ant-input-disabled {
30
+ color: var(--font-secondary);
31
+ cursor: not-allowed;
32
+ background: #f5f5f5;
33
+ }
34
+
35
+ &[readonly] {
36
+ cursor: not-allowed;
37
+ background: #fafafa;
38
+ }
39
+ }
40
+
41
+ .custom-style-config-setting-btn {
42
+ flex-shrink: 0;
43
+ width: 35px;
44
+ height: 35px;
45
+ padding: 0;
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ border: 1px solid #d9d9d9;
50
+ // border-left: none;
51
+ border-radius: 0 2px 2px 0;
52
+ background: var(--background-light);
53
+ cursor: pointer;
54
+ transition: all 0.2s ease;
55
+ margin-left: 0;
56
+
57
+ &:hover:not(:disabled) {
58
+ border-color: #40a9ff;
59
+ color: #40a9ff;
60
+ }
61
+
62
+ &:disabled {
63
+ color: var(--font-secondary);
64
+ border-color: #d9d9d9;
65
+ background: #f5f5f5;
66
+ cursor: not-allowed;
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ .custom-style-config-modal-content {
73
+ min-height: 400px;
74
+ max-height: 600px;
75
+ overflow-y: auto;
76
+ }
@@ -0,0 +1,148 @@
1
+ // @ts-ignore
2
+ import { FormItem, RendererProps } from 'amis';
3
+ import React from 'react';
4
+ import { Input, Button, Modal, message } from 'antd';
5
+ import { SettingOutlined } from '@ant-design/icons';
6
+ // @ts-ignore
7
+ import JSONEditor from '@wibetter/json-editor';
8
+ import '@wibetter/json-editor/lib/index.css';
9
+
10
+ import { configSchema } from './configSchema';
11
+ import './index.scss';
12
+
13
+ interface ICustomStyleConfigProps {
14
+ onChange?: Function;
15
+ value?: any;
16
+ name: string;
17
+ }
18
+
19
+ interface ICustomStyleConfigState {
20
+ isModalVisible: boolean;
21
+ editorValue: any;
22
+ }
23
+
24
+ @FormItem({
25
+ type: 'customStyleConfig',
26
+ detectProps: ['data'],
27
+ })
28
+ export default class CustomStyleConfigRenderer extends React.Component<
29
+ RendererProps & ICustomStyleConfigProps,
30
+ ICustomStyleConfigState
31
+ > {
32
+ constructor(props: RendererProps & ICustomStyleConfigProps) {
33
+ super(props);
34
+
35
+ this.state = {
36
+ isModalVisible: false,
37
+ editorValue: props.value || {},
38
+ };
39
+
40
+ this.showModal = this.showModal.bind(this);
41
+ this.handleModalOk = this.handleModalOk.bind(this);
42
+ this.handleModalCancel = this.handleModalCancel.bind(this);
43
+ this.handleEditorChange = this.handleEditorChange.bind(this);
44
+ }
45
+
46
+ handleEditorChange = (value: any) => {
47
+ this.setState({ editorValue: value });
48
+ };
49
+
50
+ handleModalOk = () => {
51
+ const { editorValue } = this.state;
52
+ const { onChange } = this.props;
53
+
54
+ try {
55
+ onChange && onChange(editorValue);
56
+ this.setState({ isModalVisible: false });
57
+ } catch (error) {
58
+ message.error('保存配置失败');
59
+ }
60
+ };
61
+
62
+ handleModalCancel = () => {
63
+ // 取消时恢复原始值
64
+ const value = this.props.value || {};
65
+ this.setState({
66
+ isModalVisible: false,
67
+ editorValue: value,
68
+ });
69
+ };
70
+
71
+ showModal = () => {
72
+ const value = this.props.value || {};
73
+ this.setState({
74
+ isModalVisible: true,
75
+ editorValue: value,
76
+ });
77
+ };
78
+
79
+ getDisplayValue = () => {
80
+ const { value } = this.props;
81
+
82
+ if (!value || Object.keys(value).length === 0) {
83
+ return '';
84
+ }
85
+
86
+ return '已配置自定义样式';
87
+ };
88
+
89
+ render() {
90
+ const { disabled, viewStyle, wideScreen, value } = this.props;
91
+ const { isModalVisible } = this.state;
92
+ console.log('customStyleConfig:', this.props);
93
+ console.log('test1:', this.props.data['__NeoPageInfo']);
94
+
95
+ return (
96
+ <div className="properties-panel-custom-style-config">
97
+ <div className="custom-style-config-input-container">
98
+ <Input
99
+ value={this.getDisplayValue()}
100
+ placeholder="请配置自定义样式"
101
+ disabled={disabled}
102
+ readOnly
103
+ className="custom-style-config-input"
104
+ />
105
+ <Button
106
+ type="text"
107
+ icon={<SettingOutlined />}
108
+ onClick={this.showModal}
109
+ disabled={disabled}
110
+ className="custom-style-config-setting-btn"
111
+ />
112
+ </div>
113
+ <Modal
114
+ title="自定义样式配置"
115
+ visible={isModalVisible}
116
+ onOk={this.handleModalOk}
117
+ onCancel={this.handleModalCancel}
118
+ width={800}
119
+ centered={true}
120
+ okText="确定"
121
+ cancelText="取消"
122
+ destroyOnClose={false}
123
+ >
124
+ <div className="custom-style-config-modal-content">
125
+ {isModalVisible && (
126
+ <JSONEditor
127
+ schemaData={configSchema}
128
+ jsonData={value}
129
+ onChange={this.handleEditorChange}
130
+ viewStyle={viewStyle || 'tabs'}
131
+ tabPosition="left"
132
+ wideScreen={wideScreen ?? false}
133
+ />
134
+ )}
135
+ </div>
136
+ </Modal>
137
+ </div>
138
+ );
139
+ }
140
+ }
141
+
142
+ /*
143
+ // 通过 普通方式进行
144
+ FormItem({
145
+ type: 'customStyleConfig',
146
+ detectProps: ['data'],
147
+ })(CustomStyleConfigRenderer);
148
+ */
@@ -0,0 +1,440 @@
1
+ /**
2
+ * @file 数值指标组件
3
+ * @description 基于 Neo 平台的 XObject 实体对象数值指标组件,用于展示多个关键数值指标
4
+ */
5
+ import * as React from 'react';
6
+ // @ts-ignore
7
+ import { xObject } from 'neo-open-api'; // Neo Open API
8
+
9
+ // 引入 neo-ui-common / NeoEvent
10
+ // @ts-ignore
11
+ import { BaseCmp, StatusHoc, NeoEvent } from 'neo-ui-common';
12
+
13
+ import './style.scss';
14
+
15
+ interface EntityApiKey {
16
+ xObjectApiKey: string;
17
+ objectId: string;
18
+ fieldDescList?: any[];
19
+ fields?: string[];
20
+ autoFetchData?: boolean;
21
+ }
22
+
23
+ /**
24
+ * 字段描述信息接口
25
+ */
26
+ interface FieldDesc {
27
+ value: string;
28
+ label: string;
29
+ type?: string;
30
+ }
31
+
32
+ /**
33
+ * 自定义样式配置接口
34
+ */
35
+ interface TargetNumberStyle {
36
+ baseStyle?: {
37
+ backgroundColor?: string;
38
+ paddingMargin?: {
39
+ margin?: string;
40
+ padding?: string;
41
+ quantity?: string;
42
+ };
43
+ };
44
+ layoutStyle?: {
45
+ alignClass?: string;
46
+ legendOrient?: string;
47
+ };
48
+ titleStyle?: {
49
+ show?: boolean;
50
+ text?: string;
51
+ fontSize?: number;
52
+ fontWeight?: number;
53
+ color?: string;
54
+ backgroundColor?: string;
55
+ };
56
+ numberStyle?: {
57
+ fontSize?: number;
58
+ fontWeight?: number;
59
+ color?: string;
60
+ linkHref?: string;
61
+ };
62
+ numberTitleStyle?: {
63
+ fontSize?: number;
64
+ fontWeight?: number;
65
+ color?: string;
66
+ linkHref?: string;
67
+ };
68
+ }
69
+
70
+ /**
71
+ * 组件属性接口
72
+ */
73
+ interface TargetNumberProps {
74
+ /** 实体对象的 API Key */
75
+ entityApiKey?: EntityApiKey;
76
+ /** 绑定的字段描述信息数组 */
77
+ selectFieldDesc?: FieldDesc[];
78
+ /** 自定义样式配置 */
79
+ targetNumberStyle?: TargetNumberStyle;
80
+ /** 数值格式化函数 */
81
+ formatter?: (value: any, fieldDesc?: FieldDesc) => string;
82
+ /** Neo 平台传递的数据 */
83
+ data?: any;
84
+ /** 组件类名 */
85
+ className?: string;
86
+ }
87
+
88
+ /**
89
+ * 字段值接口
90
+ */
91
+ interface FieldValue {
92
+ fieldDesc: FieldDesc;
93
+ value: any;
94
+ }
95
+
96
+ /**
97
+ * 组件状态接口
98
+ */
99
+ interface TargetNumberState {
100
+ recordData: any; // 数据列表
101
+ /** 当前字段值数组 */
102
+ targetNumbers: FieldValue[];
103
+ /** 加载状态 */
104
+ loading: boolean;
105
+ /** 错误信息 */
106
+ error: string | null;
107
+ }
108
+
109
+ /**
110
+ * 数值指标组件
111
+ * 用于展示多个关键数值指标,支持从 XObject 实体对象获取动态数据
112
+ * 每个字段都会显示为一个独立的数值块,包含字段标签和数值
113
+ */
114
+ class TargetNumber extends BaseCmp<TargetNumberProps, TargetNumberState> {
115
+ constructor(props: TargetNumberProps) {
116
+ super(props);
117
+
118
+ // 初始化组件状态
119
+ this.state = {
120
+ recordData: {},
121
+ targetNumbers: [],
122
+ loading: false,
123
+ error: null,
124
+ };
125
+
126
+ // 绑定方法上下文
127
+ this.loadData = this.loadData.bind(this);
128
+ }
129
+
130
+ componentDidMount() {
131
+ const { entityApiKey, selectFieldDesc } = this.props;
132
+
133
+ if (entityApiKey && this.hasValidFieldDesc(selectFieldDesc)) {
134
+ // 加载数据
135
+ this.loadData();
136
+ }
137
+
138
+ // 监听一个广播事件
139
+ console.log('TargetNumber 注册了一个广播监听事件 SavePageEvent');
140
+ NeoEvent.listen('SavePageEvent', (eventData: any) => {
141
+ console.log(
142
+ 'TargetNumber 监听到了一个广播事件 SavePageEvent: ',
143
+ eventData,
144
+ );
145
+ });
146
+ }
147
+
148
+ /**
149
+ * 检查字段描述是否有效
150
+ */
151
+ hasValidFieldDesc(selectFieldDesc?: FieldDesc[]): boolean {
152
+ return Array.isArray(selectFieldDesc);
153
+ }
154
+
155
+ componentWillReceiveProps(nextProps: TargetNumberProps) {
156
+ const { selectFieldDesc } = nextProps;
157
+ if (selectFieldDesc && this.hasValidFieldDesc(selectFieldDesc)) {
158
+ this.getTargetNumbers(selectFieldDesc);
159
+ }
160
+ }
161
+
162
+ /**
163
+ * 组件更新后执行
164
+ * 当 entityApiKey 或 selectFieldDesc 发生变化时重新加载数据
165
+ */
166
+ async componentDidUpdate(prevProps: TargetNumberProps) {
167
+ const { entityApiKey, selectFieldDesc } = this.props;
168
+ const { entityApiKey: prevEntityApiKey } = prevProps;
169
+
170
+ if (
171
+ entityApiKey?.xObjectApiKey !== prevEntityApiKey?.xObjectApiKey ||
172
+ entityApiKey?.objectId !== prevEntityApiKey?.objectId
173
+ ) {
174
+ if (entityApiKey?.xObjectApiKey && entityApiKey?.objectId) {
175
+ this.loadData();
176
+ } else {
177
+ this.setState({
178
+ recordData: {},
179
+ targetNumbers: [],
180
+ error: null,
181
+ });
182
+ }
183
+ }
184
+ }
185
+
186
+ /**
187
+ * 提取数值指标数据
188
+ * 根据配置的 selectFieldDesc 数组,提取数据列表中的字段值
189
+ */
190
+ getTargetNumbers(selectFieldDesc: FieldDesc[]) {
191
+ const { recordData } = this.state;
192
+
193
+ let fieldValues: FieldValue[] = [];
194
+ if (selectFieldDesc && selectFieldDesc.length > 0) {
195
+ fieldValues = selectFieldDesc.map((fieldDesc: FieldDesc) => ({
196
+ fieldDesc,
197
+ value: recordData[fieldDesc.value],
198
+ }));
199
+ }
200
+
201
+ this.setState({
202
+ targetNumbers: fieldValues,
203
+ });
204
+ }
205
+
206
+ /**
207
+ * 加载数据
208
+ * 从 Neo 平台获取 XObject 实体数据,直接使用查询结果的第一条记录
209
+ */
210
+ @NeoEvent.function
211
+ async loadData() {
212
+ const { entityApiKey, selectFieldDesc } = this.props;
213
+ this.setState({ loading: true, error: null });
214
+
215
+ try {
216
+ // 查询数据
217
+ const result = await xObject.get(entityApiKey);
218
+
219
+ if (result && result.status) {
220
+ const recordData = result.data || {};
221
+ let fieldValues: FieldValue[] = [];
222
+
223
+ if (selectFieldDesc && selectFieldDesc.length > 0) {
224
+ fieldValues = selectFieldDesc.map((fieldDesc) => ({
225
+ fieldDesc,
226
+ value: recordData[fieldDesc.value],
227
+ }));
228
+ }
229
+
230
+ this.setState({
231
+ recordData: recordData,
232
+ targetNumbers: fieldValues,
233
+ loading: false,
234
+ });
235
+ } else {
236
+ this.setState({
237
+ recordData: {},
238
+ error: result?.msg || '获取数据失败',
239
+ loading: false,
240
+ });
241
+ }
242
+ } catch (error: any) {
243
+ this.setState({
244
+ recordData: {},
245
+ error: error.message || '获取数据失败',
246
+ loading: false,
247
+ });
248
+ }
249
+ }
250
+
251
+ /**
252
+ * 格式化数值
253
+ */
254
+ formatValue(value: any, fieldDesc?: FieldDesc): string {
255
+ const { formatter } = this.props;
256
+
257
+ if (value === null || value === undefined) {
258
+ return '-';
259
+ }
260
+
261
+ // 如果提供了自定义格式化函数,使用自定义格式化
262
+ if (formatter) {
263
+ return formatter(value, fieldDesc);
264
+ }
265
+
266
+ // 根据字段类型进行格式化
267
+ if (
268
+ fieldDesc &&
269
+ (fieldDesc.type === 'number' || fieldDesc.type === 'currency')
270
+ ) {
271
+ // 数字类型,添加千分位
272
+ const numValue = Number(value);
273
+ if (!isNaN(numValue)) {
274
+ return numValue.toLocaleString('zh-CN');
275
+ }
276
+ }
277
+
278
+ return String(value);
279
+ }
280
+
281
+ /**
282
+ * 渲染组件
283
+ */
284
+ render() {
285
+ const { targetNumbers, loading, error } = this.state;
286
+ const { className, targetNumberStyle = {} } = this.props;
287
+
288
+ console.log('TargetNumber:', this.props);
289
+
290
+ // 从 targetNumberStyle 中获取样式配置
291
+ const {
292
+ baseStyle = {},
293
+ layoutStyle = {},
294
+ titleStyle: titleStyleConfig = {},
295
+ numberStyle: numberStyleConfig = {},
296
+ numberTitleStyle: numberTitleStyleConfig = {},
297
+ } = targetNumberStyle;
298
+
299
+ // 获取布局方式,默认为 flex-col
300
+ // 兼容 configSchema 中的值映射:horizontal -> flex-col
301
+ let alignClass = layoutStyle?.alignClass || 'flex-col';
302
+ if (alignClass === 'horizontal') {
303
+ alignClass = 'flex-col';
304
+ }
305
+
306
+ // 获取基础样式
307
+ const baseStyleObj: React.CSSProperties = {
308
+ backgroundColor: baseStyle?.backgroundColor || '#ffffff',
309
+ };
310
+
311
+ // 处理内外边距
312
+ const paddingMargin = baseStyle?.paddingMargin || {};
313
+ const margin = paddingMargin.margin || '0';
314
+ const padding = paddingMargin.padding || '0';
315
+ const quantity = paddingMargin.quantity || 'px';
316
+
317
+ // 应用单位到 margin 和 padding
318
+ const formatSpacing = (value: string, unit: string) => {
319
+ if (!value || value === '0') return '0';
320
+ // 如果值已经包含单位,直接返回;否则添加单位
321
+ if (/\d+(px|rem|em|%)$/.test(value.trim())) {
322
+ return value;
323
+ }
324
+ return `${value}${unit}`;
325
+ };
326
+
327
+ baseStyleObj.margin = formatSpacing(margin, quantity);
328
+ // 如果 padding 为 0,使用默认的 16px(保持原有样式)
329
+ const finalPadding =
330
+ padding === '0' ? '16px' : formatSpacing(padding, quantity);
331
+ baseStyleObj.padding = finalPadding;
332
+
333
+ // 数值样式
334
+ const numberStyle: React.CSSProperties = {
335
+ fontSize: numberStyleConfig?.fontSize
336
+ ? `${numberStyleConfig.fontSize}px`
337
+ : '32px',
338
+ fontWeight: numberStyleConfig?.fontWeight || 600,
339
+ color: numberStyleConfig?.color || '#262626',
340
+ };
341
+
342
+ // 数值标题样式(字段标签)
343
+ const numberTitleStyle: React.CSSProperties = {
344
+ fontSize: numberTitleStyleConfig?.fontSize
345
+ ? `${numberTitleStyleConfig.fontSize}px`
346
+ : '14px',
347
+ fontWeight: numberTitleStyleConfig?.fontWeight || 400,
348
+ color: numberTitleStyleConfig?.color || '#8c8c8c',
349
+ };
350
+
351
+ // 组件标题样式(根据 titleStyle 配置,仅当 show 为 true 时展示)
352
+ const showTitle = titleStyleConfig?.show === true;
353
+ const titleStyle: React.CSSProperties = {
354
+ fontSize:
355
+ titleStyleConfig?.fontSize != null
356
+ ? `${titleStyleConfig.fontSize}px`
357
+ : '24px',
358
+ fontWeight: titleStyleConfig?.fontWeight ?? 400,
359
+ color: titleStyleConfig?.color || '#000000',
360
+ backgroundColor: titleStyleConfig?.backgroundColor || '#eaf3fc',
361
+ };
362
+
363
+ // 渲染单个数值块
364
+ const renderFieldValue = (fieldValue: FieldValue, index: number) => {
365
+ const fieldLabel = fieldValue.fieldDesc?.label || '数值指标';
366
+ const formattedValue = this.formatValue(
367
+ fieldValue.value,
368
+ fieldValue.fieldDesc,
369
+ );
370
+
371
+ // 处理数值链接
372
+ const numberLinkHref = numberStyleConfig?.linkHref;
373
+ const numberContent = numberLinkHref ? (
374
+ <a
375
+ href={numberLinkHref}
376
+ style={{ ...numberStyle, textDecoration: 'none' }}
377
+ >
378
+ {formattedValue}
379
+ </a>
380
+ ) : (
381
+ formattedValue
382
+ );
383
+
384
+ // 处理标题链接
385
+ const titleLinkHref = numberTitleStyleConfig?.linkHref;
386
+ const titleContent = titleLinkHref ? (
387
+ <a
388
+ href={titleLinkHref}
389
+ style={{ ...numberTitleStyle, textDecoration: 'none' }}
390
+ >
391
+ {fieldLabel}
392
+ </a>
393
+ ) : (
394
+ fieldLabel
395
+ );
396
+
397
+ return (
398
+ <div key={index} className={`target-number-item ${alignClass}`}>
399
+ <div className="target-number-value" style={numberStyle}>
400
+ {numberContent}
401
+ </div>
402
+ <div className="target-number-label" style={numberTitleStyle}>
403
+ {titleContent}
404
+ </div>
405
+ </div>
406
+ );
407
+ };
408
+
409
+ return (
410
+ <div
411
+ className={`targetNumber__c ${className || ''}`}
412
+ style={baseStyleObj}
413
+ >
414
+ {showTitle && (titleStyleConfig?.text ?? '') !== '' && (
415
+ <div className="target-number-title" style={titleStyle}>
416
+ {titleStyleConfig?.text}
417
+ </div>
418
+ )}
419
+ <div className="target-number-block multiple-fields">
420
+ {loading ? (
421
+ <div className="target-number-loading">加载中...</div>
422
+ ) : error ? (
423
+ <div className="target-number-error">{error}</div>
424
+ ) : targetNumbers.length > 0 ? (
425
+ <div className="target-number-list">
426
+ {targetNumbers.map((fieldValue, index) =>
427
+ renderFieldValue(fieldValue, index),
428
+ )}
429
+ </div>
430
+ ) : (
431
+ <div className="target-number-empty">暂无数据</div>
432
+ )}
433
+ </div>
434
+ </div>
435
+ );
436
+ }
437
+ }
438
+
439
+ // 使用 StatusHoc 包裹组件,用于支持组件显示隐藏控制
440
+ export default StatusHoc(TargetNumber);