@robsun/create-keystone-app 0.1.13 → 0.1.15
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/README.md +19 -10
- package/bin/create-keystone-app.js +199 -60
- package/package.json +1 -1
- package/template/.husky/pre-commit +4 -0
- package/template/.lintstagedrc.json +5 -0
- package/template/.prettierrc +9 -0
- package/template/README.md +45 -23
- package/template/apps/server/.air.toml +44 -0
- package/template/apps/server/README.md +27 -0
- package/template/apps/server/cmd/server/main.go +213 -0
- package/template/apps/server/config.yaml +51 -0
- package/template/apps/server/docs/docs.go +34 -0
- package/template/apps/server/go.mod +13 -0
- package/template/apps/server/go.sum +72 -0
- package/template/apps/server/internal/app/routes/module_routes.go +16 -0
- package/template/apps/server/internal/app/routes/routes.go +226 -0
- package/template/apps/server/internal/app/startup/startup.go +74 -0
- package/template/apps/server/internal/frontend/dist/.gitkeep +1 -0
- package/template/apps/server/internal/frontend/embed.go +28 -0
- package/template/apps/server/internal/frontend/handler.go +122 -0
- package/template/apps/server/internal/{demo/demo.go → modules/demo/handlers.go} +4 -1
- package/template/apps/server/internal/modules/demo/module.go +55 -0
- package/template/apps/server/internal/modules/manifest.go +11 -0
- package/template/apps/server/internal/modules/registry.go +145 -0
- package/template/apps/web/.env.example +3 -0
- package/template/apps/web/README.md +29 -0
- package/template/apps/web/eslint.config.js +35 -0
- package/template/apps/web/package.json +27 -10
- package/template/apps/web/postcss.config.js +6 -0
- package/template/apps/web/src/index.css +3 -0
- package/template/apps/web/src/main.tsx +1 -0
- package/template/apps/web/src/modules/demo/help/overview.md +12 -0
- package/template/apps/web/src/modules/demo/routes.tsx +2 -0
- package/template/apps/web/tailwind.config.js +18 -0
- package/template/apps/web/tests/setup.ts +37 -0
- package/template/apps/web/tsconfig.app.json +3 -3
- package/template/apps/web/vite.config.ts +28 -2
- package/template/docker-compose.yml +45 -0
- package/template/docs/CODE_STYLE.md +34 -0
- package/template/docs/CONVENTIONS.md +62 -88
- package/template/package.json +15 -3
- package/template/scripts/build.bat +133 -0
- package/template/scripts/build.js +25 -0
- package/template/scripts/build.sh +99 -0
- package/template/scripts/clean.bat +35 -0
- package/template/scripts/clean.js +25 -0
- package/template/scripts/clean.sh +34 -0
- package/template/scripts/dev.bat +82 -0
- package/template/scripts/dev.js +25 -0
- package/template/scripts/dev.sh +88 -0
- package/template/scripts/test.bat +86 -0
- package/template/scripts/test.js +25 -0
- package/template/scripts/test.sh +86 -0
- package/template/apps/server/main.go +0 -28
- /package/template/{config.yaml → apps/server/config.example.yaml} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Web Shell
|
|
2
|
+
|
|
3
|
+
## 定位
|
|
4
|
+
- 负责渲染平台 UI,业务模块统一放在 `src/modules/*`。
|
|
5
|
+
- 平台核心能力由 `@robsun/keystone-web-core` 提供。
|
|
6
|
+
|
|
7
|
+
## 入口
|
|
8
|
+
- `src/main.tsx`:应用入口。
|
|
9
|
+
- `src/app.config.ts`:品牌与模块启用配置。
|
|
10
|
+
- `src/modules/<module>/index.ts`:模块注册入口。
|
|
11
|
+
- `apps/web/.env.example`:前端环境变量模板(Vite 读取)。
|
|
12
|
+
|
|
13
|
+
## 新建模块
|
|
14
|
+
1) 创建目录:`src/modules/<module>`。
|
|
15
|
+
2) 在 `index.ts` 调用 `registerModule` 注册路由。
|
|
16
|
+
3) 在 `routes.tsx` 配置菜单、权限、帮助文档。
|
|
17
|
+
4) 在 `app.config.ts` 的 `modules.enabled` 启用模块。
|
|
18
|
+
|
|
19
|
+
## 帮助文档
|
|
20
|
+
- 放在 `src/modules/<module>/help/**`。
|
|
21
|
+
- `helpKey` 与路由 `handle.helpKey` 保持一致。
|
|
22
|
+
|
|
23
|
+
## 常用命令
|
|
24
|
+
- 开发:`pnpm web:dev`
|
|
25
|
+
- Lint:`pnpm -C apps/web lint`
|
|
26
|
+
- 测试:`pnpm -C apps/web test`
|
|
27
|
+
|
|
28
|
+
## 嵌入式构建
|
|
29
|
+
- `pnpm build` 会将产物写入 `apps/server/internal/frontend/dist`。
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks'
|
|
4
|
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
5
|
+
import tseslint from 'typescript-eslint'
|
|
6
|
+
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
7
|
+
|
|
8
|
+
export default defineConfig([
|
|
9
|
+
globalIgnores([
|
|
10
|
+
'dist',
|
|
11
|
+
'build',
|
|
12
|
+
'*.min.js',
|
|
13
|
+
'coverage',
|
|
14
|
+
'node_modules',
|
|
15
|
+
'.turbo',
|
|
16
|
+
'.pnpm-store',
|
|
17
|
+
'apps/server/tmp',
|
|
18
|
+
'data',
|
|
19
|
+
'*.log',
|
|
20
|
+
'.env*',
|
|
21
|
+
]),
|
|
22
|
+
{
|
|
23
|
+
files: ['**/*.{ts,tsx}'],
|
|
24
|
+
extends: [
|
|
25
|
+
js.configs.recommended,
|
|
26
|
+
tseslint.configs.recommended,
|
|
27
|
+
reactHooks.configs.flat.recommended,
|
|
28
|
+
reactRefresh.configs.vite,
|
|
29
|
+
],
|
|
30
|
+
languageOptions: {
|
|
31
|
+
ecmaVersion: 2020,
|
|
32
|
+
globals: globals.browser,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
])
|
|
@@ -6,28 +6,45 @@
|
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite",
|
|
8
8
|
"build": "vite build",
|
|
9
|
-
"
|
|
9
|
+
"build:strict": "tsc -b && vite build",
|
|
10
|
+
"lint": "eslint .",
|
|
11
|
+
"preview": "vite preview",
|
|
12
|
+
"lint:fix": "eslint . --fix",
|
|
13
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
14
|
+
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"test": "vitest"
|
|
10
17
|
},
|
|
11
18
|
"dependencies": {
|
|
12
19
|
"@ant-design/icons": "^6.1.0",
|
|
13
20
|
"@robsun/keystone-web-core": "0.1.7",
|
|
14
21
|
"antd": "^6.0.1",
|
|
15
|
-
"cookie": "^1.1.1",
|
|
16
22
|
"dayjs": "^1.11.19",
|
|
17
|
-
"debug": "^4.4.3",
|
|
18
|
-
"extend": "^3.0.2",
|
|
19
23
|
"react": "^19.2.0",
|
|
20
24
|
"react-dom": "^19.2.0",
|
|
21
|
-
"react-router": "^7.10.1"
|
|
22
|
-
"react-router-dom": "^7.10.1",
|
|
23
|
-
"style-to-js": "^1.1.21"
|
|
25
|
+
"react-router-dom": "^7.10.1"
|
|
24
26
|
},
|
|
25
27
|
"devDependencies": {
|
|
28
|
+
"@eslint/js": "^9.39.1",
|
|
29
|
+
"@tailwindcss/postcss": "^4.1.17",
|
|
30
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
31
|
+
"@testing-library/react": "^16.2.0",
|
|
32
|
+
"@testing-library/user-event": "^14.6.1",
|
|
26
33
|
"@types/node": "^24.10.1",
|
|
27
34
|
"@types/react": "^19.2.5",
|
|
28
35
|
"@types/react-dom": "^19.2.3",
|
|
36
|
+
"@vitejs/plugin-react": "^5.1.1",
|
|
37
|
+
"autoprefixer": "^10.4.22",
|
|
38
|
+
"eslint": "^9.39.1",
|
|
39
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
40
|
+
"eslint-plugin-react-refresh": "^0.4.24",
|
|
41
|
+
"globals": "^16.5.0",
|
|
42
|
+
"jsdom": "^24.1.0",
|
|
43
|
+
"postcss": "^8.5.6",
|
|
44
|
+
"tailwindcss": "^4.1.17",
|
|
29
45
|
"typescript": "~5.9.3",
|
|
30
|
-
"
|
|
46
|
+
"typescript-eslint": "^8.46.4",
|
|
47
|
+
"vite": "^7.2.4",
|
|
48
|
+
"vitest": "^2.1.4"
|
|
31
49
|
}
|
|
32
|
-
}
|
|
33
|
-
|
|
50
|
+
}
|
|
@@ -4,6 +4,7 @@ import dayjs from 'dayjs'
|
|
|
4
4
|
import { KeystoneApp, getKeystoneConfig, setKeystoneConfig } from '@robsun/keystone-web-core'
|
|
5
5
|
import { appConfig } from './app.config'
|
|
6
6
|
import '@robsun/keystone-web-core/styles/keystone.css'
|
|
7
|
+
import './index.css'
|
|
7
8
|
import './modules/demo'
|
|
8
9
|
|
|
9
10
|
setKeystoneConfig(appConfig)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
helpKey: "demo/tasks"
|
|
3
|
+
title: "Demo Tasks"
|
|
4
|
+
description: "Manage sample tasks in the scaffold."
|
|
5
|
+
category: "demo"
|
|
6
|
+
tags: ["demo", "tasks"]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Demo Tasks
|
|
10
|
+
|
|
11
|
+
Use this module as a reference for list + create + update flows. Replace it
|
|
12
|
+
with real business logic when you build new modules.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
export default {
|
|
3
|
+
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
|
4
|
+
corePlugins: {
|
|
5
|
+
preflight: false,
|
|
6
|
+
},
|
|
7
|
+
theme: {
|
|
8
|
+
extend: {
|
|
9
|
+
colors: {
|
|
10
|
+
primary: '#1890ff',
|
|
11
|
+
success: '#52c41a',
|
|
12
|
+
warning: '#faad14',
|
|
13
|
+
error: '#ff4d4f',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
plugins: [],
|
|
18
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
window.matchMedia = vi.fn().mockImplementation((query: string) => ({
|
|
5
|
+
matches: false,
|
|
6
|
+
media: query,
|
|
7
|
+
onchange: null,
|
|
8
|
+
addListener: vi.fn(),
|
|
9
|
+
removeListener: vi.fn(),
|
|
10
|
+
addEventListener: vi.fn(),
|
|
11
|
+
removeEventListener: vi.fn(),
|
|
12
|
+
dispatchEvent: vi.fn(),
|
|
13
|
+
}))
|
|
14
|
+
|
|
15
|
+
if (!('ResizeObserver' in window)) {
|
|
16
|
+
class ResizeObserver {
|
|
17
|
+
observe() {}
|
|
18
|
+
unobserve() {}
|
|
19
|
+
disconnect() {}
|
|
20
|
+
}
|
|
21
|
+
// @ts-expect-error test shim
|
|
22
|
+
window.ResizeObserver = ResizeObserver
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const originalGetComputedStyle = window.getComputedStyle
|
|
26
|
+
window.getComputedStyle = (elt: Element) => {
|
|
27
|
+
const style = originalGetComputedStyle
|
|
28
|
+
? originalGetComputedStyle(elt)
|
|
29
|
+
: ({} as CSSStyleDeclaration)
|
|
30
|
+
return {
|
|
31
|
+
...style,
|
|
32
|
+
getPropertyValue: (prop: string) =>
|
|
33
|
+
style?.getPropertyValue ? style.getPropertyValue(prop) : '',
|
|
34
|
+
} as CSSStyleDeclaration
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export {}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"useDefineForClassFields": true,
|
|
6
6
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
7
|
"module": "ESNext",
|
|
8
|
-
"types": ["vite/client"],
|
|
8
|
+
"types": ["vite/client", "vitest/globals", "@testing-library/jest-dom"],
|
|
9
9
|
"skipLibCheck": true,
|
|
10
10
|
"moduleResolution": "bundler",
|
|
11
11
|
"allowImportingTsExtensions": true,
|
|
@@ -19,5 +19,5 @@
|
|
|
19
19
|
"noFallthroughCasesInSwitch": true,
|
|
20
20
|
"noUncheckedSideEffectImports": true
|
|
21
21
|
},
|
|
22
|
-
"include": ["src"]
|
|
23
|
-
}
|
|
22
|
+
"include": ["src", "tests"]
|
|
23
|
+
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
/// <reference types="vitest" />
|
|
1
2
|
import { createKeystoneViteConfig } from '@robsun/keystone-web-core/vite'
|
|
3
|
+
import path from 'path'
|
|
2
4
|
|
|
3
5
|
export default createKeystoneViteConfig({
|
|
6
|
+
helpDir: path.resolve(__dirname, './src/modules'),
|
|
7
|
+
resolve: {
|
|
8
|
+
alias: {
|
|
9
|
+
'@app': path.resolve(__dirname, './src'),
|
|
10
|
+
},
|
|
11
|
+
},
|
|
4
12
|
server: {
|
|
5
13
|
port: 3000,
|
|
6
14
|
proxy: {
|
|
@@ -10,5 +18,23 @@ export default createKeystoneViteConfig({
|
|
|
10
18
|
},
|
|
11
19
|
},
|
|
12
20
|
},
|
|
13
|
-
|
|
14
|
-
|
|
21
|
+
build: {
|
|
22
|
+
outDir: path.resolve(__dirname, '../server/internal/frontend/dist'),
|
|
23
|
+
emptyOutDir: false,
|
|
24
|
+
sourcemap: true,
|
|
25
|
+
rollupOptions: {
|
|
26
|
+
output: {
|
|
27
|
+
manualChunks: {
|
|
28
|
+
vendor: ['react', 'react-dom'],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
test: {
|
|
34
|
+
globals: true,
|
|
35
|
+
environment: 'jsdom',
|
|
36
|
+
setupFiles: './tests/setup.ts',
|
|
37
|
+
include: ['tests/**/*.{test,spec}.{ts,tsx}'],
|
|
38
|
+
exclude: ['**/node_modules/**', 'tests/e2e/**'],
|
|
39
|
+
},
|
|
40
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
postgres:
|
|
5
|
+
image: postgres:17
|
|
6
|
+
container_name: keystone-postgres
|
|
7
|
+
restart: unless-stopped
|
|
8
|
+
ports:
|
|
9
|
+
- '5432:5432'
|
|
10
|
+
environment:
|
|
11
|
+
POSTGRES_DB: keystone
|
|
12
|
+
POSTGRES_USER: keystone
|
|
13
|
+
POSTGRES_PASSWORD: keystone
|
|
14
|
+
volumes:
|
|
15
|
+
- pgdata:/var/lib/postgresql/data
|
|
16
|
+
healthcheck:
|
|
17
|
+
test: ['CMD-SHELL', 'pg_isready -U keystone -d keystone']
|
|
18
|
+
interval: 10s
|
|
19
|
+
timeout: 5s
|
|
20
|
+
retries: 5
|
|
21
|
+
|
|
22
|
+
# Redis (optional, uncomment when needed)
|
|
23
|
+
# redis:
|
|
24
|
+
# image: redis:7-alpine
|
|
25
|
+
# container_name: keystone-redis
|
|
26
|
+
# restart: unless-stopped
|
|
27
|
+
# ports:
|
|
28
|
+
# - '6379:6379'
|
|
29
|
+
# volumes:
|
|
30
|
+
# - redisdata:/data
|
|
31
|
+
# healthcheck:
|
|
32
|
+
# test: ['CMD', 'redis-cli', 'ping']
|
|
33
|
+
# interval: 10s
|
|
34
|
+
# timeout: 5s
|
|
35
|
+
# retries: 5
|
|
36
|
+
|
|
37
|
+
volumes:
|
|
38
|
+
pgdata:
|
|
39
|
+
driver: local
|
|
40
|
+
# redisdata:
|
|
41
|
+
# driver: local
|
|
42
|
+
|
|
43
|
+
networks:
|
|
44
|
+
default:
|
|
45
|
+
name: keystone-network
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# 编码规范
|
|
2
|
+
|
|
3
|
+
## 总体原则
|
|
4
|
+
- 依赖自动格式化工具,不做手工对齐。
|
|
5
|
+
- 代码优先可读性,遵循项目约定的目录与命名规则。
|
|
6
|
+
- Web 端使用 ESLint + Prettier;Go 端使用 gofmt。
|
|
7
|
+
|
|
8
|
+
## TypeScript / React
|
|
9
|
+
- 组件名用 `PascalCase`,hooks 用 `useXxx`。
|
|
10
|
+
- 变量/函数用 `camelCase`,常量用 `UPPER_SNAKE_CASE`。
|
|
11
|
+
- 模块目录固定在 `apps/web/src/modules/<module>`。
|
|
12
|
+
- 路由/菜单/权限统一写在 `routes.tsx` 的 `handle` 元信息里。
|
|
13
|
+
- API 请求集中在 `services/`,类型定义放在 `types.ts`。
|
|
14
|
+
- 使用 Prettier 默认风格(2 空格、单引号、无分号)。
|
|
15
|
+
|
|
16
|
+
## CSS / UI
|
|
17
|
+
- 全局样式入口:`apps/web/src/index.css`。
|
|
18
|
+
- 组件级样式靠近组件放置,避免跨模块引用。
|
|
19
|
+
|
|
20
|
+
## Go
|
|
21
|
+
- 文件名用 `snake_case.go`,package 名小写。
|
|
22
|
+
- 统一使用 `gofmt` 格式化代码。
|
|
23
|
+
- 错误处理使用 `fmt.Errorf("...: %w", err)` 包裹。
|
|
24
|
+
- 新模块放在 `apps/server/internal/modules/<module>`,实现 `Module` 接口。
|
|
25
|
+
|
|
26
|
+
## 常用命令
|
|
27
|
+
```bash
|
|
28
|
+
# Web 端格式化与检查
|
|
29
|
+
pnpm format
|
|
30
|
+
pnpm -C apps/web lint
|
|
31
|
+
|
|
32
|
+
# Go 端格式化
|
|
33
|
+
gofmt -w apps/server
|
|
34
|
+
```
|
|
@@ -1,33 +1,43 @@
|
|
|
1
|
-
# Keystone App Conventions
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- `apps/
|
|
11
|
-
- `apps/
|
|
12
|
-
- `
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
# Keystone App Conventions
|
|
2
|
+
|
|
3
|
+
## 目标
|
|
4
|
+
- Web/Server 模块结构统一,便于复用与维护。
|
|
5
|
+
- 路由、菜单、权限、帮助文档集中管理。
|
|
6
|
+
- 约定优于配置,降低上手成本。
|
|
7
|
+
编码规范请参考 `docs/CODE_STYLE.md`。
|
|
8
|
+
|
|
9
|
+
## 项目结构
|
|
10
|
+
- `apps/web/`:React + Vite 壳与业务模块。
|
|
11
|
+
- `apps/server/`:Go 服务端(`cmd/server` 启动,`internal/app` 编排)。
|
|
12
|
+
- `apps/server/internal/modules/`:后端模块注册与实现。
|
|
13
|
+
- `apps/server/internal/frontend/`:嵌入式前端输出目录(`dist/`)。
|
|
14
|
+
- `scripts/`:跨平台 dev/build/test/clean。
|
|
15
|
+
- `apps/server/config.yaml`:服务端配置(支持环境变量覆盖,`docker-compose.yml` 提供依赖)。
|
|
16
|
+
提示:更细节的入口说明见 `apps/web/README.md` 与 `apps/server/README.md`。
|
|
17
|
+
|
|
18
|
+
## 开发流程
|
|
19
|
+
- `pnpm dev`:执行 `scripts/dev.*`,检查环境并启动 Air + Vite。
|
|
20
|
+
- `apps/server/.air.toml`:后端热重载配置。
|
|
21
|
+
- `pnpm server:dev` 仅启动后端;`pnpm web:dev` 仅启动前端。
|
|
22
|
+
|
|
23
|
+
## 配置与环境
|
|
24
|
+
- `apps/server/config.yaml` 为默认配置;`apps/server/config.example.yaml` 用作模板。
|
|
25
|
+
- `apps/server/config.yaml` 供 `go run`/Air 使用。
|
|
26
|
+
- 前端环境变量参考 `apps/web/.env.example`(Vite 会读取)。
|
|
27
|
+
- 后端环境变量需通过 Shell/工具注入(服务端不读取 `.env`)。
|
|
28
|
+
- 本地数据库默认 SQLite:`apps/server/data/keystone-local.db`。
|
|
29
|
+
- 本地存储目录:`apps/server/storage/`(当 `storage.driver=local`)。
|
|
30
|
+
|
|
31
|
+
## 前端模块规范
|
|
32
|
+
- 模块目录:`apps/web/src/modules/<module>`。
|
|
33
|
+
- 在 `index.ts` 注册模块:
|
|
23
34
|
```ts
|
|
24
35
|
import { registerModule } from '@robsun/keystone-web-core'
|
|
25
36
|
import { orderRoutes } from './routes'
|
|
26
37
|
|
|
27
38
|
registerModule({ name: 'orders', routes: orderRoutes })
|
|
28
39
|
```
|
|
29
|
-
|
|
30
|
-
Example route meta:
|
|
40
|
+
- 在 `routes.tsx` 配置菜单/权限/帮助:
|
|
31
41
|
```tsx
|
|
32
42
|
{
|
|
33
43
|
path: 'orders',
|
|
@@ -40,62 +50,15 @@ Example route meta:
|
|
|
40
50
|
},
|
|
41
51
|
}
|
|
42
52
|
```
|
|
53
|
+
- 页面放 `pages/`,组件放 `components/`,API 放 `services/`。
|
|
43
54
|
|
|
44
|
-
##
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
- Use `handle.menu.order` to control menu ordering when needed.
|
|
49
|
-
|
|
50
|
-
## Help Docs
|
|
51
|
-
- Store help markdown under `apps/web/src/modules/<module>/help/**`.
|
|
52
|
-
- Use frontmatter keys: `helpKey`, `title`, `description`, `category`, `tags`, `relatedKeys`.
|
|
53
|
-
- Route `handle.helpKey` must match the markdown `helpKey`.
|
|
54
|
-
- Enable app help by pointing Vite to your help folder (`helpDir` in `vite.config.ts`).
|
|
55
|
-
|
|
56
|
-
Frontmatter example:
|
|
57
|
-
```md
|
|
58
|
-
---
|
|
59
|
-
helpKey: "inventory/warehouses"
|
|
60
|
-
title: "Warehouse Management"
|
|
61
|
-
description: "Manage warehouses and locations."
|
|
62
|
-
category: "inventory"
|
|
63
|
-
tags: ["warehouse", "location"]
|
|
64
|
-
relatedKeys: ["inventory/stock"]
|
|
65
|
-
---
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## API and Services
|
|
69
|
-
- Use `api` from `@robsun/keystone-web-core` for HTTP.
|
|
70
|
-
- If your API base path changes, call `setApiBaseUrl` once at startup.
|
|
71
|
-
- Keep API calls in `services/*.ts`, return typed data, and let pages handle UI state.
|
|
72
|
-
- For paginated endpoints, map `page`/`page_size` (server) to `page`/`pageSize` (web).
|
|
73
|
-
|
|
74
|
-
Example service:
|
|
75
|
-
```ts
|
|
76
|
-
import { api, type ApiResponse, type PaginatedData } from '@robsun/keystone-web-core'
|
|
77
|
-
|
|
78
|
-
export async function listItems(page = 1, pageSize = 20) {
|
|
79
|
-
const { data } = await api.get<ApiResponse<PaginatedData<Item>>>('/items', {
|
|
80
|
-
params: { page, page_size: pageSize },
|
|
81
|
-
})
|
|
82
|
-
return data.data
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## State
|
|
87
|
-
- Use local state for single pages.
|
|
88
|
-
- Use zustand stores for shared state or cross-page flows.
|
|
89
|
-
- Store shape should include `loading`, `error`, `filters`, and a `fetch` method.
|
|
90
|
-
|
|
91
|
-
## Naming
|
|
92
|
-
- Page components: `*Page.tsx` (e.g. `InventoryListPage.tsx`).
|
|
93
|
-
- API modules: `services/api.ts` or `services/<feature>.ts`.
|
|
94
|
-
- Store modules: `stores/*Store.ts` or `stores/use*Store.ts`.
|
|
95
|
-
- Domain types: `types.ts` (export interfaces and enums).
|
|
55
|
+
## Help 文档
|
|
56
|
+
- 文档放在 `apps/web/src/modules/<module>/help/**`。
|
|
57
|
+
- `helpKey` 与路由 `handle.helpKey` 保持一致。
|
|
58
|
+
- Vite 已配置 `helpDir` 扫描 `src/modules`。
|
|
96
59
|
|
|
97
|
-
##
|
|
98
|
-
|
|
60
|
+
## 后端模块规范
|
|
61
|
+
推荐结构:
|
|
99
62
|
```
|
|
100
63
|
apps/server/internal/modules/<module>/
|
|
101
64
|
module.go
|
|
@@ -107,7 +70,7 @@ apps/server/internal/modules/<module>/
|
|
|
107
70
|
bootstrap/seeds/
|
|
108
71
|
```
|
|
109
72
|
|
|
110
|
-
|
|
73
|
+
模块接口:
|
|
111
74
|
```go
|
|
112
75
|
type Module interface {
|
|
113
76
|
Name() string
|
|
@@ -120,15 +83,26 @@ type Module interface {
|
|
|
120
83
|
}
|
|
121
84
|
```
|
|
122
85
|
|
|
123
|
-
|
|
124
|
-
|
|
86
|
+
## 模块注册与启用
|
|
87
|
+
- 在 `apps/server/internal/modules/manifest.go` 注册模块。
|
|
88
|
+
- 在 `apps/server/config.yaml` 的 `modules.enabled` 启用模块。
|
|
89
|
+
- Web 端 `app.config.ts` 保持同名启用列表。
|
|
90
|
+
|
|
91
|
+
## 迁移 / 种子 / 权限
|
|
92
|
+
- `startup.InitializeDatabase` 统一执行:核心迁移 + 模块迁移 + 种子 + 权限注册。
|
|
93
|
+
- 模块权限通过 `RegisterPermissions` 注入。
|
|
94
|
+
|
|
95
|
+
## 任务与队列
|
|
96
|
+
- Worker 在 `cmd/server/main.go` 启动。
|
|
97
|
+
- 模块通过 `RegisterJobs` 注册任务处理器。
|
|
98
|
+
- 队列配置在 `apps/server/config.yaml` 的 `queue` 部分。
|
|
125
99
|
|
|
126
|
-
##
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
100
|
+
## 嵌入式前端
|
|
101
|
+
- Vite `build.outDir` 指向 `apps/server/internal/frontend/dist`。
|
|
102
|
+
- Go 通过 `//go:embed` 嵌入前端产物。
|
|
103
|
+
- 保留 `apps/server/internal/frontend/dist/.gitkeep`,避免 embed 找不到目录。
|
|
130
104
|
|
|
131
105
|
## Testing
|
|
132
|
-
- Web
|
|
133
|
-
- Server
|
|
134
|
-
-
|
|
106
|
+
- Web:`pnpm -C apps/web test`(Vitest)。
|
|
107
|
+
- Server:`go -C apps/server test ./...`。
|
|
108
|
+
- 全量:`pnpm test`。
|
package/template/package.json
CHANGED
|
@@ -3,9 +3,21 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"dev": "
|
|
6
|
+
"dev": "node scripts/dev.js",
|
|
7
|
+
"build": "node scripts/build.js",
|
|
8
|
+
"build:web": "pnpm --filter web build",
|
|
9
|
+
"test": "node scripts/test.js",
|
|
10
|
+
"lint": "pnpm --filter web lint",
|
|
11
|
+
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
|
|
12
|
+
"clean": "node scripts/clean.js",
|
|
7
13
|
"web:dev": "pnpm -C apps/web dev",
|
|
8
|
-
"server:dev": "go -C apps/server run
|
|
14
|
+
"server:dev": "go -C apps/server run ./cmd/server",
|
|
15
|
+
"prepare": "husky"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"husky": "^9.1.7",
|
|
19
|
+
"lint-staged": "^16.2.7",
|
|
20
|
+
"prettier": "^3.7.4"
|
|
9
21
|
},
|
|
10
22
|
"packageManager": "pnpm@9.15.0"
|
|
11
|
-
}
|
|
23
|
+
}
|