create-dwy 0.8.0 → 0.9.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.
- package/package.json +1 -1
- package/src/create.js +50 -3
- package/src/utils.js +4 -0
- package/templates/claude-global/CLAUDE.md +81 -6
- package/templates/claude-global/rules/dwy-postgres.md +89 -0
- package/templates/claude-global/rules/dwy-python-backend.md +492 -0
- package/templates/claude-global/rules/dwy-python-core.md +94 -634
- package/templates/claude-global/rules/dwy-python-testing.md +115 -13
- package/templates/claude-global/rules/dwy-redis.md +88 -0
- package/templates/claude-global/rules/dwy-vue-core.md +341 -645
- package/templates/claude-global/rules/dwy-vue-pinia.md +47 -34
- package/templates/claude-global/rules/dwy-vue-testing.md +120 -55
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +1 -1
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +1 -1
- package/templates/claude-global/skills/dwy-dolphindb/SKILL.md +22 -15
- package/templates/claude-global/skills/dwy-dolphindb/evals/evals.json +23 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/dolphindb-rules.md +100 -1
- package/templates/claude-global/skills/dwy-ekit/SKILL.md +142 -276
- package/templates/claude-global/skills/dwy-eui/SKILL.md +264 -182
- package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +117 -129
- package/templates/claude-global/skills/dwy-git-commit/SKILL.md +125 -0
- package/templates/claude-global/skills/dwy-publish/SKILL.md +1 -1
- package/templates/claude-global/{rules/dwy-tech-stack.md → skills/dwy-tech-stack/SKILL.md} +4 -3
- package/templates/project/fullstack-monorepo/CLAUDE.md.hbs +81 -0
- package/templates/project/fullstack-monorepo/backend/.python-version +1 -0
- package/templates/project/fullstack-monorepo/backend/CLAUDE.md.hbs +68 -0
- package/templates/project/fullstack-monorepo/backend/Dockerfile.dev +23 -0
- package/templates/project/fullstack-monorepo/backend/alembic/env.py.hbs +68 -0
- package/templates/project/fullstack-monorepo/backend/alembic/script.py.mako +26 -0
- package/templates/project/fullstack-monorepo/backend/alembic.ini.hbs +43 -0
- package/templates/project/fullstack-monorepo/backend/packages/dwy-shared/pyproject.toml +16 -0
- package/templates/project/fullstack-monorepo/backend/packages/dwy-shared/src/dwy_shared/__init__.py +12 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/pyproject.toml.hbs +20 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/__init__.py.hbs +40 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/config.py +9 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/models.py.hbs +17 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/protocols.py +14 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/router.py.hbs +22 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/schemas.py +21 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/service.py +11 -0
- package/templates/project/fullstack-monorepo/backend/pyproject.toml.hbs +84 -0
- package/templates/project/fullstack-monorepo/backend/src/app/__init__.py +3 -0
- package/templates/project/fullstack-monorepo/backend/src/app/config.py.hbs +21 -0
- package/templates/project/fullstack-monorepo/backend/src/app/exceptions.py +26 -0
- package/templates/project/fullstack-monorepo/backend/src/app/factories.py +16 -0
- package/templates/project/fullstack-monorepo/backend/src/app/infra/__init__.py +1 -0
- package/templates/project/fullstack-monorepo/backend/src/app/main.py.hbs +65 -0
- package/templates/project/fullstack-monorepo/backend/src/app/middleware/__init__.py +1 -0
- package/templates/project/fullstack-monorepo/backend/tests/__init__.py +0 -0
- package/templates/project/fullstack-monorepo/backend/tests/conftest.py.hbs +42 -0
- package/templates/project/fullstack-monorepo/dev.sh.hbs +83 -0
- package/templates/project/fullstack-monorepo/docker-compose.dev.yml.hbs +36 -0
- package/templates/project/fullstack-monorepo/frontend/CLAUDE.md.hbs +35 -0
- package/templates/project/fullstack-monorepo/frontend/Dockerfile +18 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/index.html.hbs +13 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/package.json.hbs +31 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/App.vue.hbs +10 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/api/client.ts.hbs +11 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/features/{{initialDomain}}/.gitkeep +0 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/index.css +12 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/main.ts +17 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/router/index.ts.hbs +24 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/stores/.gitkeep +0 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/types/.gitkeep +0 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/views/HomeView.vue.hbs +36 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/tsconfig.json +7 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/vite.config.ts.hbs +27 -0
- package/templates/project/fullstack-monorepo/frontend/nginx.conf +11 -0
- package/templates/project/fullstack-monorepo/frontend/package.json.hbs +13 -0
- package/templates/project/fullstack-monorepo/frontend/packages/core/package.json.hbs +15 -0
- package/templates/project/fullstack-monorepo/frontend/packages/core/src/index.ts +3 -0
- package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/package.json.hbs +14 -0
- package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/src/index.ts.hbs +16 -0
- package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/src/pages/IndexPage.vue.hbs +9 -0
- package/templates/project/fullstack-monorepo/frontend/packages/ui/package.json.hbs +15 -0
- package/templates/project/fullstack-monorepo/frontend/packages/ui/src/index.ts +3 -0
- package/templates/project/fullstack-monorepo/frontend/pnpm-workspace.yaml.hbs +23 -0
- package/templates/project/fullstack-monorepo/frontend/tsconfig.base.json.hbs +23 -0
- package/templates/claude-global/rules/dwy-api-design.md +0 -110
- package/templates/claude-global/rules/dwy-git-commit-convention.md +0 -82
- package/templates/claude-global/rules/dwy-git-security.md +0 -53
- package/templates/claude-global/rules/dwy-payload-limits.md +0 -186
- package/templates/claude-global/rules/dwy-prefer-open-source.md +0 -32
- package/templates/claude-global/rules/dwy-project-structure.md +0 -181
- package/templates/claude-global/rules/dwy-python-fastapi.md +0 -272
- package/templates/claude-global/rules/dwy-python-orm.md +0 -61
- package/templates/claude-global/rules/dwy-python-security.md +0 -636
- package/templates/claude-global/rules/dwy-server-security.md +0 -518
- package/templates/claude-global/rules/dwy-vue-api.md +0 -146
- package/templates/claude-global/skills/dwy-ekit/references/data.md +0 -135
- package/templates/claude-global/skills/dwy-ekit/references/form.md +0 -68
- package/templates/claude-global/skills/dwy-ekit/references/request.md +0 -183
- package/templates/claude-global/skills/dwy-ekit/references/ui.md +0 -109
- package/templates/claude-global/skills/dwy-vue-core/SKILL.md +0 -890
- /package/templates/{claude-global/commands → project/fullstack-monorepo/backend/alembic/versions}/.gitkeep +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""测试通用 fixture。
|
|
2
|
+
|
|
3
|
+
测试环境变量必须在 import app 之前设置,否则 Settings 实例化会失败。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
# ── 测试环境变量(必须在导入应用前设置)──
|
|
9
|
+
os.environ.setdefault("ENVIRONMENT", "dev")
|
|
10
|
+
os.environ.setdefault("DATABASE_URL", "sqlite+aiosqlite:///./test.db")
|
|
11
|
+
os.environ.setdefault("SECRET_KEY", "test-secret-only-for-tests")
|
|
12
|
+
os.environ.setdefault("REDIS_URL", "redis://localhost:{{portPrefix}}6379/0")
|
|
13
|
+
|
|
14
|
+
from collections.abc import AsyncGenerator # noqa: E402
|
|
15
|
+
|
|
16
|
+
import pytest_asyncio # noqa: E402
|
|
17
|
+
from httpx import ASGITransport, AsyncClient # noqa: E402
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest_asyncio.fixture(autouse=True)
|
|
21
|
+
async def setup_db() -> AsyncGenerator[None]:
|
|
22
|
+
"""每个测试用例前后创建/销毁表。"""
|
|
23
|
+
from dwyeapi.database import Base
|
|
24
|
+
|
|
25
|
+
from app.main import app # noqa: F401 -- 触发应用装配
|
|
26
|
+
from dwy_shared.database import engine # 若 dwy-shared 没有 engine,则改 import 来源
|
|
27
|
+
|
|
28
|
+
async with engine.begin() as conn:
|
|
29
|
+
await conn.run_sync(Base.metadata.create_all)
|
|
30
|
+
yield
|
|
31
|
+
async with engine.begin() as conn:
|
|
32
|
+
await conn.run_sync(Base.metadata.drop_all)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest_asyncio.fixture
|
|
36
|
+
async def client() -> AsyncGenerator[AsyncClient]:
|
|
37
|
+
"""异步 HTTP 测试客户端。"""
|
|
38
|
+
from app.main import app
|
|
39
|
+
|
|
40
|
+
transport = ASGITransport(app=app)
|
|
41
|
+
async with AsyncClient(transport=transport, base_url="http://test") as ac:
|
|
42
|
+
yield ac
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
BACKEND_PORT={{portPrefix}}8001
|
|
6
|
+
FRONTEND_PORT={{portPrefix}}5173
|
|
7
|
+
BACKEND_PID=""
|
|
8
|
+
FRONTEND_PID=""
|
|
9
|
+
|
|
10
|
+
# ── 清理函数 ──
|
|
11
|
+
|
|
12
|
+
cleanup() {
|
|
13
|
+
echo ""
|
|
14
|
+
echo "正在关闭服务..."
|
|
15
|
+
[ -n "$FRONTEND_PID" ] && kill "$FRONTEND_PID" 2>/dev/null
|
|
16
|
+
[ -n "$BACKEND_PID" ] && kill "$BACKEND_PID" 2>/dev/null
|
|
17
|
+
wait 2>/dev/null
|
|
18
|
+
echo "已关闭前后端进程。Docker 基础设施保持运行。"
|
|
19
|
+
}
|
|
20
|
+
trap cleanup EXIT
|
|
21
|
+
|
|
22
|
+
kill_port() {
|
|
23
|
+
local port=$1
|
|
24
|
+
local pids
|
|
25
|
+
pids=$(lsof -ti:"$port" 2>/dev/null || true)
|
|
26
|
+
if [ -n "$pids" ]; then
|
|
27
|
+
echo " 端口 $port 被占用 (PID: $pids),正在释放..."
|
|
28
|
+
echo "$pids" | xargs kill -9 2>/dev/null || true
|
|
29
|
+
sleep 0.5
|
|
30
|
+
fi
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# ── 1. 释放被占用的端口 ──
|
|
34
|
+
|
|
35
|
+
echo "▶ 检查端口占用..."
|
|
36
|
+
kill_port $BACKEND_PORT
|
|
37
|
+
kill_port $FRONTEND_PORT
|
|
38
|
+
|
|
39
|
+
# ── 2. 启动 Docker 基础设施 ──
|
|
40
|
+
|
|
41
|
+
echo "▶ 启动 Docker 基础设施..."
|
|
42
|
+
docker compose -f "$SCRIPT_DIR/docker-compose.dev.yml" up -d --remove-orphans
|
|
43
|
+
|
|
44
|
+
echo "▶ 等待 PostgreSQL 就绪..."
|
|
45
|
+
until docker compose -f "$SCRIPT_DIR/docker-compose.dev.yml" exec -T db pg_isready > /dev/null 2>&1; do
|
|
46
|
+
sleep 1
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
echo "▶ 等待 Redis 就绪..."
|
|
50
|
+
until docker compose -f "$SCRIPT_DIR/docker-compose.dev.yml" exec -T redis redis-cli ping > /dev/null 2>&1; do
|
|
51
|
+
sleep 1
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
echo ""
|
|
55
|
+
echo "✔ 基础设施就绪"
|
|
56
|
+
echo " PostgreSQL → 127.0.0.1:{{portPrefix}}5432"
|
|
57
|
+
echo " Redis → 127.0.0.1:{{portPrefix}}6379"
|
|
58
|
+
echo ""
|
|
59
|
+
|
|
60
|
+
# ── 3. 启动后端 ──
|
|
61
|
+
|
|
62
|
+
echo "▶ 启动后端 (port $BACKEND_PORT)..."
|
|
63
|
+
cd "$SCRIPT_DIR/backend"
|
|
64
|
+
uv run uvicorn app.main:app --reload --port $BACKEND_PORT &
|
|
65
|
+
BACKEND_PID=$!
|
|
66
|
+
|
|
67
|
+
# ── 4. 启动前端 ──
|
|
68
|
+
|
|
69
|
+
echo "▶ 启动前端 (port $FRONTEND_PORT)..."
|
|
70
|
+
cd "$SCRIPT_DIR/frontend"
|
|
71
|
+
pnpm dev &
|
|
72
|
+
FRONTEND_PID=$!
|
|
73
|
+
|
|
74
|
+
echo ""
|
|
75
|
+
echo "════════════════════════════════════════"
|
|
76
|
+
echo " {{projectName}} 开发环境已启动"
|
|
77
|
+
echo " 前端: http://localhost:$FRONTEND_PORT"
|
|
78
|
+
echo " 后端: http://localhost:$BACKEND_PORT"
|
|
79
|
+
echo " 按 Ctrl+C 关闭前后端"
|
|
80
|
+
echo "════════════════════════════════════════"
|
|
81
|
+
echo ""
|
|
82
|
+
|
|
83
|
+
wait
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
services:
|
|
2
|
+
db:
|
|
3
|
+
image: postgres:16.12-alpine3.23
|
|
4
|
+
ports:
|
|
5
|
+
- "127.0.0.1:{{portPrefix}}5432:5432"
|
|
6
|
+
environment:
|
|
7
|
+
TZ: Asia/Shanghai
|
|
8
|
+
PGTZ: Asia/Shanghai
|
|
9
|
+
POSTGRES_DB: ${POSTGRES_DB}
|
|
10
|
+
POSTGRES_USER: ${POSTGRES_USER}
|
|
11
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
12
|
+
command: ["postgres", "-c", "timezone=Asia/Shanghai"]
|
|
13
|
+
volumes:
|
|
14
|
+
- pgdata:/var/lib/postgresql/data
|
|
15
|
+
healthcheck:
|
|
16
|
+
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
|
|
17
|
+
interval: 5s
|
|
18
|
+
timeout: 5s
|
|
19
|
+
retries: 5
|
|
20
|
+
|
|
21
|
+
redis:
|
|
22
|
+
image: redis:7.4.9-alpine3.21
|
|
23
|
+
ports:
|
|
24
|
+
- "127.0.0.1:{{portPrefix}}6379:6379"
|
|
25
|
+
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
|
|
26
|
+
volumes:
|
|
27
|
+
- redisdata:/data
|
|
28
|
+
healthcheck:
|
|
29
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
30
|
+
interval: 5s
|
|
31
|
+
timeout: 3s
|
|
32
|
+
retries: 5
|
|
33
|
+
|
|
34
|
+
volumes:
|
|
35
|
+
pgdata:
|
|
36
|
+
redisdata:
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Frontend 架构约束
|
|
2
|
+
|
|
3
|
+
## 技术栈
|
|
4
|
+
|
|
5
|
+
- Vue 3 + TypeScript + Vite + Pinia + Vue Router
|
|
6
|
+
- 组件库:`@dwydev/eui`(**禁止**引入 Element Plus、Ant Design Vue、Naive UI 等其他组件库)
|
|
7
|
+
- 工具库:`@dwydev/ekit`(HTTP、storage、cookie、date、validators、copy、qs、file、hooks、masking)
|
|
8
|
+
|
|
9
|
+
## 仓库布局
|
|
10
|
+
|
|
11
|
+
pnpm workspace monorepo:
|
|
12
|
+
|
|
13
|
+
- `apps/web/` — 主 SPA 应用,唯一入口
|
|
14
|
+
- `packages/core/` — 横切核心(HTTP / config / format / 加密等通用工具)
|
|
15
|
+
- `packages/ui/` — 项目级共享 UI 组件(基于 `@dwydev/eui` 二次封装)
|
|
16
|
+
- `packages/features/<domain>/` — 业务域分包,每个 feature 自管路由 + 页面
|
|
17
|
+
|
|
18
|
+
## 依赖管理
|
|
19
|
+
|
|
20
|
+
所有第三方包版本统一在 `pnpm-workspace.yaml` 的 `catalog:` 字段管理,子包用 `catalog:` 引用。
|
|
21
|
+
|
|
22
|
+
## 加新业务域 feature
|
|
23
|
+
|
|
24
|
+
复制 `packages/features/{{initialDomain}}/` 改名 → 在 `apps/web/src/router/index.ts` 用 `createFeatureRoutes()` 注册。
|
|
25
|
+
|
|
26
|
+
## 禁止事项
|
|
27
|
+
|
|
28
|
+
- 禁止重复造轮子:HTTP 用 `@dwydev/ekit` 的 `createRequest`,日期用其 date 模块,剪贴板/防抖等用其 hooks
|
|
29
|
+
- 禁止在 `apps/web/` 中堆积业务页面,新业务必须放到 `packages/features/<domain>/`
|
|
30
|
+
- 禁止跨 feature 直接 import 对方内部模块,必须经由 `packages/core` 或事件总线
|
|
31
|
+
|
|
32
|
+
## 测试
|
|
33
|
+
|
|
34
|
+
- 类型检查:`pnpm typecheck`
|
|
35
|
+
- 构建:`pnpm build`
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# 构建阶段
|
|
2
|
+
FROM node:20-alpine AS builder
|
|
3
|
+
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
COPY . .
|
|
7
|
+
|
|
8
|
+
RUN corepack enable && pnpm install --frozen-lockfile && pnpm build
|
|
9
|
+
|
|
10
|
+
# 生产阶段
|
|
11
|
+
FROM nginx:alpine
|
|
12
|
+
|
|
13
|
+
COPY --from=builder /app/apps/web/dist /usr/share/nginx/html
|
|
14
|
+
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
15
|
+
|
|
16
|
+
EXPOSE 80
|
|
17
|
+
|
|
18
|
+
CMD ["nginx", "-g", "daemon off;"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="description" content="{{projectDescription}}" />
|
|
7
|
+
<title>{{projectName}}</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.ts"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/web",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "vite",
|
|
7
|
+
"build": "vue-tsc -b && vite build",
|
|
8
|
+
"preview": "vite preview",
|
|
9
|
+
"typecheck": "vue-tsc --noEmit"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"vue": "catalog:",
|
|
13
|
+
"vue-router": "catalog:",
|
|
14
|
+
"pinia": "catalog:",
|
|
15
|
+
"axios": "catalog:",
|
|
16
|
+
"@dwydev/eui": "catalog:",
|
|
17
|
+
"@dwydev/ekit": "catalog:",
|
|
18
|
+
"@{{projectName}}/core": "workspace:*",
|
|
19
|
+
"@{{projectName}}/ui": "workspace:*",
|
|
20
|
+
"@{{projectName}}/features-{{initialDomain}}": "workspace:*"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"vite": "catalog:",
|
|
24
|
+
"typescript": "catalog:",
|
|
25
|
+
"vue-tsc": "catalog:",
|
|
26
|
+
"@vitejs/plugin-vue": "catalog:",
|
|
27
|
+
"@tailwindcss/vite": "catalog:",
|
|
28
|
+
"tailwindcss": "catalog:",
|
|
29
|
+
"@vue/tsconfig": "catalog:"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createRequest } from '@dwydev/ekit'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 全局 HTTP 客户端。
|
|
5
|
+
* - baseURL `/api` 走 vite dev proxy 转发到后端
|
|
6
|
+
* - token 注入 / 401 refresh / 响应 unwrap 由 ekit 插件链处理,按需开启
|
|
7
|
+
*/
|
|
8
|
+
export const http = createRequest({
|
|
9
|
+
baseURL: '/api',
|
|
10
|
+
timeout: 15000,
|
|
11
|
+
})
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createApp } from 'vue'
|
|
2
|
+
import { createPinia } from 'pinia'
|
|
3
|
+
import { EUI } from '@dwydev/eui'
|
|
4
|
+
|
|
5
|
+
import App from './App.vue'
|
|
6
|
+
import router from './router'
|
|
7
|
+
|
|
8
|
+
import '@dwydev/eui/theme/tokens.css'
|
|
9
|
+
import './index.css'
|
|
10
|
+
|
|
11
|
+
const app = createApp(App)
|
|
12
|
+
|
|
13
|
+
app.use(createPinia())
|
|
14
|
+
app.use(router)
|
|
15
|
+
app.use(EUI)
|
|
16
|
+
|
|
17
|
+
app.mount('#app')
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
|
|
2
|
+
import { createFeatureRoutes } from '@{{projectName}}/features-{{initialDomain}}'
|
|
3
|
+
|
|
4
|
+
const routes: RouteRecordRaw[] = [
|
|
5
|
+
{
|
|
6
|
+
path: '/',
|
|
7
|
+
name: 'home',
|
|
8
|
+
component: () => import('../views/HomeView.vue'),
|
|
9
|
+
meta: { title: '{{projectName}}' },
|
|
10
|
+
},
|
|
11
|
+
...createFeatureRoutes(),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
const router = createRouter({
|
|
15
|
+
history: createWebHistory(),
|
|
16
|
+
routes,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
router.afterEach((to) => {
|
|
20
|
+
const title = (to.meta?.title as string | undefined) || '{{projectName}}'
|
|
21
|
+
document.title = title
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export default router
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { http } from '../api/client'
|
|
4
|
+
|
|
5
|
+
const status = ref<string>('')
|
|
6
|
+
const loading = ref(false)
|
|
7
|
+
|
|
8
|
+
async function checkBackend() {
|
|
9
|
+
loading.value = true
|
|
10
|
+
try {
|
|
11
|
+
const res = await http.get('/health')
|
|
12
|
+
status.value = JSON.stringify(res, null, 2)
|
|
13
|
+
} catch (err) {
|
|
14
|
+
status.value = `error: ${(err as Error).message}`
|
|
15
|
+
} finally {
|
|
16
|
+
loading.value = false
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<main class="min-h-screen flex flex-col items-center justify-center gap-6 p-8">
|
|
23
|
+
<h1 class="text-3xl font-semibold">Welcome to {{projectName}}</h1>
|
|
24
|
+
<p class="text-sm text-gray-500">{{projectDescription}}</p>
|
|
25
|
+
|
|
26
|
+
<button
|
|
27
|
+
class="px-4 py-2 rounded-md bg-black text-white text-sm disabled:opacity-50"
|
|
28
|
+
:disabled="loading"
|
|
29
|
+
@click="checkBackend"
|
|
30
|
+
>
|
|
31
|
+
\{{ loading ? 'Checking...' : 'Check backend /api/health' }}
|
|
32
|
+
</button>
|
|
33
|
+
|
|
34
|
+
<pre v-if="status" class="text-xs bg-gray-100 p-3 rounded max-w-xl overflow-auto">\{{ status }}</pre>
|
|
35
|
+
</main>
|
|
36
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
4
|
+
import { fileURLToPath, URL } from 'node:url'
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [vue(), tailwindcss()],
|
|
8
|
+
resolve: {
|
|
9
|
+
alias: {
|
|
10
|
+
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
|
11
|
+
'@{{projectName}}/core': fileURLToPath(new URL('../../packages/core/src/index.ts', import.meta.url)),
|
|
12
|
+
'@{{projectName}}/ui': fileURLToPath(new URL('../../packages/ui/src/index.ts', import.meta.url)),
|
|
13
|
+
'@{{projectName}}/features-{{initialDomain}}': fileURLToPath(
|
|
14
|
+
new URL('../../packages/features/{{initialDomain}}/src/index.ts', import.meta.url),
|
|
15
|
+
),
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
server: {
|
|
19
|
+
port: {{portPrefix}}5173,
|
|
20
|
+
proxy: {
|
|
21
|
+
'/api': {
|
|
22
|
+
target: '{{apiProxy}}',
|
|
23
|
+
changeOrigin: true,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}-frontend-workspace",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "pnpm --filter web dev",
|
|
7
|
+
"build": "pnpm -r build",
|
|
8
|
+
"preview": "pnpm --filter web preview",
|
|
9
|
+
"typecheck": "pnpm -r typecheck"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {},
|
|
12
|
+
"packageManager": "pnpm@10.19.0"
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/core",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"axios": "catalog:",
|
|
9
|
+
"zod": "catalog:",
|
|
10
|
+
"@dwydev/ekit": "catalog:"
|
|
11
|
+
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"vue": "catalog:"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/features-{{initialDomain}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@{{projectName}}/core": "workspace:*",
|
|
9
|
+
"@{{projectName}}/ui": "workspace:*",
|
|
10
|
+
"vue": "catalog:",
|
|
11
|
+
"vue-router": "catalog:",
|
|
12
|
+
"@dwydev/eui": "catalog:"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 业务域 `{{initialDomain}}` 的路由工厂。
|
|
5
|
+
* 主 app 通过 `...createFeatureRoutes()` 装载本域路由。
|
|
6
|
+
*/
|
|
7
|
+
export function createFeatureRoutes(): RouteRecordRaw[] {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
path: '/{{initialDomain}}',
|
|
11
|
+
name: '{{initialDomain}}-index',
|
|
12
|
+
component: () => import('./pages/IndexPage.vue'),
|
|
13
|
+
meta: { title: '{{initialDomain}}' },
|
|
14
|
+
},
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
</script>
|
|
3
|
+
|
|
4
|
+
<template>
|
|
5
|
+
<div class="p-8">
|
|
6
|
+
<h2 class="text-xl font-semibold">{{initialDomain}} feature index</h2>
|
|
7
|
+
<p class="text-sm text-gray-500 mt-2">Edit packages/features/{{initialDomain}}/src/pages/IndexPage.vue</p>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/ui",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@dwydev/eui": "catalog:",
|
|
9
|
+
"@dwydev/ekit": "catalog:"
|
|
10
|
+
},
|
|
11
|
+
"peerDependencies": {
|
|
12
|
+
"vue": "catalog:",
|
|
13
|
+
"vue-router": "catalog:"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
packages:
|
|
2
|
+
- 'apps/*'
|
|
3
|
+
- 'packages/*'
|
|
4
|
+
- 'packages/features/*'
|
|
5
|
+
|
|
6
|
+
catalog:
|
|
7
|
+
vue: ^3.5.32
|
|
8
|
+
vue-router: ^4.6.4
|
|
9
|
+
pinia: ^3.0.4
|
|
10
|
+
axios: ^1.7.0
|
|
11
|
+
dayjs: ^1.11.13
|
|
12
|
+
zod: ^3.23.0
|
|
13
|
+
'@vueuse/core': ^11.0.0
|
|
14
|
+
lucide-vue-next: ^0.460.0
|
|
15
|
+
tailwindcss: ^4.0.0
|
|
16
|
+
'@tailwindcss/vite': ^4.0.0
|
|
17
|
+
'@dwydev/eui': ^2.1.0
|
|
18
|
+
'@dwydev/ekit': ^0.8.0
|
|
19
|
+
vite: ^8.0.0
|
|
20
|
+
typescript: ^5.6.0
|
|
21
|
+
vue-tsc: ^2.1.0
|
|
22
|
+
'@vitejs/plugin-vue': ^5.1.0
|
|
23
|
+
'@vue/tsconfig': ^0.7.0
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"jsx": "preserve",
|
|
14
|
+
"useDefineForClassFields": true,
|
|
15
|
+
"baseUrl": ".",
|
|
16
|
+
"paths": {
|
|
17
|
+
"@/*": ["apps/web/src/*"],
|
|
18
|
+
"@{{projectName}}/core": ["packages/core/src/index.ts"],
|
|
19
|
+
"@{{projectName}}/ui": ["packages/ui/src/index.ts"],
|
|
20
|
+
"@{{projectName}}/features-{{initialDomain}}": ["packages/features/{{initialDomain}}/src/index.ts"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|