@lovrabet/cli 1.1.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 (76) hide show
  1. package/README.md +28 -0
  2. package/lib/add-page/input-page-router.js +1 -0
  3. package/lib/add-page/main.js +1 -0
  4. package/lib/add-page/select-page-template.js +1 -0
  5. package/lib/api/api-pull-ui.js +1 -0
  6. package/lib/api/format-dataset.js +1 -0
  7. package/lib/api/generate-api-file.js +1 -0
  8. package/lib/api/main.js +1 -0
  9. package/lib/api/pull-silent.js +1 -0
  10. package/lib/api/pull.js +1 -0
  11. package/lib/api/types.js +1 -0
  12. package/lib/auth/auth-server-ui.js +1 -0
  13. package/lib/auth/auth-server.js +1 -0
  14. package/lib/auth/constant.js +1 -0
  15. package/lib/auth/get-cookie.js +1 -0
  16. package/lib/auth/is-session-valid.js +1 -0
  17. package/lib/auth/logout.js +1 -0
  18. package/lib/cli.js +2 -0
  19. package/lib/cmd/build-watch.js +1 -0
  20. package/lib/cmd/build.js +1 -0
  21. package/lib/cmd/logs.js +1 -0
  22. package/lib/cmd/preview.js +1 -0
  23. package/lib/cmd/start.js +1 -0
  24. package/lib/config/config-help.js +1 -0
  25. package/lib/config/main.js +1 -0
  26. package/lib/constant/domain.js +1 -0
  27. package/lib/constant/env.js +1 -0
  28. package/lib/create-app/enhanced-guided-create.js +1 -0
  29. package/lib/create-app/format-elapsed.js +1 -0
  30. package/lib/create-app/main.js +1 -0
  31. package/lib/create-app/task-finished.js +1 -0
  32. package/lib/create-app/task-loading.js +1 -0
  33. package/lib/create-app/task-running.js +1 -0
  34. package/lib/create-app/task-time.js +1 -0
  35. package/lib/create-app/use-copy-project-template.js +1 -0
  36. package/lib/create-app/use-format-code.js +1 -0
  37. package/lib/create-app/use-install-dependencies.js +1 -0
  38. package/lib/help.js +1 -0
  39. package/lib/utils/config.js +1 -0
  40. package/lib/utils/copy-directory.js +1 -0
  41. package/lib/utils/logger.js +1 -0
  42. package/lib/utils/router-updater.js +1 -0
  43. package/lib/utils/template-replacer.js +1 -0
  44. package/package.json +41 -0
  45. package/templates/README.md +115 -0
  46. package/templates/generate-api/api.ts.tpl +42 -0
  47. package/templates/generate-api/client.ts.tpl +64 -0
  48. package/templates/pages/blank/index.tsx.tpl +13 -0
  49. package/templates/pages/sdk-fetch/index.tsx.tpl +82 -0
  50. package/templates/projects/sub-app-react-demo/.prettierrc +1 -0
  51. package/templates/projects/sub-app-react-demo/.vscode/extensions.json +3 -0
  52. package/templates/projects/sub-app-react-demo/.vscode/settings.json +57 -0
  53. package/templates/projects/sub-app-react-demo/CHANGELOG.md +37 -0
  54. package/templates/projects/sub-app-react-demo/README.md +209 -0
  55. package/templates/projects/sub-app-react-demo/bun.lock +600 -0
  56. package/templates/projects/sub-app-react-demo/docs/API_RULE_CHANGE.md +212 -0
  57. package/templates/projects/sub-app-react-demo/docs/quick-start.md +526 -0
  58. package/templates/projects/sub-app-react-demo/index.html +39 -0
  59. package/templates/projects/sub-app-react-demo/package.json +34 -0
  60. package/templates/projects/sub-app-react-demo/public/vite.svg +1 -0
  61. package/templates/projects/sub-app-react-demo/src/api/api.ts +66 -0
  62. package/templates/projects/sub-app-react-demo/src/api/client.ts +63 -0
  63. package/templates/projects/sub-app-react-demo/src/components/ApiUrlDisplay.tsx +111 -0
  64. package/templates/projects/sub-app-react-demo/src/layouts/MainLayout.tsx +116 -0
  65. package/templates/projects/sub-app-react-demo/src/main.tsx +48 -0
  66. package/templates/projects/sub-app-react-demo/src/pages/chart-fetch/index.tsx +137 -0
  67. package/templates/projects/sub-app-react-demo/src/pages/dashboard/index.tsx +572 -0
  68. package/templates/projects/sub-app-react-demo/src/pages/index.tsx +129 -0
  69. package/templates/projects/sub-app-react-demo/src/pages/sdk-demo/index.tsx +182 -0
  70. package/templates/projects/sub-app-react-demo/src/pages/table-display.tsx +130 -0
  71. package/templates/projects/sub-app-react-demo/src/router/index.tsx +30 -0
  72. package/templates/projects/sub-app-react-demo/src/style.css +47 -0
  73. package/templates/projects/sub-app-react-demo/src/utils/api.ts +12 -0
  74. package/templates/projects/sub-app-react-demo/src/vite-env.d.ts +2 -0
  75. package/templates/projects/sub-app-react-demo/tsconfig.json +26 -0
  76. package/templates/projects/sub-app-react-demo/vite.config.ts +88 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Lovrabet SDK 客户端实例
