@lark-apaas/coding-templates 0.1.35 → 0.1.37
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/package.json +1 -1
- package/template-apex/README.md +13 -9
- package/template-apex/client/src/app.tsx +7 -9
- package/template-apex/client/src/main.tsx +6 -4
- package/template-apex/package.json +8 -5
- package/template-apex/server/db/schema.ts +1 -11
- package/template-apex/server/index.ts +3 -21
- package/template-apex/server/routes/index.ts +6 -2
- package/template-apex/server/routes/view.ts +16 -0
- package/template-apex/tsconfig.server.json +0 -2
- package/template-vite-react/README.md +12 -13
- package/template-vite-react/client/src/app.tsx +7 -9
- package/template-vite-react/client/src/main.tsx +6 -4
- package/template-vite-react/package.json +5 -7
- package/template-apex/_env.local.example +0 -1
- package/template-apex/package-lock.json +0 -11958
- package/template-vite-react/package-lock.json +0 -10316
package/package.json
CHANGED
package/template-apex/README.md
CHANGED
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
```
|
|
21
21
|
├── client/src/ # 前端代码
|
|
22
|
-
│ ├──
|
|
22
|
+
│ ├── main.tsx # 入口(Provider 层级 + 样式引入,勿修改)
|
|
23
|
+
│ ├── app.tsx # 路由配置
|
|
23
24
|
│ ├── index.css # 全局样式 + 主题变量
|
|
24
25
|
│ ├── api/ # API 请求封装
|
|
25
26
|
│ │ └── index.ts
|
|
@@ -34,12 +35,12 @@
|
|
|
34
35
|
│ ├── hooks/ # 自定义 Hooks
|
|
35
36
|
│ └── lib/ # 工具函数(cn() 等)
|
|
36
37
|
├── server/ # 后端代码
|
|
37
|
-
│ ├── index.ts # Express
|
|
38
|
-
│ ├── routes/ #
|
|
39
|
-
│ │
|
|
38
|
+
│ ├── index.ts # Express 入口
|
|
39
|
+
│ ├── routes/ # 路由
|
|
40
|
+
│ │ ├── index.ts # 路由注册
|
|
41
|
+
│ │ └── view.ts # 页面渲染(catch-all HTML 响应,勿修改)
|
|
40
42
|
│ └── db/ # 数据库层
|
|
41
|
-
│ ├── schema.ts # Drizzle schema
|
|
42
|
-
│ └── index.ts # 数据库连接
|
|
43
|
+
│ ├── schema.ts # Drizzle schema 定义(工具自动生成,勿手动修改)
|
|
43
44
|
├── shared/ # 前后端共享(不依赖 client 或 server)
|
|
44
45
|
│ ├── types.ts # 数据模型类型
|
|
45
46
|
│ └── api.interface.ts # zod schema + API 入参/出参类型
|
|
@@ -76,7 +77,7 @@ export type ListPostsResponse = Post[];
|
|
|
76
77
|
|
|
77
78
|
### 2. server/ — 数据库和路由
|
|
78
79
|
|
|
79
|
-
`server/db/schema.ts`
|
|
80
|
+
`server/db/schema.ts` 由 `npm run gen:db-schema` 自动生成,无需手动编写。运行命令后会根据数据库表结构自动生成如下定义:
|
|
80
81
|
|
|
81
82
|
```typescript
|
|
82
83
|
export const posts = pgTable("posts", {
|
|
@@ -91,7 +92,7 @@ export const posts = pgTable("posts", {
|
|
|
91
92
|
|
|
92
93
|
```typescript
|
|
93
94
|
import { Router } from "express";
|
|
94
|
-
import { db } from "
|
|
95
|
+
import { db } from "@lark-apaas/express-core";
|
|
95
96
|
import { posts } from "../db/schema";
|
|
96
97
|
import { createPostSchema } from "@shared/api.interface";
|
|
97
98
|
|
|
@@ -120,11 +121,12 @@ app.use("/api/posts", postsRouter);
|
|
|
120
121
|
|
|
121
122
|
> ⚠️ **客户端所有 HTTP 请求必须使用 `axiosForBackend`**
|
|
122
123
|
>
|
|
123
|
-
> `axiosForBackend` 由 `@lark-apaas/client-toolkit-lite`
|
|
124
|
+
> `axiosForBackend` 由 `@lark-apaas/client-toolkit-lite` 提供,内置平台鉴权、CSRF token 注入和请求上下文。**禁止使用** `fetch`、`axios`、`XMLHttpRequest` 或其他 HTTP 客户端直接发起请求。
|
|
124
125
|
|
|
125
126
|
`client/src/api/index.ts` 增加封装:
|
|
126
127
|
|
|
127
128
|
```typescript
|
|
129
|
+
import { axiosForBackend } from "@lark-apaas/client-toolkit-lite";
|
|
128
130
|
import type { ListPostsResponse } from "@shared/api.interface";
|
|
129
131
|
|
|
130
132
|
export async function listPosts(): Promise<ListPostsResponse> {
|
|
@@ -216,6 +218,8 @@ export default function DashboardPage() {
|
|
|
216
218
|
</Route>
|
|
217
219
|
```
|
|
218
220
|
|
|
221
|
+
> **注意:** `BrowserRouter` 已在 `main.tsx` 中统一配置,`app.tsx` 中**禁止**再包裹 `BrowserRouter`,否则会报嵌套 Router 错误。
|
|
222
|
+
|
|
219
223
|
**新增页面步骤:**
|
|
220
224
|
|
|
221
225
|
1. 在 `client/src/pages/` 下新建页面目录(如 `SettingsPage`)和 `SettingsPage.tsx`
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Routes, Route } from "react-router-dom";
|
|
2
2
|
import { Layout } from "@/components/layout";
|
|
3
3
|
import HomePage from "@/pages/HomePage/HomePage";
|
|
4
4
|
import NotFoundPage from "@/pages/NotFoundPage/NotFoundPage";
|
|
5
5
|
|
|
6
6
|
export default function App() {
|
|
7
7
|
return (
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
<Route element={<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
</Routes>
|
|
15
|
-
</BrowserRouter>
|
|
8
|
+
<Routes>
|
|
9
|
+
<Route element={<Layout />}>
|
|
10
|
+
<Route index element={<HomePage />} />
|
|
11
|
+
<Route path="*" element={<NotFoundPage />} />
|
|
12
|
+
</Route>
|
|
13
|
+
</Routes>
|
|
16
14
|
);
|
|
17
15
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { StrictMode } from "react";
|
|
2
2
|
import { createRoot } from "react-dom/client";
|
|
3
|
+
import { BrowserRouter } from "react-router-dom";
|
|
3
4
|
import { AppContainer } from "@lark-apaas/client-toolkit-lite";
|
|
4
|
-
import "@lark-apaas/client-toolkit-lite/styles.css";
|
|
5
5
|
import App from "./app";
|
|
6
6
|
import "./index.css";
|
|
7
7
|
|
|
8
8
|
createRoot(document.getElementById("root")!).render(
|
|
9
9
|
<StrictMode>
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
10
|
+
<BrowserRouter basename={process.env.CLIENT_BASE_PATH || "/"}>
|
|
11
|
+
<AppContainer>
|
|
12
|
+
<App />
|
|
13
|
+
</AppContainer>
|
|
14
|
+
</BrowserRouter>
|
|
13
15
|
</StrictMode>,
|
|
14
16
|
);
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"dev": "tsx --watch --watch-path server --watch-path shared server/index.ts",
|
|
15
14
|
"build": "bash scripts/build.sh",
|
|
16
|
-
"typecheck": "npm
|
|
15
|
+
"typecheck": "concurrently npm:typecheck:client npm:typecheck:server",
|
|
17
16
|
"typecheck:client": "tsc -p tsconfig.app.json",
|
|
18
|
-
"
|
|
17
|
+
"typecheck:server": "tsc --noEmit -p tsconfig.server.json",
|
|
18
|
+
"lint": "concurrently npm:typecheck npm:lint:client npm:lint:server",
|
|
19
19
|
"lint:client": "eslint client/src",
|
|
20
20
|
"lint:server": "eslint server",
|
|
21
21
|
"gen:db-schema": "npx -y @lark-apaas/db-schema-sync@latest --output server/db/schema.ts"
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
"@radix-ui/react-toggle": "^1.1.9",
|
|
49
49
|
"@radix-ui/react-toggle-group": "^1.1.10",
|
|
50
50
|
"@radix-ui/react-tooltip": "^1.1.18",
|
|
51
|
-
"@lark-apaas/client-toolkit-lite": "^1.
|
|
52
|
-
"@lark-apaas/express-core": "^0.0
|
|
51
|
+
"@lark-apaas/client-toolkit-lite": "^1.1.0",
|
|
52
|
+
"@lark-apaas/express-core": "^1.0.0",
|
|
53
53
|
"@formkit/auto-animate": "^0.9.0",
|
|
54
54
|
"framer-motion": "^12.38.0",
|
|
55
55
|
"class-variance-authority": "^0.7.1",
|
|
@@ -57,6 +57,8 @@
|
|
|
57
57
|
"cmdk": "^1.1.1",
|
|
58
58
|
"date-fns": "^4.1.0",
|
|
59
59
|
"drizzle-orm": "0.44.6",
|
|
60
|
+
"echarts": "~5.6.0",
|
|
61
|
+
"echarts-for-react": "~3.0.2",
|
|
60
62
|
"embla-carousel-react": "^8.6.0",
|
|
61
63
|
"express": "^5.1.0",
|
|
62
64
|
"hbs": "^4.2.0",
|
|
@@ -85,6 +87,7 @@
|
|
|
85
87
|
"@types/node": "^24",
|
|
86
88
|
"@types/react": "^19",
|
|
87
89
|
"@types/react-dom": "^19",
|
|
90
|
+
"concurrently": "^9",
|
|
88
91
|
"eslint": "^9",
|
|
89
92
|
"tsc-alias": "^1.8.15",
|
|
90
93
|
"tsx": "^4",
|
|
@@ -1,11 +1 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
|
|
4
|
-
//
|
|
5
|
-
// 使用示例:
|
|
6
|
-
// export const posts = pgTable("posts", {
|
|
7
|
-
// id: serial("id").primaryKey(),
|
|
8
|
-
// title: text("title").notNull(),
|
|
9
|
-
// content: text("content"),
|
|
10
|
-
// createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
11
|
-
// });
|
|
1
|
+
/* 该文件通过 npm run gen:db-schema 自动生成*/
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import express
|
|
2
|
-
import
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import hbs from "hbs";
|
|
5
|
-
import { setup, createViewContextMiddleware } from "@lark-apaas/express-core";
|
|
6
|
-
import * as schema from "./db/schema";
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { setup } from "@lark-apaas/express-core";
|
|
7
3
|
import { registerRoutes } from "./routes/index";
|
|
8
4
|
|
|
9
5
|
const port = Number(process.env.FORCE_SERVER_PORT) || 3000;
|
|
@@ -13,26 +9,12 @@ const basePath = process.env.CLIENT_BASE_PATH || "/";
|
|
|
13
9
|
const app = express();
|
|
14
10
|
|
|
15
11
|
// Platform middleware
|
|
16
|
-
const { close } = setup(app
|
|
17
|
-
database: { schema },
|
|
18
|
-
csrf: { enabled: false },
|
|
19
|
-
viewContext: { enabled: false },
|
|
20
|
-
});
|
|
12
|
+
const { close } = setup(app);
|
|
21
13
|
|
|
22
14
|
// All routes under basePath
|
|
23
15
|
const router = express.Router();
|
|
24
16
|
registerRoutes(router);
|
|
25
17
|
|
|
26
|
-
const templatePath = path.resolve(__dirname, "../index.html");
|
|
27
|
-
const template = hbs.compile(fs.readFileSync(templatePath, "utf-8"));
|
|
28
|
-
|
|
29
|
-
router.get("/{*path}", createViewContextMiddleware(), (_req: Request, res: Response) => {
|
|
30
|
-
const html = template({
|
|
31
|
-
...res.locals,
|
|
32
|
-
});
|
|
33
|
-
res.type("html").send(html);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
18
|
app.use(basePath, router);
|
|
37
19
|
|
|
38
20
|
const server = app.listen(port, host, () => {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { Router } from "express";
|
|
2
|
+
import { registerViewRoute } from "./view";
|
|
2
3
|
// import postsRouter from "./posts";
|
|
3
4
|
|
|
4
|
-
export function registerRoutes(
|
|
5
|
+
export function registerRoutes(router: Router) {
|
|
5
6
|
// 在此注册 API 路由
|
|
6
7
|
//
|
|
7
8
|
// 使用示例:
|
|
8
|
-
//
|
|
9
|
+
// router.use("/api/posts", postsRouter);
|
|
10
|
+
|
|
11
|
+
// HTML 页面渲染(catch-all,必须放在最后)
|
|
12
|
+
registerViewRoute(router);
|
|
9
13
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import hbs from "hbs";
|
|
4
|
+
import type { Request, Response, Router } from "express";
|
|
5
|
+
|
|
6
|
+
const templatePath = path.resolve(__dirname, "../../index.html");
|
|
7
|
+
const template = hbs.compile(fs.readFileSync(templatePath, "utf-8"));
|
|
8
|
+
|
|
9
|
+
export function registerViewRoute(router: Router) {
|
|
10
|
+
router.get("/{*path}", (_req: Request, res: Response) => {
|
|
11
|
+
const html = template({
|
|
12
|
+
...res.locals,
|
|
13
|
+
});
|
|
14
|
+
res.type("html").send(html);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
"rootDir": ".",
|
|
6
6
|
"module": "commonjs",
|
|
7
7
|
"moduleResolution": "node",
|
|
8
|
-
"verbatimModuleSyntax": false,
|
|
9
8
|
"esModuleInterop": true,
|
|
10
9
|
"noEmit": false,
|
|
11
10
|
"allowImportingTsExtensions": false,
|
|
12
|
-
"noUnusedParameters": false,
|
|
13
11
|
"baseUrl": ".",
|
|
14
12
|
"paths": {
|
|
15
13
|
"@shared/*": ["./shared/*"]
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
```
|
|
18
18
|
client/src/
|
|
19
|
-
├──
|
|
19
|
+
├── main.tsx # 入口(Provider 层级 + 样式引入,勿修改)
|
|
20
|
+
├── app.tsx # 路由配置
|
|
20
21
|
├── index.css # 全局样式 + 主题变量
|
|
21
22
|
├── components/ # 基础 UI 组件(禁止存放业务组件)
|
|
22
23
|
│ └── ui/ # shadcn/ui 内置组件(勿手动修改)
|
|
@@ -94,28 +95,26 @@ export default function DashboardPage() {
|
|
|
94
95
|
**替换后的路由示例:**
|
|
95
96
|
|
|
96
97
|
```tsx
|
|
97
|
-
import {
|
|
98
|
+
import { Routes, Route } from "react-router-dom";
|
|
98
99
|
import { Layout } from "@/components/layout";
|
|
99
100
|
import DashboardPage from "@/pages/DashboardPage/DashboardPage";
|
|
100
101
|
import NotFoundPage from "@/pages/NotFoundPage/NotFoundPage";
|
|
101
102
|
|
|
102
|
-
declare const __APP_BASE_PATH__: string;
|
|
103
|
-
|
|
104
103
|
export default function App() {
|
|
105
104
|
return (
|
|
106
|
-
<
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
</Routes>
|
|
114
|
-
</BrowserRouter>
|
|
105
|
+
<Routes>
|
|
106
|
+
<Route element={<Layout />}>
|
|
107
|
+
{/* 将 index 路由指向真实的业务首页 */}
|
|
108
|
+
<Route index element={<DashboardPage />} />
|
|
109
|
+
<Route path="*" element={<NotFoundPage />} />
|
|
110
|
+
</Route>
|
|
111
|
+
</Routes>
|
|
115
112
|
);
|
|
116
113
|
}
|
|
117
114
|
```
|
|
118
115
|
|
|
116
|
+
> **注意:** `BrowserRouter` 已在 `main.tsx` 中统一配置,`app.tsx` 中**禁止**再包裹 `BrowserRouter`,否则会报嵌套 Router 错误。
|
|
117
|
+
|
|
119
118
|
**新增页面步骤:**
|
|
120
119
|
|
|
121
120
|
1. 在 `client/src/pages/` 下新建页面目录(如 `SettingsPage`)和 `SettingsPage.tsx`
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Routes, Route } from "react-router-dom";
|
|
2
2
|
import { Layout } from "@/components/layout";
|
|
3
3
|
import HomePage from "@/pages/HomePage/HomePage";
|
|
4
4
|
import NotFoundPage from "@/pages/NotFoundPage/NotFoundPage";
|
|
5
5
|
|
|
6
6
|
export default function App() {
|
|
7
7
|
return (
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
<Route element={<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
</Routes>
|
|
15
|
-
</BrowserRouter>
|
|
8
|
+
<Routes>
|
|
9
|
+
<Route element={<Layout />}>
|
|
10
|
+
<Route index element={<HomePage />} />
|
|
11
|
+
<Route path="*" element={<NotFoundPage />} />
|
|
12
|
+
</Route>
|
|
13
|
+
</Routes>
|
|
16
14
|
);
|
|
17
15
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { StrictMode } from "react";
|
|
2
2
|
import { createRoot } from "react-dom/client";
|
|
3
|
+
import { BrowserRouter } from "react-router-dom";
|
|
3
4
|
import { AppContainer } from "@lark-apaas/client-toolkit-lite";
|
|
4
|
-
import "@lark-apaas/client-toolkit-lite/styles.css";
|
|
5
5
|
import App from "./app";
|
|
6
6
|
import "./index.css";
|
|
7
7
|
|
|
8
8
|
createRoot(document.getElementById("root")!).render(
|
|
9
9
|
<StrictMode>
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
10
|
+
<BrowserRouter basename={process.env.CLIENT_BASE_PATH || "/"}>
|
|
11
|
+
<AppContainer>
|
|
12
|
+
<App />
|
|
13
|
+
</AppContainer>
|
|
14
|
+
</BrowserRouter>
|
|
13
15
|
</StrictMode>,
|
|
14
16
|
);
|
|
@@ -10,13 +10,10 @@
|
|
|
10
10
|
]
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
-
"dev": "vite",
|
|
14
13
|
"build": "bash scripts/build.sh",
|
|
15
|
-
"typecheck": "
|
|
16
|
-
"
|
|
17
|
-
"lint": "
|
|
18
|
-
"lint:client": "eslint client/src",
|
|
19
|
-
"preview": "vite preview"
|
|
14
|
+
"typecheck": "tsc -p tsconfig.app.json",
|
|
15
|
+
"lint": "concurrently npm:typecheck npm:lint:eslint",
|
|
16
|
+
"lint:eslint": "eslint client/src"
|
|
20
17
|
},
|
|
21
18
|
"dependencies": {
|
|
22
19
|
"@hookform/resolvers": "^5.2.2",
|
|
@@ -46,7 +43,7 @@
|
|
|
46
43
|
"@radix-ui/react-toggle": "^1.1.9",
|
|
47
44
|
"@radix-ui/react-toggle-group": "^1.1.10",
|
|
48
45
|
"@radix-ui/react-tooltip": "^1.1.18",
|
|
49
|
-
"@lark-apaas/client-toolkit-lite": "^1.
|
|
46
|
+
"@lark-apaas/client-toolkit-lite": "^1.1.0",
|
|
50
47
|
"@formkit/auto-animate": "^0.9.0",
|
|
51
48
|
"framer-motion": "^12.38.0",
|
|
52
49
|
"class-variance-authority": "^0.7.1",
|
|
@@ -80,6 +77,7 @@
|
|
|
80
77
|
"@types/react-dom": "^19",
|
|
81
78
|
"@lark-apaas/coding-presets": "^1.0.0",
|
|
82
79
|
"@lark-apaas/coding-vite-preset": "^1.0.0",
|
|
80
|
+
"concurrently": "^9",
|
|
83
81
|
"eslint": "^9",
|
|
84
82
|
"eslint-plugin-react-hooks": "^7",
|
|
85
83
|
"eslint-plugin-react-refresh": "^0.5",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/openclaw
|