@lovrabet/cli 1.1.12 → 1.1.13
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/lib/add-page/input-page-router.js +1 -1
- package/lib/add-page/main.js +1 -1
- package/lib/add-page/select-page-template.js +1 -1
- package/lib/api/api-doc-ui.js +1 -1
- package/lib/api/api-doc.js +1 -1
- package/lib/api/api-pull-ui.js +1 -1
- package/lib/api/fetch-datasets.js +1 -1
- package/lib/api/format-dataset.js +1 -1
- package/lib/api/generate-api-file.js +1 -1
- package/lib/api/main.js +1 -1
- package/lib/api/pull-silent.js +1 -1
- package/lib/api/pull.js +1 -1
- package/lib/auth/auth-server-ui.js +1 -1
- package/lib/auth/auth-server.js +1 -1
- package/lib/auth/constant.js +1 -1
- package/lib/auth/get-cookie.js +1 -1
- package/lib/auth/is-session-valid.js +1 -1
- package/lib/auth/logout.js +1 -1
- package/lib/cli.js +1 -1
- package/lib/cmd/build-watch.js +1 -1
- package/lib/cmd/build.js +1 -1
- package/lib/cmd/logs.js +1 -1
- package/lib/cmd/preview.js +1 -1
- package/lib/cmd/start.js +1 -1
- package/lib/config/config-help.js +1 -1
- package/lib/config/main.js +1 -1
- package/lib/constant/domain.js +1 -1
- package/lib/constant/env.js +1 -1
- package/lib/create-app/enhanced-guided-create.js +1 -1
- package/lib/create-app/format-elapsed.js +1 -1
- package/lib/create-app/main.js +1 -1
- package/lib/create-app/task-finished.js +1 -1
- package/lib/create-app/task-loading.js +1 -1
- package/lib/create-app/task-running.js +1 -1
- package/lib/create-app/task-time.js +1 -1
- package/lib/create-app/use-copy-project-template.js +1 -1
- package/lib/create-app/use-format-code.js +1 -1
- package/lib/create-app/use-install-dependencies.js +1 -1
- package/lib/help.js +1 -1
- package/lib/init/main.js +1 -1
- package/lib/utils/config.js +1 -1
- package/lib/utils/copy-directory.js +1 -1
- package/lib/utils/http-client.js +1 -1
- package/lib/utils/logger.js +1 -1
- package/lib/utils/router-updater.js +1 -1
- package/lib/utils/template-replacer.js +1 -1
- package/package.json +1 -1
- package/templates/projects/sub-app-react-demo/package-lock.json +18 -11
- package/templates/projects/sub-app-react-demo/package.json +1 -1
- package/templates/projects/sub-app-react-demo/src/layouts/MainLayout.tsx +1 -10
- package/templates/projects/sub-app-react-demo/src/pages/sdk-demo/index.tsx +166 -2
- package/templates/projects/sub-app-react-demo/src/components/ApiUrlDisplay.tsx +0 -111
- package/templates/projects/sub-app-react-demo/src/pages/chart-fetch/index.tsx +0 -137
- package/templates/projects/sub-app-react-demo/src/pages/table-display.tsx +0 -130
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@ant-design/icons": "^6.0.0",
|
|
12
12
|
"@ice/stark-app": "^1.5.0",
|
|
13
|
-
"@lovrabet/sdk": "^1.1.
|
|
13
|
+
"@lovrabet/sdk": "^1.1.17",
|
|
14
14
|
"antd": "^5.26.7",
|
|
15
15
|
"echarts": "^5.6.0",
|
|
16
16
|
"echarts-for-react": "^3.0.2",
|
|
@@ -33,16 +33,27 @@
|
|
|
33
33
|
},
|
|
34
34
|
"../../../gitlab/yuntoo/open/lovrabet-node-sdk": {
|
|
35
35
|
"name": "@lovrabet/sdk",
|
|
36
|
-
"version": "1.1.
|
|
37
|
-
"
|
|
36
|
+
"version": "1.1.17",
|
|
37
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/bun": "latest",
|
|
40
|
-
"
|
|
40
|
+
"@vitest/coverage-v8": "^2.1.9",
|
|
41
|
+
"@vitest/ui": "^2.1.8",
|
|
42
|
+
"c8": "^10.1.2",
|
|
43
|
+
"chokidar-cli": "^3.0.0",
|
|
44
|
+
"javascript-obfuscator": "^4.1.1",
|
|
45
|
+
"vitest": "^2.1.8"
|
|
41
46
|
},
|
|
42
47
|
"peerDependencies": {
|
|
43
48
|
"typescript": "^5"
|
|
44
49
|
}
|
|
45
50
|
},
|
|
51
|
+
"../lovrabet-node-sdk": {
|
|
52
|
+
"extraneous": true
|
|
53
|
+
},
|
|
54
|
+
"../lovrabet-node-sdk/dist/index.js": {
|
|
55
|
+
"extraneous": true
|
|
56
|
+
},
|
|
46
57
|
"node_modules/@ant-design/colors": {
|
|
47
58
|
"version": "8.0.0",
|
|
48
59
|
"license": "MIT",
|
|
@@ -450,13 +461,8 @@
|
|
|
450
461
|
}
|
|
451
462
|
},
|
|
452
463
|
"node_modules/@lovrabet/sdk": {
|
|
453
|
-
"
|
|
454
|
-
"
|
|
455
|
-
"integrity": "sha512-Pbw7bK54QrPidliob6RkYNcAAy1kBk7Fvpj2y+z3K6PON8ptdjAeQs6vjBtBYei/Uuv3ggjkNVZh6Ickqand9g==",
|
|
456
|
-
"license": "SEE LICENSE IN LICENSE",
|
|
457
|
-
"peerDependencies": {
|
|
458
|
-
"typescript": "^5"
|
|
459
|
-
}
|
|
464
|
+
"resolved": "../../../gitlab/yuntoo/open/lovrabet-node-sdk",
|
|
465
|
+
"link": true
|
|
460
466
|
},
|
|
461
467
|
"node_modules/@rc-component/async-validator": {
|
|
462
468
|
"version": "5.0.4",
|
|
@@ -2563,6 +2569,7 @@
|
|
|
2563
2569
|
},
|
|
2564
2570
|
"node_modules/typescript": {
|
|
2565
2571
|
"version": "5.8.3",
|
|
2572
|
+
"dev": true,
|
|
2566
2573
|
"license": "Apache-2.0",
|
|
2567
2574
|
"bin": {
|
|
2568
2575
|
"tsc": "bin/tsc",
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
TableOutlined,
|
|
9
9
|
FolderOutlined,
|
|
10
10
|
RocketOutlined,
|
|
11
|
+
DownOutlined,
|
|
11
12
|
} from "@ant-design/icons";
|
|
12
13
|
|
|
13
14
|
const { Header, Sider, Content } = Layout;
|
|
@@ -30,16 +31,6 @@ const MainLayout: React.FC = () => {
|
|
|
30
31
|
icon: <SmileOutlined />,
|
|
31
32
|
label: "Dashboard静态模板",
|
|
32
33
|
},
|
|
33
|
-
{
|
|
34
|
-
key: "/chart-fetch",
|
|
35
|
-
icon: <PieChartOutlined />,
|
|
36
|
-
label: "数据图表",
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
key: "/table-display",
|
|
40
|
-
icon: <TableOutlined />,
|
|
41
|
-
label: "数据表格",
|
|
42
|
-
},
|
|
43
34
|
{
|
|
44
35
|
key: "/sdk-demo",
|
|
45
36
|
icon: <FolderOutlined />,
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
Table,
|
|
5
|
+
Card,
|
|
6
|
+
Typography,
|
|
7
|
+
Space,
|
|
8
|
+
message,
|
|
9
|
+
Select,
|
|
10
|
+
Input,
|
|
11
|
+
Tag,
|
|
12
|
+
} from "antd";
|
|
3
13
|
import { ApiOutlined } from "@ant-design/icons";
|
|
4
14
|
import { lovrabetClient } from "../../api/client";
|
|
5
15
|
import { SortOrder } from "@lovrabet/sdk";
|
|
@@ -12,6 +22,9 @@ export default function SdkDemo() {
|
|
|
12
22
|
const [columns, setColumns] = useState<any[]>([]);
|
|
13
23
|
const [modelList, setModelList] = useState<string[]>([]);
|
|
14
24
|
const [selectedModel, setSelectedModel] = useState<string>("");
|
|
25
|
+
const [selectOptions, setSelectOptions] = useState<any[]>([]);
|
|
26
|
+
const [codeField, setCodeField] = useState<string>("id");
|
|
27
|
+
const [labelField, setLabelField] = useState<string>("");
|
|
15
28
|
|
|
16
29
|
/**
|
|
17
30
|
* 加载可用的数据模型列表
|
|
@@ -105,6 +118,42 @@ export default function SdkDemo() {
|
|
|
105
118
|
message.success(successMessage);
|
|
106
119
|
};
|
|
107
120
|
|
|
121
|
+
/**
|
|
122
|
+
* 获取下拉选项
|
|
123
|
+
*/
|
|
124
|
+
const loadSelectOptions = async () => {
|
|
125
|
+
if (!selectedModel) {
|
|
126
|
+
message.warning("请先选择一个数据模型");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!codeField || !labelField) {
|
|
131
|
+
message.warning("请输入 code 和 label 字段名");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
setLoading(true);
|
|
136
|
+
setSelectOptions([]);
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const options = await lovrabetClient.models[
|
|
140
|
+
selectedModel
|
|
141
|
+
].getSelectOptions({
|
|
142
|
+
code: codeField,
|
|
143
|
+
label: labelField,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
setSelectOptions(options);
|
|
147
|
+
message.success(`成功获取 ${options.length} 个下拉选项`);
|
|
148
|
+
console.log("下拉选项数据:", options);
|
|
149
|
+
} catch (error: any) {
|
|
150
|
+
console.error("获取下拉选项失败:", error);
|
|
151
|
+
message.error(`获取下拉选项失败: ${error.message}`);
|
|
152
|
+
} finally {
|
|
153
|
+
setLoading(false);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
108
157
|
/**
|
|
109
158
|
* 处理错误
|
|
110
159
|
*/
|
|
@@ -225,7 +274,7 @@ const response = await model.getList({
|
|
|
225
274
|
|
|
226
275
|
{/* 数据表格 */}
|
|
227
276
|
{data.length > 0 && (
|
|
228
|
-
<Card title="数据结果" size="small">
|
|
277
|
+
<Card title="数据结果" size="small" style={{ marginBottom: 16 }}>
|
|
229
278
|
<Table
|
|
230
279
|
columns={columns}
|
|
231
280
|
dataSource={data}
|
|
@@ -236,6 +285,121 @@ const response = await model.getList({
|
|
|
236
285
|
/>
|
|
237
286
|
</Card>
|
|
238
287
|
)}
|
|
288
|
+
|
|
289
|
+
{/* 获取下拉选项 */}
|
|
290
|
+
<Card title="📋 获取下拉选项" size="small" style={{ marginBottom: 16 }}>
|
|
291
|
+
<Space direction="vertical" style={{ width: "100%" }}>
|
|
292
|
+
<div style={{ color: "#666" }}>
|
|
293
|
+
用于获取数据表的下拉选项数据,适用于 Select、Radio、Checkbox
|
|
294
|
+
等表单组件(仅 WebAPI 模式支持)
|
|
295
|
+
</div>
|
|
296
|
+
|
|
297
|
+
{/* 显示可用字段 */}
|
|
298
|
+
{columns.length > 0 && (
|
|
299
|
+
<div style={{ marginBottom: 8 }}>
|
|
300
|
+
<span style={{ color: "#666", marginRight: 8 }}>
|
|
301
|
+
可用字段(点击快速填入):
|
|
302
|
+
</span>
|
|
303
|
+
<Space wrap size={[4, 4]}>
|
|
304
|
+
{columns.map((column: any) => (
|
|
305
|
+
<Tag
|
|
306
|
+
key={column.dataIndex}
|
|
307
|
+
color="blue"
|
|
308
|
+
style={{ cursor: "pointer" }}
|
|
309
|
+
onClick={() => {
|
|
310
|
+
if (!codeField) {
|
|
311
|
+
setCodeField(column.dataIndex);
|
|
312
|
+
message.success(
|
|
313
|
+
`已填入 Code 字段: ${column.dataIndex}`,
|
|
314
|
+
);
|
|
315
|
+
} else if (!labelField) {
|
|
316
|
+
setLabelField(column.dataIndex);
|
|
317
|
+
message.success(
|
|
318
|
+
`已填入 Label 字段: ${column.dataIndex}`,
|
|
319
|
+
);
|
|
320
|
+
} else {
|
|
321
|
+
message.info("Code 和 Label 已填写,如需更换请先清空");
|
|
322
|
+
}
|
|
323
|
+
}}
|
|
324
|
+
>
|
|
325
|
+
{column.dataIndex}
|
|
326
|
+
</Tag>
|
|
327
|
+
))}
|
|
328
|
+
</Space>
|
|
329
|
+
</div>
|
|
330
|
+
)}
|
|
331
|
+
|
|
332
|
+
<Space wrap>
|
|
333
|
+
<span>Code 字段:</span>
|
|
334
|
+
<Input
|
|
335
|
+
placeholder="用作选项值的字段名"
|
|
336
|
+
value={codeField}
|
|
337
|
+
onChange={(e) => setCodeField(e.target.value)}
|
|
338
|
+
style={{ width: 200 }}
|
|
339
|
+
allowClear
|
|
340
|
+
/>
|
|
341
|
+
<span>Label 字段:</span>
|
|
342
|
+
<Input
|
|
343
|
+
placeholder="用作显示文本的字段名"
|
|
344
|
+
value={labelField}
|
|
345
|
+
onChange={(e) => setLabelField(e.target.value)}
|
|
346
|
+
style={{ width: 200 }}
|
|
347
|
+
allowClear
|
|
348
|
+
/>
|
|
349
|
+
<Button
|
|
350
|
+
type="primary"
|
|
351
|
+
loading={loading}
|
|
352
|
+
onClick={loadSelectOptions}
|
|
353
|
+
icon={<ApiOutlined />}
|
|
354
|
+
disabled={!selectedModel || !codeField || !labelField}
|
|
355
|
+
>
|
|
356
|
+
获取选项
|
|
357
|
+
</Button>
|
|
358
|
+
</Space>
|
|
359
|
+
|
|
360
|
+
<pre
|
|
361
|
+
style={{
|
|
362
|
+
background: "#f5f5f5",
|
|
363
|
+
padding: "12px",
|
|
364
|
+
borderRadius: "4px",
|
|
365
|
+
margin: "8px 0 0 0",
|
|
366
|
+
fontSize: "13px",
|
|
367
|
+
border: "1px solid #d9d9d9",
|
|
368
|
+
}}
|
|
369
|
+
>
|
|
370
|
+
{`// 调用示例
|
|
371
|
+
const options = await lovrabetClient
|
|
372
|
+
.models.${selectedModel || "Requirements"}.getSelectOptions({
|
|
373
|
+
code: "${codeField || "id"}",
|
|
374
|
+
label: "${labelField || "name"}"
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// 返回格式:[{ label: "显示文本", value: "选项值" }]`}
|
|
378
|
+
</pre>
|
|
379
|
+
</Space>
|
|
380
|
+
</Card>
|
|
381
|
+
|
|
382
|
+
{/* 下拉选项结果 */}
|
|
383
|
+
{selectOptions.length > 0 && (
|
|
384
|
+
<Card
|
|
385
|
+
title={`下拉选项结果(共 ${selectOptions.length} 个)`}
|
|
386
|
+
size="small"
|
|
387
|
+
>
|
|
388
|
+
<pre
|
|
389
|
+
style={{
|
|
390
|
+
background: "#f5f5f5",
|
|
391
|
+
padding: "12px",
|
|
392
|
+
borderRadius: "4px",
|
|
393
|
+
margin: 0,
|
|
394
|
+
fontSize: "13px",
|
|
395
|
+
maxHeight: "400px",
|
|
396
|
+
overflow: "auto",
|
|
397
|
+
}}
|
|
398
|
+
>
|
|
399
|
+
{JSON.stringify(selectOptions, null, 2)}
|
|
400
|
+
</pre>
|
|
401
|
+
</Card>
|
|
402
|
+
)}
|
|
239
403
|
</div>
|
|
240
404
|
);
|
|
241
405
|
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Typography, Tag, Divider } from "antd";
|
|
3
|
-
|
|
4
|
-
const { Title } = Typography;
|
|
5
|
-
|
|
6
|
-
interface ApiUrlDisplayProps {
|
|
7
|
-
apiUrl: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const ApiUrlDisplay: React.FC<ApiUrlDisplayProps> = ({ apiUrl }) => {
|
|
11
|
-
let domain = "https://runtime.lovrabet.com";
|
|
12
|
-
let pathParts: string[];
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
// 尝试解析为完整URL
|
|
16
|
-
const fullUrl = new URL(apiUrl);
|
|
17
|
-
domain = `${fullUrl.protocol}//${fullUrl.host}`;
|
|
18
|
-
pathParts = fullUrl.pathname.split("/");
|
|
19
|
-
} catch {
|
|
20
|
-
// 如果不是完整URL,按相对路径处理
|
|
21
|
-
pathParts = apiUrl.split("/");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const apiPrefix = pathParts[1]; // api (固定开头)
|
|
25
|
-
const appCode = pathParts[2]; // app-{appCode}
|
|
26
|
-
const datasetCode = pathParts[3]; // 数据集code
|
|
27
|
-
const apiName = pathParts[4]; // 接口名称
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<div
|
|
31
|
-
style={{
|
|
32
|
-
marginTop: "24px",
|
|
33
|
-
padding: "20px",
|
|
34
|
-
background: "#f5f5f5",
|
|
35
|
-
borderRadius: "8px",
|
|
36
|
-
}}
|
|
37
|
-
>
|
|
38
|
-
<Title level={4} style={{ marginBottom: "18px" }}>
|
|
39
|
-
API 接口解析
|
|
40
|
-
</Title>
|
|
41
|
-
<div style={{ marginBottom: "18px", fontSize: "16px" }}>
|
|
42
|
-
<Tag color="geekblue" style={{ fontSize: "14px", padding: "4px 8px" }}>
|
|
43
|
-
{domain}
|
|
44
|
-
</Tag>
|
|
45
|
-
<span style={{ margin: "0 4px" }}>/</span>
|
|
46
|
-
<Tag color="blue" style={{ fontSize: "14px", padding: "4px 8px" }}>
|
|
47
|
-
{apiPrefix}
|
|
48
|
-
</Tag>
|
|
49
|
-
<span style={{ margin: "0 4px" }}>/</span>
|
|
50
|
-
<Tag color="purple" style={{ fontSize: "14px", padding: "4px 8px" }}>
|
|
51
|
-
{appCode}
|
|
52
|
-
</Tag>
|
|
53
|
-
<span style={{ margin: "0 4px" }}>/</span>
|
|
54
|
-
<Tag color="red" style={{ fontSize: "14px", padding: "4px 8px" }}>
|
|
55
|
-
{datasetCode}
|
|
56
|
-
</Tag>
|
|
57
|
-
<span style={{ margin: "0 4px" }}>/</span>
|
|
58
|
-
<Tag color="cyan" style={{ fontSize: "14px", padding: "4px 8px" }}>
|
|
59
|
-
{apiName}
|
|
60
|
-
</Tag>
|
|
61
|
-
</div>
|
|
62
|
-
<Divider style={{ margin: "18px 0" }} />
|
|
63
|
-
<div style={{ fontSize: "14px", color: "#666", lineHeight: "1.8" }}>
|
|
64
|
-
<div style={{ marginBottom: "8px" }}>
|
|
65
|
-
<Tag color="geekblue">域名地址</Tag> {domain} - API服务域名
|
|
66
|
-
</div>
|
|
67
|
-
<div style={{ marginBottom: "8px" }}>
|
|
68
|
-
<Tag color="blue">固定前缀</Tag> {apiPrefix} - API固定开头
|
|
69
|
-
</div>
|
|
70
|
-
<div style={{ marginBottom: "8px" }}>
|
|
71
|
-
<Tag color="purple">应用代码</Tag> {appCode} - 应用唯一标识
|
|
72
|
-
</div>
|
|
73
|
-
<div style={{ marginBottom: "8px" }}>
|
|
74
|
-
<Tag color="red">数据集代码</Tag> {datasetCode} - 数据集唯一标识
|
|
75
|
-
</div>
|
|
76
|
-
<div style={{ marginBottom: "8px" }}>
|
|
77
|
-
<Tag color="cyan">接口名称</Tag> {apiName} - 具体的API接口名
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
<Divider style={{ margin: "20px 0" }} />
|
|
82
|
-
<div
|
|
83
|
-
style={{
|
|
84
|
-
fontSize: "14px",
|
|
85
|
-
color: "#d46b08",
|
|
86
|
-
background: "#fff7e6",
|
|
87
|
-
padding: "12px",
|
|
88
|
-
borderRadius: "6px",
|
|
89
|
-
border: "1px solid #ffd591",
|
|
90
|
-
}}
|
|
91
|
-
>
|
|
92
|
-
<Title level={5} style={{ color: "#d46b08", marginBottom: "12px" }}>
|
|
93
|
-
⚠️ 常见问题说明
|
|
94
|
-
</Title>
|
|
95
|
-
<div style={{ lineHeight: "1.8" }}>
|
|
96
|
-
<div style={{ marginBottom: "8px" }}>
|
|
97
|
-
<Tag color="warning">无权限</Tag>
|
|
98
|
-
如果接口返回无权限,请先确认是否是自己有权限的应用,在{" "}
|
|
99
|
-
<code>app.lovrabet.com</code> 是否已经登录
|
|
100
|
-
</div>
|
|
101
|
-
<div style={{ marginBottom: "8px" }}>
|
|
102
|
-
<Tag color="error">跨域问题</Tag>
|
|
103
|
-
出现跨域错误,确保项目运行在 <code>dev.lovrabet.com</code> 域名下
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
);
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
export default ApiUrlDisplay;
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Card, Spin, Alert, Typography } from "antd";
|
|
3
|
-
import ReactECharts from "echarts-for-react";
|
|
4
|
-
import ApiUrlDisplay from "../../components/ApiUrlDisplay";
|
|
5
|
-
import { apiRequest } from "../../utils/api";
|
|
6
|
-
|
|
7
|
-
const { Title, Paragraph } = Typography;
|
|
8
|
-
|
|
9
|
-
const API_URL = "/api/app-c4055413/76a873945291498498737bc85677983d/getList";
|
|
10
|
-
|
|
11
|
-
function ChartFetch() {
|
|
12
|
-
const [loading, setLoading] = useState(true);
|
|
13
|
-
const [error, setError] = useState(null);
|
|
14
|
-
const [chartData, setChartData] = useState([]);
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
// 获取客户数据
|
|
18
|
-
const fetchData = async () => {
|
|
19
|
-
try {
|
|
20
|
-
setLoading(true);
|
|
21
|
-
// 数据统计接口
|
|
22
|
-
const data = await apiRequest(API_URL, {
|
|
23
|
-
method: "POST",
|
|
24
|
-
body: JSON.stringify({ pageSize: 20, currentPage: 1 }),
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
if (data.success) {
|
|
28
|
-
// 字段类型名称映射
|
|
29
|
-
const typeNameMap: Record<string, string> = {
|
|
30
|
-
TEXT: "文本字段",
|
|
31
|
-
RADIO: "单选字段",
|
|
32
|
-
SELECT: "下拉选择",
|
|
33
|
-
NUMBER: "数字字段",
|
|
34
|
-
DATE: "日期字段",
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// 根据新的API格式,统计tableColumns中的字段类型分布
|
|
38
|
-
const typeMap: Record<string, number> = {};
|
|
39
|
-
data.data.tableColumns.forEach((column: any) => {
|
|
40
|
-
const dataIndex = column.dataIndex || "未知类型";
|
|
41
|
-
// 按字段类型前缀分类(如 TEXT_, RADIO_, SELECT_ 等)
|
|
42
|
-
const typePrefix = dataIndex.split("_")[0] || "其他";
|
|
43
|
-
typeMap[typePrefix] = (typeMap[typePrefix] || 0) + 1;
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// 转换为饼图数据格式
|
|
47
|
-
const pieData = Object.keys(typeMap).map((key) => ({
|
|
48
|
-
name: typeNameMap[key] || key,
|
|
49
|
-
value: typeMap[key],
|
|
50
|
-
}));
|
|
51
|
-
|
|
52
|
-
setChartData(pieData);
|
|
53
|
-
}
|
|
54
|
-
} catch (err) {
|
|
55
|
-
console.error("数据获取失败:", err);
|
|
56
|
-
setError(err.message || "数据字段信息加载失败");
|
|
57
|
-
} finally {
|
|
58
|
-
setLoading(false);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
fetchData();
|
|
63
|
-
}, []);
|
|
64
|
-
|
|
65
|
-
// 饼图配置
|
|
66
|
-
const getOption = () => ({
|
|
67
|
-
title: {
|
|
68
|
-
text: "数据字段类型分布",
|
|
69
|
-
left: "center",
|
|
70
|
-
},
|
|
71
|
-
tooltip: {
|
|
72
|
-
trigger: "item",
|
|
73
|
-
formatter: "{a} <br/>{b}: {c} ({d}%)",
|
|
74
|
-
},
|
|
75
|
-
legend: {
|
|
76
|
-
orient: "vertical",
|
|
77
|
-
left: "left",
|
|
78
|
-
},
|
|
79
|
-
series: [
|
|
80
|
-
{
|
|
81
|
-
name: "字段类型",
|
|
82
|
-
type: "pie",
|
|
83
|
-
radius: "50%",
|
|
84
|
-
data: chartData,
|
|
85
|
-
emphasis: {
|
|
86
|
-
itemStyle: {
|
|
87
|
-
shadowBlur: 10,
|
|
88
|
-
shadowOffsetX: 0,
|
|
89
|
-
shadowColor: "rgba(0, 0, 0, 0.5)",
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
],
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<div style={{ padding: "24px" }}>
|
|
98
|
-
<Card>
|
|
99
|
-
<Title level={2}>数据字段统计图表</Title>
|
|
100
|
-
<Paragraph>
|
|
101
|
-
这是一个从真实API获取数据并展示的饼图示例,展示了数据表字段类型的分布情况。
|
|
102
|
-
</Paragraph>
|
|
103
|
-
<Paragraph>数据来源:{API_URL}</Paragraph>
|
|
104
|
-
<Paragraph>
|
|
105
|
-
访问请先确保:1. app.lovrabet.com已登录 2.应用有权限 3. 接口有权限
|
|
106
|
-
</Paragraph>
|
|
107
|
-
</Card>
|
|
108
|
-
|
|
109
|
-
<Card style={{ marginTop: "24px" }}>
|
|
110
|
-
{loading ? (
|
|
111
|
-
<div style={{ textAlign: "center", padding: "50px" }}>
|
|
112
|
-
<Spin size="large" tip="正在加载字段统计数据..." />
|
|
113
|
-
</div>
|
|
114
|
-
) : error ? (
|
|
115
|
-
<Alert
|
|
116
|
-
message="字段统计数据加载失败"
|
|
117
|
-
description={error}
|
|
118
|
-
type="error"
|
|
119
|
-
showIcon
|
|
120
|
-
/>
|
|
121
|
-
) : (
|
|
122
|
-
<>
|
|
123
|
-
<ReactECharts
|
|
124
|
-
option={getOption()}
|
|
125
|
-
style={{ height: "400px" }}
|
|
126
|
-
notMerge={true}
|
|
127
|
-
lazyUpdate={true}
|
|
128
|
-
/>
|
|
129
|
-
<ApiUrlDisplay apiUrl={API_URL} />
|
|
130
|
-
</>
|
|
131
|
-
)}
|
|
132
|
-
</Card>
|
|
133
|
-
</div>
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export default ChartFetch;
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Card, Spin, Alert, Typography, Table } from "antd";
|
|
3
|
-
import ApiUrlDisplay from "../components/ApiUrlDisplay";
|
|
4
|
-
import { apiRequest } from "../utils/api";
|
|
5
|
-
|
|
6
|
-
const { Title, Paragraph } = Typography;
|
|
7
|
-
|
|
8
|
-
const API_URL = "/api/app-c4055413/76a873945291498498737bc85677983d/getList";
|
|
9
|
-
|
|
10
|
-
function TableDisplay() {
|
|
11
|
-
const [loading, setLoading] = useState(true);
|
|
12
|
-
const [error, setError] = useState<string | null>(null);
|
|
13
|
-
const [tableData, setTableData] = useState([]);
|
|
14
|
-
const [columns, setColumns] = useState([]);
|
|
15
|
-
const [pagination, setPagination] = useState({
|
|
16
|
-
current: 1,
|
|
17
|
-
pageSize: 20,
|
|
18
|
-
total: 0,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const fetchData = async (page: number = 1, size: number = 20) => {
|
|
22
|
-
try {
|
|
23
|
-
setLoading(true);
|
|
24
|
-
// 数据表格接口
|
|
25
|
-
const data = await apiRequest(API_URL, {
|
|
26
|
-
method: "POST",
|
|
27
|
-
body: JSON.stringify({ pageSize: size, currentPage: page }),
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
if (data.success) {
|
|
31
|
-
// 设置表格数据
|
|
32
|
-
setTableData(data.data.tableData || []);
|
|
33
|
-
|
|
34
|
-
// 设置分页信息
|
|
35
|
-
setPagination({
|
|
36
|
-
current: data.data.paging?.currentPage || page,
|
|
37
|
-
pageSize: data.data.paging?.pageSize || size,
|
|
38
|
-
total: data.data.paging?.totalCount || 0,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// 根据 tableColumns 动态生成表格列配置
|
|
42
|
-
if (data.data.tableColumns && data.data.tableColumns.length > 0) {
|
|
43
|
-
const tableColumns = data.data.tableColumns.map((column: any) => ({
|
|
44
|
-
title: column.title || column.dataIndex,
|
|
45
|
-
dataIndex: column.dataIndex,
|
|
46
|
-
key: column.dataIndex,
|
|
47
|
-
width: 150,
|
|
48
|
-
ellipsis: true,
|
|
49
|
-
render: (text: any) => {
|
|
50
|
-
// 处理复杂数据类型的显示
|
|
51
|
-
if (Array.isArray(text) && text.length > 0 && text[0]?.label) {
|
|
52
|
-
return text.map((item: any) => item.label).join(", ");
|
|
53
|
-
}
|
|
54
|
-
if (typeof text === "object" && text !== null) {
|
|
55
|
-
return JSON.stringify(text);
|
|
56
|
-
}
|
|
57
|
-
return text;
|
|
58
|
-
},
|
|
59
|
-
}));
|
|
60
|
-
setColumns(tableColumns);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
} catch (err: any) {
|
|
64
|
-
console.error("数据获取失败:", err);
|
|
65
|
-
setError(err.message || "表格数据加载失败");
|
|
66
|
-
} finally {
|
|
67
|
-
setLoading(false);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
fetchData();
|
|
73
|
-
}, []);
|
|
74
|
-
|
|
75
|
-
const handleTableChange = (paginationConfig: any) => {
|
|
76
|
-
fetchData(paginationConfig.current, paginationConfig.pageSize);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<div style={{ padding: "24px" }}>
|
|
81
|
-
<Card>
|
|
82
|
-
<Title level={2}>数据表格展示</Title>
|
|
83
|
-
<Paragraph>
|
|
84
|
-
这是一个从真实API获取数据并展示的表格示例,展示了接口返回的tableData中的所有数据。
|
|
85
|
-
</Paragraph>
|
|
86
|
-
<Paragraph>数据来源:{API_URL}</Paragraph>
|
|
87
|
-
<Paragraph>
|
|
88
|
-
访问请先确保:1. app.lovrabet.com已登录 2.应用有权限 3. 接口有权限
|
|
89
|
-
</Paragraph>
|
|
90
|
-
</Card>
|
|
91
|
-
|
|
92
|
-
<Card style={{ marginTop: "24px" }}>
|
|
93
|
-
{loading ? (
|
|
94
|
-
<div style={{ textAlign: "center", padding: "50px" }}>
|
|
95
|
-
<Spin size="large" tip="正在加载表格数据..." />
|
|
96
|
-
</div>
|
|
97
|
-
) : error ? (
|
|
98
|
-
<Alert
|
|
99
|
-
message="表格数据加载失败,请检查接口权限,可以替换为自己有权限的接口"
|
|
100
|
-
description={error}
|
|
101
|
-
type="error"
|
|
102
|
-
showIcon
|
|
103
|
-
/>
|
|
104
|
-
) : (
|
|
105
|
-
<>
|
|
106
|
-
<Table
|
|
107
|
-
dataSource={tableData}
|
|
108
|
-
columns={columns}
|
|
109
|
-
pagination={{
|
|
110
|
-
...pagination,
|
|
111
|
-
showSizeChanger: true,
|
|
112
|
-
showQuickJumper: true,
|
|
113
|
-
showTotal: (total, range) =>
|
|
114
|
-
`第 ${range[0]}-${range[1]} 条/共 ${total} 条`,
|
|
115
|
-
pageSizeOptions: ["10", "20", "50", "100"],
|
|
116
|
-
}}
|
|
117
|
-
scroll={{ x: 1200 }}
|
|
118
|
-
onChange={handleTableChange}
|
|
119
|
-
rowKey={(record, index) => `${record.id || index}`}
|
|
120
|
-
size="middle"
|
|
121
|
-
/>
|
|
122
|
-
<ApiUrlDisplay apiUrl={API_URL} />
|
|
123
|
-
</>
|
|
124
|
-
)}
|
|
125
|
-
</Card>
|
|
126
|
-
</div>
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export default TableDisplay;
|