3
+ *
4
+ * 这个文件负责创建和导出统一的客户端实例,供整个应用使用
5
+ * 通过导入 './api' 文件来自动执行配置注册
6
+ */
7
+
8
+ import { createClient } from "@lovrabet/sdk";
9
+ import "./api"; // 导入配置文件,执行 registerModels() 注册
10
+
11
+ /**
12
+ * 主要的客户端实例
13
+ * 使用最简洁的方式创建:无参数调用,自动使用已注册的 'default' 配置
14
+ */
15
+ export const lovrabetClient = createClient();
16
+
17
+ /**
18
+ * 使用示例:
19
+ *
20
+ * // 在组件中使用
21
+ * import { lovrabetClient } from '@/api/client';
22
+ *
23
+ * // 获取数据列表
24
+ * const requirements = await lovrabetClient.models.Requirements.getList({
25
+ * currentPage: 1,
26
+ * pageSize: 20
27
+ * });
28
+ *
29
+ * // 获取单条记录
30
+ * const requirement = await lovrabetClient.models.Requirements.getOne('123');
31
+ *
32
+ * // 创建新记录
33
+ * const newRequirement = await lovrabetClient.models.Requirements.create({
34
+ * title: '新需求',
35
+ * description: '需求描述'
36
+ * });
37
+ */
38
+
39
+ /**
40
+ * 其他创建客户端的方式(根据需要选择):
41
+ *
42
+ * 1. 明确指定配置名称:
43
+ * export const lovrabetClient = createClient('default');
44
+ *
45
+ * 2. 使用其他已注册的配置:
46
+ * export const devClient = createClient('dev');
47
+ * export const prodClient = createClient('prod');
48
+ *
49
+ * 3. 直接传入配置对象:
50
+ * import { LOVRABET_MODELS_CONFIG } from './api';
51
+ * export const lovrabetClient = createClient(LOVRABET_MODELS_CONFIG);
52
+ *
53
+ * 4. 通过 ClientConfig 指定配置名和其他选项:
54
+ * export const lovrabetClient = createClient({
55
+ * modelConfigName: 'default',
56
+ * token: 'custom-token',
57
+ * env: 'daily'
58
+ * });
59
+ *
60
+ * 5. 多项目支持示例:
61
+ * export const projectAClient = createClient('project-a');
62
+ * export const projectBClient = createClient('project-b');
63
+ */
@@ -0,0 +1,111 @@
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;
@@ -0,0 +1,116 @@
1
+ import React from "react";
2
+ import { isInIcestark } from "@ice/stark-app";
3
+ import { Outlet, useNavigate, useLocation } from "react-router-dom";
4
+ import { Layout, Menu, theme } from "antd";
5
+ import {
6
+ SmileOutlined,
7
+ PieChartOutlined,
8
+ TableOutlined,
9
+ FolderOutlined,
10
+ RocketOutlined,
11
+ } from "@ant-design/icons";
12
+
13
+ const { Header, Sider, Content } = Layout;
14
+
15
+ const MainLayout: React.FC = () => {
16
+ const navigate = useNavigate();
17
+ const location = useLocation();
18
+ const {
19
+ token: { colorBgContainer, borderRadiusLG },
20
+ } = theme.useToken();
21
+
22
+ const menuItems = [
23
+ {
24
+ key: "/",
25
+ icon: <SmileOutlined />,
26
+ label: "Hello World",
27
+ },
28
+ {
29
+ key: "/dashboard",
30
+ icon: <SmileOutlined />,
31
+ label: "Dashboard静态模板",
32
+ },
33
+ {
34
+ key: "/chart-fetch",
35
+ icon: <PieChartOutlined />,
36
+ label: "数据图表",
37
+ },
38
+ {
39
+ key: "/table-display",
40
+ icon: <TableOutlined />,
41
+ label: "数据表格",
42
+ },
43
+ {
44
+ key: "/sdk-demo",
45
+ icon: <FolderOutlined />,
46
+ label: "Lovrabet SDK使用案例",
47
+ },
48
+ ];
49
+
50
+ const handleMenuClick = ({ key }: { key: string }) => {
51
+ navigate(key);
52
+ };
53
+
54
+ // 可选:根据isInIcestark()判断当前运行环境,被嵌入时,不渲染layout布局
55
+ if (isInIcestark()) {
56
+ return (
57
+ <div style={{ padding: "16px 20px" }}>
58
+ <Outlet />
59
+ </div>
60
+ );
61
+ }
62
+
63
+ return (
64
+ <Layout style={{ minHeight: "100vh" }}>
65
+ <Sider
66
+ breakpoint="lg"
67
+ collapsedWidth="0"
68
+ style={{
69
+ background: colorBgContainer,
70
+ }}
71
+ >
72
+ <div
73
+ style={{
74
+ height: 32,
75
+ margin: 16,
76
+ background: "rgba(255, 255, 255, 0.2)",
77
+ textAlign: "center",
78
+ }}
79
+ >
80
+ <img src="/vite.svg" alt="logo" />
81
+ </div>
82
+ <Menu
83
+ mode="inline"
84
+ selectedKeys={[location.pathname]}
85
+ items={menuItems}
86
+ onClick={handleMenuClick}
87
+ style={{ height: "100%", borderRight: 0 }}
88
+ />
89
+ </Sider>
90
+ <Layout>
91
+ <Header style={{ padding: 0, background: colorBgContainer }}>
92
+ <div
93
+ style={{ padding: "0 24px", fontSize: "18px", fontWeight: "bold" }}
94
+ >
95
+ React SPA 应用
96
+ </div>
97
+ </Header>
98
+ <Content style={{ margin: "0 16px" }}>
99
+ <div
100
+ style={{
101
+ padding: 24,
102
+ minHeight: 360,
103
+ background: colorBgContainer,
104
+ borderRadius: borderRadiusLG,
105
+ margin: "16px 0",
106
+ }}
107
+ >
108
+ <Outlet />
109
+ </div>
110
+ </Content>
111
+ </Layout>
112
+ </Layout>
113
+ );
114
+ };
115
+
116
+ export default MainLayout;
@@ -0,0 +1,48 @@
1
+ import React from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { isInIcestark } from "@ice/stark-app";
4
+ import { ConfigProvider } from "antd";
5
+ import zhCN from "antd/locale/zh_CN";
6
+ import App from "./router";
7
+ import "./style.css";
8
+
9
+ // 可选:根据 isInIcestark() 判断当前的运行环境,可同时兼容独立使用和嵌入使用
10
+ if (!isInIcestark()) {
11
+ const container = document.getElementById("root");
12
+ if (container) {
13
+ const root = createRoot(container);
14
+ root.render(
15
+ <ConfigProvider locale={zhCN}>
16
+ <App />
17
+ </ConfigProvider>,
18
+ );
19
+ }
20
+ }
21
+
22
+ // 关键:暴露 mount 供主应用加载时调用
23
+ export function mount({
24
+ container,
25
+ customProps,
26
+ }: {
27
+ container: HTMLElement;
28
+ customProps: object;
29
+ }) {
30
+ const root = createRoot(container);
31
+ root.render(
32
+ <React.StrictMode>
33
+ <ConfigProvider locale={zhCN}>
34
+ <App {...customProps} />
35
+ </ConfigProvider>
36
+ </React.StrictMode>,
37
+ );
38
+ return root;
39
+ }
40
+
41
+ // 关键:暴露 unmount 供主应用卸载时调用
42
+ export function unmount({ container }: { container: HTMLElement }) {
43
+ // React 18 中不再需要手动卸载,但为了兼容性保留
44
+ const root = (container as any)._reactRoot;
45
+ if (root) {
46
+ root.unmount();
47
+ }
48
+ }
@@ -0,0 +1,137 @@
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;