@lovrabet/rabetbase-cli 2.0.2-beta.1

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 (163) hide show
  1. package/LICENSE +45 -0
  2. package/README.md +605 -0
  3. package/lib/ai-setup/config.js +1 -0
  4. package/lib/api/api-doc-ui.js +1 -0
  5. package/lib/api/api-doc.js +1 -0
  6. package/lib/api/api-pull-ui.js +1 -0
  7. package/lib/api/fetch-model-list.js +1 -0
  8. package/lib/api/generate-api-file.js +1 -0
  9. package/lib/api/main.js +1 -0
  10. package/lib/api/pull-silent.js +1 -0
  11. package/lib/app-menu/app-menu-sync-ui.js +1 -0
  12. package/lib/app-menu/create-menu.js +1 -0
  13. package/lib/app-menu/get-local-pages.js +1 -0
  14. package/lib/app-menu/get-online-menu-list.js +1 -0
  15. package/lib/app-menu/types.js +1 -0
  16. package/lib/app-menu/use-get-online-menu-list.js +1 -0
  17. package/lib/app-menu/utils.js +1 -0
  18. package/lib/app-menu/valid-url.js +1 -0
  19. package/lib/app-menu-update-cdn/current-content.js +1 -0
  20. package/lib/app-menu-update-cdn/input-cdn-asset.js +1 -0
  21. package/lib/app-menu-update-cdn/main.js +1 -0
  22. package/lib/app-menu-update-cdn/types.js +1 -0
  23. package/lib/app-menu-update-cdn/update-menu-cdn-url.js +1 -0
  24. package/lib/auth/auth-server-ui.js +1 -0
  25. package/lib/auth/auth-server.js +1 -0
  26. package/lib/auth/constant.js +1 -0
  27. package/lib/auth/get-cookie.js +1 -0
  28. package/lib/auth/is-session-valid.js +1 -0
  29. package/lib/auth/logout.js +1 -0
  30. package/lib/cli-flags.js +1 -0
  31. package/lib/cli.js +2 -0
  32. package/lib/commands/api.js +1 -0
  33. package/lib/commands/app.js +1 -0
  34. package/lib/commands/auth.js +1 -0
  35. package/lib/commands/bff/delete.js +1 -0
  36. package/lib/commands/bff/detail.js +1 -0
  37. package/lib/commands/bff/index.js +1 -0
  38. package/lib/commands/bff/list.js +1 -0
  39. package/lib/commands/bff/new.js +1 -0
  40. package/lib/commands/bff/pull.js +1 -0
  41. package/lib/commands/bff/push.js +1 -0
  42. package/lib/commands/bff/status.js +1 -0
  43. package/lib/commands/build.js +1 -0
  44. package/lib/commands/codegen/index.js +1 -0
  45. package/lib/commands/codegen/sdk.js +1 -0
  46. package/lib/commands/codegen/sql.js +1 -0
  47. package/lib/commands/config.js +1 -0
  48. package/lib/commands/create.js +1 -0
  49. package/lib/commands/dataset/detail.js +1 -0
  50. package/lib/commands/dataset/index.js +1 -0
  51. package/lib/commands/dataset/links.js +1 -0
  52. package/lib/commands/dataset/list.js +1 -0
  53. package/lib/commands/dataset/operations.js +1 -0
  54. package/lib/commands/init.js +1 -0
  55. package/lib/commands/logout.js +1 -0
  56. package/lib/commands/logs.js +1 -0
  57. package/lib/commands/mcp.js +1 -0
  58. package/lib/commands/menu.js +1 -0
  59. package/lib/commands/preview.js +1 -0
  60. package/lib/commands/registry.js +1 -0
  61. package/lib/commands/run.js +1 -0
  62. package/lib/commands/skill.js +1 -0
  63. package/lib/commands/sql/detail.js +1 -0
  64. package/lib/commands/sql/exec.js +1 -0
  65. package/lib/commands/sql/index.js +1 -0
  66. package/lib/commands/sql/list.js +1 -0
  67. package/lib/commands/sql/save.js +1 -0
  68. package/lib/commands/sql/validate.js +1 -0
  69. package/lib/commands/start.js +1 -0
  70. package/lib/config/config-help.js +1 -0
  71. package/lib/config/main.js +1 -0
  72. package/lib/constant/cli.js +1 -0
  73. package/lib/constant/domain.js +1 -0
  74. package/lib/constant/env.js +1 -0
  75. package/lib/context.js +1 -0
  76. package/lib/core/alias-resolver.js +1 -0
  77. package/lib/core/api-client.js +1 -0
  78. package/lib/core/bff/config.js +1 -0
  79. package/lib/core/bff/file-system.js +1 -0
  80. package/lib/core/bff/hash.js +1 -0
  81. package/lib/core/bff/lock.js +1 -0
  82. package/lib/core/bff/types.js +1 -0
  83. package/lib/core/bff/utils.js +1 -0
  84. package/lib/core/db-resolver.js +1 -0
  85. package/lib/core/sql-validator.js +1 -0
  86. package/lib/create-app/enhanced-guided-create.js +1 -0
  87. package/lib/create-app/format-elapsed.js +1 -0
  88. package/lib/create-app/main.js +1 -0
  89. package/lib/create-app/non-interactive.js +1 -0
  90. package/lib/create-app/task-finished.js +1 -0
  91. package/lib/create-app/task-loading.js +1 -0
  92. package/lib/create-app/task-running.js +1 -0
  93. package/lib/create-app/task-time.js +1 -0
  94. package/lib/create-app/use-copy-project-template.js +1 -0
  95. package/lib/create-app/use-format-code.js +1 -0
  96. package/lib/create-app/use-install-dependencies.js +1 -0
  97. package/lib/errors.js +1 -0
  98. package/lib/framework/flags.js +1 -0
  99. package/lib/framework/help.js +1 -0
  100. package/lib/framework/index.js +1 -0
  101. package/lib/framework/output.js +1 -0
  102. package/lib/framework/response.js +1 -0
  103. package/lib/framework/runner.js +1 -0
  104. package/lib/framework/types.js +1 -0
  105. package/lib/generated/build-info.js +1 -0
  106. package/lib/help.js +1 -0
  107. package/lib/init/main.js +1 -0
  108. package/lib/mcp/McpInstallUI.js +1 -0
  109. package/lib/mcp/claude.js +1 -0
  110. package/lib/mcp/cursor.js +1 -0
  111. package/lib/mcp/main.js +1 -0
  112. package/lib/mcp/mcp-install-non-interactive.js +1 -0
  113. package/lib/mcp/mcp-installer.js +1 -0
  114. package/lib/skills/main.js +1 -0
  115. package/lib/skills/npx-skills-add.js +1 -0
  116. package/lib/types/index.js +1 -0
  117. package/lib/ui/IDESelector.js +1 -0
  118. package/lib/ui/useIDESelection.js +1 -0
  119. package/lib/utils/ai_config.js +1 -0
  120. package/lib/utils/cdn-config.js +1 -0
  121. package/lib/utils/check-sdk-version.js +1 -0
  122. package/lib/utils/cli-version-check.js +1 -0
  123. package/lib/utils/config.js +1 -0
  124. package/lib/utils/copy-directory.js +1 -0
  125. package/lib/utils/file-utils.js +1 -0
  126. package/lib/utils/guides-cdn.js +1 -0
  127. package/lib/utils/http-client.js +1 -0
  128. package/lib/utils/logger.js +1 -0
  129. package/lib/utils/rules-cdn.js +1 -0
  130. package/lib/utils/sleep.js +1 -0
  131. package/lib/utils/template-replacer.js +1 -0
  132. package/package.json +77 -0
  133. package/sidecar/flags.json +10 -0
  134. package/templates/README.md +97 -0
  135. package/templates/generate-api/api.ts.tpl +47 -0
  136. package/templates/generate-api/client.ts.tpl +64 -0
  137. package/templates/projects/sub-app-react-demo/.prettierrc +1 -0
  138. package/templates/projects/sub-app-react-demo/CHANGELOG.md +53 -0
  139. package/templates/projects/sub-app-react-demo/README.md +318 -0
  140. package/templates/projects/sub-app-react-demo/docs/API_RULE_CHANGE.md +212 -0
  141. package/templates/projects/sub-app-react-demo/docs/quick-start.md +526 -0
  142. package/templates/projects/sub-app-react-demo/index.html +27 -0
  143. package/templates/projects/sub-app-react-demo/package.json +34 -0
  144. package/templates/projects/sub-app-react-demo/public/logo.svg +1 -0
  145. package/templates/projects/sub-app-react-demo/public/vite.svg +1 -0
  146. package/templates/projects/sub-app-react-demo/src/api/api.ts +83 -0
  147. package/templates/projects/sub-app-react-demo/src/api/client.ts +64 -0
  148. package/templates/projects/sub-app-react-demo/src/layouts/MainLayout.tsx +311 -0
  149. package/templates/projects/sub-app-react-demo/src/main.tsx +48 -0
  150. package/templates/projects/sub-app-react-demo/src/pages/dashboard/index.tsx +572 -0
  151. package/templates/projects/sub-app-react-demo/src/pages/data-screen/index.tsx +526 -0
  152. package/templates/projects/sub-app-react-demo/src/pages/index.tsx +467 -0
  153. package/templates/projects/sub-app-react-demo/src/pages/sdk-demo/index.tsx +453 -0
  154. package/templates/projects/sub-app-react-demo/src/pages/workbench/index.module.css +293 -0
  155. package/templates/projects/sub-app-react-demo/src/pages/workbench/index.tsx +155 -0
  156. package/templates/projects/sub-app-react-demo/src/router/index.tsx +30 -0
  157. package/templates/projects/sub-app-react-demo/src/style.css +92 -0
  158. package/templates/projects/sub-app-react-demo/src/utils/api.ts +12 -0
  159. package/templates/projects/sub-app-react-demo/src/vite-env.d.ts +2 -0
  160. package/templates/projects/sub-app-react-demo/tsconfig.json +26 -0
  161. package/templates/projects/sub-app-react-demo/vite.config.ts +111 -0
  162. package/templates/rules/lovrabet_rules.mdc.tpl +891 -0
  163. package/templates/skill/SKILL.md.tpl +120 -0
@@ -0,0 +1,453 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import {
3
+ Button,
4
+ Table,
5
+ Card,
6
+ Typography,
7
+ Space,
8
+ message,
9
+ Select,
10
+ Input,
11
+ Tag,
12
+ } from "antd";
13
+ import { ApiOutlined } from "@ant-design/icons";
14
+ import { lovrabetClient } from "@/api/client";
15
+
16
+ const { Title, Paragraph, Text } = Typography;
17
+
18
+ export default function SdkDemo() {
19
+ const [loading, setLoading] = useState(false);
20
+ const [data, setData] = useState<any[]>([]);
21
+ const [columns, setColumns] = useState<any[]>([]);
22
+ const [modelList, setModelList] = useState<
23
+ Array<{
24
+ value: string;
25
+ label: string;
26
+ alias?: string;
27
+ name?: string;
28
+ datasetCode: string;
29
+ }>
30
+ >([]);
31
+ const [selectedModel, setSelectedModel] = useState<string>("");
32
+ const [selectOptions, setSelectOptions] = useState<any[]>([]);
33
+ const [codeField, setCodeField] = useState<string>("id");
34
+ const [labelField, setLabelField] = useState<string>("");
35
+
36
+ // 获取选中模型的信息(用于代码示例展示 alias)
37
+ const selectedModelInfo = modelList.find((m) => m.value === selectedModel);
38
+
39
+ /**
40
+ * 加载可用的数据模型列表
41
+ */
42
+ useEffect(() => {
43
+ try {
44
+ // 使用 getModelListDetails 获取人类友好的模型列表
45
+ const models = lovrabetClient.getModelListDetails();
46
+ setModelList(models);
47
+ // 默认选择第一个模型
48
+ if (models.length > 0) {
49
+ setSelectedModel(models[0].value);
50
+ }
51
+ } catch (error) {
52
+ console.error("获取模型列表失败:", error);
53
+ message.error("获取模型列表失败");
54
+ }
55
+ }, []);
56
+
57
+ /**
58
+ * 使用 filter 接口查询数据
59
+ */
60
+ const loadData = async () => {
61
+ if (!selectedModel) {
62
+ message.warning("请先选择一个数据模型");
63
+ return;
64
+ }
65
+
66
+ setLoading(true);
67
+
68
+ try {
69
+ // 使用 filter 接口进行查询
70
+ const response = await lovrabetClient.models[selectedModel].filter({
71
+ currentPage: 1,
72
+ pageSize: 10,
73
+ });
74
+
75
+ processResponse(response, "查询成功!");
76
+ } catch (error: any) {
77
+ handleError(error, "查询");
78
+ } finally {
79
+ setLoading(false);
80
+ }
81
+ };
82
+
83
+ /**
84
+ * 处理响应数据
85
+ */
86
+ const processResponse = (response: any, successMessage: string) => {
87
+ try {
88
+ // filter 接口返回的数据结构
89
+ const tableData = response?.tableData || response?.data || [];
90
+
91
+ // 确保 tableData 是数组
92
+ if (!Array.isArray(tableData)) {
93
+ console.error("返回的数据不是数组格式:", tableData);
94
+ message.error("返回数据格式错误,请检查 API 响应");
95
+ return;
96
+ }
97
+
98
+ setData(tableData);
99
+
100
+ // 优先使用 tableColumns 配置
101
+ if (response?.tableColumns && Array.isArray(response.tableColumns)) {
102
+ // 如果有 tableColumns,优先使用它
103
+ const tableColumns = response.tableColumns.map((column: any) => ({
104
+ title:
105
+ column.title || column.dataIndex || column.key || String(column),
106
+ dataIndex: column.dataIndex || column.key || String(column),
107
+ key: column.dataIndex || column.key || String(column),
108
+ // 处理对象/数组类型的值,避免 React Error #31
109
+ render: (value: any) => {
110
+ if (value === null || value === undefined) {
111
+ return "-";
112
+ }
113
+ if (typeof value === "object") {
114
+ return JSON.stringify(value);
115
+ }
116
+ return String(value);
117
+ },
118
+ }));
119
+ setColumns(tableColumns);
120
+ } else if (tableData.length > 0) {
121
+ // 如果没有 tableColumns,从第一条数据中提取字段名作为列
122
+ const firstRow = tableData[0];
123
+ if (firstRow && typeof firstRow === "object") {
124
+ const tableColumns = Object.keys(firstRow).map((key) => ({
125
+ title: key,
126
+ dataIndex: key,
127
+ key: key,
128
+ // 处理对象/数组类型的值,避免 React Error #31
129
+ render: (value: any) => {
130
+ if (value === null || value === undefined) {
131
+ return "-";
132
+ }
133
+ if (typeof value === "object") {
134
+ return JSON.stringify(value);
135
+ }
136
+ return String(value);
137
+ },
138
+ }));
139
+ setColumns(tableColumns);
140
+ }
141
+ } else {
142
+ // 如果没有数据也没有列定义,清空列
143
+ setColumns([]);
144
+ }
145
+
146
+ message.success(successMessage);
147
+ } catch (error: any) {
148
+ console.error("处理响应数据失败:", error);
149
+ message.error(`处理数据失败: ${error.message}`);
150
+ }
151
+ };
152
+
153
+ /**
154
+ * 获取下拉选项
155
+ */
156
+ const loadSelectOptions = async () => {
157
+ if (!selectedModel) {
158
+ message.warning("请先选择一个数据模型");
159
+ return;
160
+ }
161
+
162
+ if (!codeField || !labelField) {
163
+ message.warning("请输入 code 和 label 字段名");
164
+ return;
165
+ }
166
+
167
+ setLoading(true);
168
+ setSelectOptions([]);
169
+
170
+ try {
171
+ const options = await lovrabetClient.models[
172
+ selectedModel
173
+ ].getSelectOptions({
174
+ code: codeField,
175
+ label: labelField,
176
+ });
177
+
178
+ setSelectOptions(options);
179
+ message.success(`成功获取 ${options.length} 个下拉选项`);
180
+ console.log("下拉选项数据:", options);
181
+ } catch (error: any) {
182
+ console.error("获取下拉选项失败:", error);
183
+ message.error(`获取下拉选项失败: ${error.message}`);
184
+ } finally {
185
+ setLoading(false);
186
+ }
187
+ };
188
+
189
+ /**
190
+ * 处理错误
191
+ */
192
+ const handleError = (error: any, action: string) => {
193
+ console.error(`${action}失败:`, error);
194
+ message.error(`${action}失败: ${error.message}`);
195
+ };
196
+
197
+ return (
198
+ <div style={{ padding: "24px" }}>
199
+ {/* 标题 */}
200
+ <Title level={2}>
201
+ <ApiOutlined /> Lovrabet SDK 简单演示
202
+ </Title>
203
+
204
+ <Paragraph style={{ color: "#666", marginBottom: 24 }}>
205
+ 演示 Lovrabet SDK 的 <Text strong>filter</Text> 接口使用方法。filter
206
+ 接口支持复杂条件查询、字段选择、多字段排序等功能。
207
+ <br />
208
+ <strong>注意:</strong>代码示例中的 "Requirements"
209
+ 是假设已经存在的数据模型名称,实际使用时请根据下拉框中的可用模型进行选择。
210
+ </Paragraph>
211
+
212
+ {/* 数据模型选择 */}
213
+ <Card title="选择数据模型" size="small" style={{ marginBottom: 16 }}>
214
+ <Space>
215
+ <Select
216
+ placeholder="选择要查询的数据模型"
217
+ style={{ width: 350 }}
218
+ value={selectedModel}
219
+ onChange={setSelectedModel}
220
+ showSearch
221
+ optionFilterProp="label"
222
+ options={modelList}
223
+ />
224
+ <Button
225
+ type="primary"
226
+ loading={loading}
227
+ onClick={loadData}
228
+ icon={<ApiOutlined />}
229
+ disabled={!selectedModel}
230
+ >
231
+ 查询数据
232
+ </Button>
233
+ </Space>
234
+ </Card>
235
+
236
+ {/* 代码示例 */}
237
+ <Card title="代码示例" size="small" style={{ marginBottom: 16 }}>
238
+ <pre
239
+ style={{
240
+ background: "#f0f8ff",
241
+ padding: "16px",
242
+ borderRadius: "4px",
243
+ margin: 0,
244
+ fontSize: "13px",
245
+ border: "1px solid #1890ff",
246
+ overflow: "auto",
247
+ }}
248
+ >
249
+ {`// ========== 方式一:标准 dataset_code 模式(推荐 AI/LLM 使用)==========
250
+ const response = await lovrabetClient
251
+ .models['${selectedModel || "dataset_xxx"}'].filter({
252
+ currentPage: 1,
253
+ pageSize: 10
254
+ });
255
+ ${
256
+ selectedModelInfo?.alias
257
+ ? `
258
+ // ========== 方式二:人类友好的 alias 模式 ==========
259
+ const response = await lovrabetClient
260
+ .models.${selectedModelInfo.alias}.filter({
261
+ currentPage: 1,
262
+ pageSize: 10
263
+ });
264
+ `
265
+ : ""
266
+ }
267
+ // ========== 完整查询示例(所有参数均为可选)==========
268
+ const response = await lovrabetClient
269
+ .models['${selectedModel || "dataset_xxx"}'].filter({
270
+ // where: 条件查询(可选)
271
+ // where: {
272
+ // age: { $gte: 18 },
273
+ // status: { $eq: 'active' }
274
+ // },
275
+
276
+ // select: 字段选择(可选)
277
+ // select: ['id', 'name', 'age'],
278
+
279
+ // orderBy: 排序(可选)
280
+ // orderBy: [{ createTime: 'desc' }],
281
+
282
+ // 分页参数(必需)
283
+ currentPage: 1,
284
+ pageSize: 10
285
+ });
286
+
287
+ // 其他可用参数(仅示例,以实际字段为准):
288
+ // - where: 支持 $eq, $ne, $gte, $lte, $in, $contain, $startWith, $endWith 等操作符
289
+ // - where: 支持 $and, $or 逻辑组合
290
+ // - select: 数组形式,指定返回的字段
291
+ // - orderBy: 数组形式,支持多字段排序 [{ field1: 'desc' }, { field2: 'asc' }]`}
292
+ </pre>
293
+ </Card>
294
+
295
+ {/* 数据表格 */}
296
+ {data.length > 0 && (
297
+ <Card title="数据结果" size="small" style={{ marginBottom: 16 }}>
298
+ <Table
299
+ columns={columns}
300
+ dataSource={data}
301
+ rowKey={(_, index) => index?.toString() || "0"}
302
+ pagination={false}
303
+ size="small"
304
+ scroll={{ x: true }}
305
+ />
306
+ </Card>
307
+ )}
308
+
309
+ {/* 获取下拉选项 */}
310
+ <Card title="📋 获取下拉选项" size="small" style={{ marginBottom: 16 }}>
311
+ <Space direction="vertical" style={{ width: "100%" }}>
312
+ <div style={{ color: "#666" }}>
313
+ 用于获取数据表的下拉选项数据,适用于 Select、Radio、Checkbox
314
+ 等表单组件(仅 WebAPI 模式支持)
315
+ </div>
316
+
317
+ {/* 显示可用字段 */}
318
+ {columns.length > 0 && (
319
+ <div style={{ marginBottom: 8 }}>
320
+ <span style={{ color: "#666", marginRight: 8 }}>
321
+ 可用字段(点击快速填入):
322
+ </span>
323
+ <Space wrap size={[4, 4]}>
324
+ {columns.map((column: any) => (
325
+ <Tag
326
+ key={column.dataIndex}
327
+ color="blue"
328
+ style={{ cursor: "pointer" }}
329
+ onClick={() => {
330
+ if (!codeField) {
331
+ setCodeField(column.dataIndex);
332
+ message.success(
333
+ `已填入 Code 字段: ${column.dataIndex}`,
334
+ );
335
+ } else if (!labelField) {
336
+ setLabelField(column.dataIndex);
337
+ message.success(
338
+ `已填入 Label 字段: ${column.dataIndex}`,
339
+ );
340
+ } else {
341
+ message.info("Code 和 Label 已填写,如需更换请先清空");
342
+ }
343
+ }}
344
+ >
345
+ {column.dataIndex}
346
+ </Tag>
347
+ ))}
348
+ </Space>
349
+ </div>
350
+ )}
351
+
352
+ <Space wrap>
353
+ <span>Code 字段:</span>
354
+ <Input
355
+ placeholder="用作选项值的字段名"
356
+ value={codeField}
357
+ onChange={(e) => setCodeField(e.target.value)}
358
+ style={{ width: 200 }}
359
+ allowClear
360
+ />
361
+ <span>Label 字段:</span>
362
+ <Input
363
+ placeholder="用作显示文本的字段名"
364
+ value={labelField}
365
+ onChange={(e) => setLabelField(e.target.value)}
366
+ style={{ width: 200 }}
367
+ allowClear
368
+ />
369
+ <Button
370
+ type="primary"
371
+ loading={loading}
372
+ onClick={loadSelectOptions}
373
+ icon={<ApiOutlined />}
374
+ disabled={!selectedModel || !codeField || !labelField}
375
+ >
376
+ 获取选项
377
+ </Button>
378
+ </Space>
379
+
380
+ <pre
381
+ style={{
382
+ background: "#f5f5f5",
383
+ padding: "12px",
384
+ borderRadius: "4px",
385
+ margin: "8px 0 0 0",
386
+ fontSize: "13px",
387
+ border: "1px solid #d9d9d9",
388
+ }}
389
+ >
390
+ {`// ========== 方式一:标准 dataset_code 模式(推荐 AI/LLM 使用)==========
391
+ const options = await lovrabetClient
392
+ .models['${selectedModel || "dataset_xxx"}'].getSelectOptions({
393
+ code: "${codeField || "id"}",
394
+ label: "${labelField || "name"}"
395
+ });
396
+ ${
397
+ selectedModelInfo?.alias
398
+ ? `
399
+ // ========== 方式二:人类友好的 alias 模式 ==========
400
+ const options = await lovrabetClient
401
+ .models.${selectedModelInfo.alias}.getSelectOptions({
402
+ code: "${codeField || "id"}",
403
+ label: "${labelField || "name"}"
404
+ });
405
+ `
406
+ : ""
407
+ }
408
+ // 返回格式:[{ label: "显示文本", value: "选项值" }]`}
409
+ </pre>
410
+ </Space>
411
+ </Card>
412
+
413
+ {/* 下拉选项结果 */}
414
+ {selectOptions.length > 0 && (
415
+ <Card
416
+ title={`下拉选项结果(共 ${selectOptions.length} 个)`}
417
+ size="small"
418
+ >
419
+ <pre
420
+ style={{
421
+ background: "#f5f5f5",
422
+ padding: "12px",
423
+ borderRadius: "4px",
424
+ margin: 0,
425
+ fontSize: "13px",
426
+ maxHeight: "400px",
427
+ overflow: "auto",
428
+ }}
429
+ >
430
+ {JSON.stringify(selectOptions, null, 2)}
431
+ </pre>
432
+ </Card>
433
+ )}
434
+
435
+ {/* API 参考文档 */}
436
+ <Card size="small" style={{ marginTop: 24, background: "#f5f5f5" }}>
437
+ <Paragraph style={{ margin: 0, textAlign: "center" }}>
438
+ <Text type="secondary">
439
+ 技术开发文档指引:{" "}
440
+ <a
441
+ href="https://open.lovrabet.com/docs/guides/tutorial"
442
+ target="_blank"
443
+ rel="noopener noreferrer"
444
+ style={{ color: "#1890ff" }}
445
+ >
446
+ https://open.lovrabet.com/docs/guides/tutorial
447
+ </a>
448
+ </Text>
449
+ </Paragraph>
450
+ </Card>
451
+ </div>
452
+ );
453
+ }