create-dwy 0.2.0 → 0.3.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.
package/bin/index.js CHANGED
@@ -1,13 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { readFileSync } from 'fs'
4
+ import { fileURLToPath } from 'url'
5
+ import { dirname, resolve } from 'path'
3
6
  import { program } from 'commander'
4
7
  import { createProject } from '../src/create.js'
5
8
  import { syncClaude, syncProjectClaude } from '../src/sync.js'
6
9
 
10
+ const __dirname = dirname(fileURLToPath(import.meta.url))
11
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8'))
12
+
7
13
  program
8
14
  .name('dwy')
9
15
  .description('Project scaffolding and Claude Code config sync')
10
- .version('0.1.0')
16
+ .version(pkg.version)
11
17
 
12
18
  program
13
19
  .command('create [name]')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dwy",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Project scaffolding and Claude Code config sync CLI",
5
5
  "type": "module",
6
6
  "bin": {
package/src/sync.js CHANGED
@@ -6,7 +6,7 @@ export async function syncClaude() {
6
6
  console.log(chalk.blue('Syncing global Claude configuration...'))
7
7
 
8
8
  const repoDir = await ensureRepoCache()
9
- const sourceDir = path.join(repoDir, 'templates', 'claude-global')
9
+ const sourceDir = path.join(repoDir, 'claude-cli', 'templates', 'claude-global')
10
10
  const targetDir = path.join(process.env.HOME, '.claude')
11
11
 
12
12
  if (!await fs.pathExists(sourceDir)) {
@@ -62,7 +62,7 @@ export async function syncProjectClaude() {
62
62
  const template = marker.template || 'web'
63
63
 
64
64
  const repoDir = await ensureRepoCache()
65
- const sourceDir = path.join(repoDir, 'templates', 'project', template, '.claude')
65
+ const sourceDir = path.join(repoDir, 'claude-cli', 'templates', 'project', template, '.claude')
66
66
  const targetDir = path.join(process.cwd(), '.claude')
67
67
 
68
68
  if (!await fs.pathExists(sourceDir)) {
package/src/utils.js CHANGED
@@ -9,7 +9,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
9
 
10
10
  export const PACKAGE_ROOT = path.resolve(__dirname, '..')
11
11
  export const CACHE_DIR = path.join(process.env.HOME, '.dwy', 'cache')
12
- export const DWY_REPO_URL = 'https://gitee.com/snailyuanyuan/dwy-cli.git'
12
+ export const DWY_REPO_URL = 'https://gitee.com/snailyuanyuan/dwy-shared.git'
13
13
 
14
14
  export async function ensureRepoCache() {
15
15
  await fs.ensureDir(CACHE_DIR)
@@ -0,0 +1,181 @@
1
+ ---
2
+ name: core
3
+ description: "@danweiyuan/core 前端工具库速查。触发条件:需要 HTTP 请求封装、localStorage、表单校验、日期格式化、Vue composable 工具时。"
4
+ ---
5
+
6
+ # @danweiyuan/core 工具库速查
7
+
8
+ Vue 3 项目通用工具库,包含 5 个模块:request、storage、validators、date、hooks。
9
+
10
+ ## 安装
11
+
12
+ ```bash
13
+ pnpm add @danweiyuan/core
14
+ # peerDependencies: axios ^1.0.0, vue ^3.4.0
15
+ ```
16
+
17
+ ```ts
18
+ import { createRequest, useStorage, isPhone, formatDate, useDebounce } from '@danweiyuan/core'
19
+ ```
20
+
21
+ ## 查阅源码
22
+
23
+ 每个模块源码在 `frontend/core/src/{module}/index.ts`,函数简短可直接阅读。
24
+
25
+ ---
26
+
27
+ ## request — Axios 插件化封装
28
+
29
+ ```ts
30
+ import { createRequest, tokenPlugin, headerPlugin, unwrapPlugin, refreshTokenPlugin } from '@danweiyuan/core'
31
+ ```
32
+
33
+ ### createRequest(options?)
34
+
35
+ 创建带插件系统的 Axios 实例。插件按顺序执行请求/响应拦截。
36
+
37
+ ```ts
38
+ const http = createRequest({
39
+ baseURL: '/api', // 默认 '/api'
40
+ timeout: 30000, // 默认 30000
41
+ plugins: [
42
+ tokenPlugin({ getToken: () => localStorage.getItem('token') }),
43
+ unwrapPlugin(),
44
+ ],
45
+ })
46
+ ```
47
+
48
+ ### 内置插件
49
+
50
+ | 插件 | 用途 | 参数 |
51
+ |------|------|------|
52
+ | tokenPlugin | 自动注入 Bearer token | `{ getToken: () => string \| null }` |
53
+ | headerPlugin | 注入自定义请求头 | `{ name: string, getValue: () => string \| null }` |
54
+ | unwrapPlugin | 解包 `{ code, data, message }` 响应,code !== 200 时 reject | 无参数 |
55
+ | refreshTokenPlugin | 401 自动刷新 token 并重试 | `{ getRefreshToken, refreshFn, onRefreshFail, isLoginUrl? }` |
56
+
57
+ ### RequestPlugin 接口
58
+
59
+ ```ts
60
+ interface RequestPlugin {
61
+ onRequest?: (config: InternalAxiosRequestConfig) => config | Promise<config>
62
+ onResponse?: (response: AxiosResponse) => response | Promise<response>
63
+ onResponseError?: (error: AxiosError) => any
64
+ }
65
+ ```
66
+
67
+ ### refreshTokenPlugin 详解
68
+
69
+ ```ts
70
+ refreshTokenPlugin({
71
+ getRefreshToken: () => localStorage.getItem('refreshToken'),
72
+ refreshFn: async (refreshToken) => {
73
+ // 调用刷新接口,返回新 access token
74
+ const { data } = await http.post('/auth/refresh', { refreshToken })
75
+ localStorage.setItem('token', data.token)
76
+ return data.token
77
+ },
78
+ onRefreshFail: () => {
79
+ // 刷新失败,跳转登录
80
+ localStorage.clear()
81
+ window.location.href = '/login'
82
+ },
83
+ isLoginUrl: (url) => url.includes('/auth/login'), // 可选,默认检测 /auth/login
84
+ })
85
+ ```
86
+
87
+ ---
88
+
89
+ ## storage — localStorage 封装
90
+
91
+ ```ts
92
+ import { useStorage, storage } from '@danweiyuan/core'
93
+ ```
94
+
95
+ ### useStorage(key, defaultValue) — 响应式
96
+
97
+ 返回 Vue `Ref<T>`,自动 JSON 序列化,深度 watch 自动同步。设为 null/undefined 时自动 removeItem。
98
+
99
+ ```ts
100
+ const token = useStorage<string>('token', '')
101
+ const config = useStorage<{ theme: string }>('config', { theme: 'light' })
102
+ ```
103
+
104
+ ### storage — 静态工具对象
105
+
106
+ | 方法 | 签名 | 说明 |
107
+ |------|------|------|
108
+ | get | `get<T>(key, defaultValue?): T \| undefined` | JSON 解析失败时返回原始字符串 |
109
+ | set | `set(key, value): void` | JSON.stringify 后存储 |
110
+ | remove | `remove(key): void` | 删除指定 key |
111
+ | clear | `clear(): void` | 清空全部 |
112
+
113
+ ---
114
+
115
+ ## validators — 校验函数
116
+
117
+ ```ts
118
+ import { isPhone, isEmail, isIdCard, isUrl, isRequired, minLength, maxLength } from '@danweiyuan/core'
119
+ ```
120
+
121
+ | 函数 | 签名 | 规则 |
122
+ |------|------|------|
123
+ | isPhone | `(value: string) => boolean` | 中国手机号 1[3-9]开头 11 位 |
124
+ | isEmail | `(value: string) => boolean` | 基础邮箱格式 |
125
+ | isIdCard | `(value: string) => boolean` | 18 位身份证(末位可为 X/x) |
126
+ | isUrl | `(value: string) => boolean` | 使用 `new URL()` 校验 |
127
+ | isRequired | `(value: any) => boolean` | 非 null/undefined/空字符串/空数组。0 和 false 视为有效 |
128
+ | minLength | `(value: string, min: number) => boolean` | 最小长度 |
129
+ | maxLength | `(value: string, max: number) => boolean` | 最大长度 |
130
+
131
+ ---
132
+
133
+ ## date — 日期格式化
134
+
135
+ ```ts
136
+ import { formatDate, formatDateTime, formatTime, formatRelativeTime } from '@danweiyuan/core'
137
+ ```
138
+
139
+ 所有函数接收 `string | number | Date` 类型输入。
140
+
141
+ | 函数 | 输出格式 | 示例 |
142
+ |------|---------|------|
143
+ | formatDate | `YYYY-MM-DD` | `2025-03-15` |
144
+ | formatDateTime | `YYYY-MM-DD HH:mm:ss` | `2025-03-15 14:30:00` |
145
+ | formatTime | `HH:mm` | `14:30` |
146
+ | formatRelativeTime | 中文相对时间 | `刚刚` / `5 分钟前` / `3 小时前` / `7 天前` / 超过 30 天返回 `toLocaleDateString('zh-CN')` |
147
+
148
+ ---
149
+
150
+ ## hooks — Vue Composables
151
+
152
+ ```ts
153
+ import { useDebounce, useClickOutside, useEventListener } from '@danweiyuan/core'
154
+ ```
155
+
156
+ ### useDebounce(value, delay?)
157
+
158
+ ```ts
159
+ const search = ref('')
160
+ const debouncedSearch = useDebounce(search, 300) // 默认 300ms
161
+ ```
162
+
163
+ 返回防抖后的 `Ref<T>`,仅在源值停止变化 delay 毫秒后更新。
164
+
165
+ ### useClickOutside(target, handler)
166
+
167
+ ```ts
168
+ const popoverRef = ref<HTMLElement | null>(null)
169
+ useClickOutside(popoverRef, () => { isOpen.value = false })
170
+ ```
171
+
172
+ 点击 target 元素外部时触发 handler。自动在 onMounted/onBeforeUnmount 绑定/解绑。
173
+
174
+ ### useEventListener(target, event, handler, options?)
175
+
176
+ ```ts
177
+ useEventListener(window, 'resize', onResize)
178
+ useEventListener(elementRef, 'scroll', onScroll, { passive: true })
179
+ ```
180
+
181
+ target 可以是 `EventTarget` 或 `Ref<EventTarget>`。自动在 onMounted/onBeforeUnmount 绑定/解绑。
@@ -0,0 +1,279 @@
1
+ ---
2
+ name: danweiyuan-base
3
+ description: "danweiyuan-base FastAPI 后端基础设施速查。触发条件:使用 FastAPI 构建后端、配置数据库/Redis/JWT/异常处理时。"
4
+ ---
5
+
6
+ # danweiyuan-base 后端基础设施速查
7
+
8
+ FastAPI 项目基础设施包,Python 3.11+,全异步。8 个扁平模块,无子包。
9
+
10
+ ## 安装
11
+
12
+ ```bash
13
+ pip install danweiyuan-base
14
+ # 或
15
+ uv add danweiyuan-base
16
+ ```
17
+
18
+ ```python
19
+ from danweiyuan_base.config import BaseSettings
20
+ from danweiyuan_base.database import Base, TimestampMixin, create_async_engine_factory, create_session_factory
21
+ from danweiyuan_base.security import hash_password, verify_password, create_token, decode_token
22
+ from danweiyuan_base.exceptions import NotFoundError, BusinessError, register_exception_handlers
23
+ from danweiyuan_base.response import success, fail, paginated
24
+ from danweiyuan_base.pagination import PaginationParams, paginate
25
+ from danweiyuan_base.cache import configure as configure_redis, get_redis, close_redis
26
+ from danweiyuan_base.dependencies import create_get_db
27
+ ```
28
+
29
+ ## 查阅源码
30
+
31
+ 每个模块在 `backend/src/danweiyuan_base/{module}.py`,单文件设计可直接阅读。
32
+
33
+ ---
34
+
35
+ ## config — Pydantic Settings
36
+
37
+ ```python
38
+ from danweiyuan_base.config import BaseSettings
39
+ ```
40
+
41
+ 子类化后使用,从 `.env` 或环境变量读取配置。
42
+
43
+ ```python
44
+ class Settings(BaseSettings):
45
+ app_name: str = "My API"
46
+ # 继承的必填字段:database_url, redis_url, secret_key
47
+ ```
48
+
49
+ | 字段 | 类型 | 默认值 | 说明 |
50
+ |------|------|--------|------|
51
+ | database_url | str | **必填** | 异步数据库 URL |
52
+ | redis_url | str | **必填** | Redis URL |
53
+ | secret_key | str | **必填** | JWT 签名密钥 |
54
+ | jwt_algorithm | str | `"HS256"` | JWT 算法 |
55
+ | access_token_expire_minutes | int | `30` | Token 过期分钟 |
56
+ | debug | bool | `False` | 调试模式 |
57
+ | allowed_origins | list[str] | `[]` | CORS 允许域名 |
58
+
59
+ ---
60
+
61
+ ## database — 异步 SQLAlchemy
62
+
63
+ ```python
64
+ from danweiyuan_base.database import Base, TimestampMixin, create_async_engine_factory, create_session_factory
65
+ ```
66
+
67
+ ### 模型定义
68
+
69
+ ```python
70
+ from sqlalchemy.orm import Mapped, mapped_column
71
+ from sqlalchemy import String
72
+
73
+ class User(Base, TimestampMixin):
74
+ __tablename__ = "users"
75
+ id: Mapped[int] = mapped_column(primary_key=True)
76
+ name: Mapped[str] = mapped_column(String(50))
77
+ # TimestampMixin 自动添加 created_at, updated_at
78
+ ```
79
+
80
+ ### 引擎和会话
81
+
82
+ ```python
83
+ engine = create_async_engine_factory(settings.database_url, echo=settings.debug)
84
+ # 参数: database_url, pool_size=5, max_overflow=10, echo=False
85
+ # SQLite 自动跳过连接池参数
86
+
87
+ session_factory = create_session_factory(engine)
88
+ # 返回 async_sessionmaker[AsyncSession],expire_on_commit=False
89
+ ```
90
+
91
+ ---
92
+
93
+ ## dependencies — FastAPI 依赖注入
94
+
95
+ ```python
96
+ from danweiyuan_base.dependencies import create_get_db
97
+ ```
98
+
99
+ ### create_get_db(session_factory)
100
+
101
+ 生成 `get_db` FastAPI Depends 函数,自动管理会话生命周期。
102
+
103
+ ```python
104
+ get_db = create_get_db(session_factory)
105
+
106
+ @router.get("/users/{user_id}")
107
+ async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
108
+ ...
109
+ ```
110
+
111
+ ---
112
+
113
+ ## security — JWT + bcrypt
114
+
115
+ ```python
116
+ from danweiyuan_base.security import hash_password, verify_password, create_token, decode_token
117
+ ```
118
+
119
+ 所有函数无状态,密钥通过参数传入。
120
+
121
+ | 函数 | 签名 | 说明 |
122
+ |------|------|------|
123
+ | hash_password | `(password: str) -> str` | bcrypt 哈希 |
124
+ | verify_password | `(plain: str, hashed: str) -> bool` | 验证密码 |
125
+ | create_token | `(data: dict, secret: str, expires_minutes: int, algorithm="HS256") -> str` | 创建 JWT,自动添加 exp |
126
+ | decode_token | `(token: str, secret: str, algorithm="HS256") -> dict \| None` | 解码 JWT,失败返回 None |
127
+
128
+ ```python
129
+ hashed = hash_password("my-password")
130
+ is_valid = verify_password("my-password", hashed)
131
+
132
+ token = create_token({"sub": str(user.id)}, settings.secret_key, settings.access_token_expire_minutes)
133
+ payload = decode_token(token, settings.secret_key) # {"sub": "1", "exp": ...} 或 None
134
+ ```
135
+
136
+ ---
137
+
138
+ ## exceptions — 异常体系
139
+
140
+ ```python
141
+ from danweiyuan_base.exceptions import (
142
+ AppError, NotFoundError, BusinessError, PermissionDeniedError, AuthenticationError,
143
+ register_exception_handlers,
144
+ )
145
+ ```
146
+
147
+ ### 异常层级
148
+
149
+ | 异常类 | HTTP 状态码 | code | message |
150
+ |--------|-----------|------|---------|
151
+ | AppError(message, code) | — | 自定义 | 自定义 |
152
+ | NotFoundError(resource) | 404 | `NOT_FOUND` | `{resource}不存在` |
153
+ | BusinessError(message, code) | 422 | 自定义 | 自定义 |
154
+ | PermissionDeniedError() | 403 | `PERMISSION_DENIED` | `权限不足` |
155
+ | AuthenticationError() | 401 | `AUTHENTICATION_FAILED` | `认证失败` |
156
+
157
+ ### 注册到 FastAPI
158
+
159
+ ```python
160
+ app = FastAPI()
161
+ register_exception_handlers(app)
162
+ # 之后在 service 层直接 raise NotFoundError("用户") 即可返回 404
163
+ ```
164
+
165
+ 响应格式统一为 `{"code": "ERROR_CODE", "message": "描述"}`。
166
+
167
+ ### 使用原则
168
+
169
+ - **service 层**抛业务异常(NotFoundError, BusinessError 等)
170
+ - **router 层**不写 try/except,由 register_exception_handlers 统一处理
171
+ - **禁止**在 service 层抛 HTTPException
172
+
173
+ ---
174
+
175
+ ## response — 统一响应
176
+
177
+ ```python
178
+ from danweiyuan_base.response import success, fail, paginated
179
+ ```
180
+
181
+ | 函数 | 签名 | 返回结构 |
182
+ |------|------|---------|
183
+ | success | `(data=None, message="success") -> dict` | `{"code": 200, "message": ..., "data": ..., "timestamp": ...}` |
184
+ | fail | `(code=400, message="fail") -> dict` | `{"code": ..., "message": ..., "data": None, "timestamp": ...}` |
185
+ | paginated | `(items, total, page, page_size) -> dict` | success 包装 `{"items": [...], "total": N, "page": N, "page_size": N}` |
186
+
187
+ ```python
188
+ @router.get("/users/{user_id}")
189
+ async def get_user(user_id: int, service=Depends(get_user_service)):
190
+ user = await service.get_by_id(user_id)
191
+ return success(data=UserResponse.model_validate(user))
192
+
193
+ @router.get("/users")
194
+ async def list_users(params: PaginationParams = Depends(), service=Depends(get_user_service)):
195
+ items, total = await service.list(params)
196
+ return paginated(items, total, params.page, params.page_size)
197
+ ```
198
+
199
+ ---
200
+
201
+ ## pagination — 分页工具
202
+
203
+ ```python
204
+ from danweiyuan_base.pagination import PaginationParams, paginate, OffsetLimit
205
+ ```
206
+
207
+ ### PaginationParams
208
+
209
+ FastAPI Depends 注入的查询参数模型。
210
+
211
+ | 字段 | 类型 | 默认 | 约束 |
212
+ |------|------|------|------|
213
+ | page | int | 1 | >= 1 |
214
+ | page_size | int | 20 | 1 ~ 100 |
215
+
216
+ ### paginate(page, page_size) -> OffsetLimit
217
+
218
+ ```python
219
+ offset_limit = paginate(params.page, params.page_size)
220
+ # OffsetLimit(offset=0, limit=20) — 可直接用于 SQLAlchemy .offset().limit()
221
+ stmt = select(User).offset(offset_limit.offset).limit(offset_limit.limit)
222
+ ```
223
+
224
+ ---
225
+
226
+ ## cache — 异步 Redis
227
+
228
+ ```python
229
+ from danweiyuan_base import cache
230
+ ```
231
+
232
+ | 函数 | 签名 | 说明 |
233
+ |------|------|------|
234
+ | cache.configure | `(redis_url: str) -> None` | 设置 Redis URL(启动时调用一次) |
235
+ | cache.get_redis | `async () -> Redis` | 获取/创建共享连接(未 configure 时抛 RuntimeError) |
236
+ | cache.close_redis | `async () -> None` | 关闭连接(安全幂等) |
237
+
238
+ ```python
239
+ # lifespan 中初始化
240
+ @asynccontextmanager
241
+ async def lifespan(app: FastAPI):
242
+ cache.configure(settings.redis_url)
243
+ yield
244
+ await cache.close_redis()
245
+
246
+ # 业务中使用
247
+ redis = await cache.get_redis()
248
+ await redis.set("key", "value", ex=3600)
249
+ val = await redis.get("key")
250
+ ```
251
+
252
+ ---
253
+
254
+ ## 典型项目接入流程
255
+
256
+ ```python
257
+ # config.py
258
+ from danweiyuan_base.config import BaseSettings
259
+
260
+ class Settings(BaseSettings):
261
+ app_name: str = "My API"
262
+
263
+ settings = Settings()
264
+
265
+ # database.py
266
+ from danweiyuan_base.database import Base, create_async_engine_factory, create_session_factory
267
+ from danweiyuan_base.dependencies import create_get_db
268
+
269
+ engine = create_async_engine_factory(settings.database_url)
270
+ session_factory = create_session_factory(engine)
271
+ get_db = create_get_db(session_factory)
272
+
273
+ # main.py
274
+ from danweiyuan_base.exceptions import register_exception_handlers
275
+ from danweiyuan_base import cache
276
+
277
+ app = FastAPI(lifespan=lifespan)
278
+ register_exception_handlers(app)
279
+ ```
@@ -0,0 +1,246 @@
1
+ ---
2
+ name: eui
3
+ description: "@danweiyuan/eui Vue 3 组件库速查。触发条件:使用 Vue 3 构建页面、选择 UI 组件、查询 eui 组件 API 时。"
4
+ ---
5
+
6
+ # @danweiyuan/eui 组件速查
7
+
8
+ 基于 Reka-ui 原语层 + shadcn-vue 设计风格 + Element Plus 式 API 的 Vue 3 组件库。
9
+
10
+ ## 安装与引入
11
+
12
+ ```bash
13
+ pnpm add @danweiyuan/eui
14
+ ```
15
+
16
+ ```ts
17
+ // 按需导入(推荐)
18
+ import { EButton, EInput, ETable } from '@danweiyuan/eui'
19
+
20
+ // 全局注册(Vue plugin)
21
+ import EUI from '@danweiyuan/eui'
22
+ app.use(EUI)
23
+ ```
24
+
25
+ ```ts
26
+ // 主题 CSS(必须引入)
27
+ import '@danweiyuan/eui/theme' // 基础样式 + tokens
28
+ import '@danweiyuan/eui/theme/dark' // 暗色模式(可选)
29
+ import '@danweiyuan/eui/theme/tokens' // 仅 design tokens(可选)
30
+ ```
31
+
32
+ ## 命名约定
33
+
34
+ - 组件前缀 `E`:`EButton`, `EInput`, `EDialog`
35
+ - Props 类型:`E{Name}Props`(如 `EButtonProps`)
36
+ - Events 类型:`E{Name}Emits`(如 `EInputEmits`)
37
+ - CVA 变体:`{name}Variants`(如 `buttonVariants`)
38
+ - 样式合并工具:`cn()` = clsx + tailwind-merge
39
+ - 尺寸统一类型:`Size = 'sm' | 'default' | 'lg'`
40
+
41
+ ## 查阅完整 API
42
+
43
+ 每个组件的完整 props/slots/events 定义在源码中:
44
+
45
+ ```
46
+ frontend/eui/src/components/{component-name}/types.ts
47
+ ```
48
+
49
+ 需要具体某个组件的详细 API 时,直接读取对应的 `types.ts` 文件。
50
+
51
+ ---
52
+
53
+ ## 组件索引
54
+
55
+ ### 基础组件
56
+
57
+ | 组件 | 用途 | 关键 Props |
58
+ |------|------|-----------|
59
+ | EButton | 按钮 | variant(default/destructive/outline/secondary/ghost/link), size(default/sm/lg/icon), loading, disabled |
60
+ | EBadge | 标签徽章 | variant(default/secondary/destructive/outline) |
61
+ | EAlert | 警告提示 | variant(default/destructive), title, description, closable |
62
+ | ECard | 卡片容器 | title, description |
63
+ | ELabel | 标签文本 | for |
64
+ | ESeparator | 分隔线 | orientation(horizontal/vertical) |
65
+ | ETypography | 排版文本 | variant, as |
66
+ | EKbd | 键盘按键展示 | — |
67
+ | ESpinner | 加载动画 | size(sm/default/lg) |
68
+ | EProgress | 进度条 | modelValue(0-100) |
69
+ | ESkeleton | 骨架屏 | — |
70
+ | EEmpty | 空状态 | title, description, icon |
71
+ | EAvatar | 头像 | src, alt, fallback, size |
72
+ | EImage | 图片 | src, alt, fit, lazy, fallback, preview |
73
+
74
+ ### 表单组件
75
+
76
+ | 组件 | 用途 | 关键 Props |
77
+ |------|------|-----------|
78
+ | EInput | 输入框 | v-model, type, placeholder, size, clearable, showPassword, disabled, readonly, maxlength |
79
+ | ETextarea | 多行输入 | v-model, placeholder, rows, autoResize, maxlength, disabled |
80
+ | ENumberField | 数字输入 | v-model, min, max, step, size, disabled |
81
+ | ESelect | 下拉选择 | v-model, options(Option[]), placeholder, clearable, size, disabled |
82
+ | ENativeSelect | 原生 select | v-model, options(SelectOption[]), placeholder, disabled |
83
+ | ECombobox | 组合框 | v-model, options(Option[]), placeholder, emptyText, disabled |
84
+ | EAutocomplete | 自动补全 | v-model, fetchSuggestions(async fn), debounce, placeholder |
85
+ | ECheckbox | 复选框 | v-model, label, disabled, indeterminate |
86
+ | ERadio | 单选组 | v-model, options(Option[]), direction(horizontal/vertical), disabled |
87
+ | ESwitch | 开关 | v-model, label, size, disabled |
88
+ | ETagsInput | 标签输入 | v-model(string[]), placeholder, max, size, disabled |
89
+ | EDatePicker | 日期选择 | v-model, placeholder, format, clearable, disabled |
90
+ | ETimePicker | 时间选择 | v-model, placeholder, format, hourStep, minuteStep, disabled |
91
+ | EColorPicker | 颜色选择 | v-model, presets(string[]), showAlpha, disabled |
92
+ | ERate | 评分 | v-model, max, allowHalf, showText, disabled |
93
+ | ESlider | 滑块 | v-model(number[]), min, max, step, orientation, disabled |
94
+ | ECascader | 级联选择 | v-model, options(CascaderOption[]), placeholder, filterable, clearable |
95
+ | ETreeSelect | 树选择 | v-model, data(TreeNode[]), placeholder, multiple, checkable, disabled |
96
+ | EMention | @提及输入 | v-model, options(MentionOption[]), prefix, loading, placeholder |
97
+ | EInputOTP | OTP 验证码 | v-model, length, disabled, placeholder |
98
+ | EPinInput | PIN 输入 | v-model(string[]), length, mask, otp, type(text/number) |
99
+ | EUpload | 文件上传 | v-model(UploadFile[]), action, accept, multiple, limit, listType(text/picture/picture-card), drag |
100
+ | ETransfer | 穿梭框 | v-model, data(TransferItem[]), filterable, titles([string, string]) |
101
+ | EForm | 表单容器 | model, rules(Zod/FormRule[]), labelWidth, labelPosition(left/right/top), size, disabled |
102
+ | EFormItem | 表单项 | (配合 EForm 使用) |
103
+ | EField | 字段布局 | orientation(vertical/horizontal/responsive), invalid |
104
+ | EInputGroup | 输入组 | 子组件 EInputGroupAddon(align) |
105
+
106
+ ### 数据展示
107
+
108
+ | 组件 | 用途 | 关键 Props |
109
+ |------|------|-----------|
110
+ | ETable | 表格 | data, columns(TableColumn[]), loading, rowKey, striped, bordered, selectable, selectedKeys |
111
+ | EVirtualTable | 虚拟滚动表格 | columns(VirtualTableColumn[]), data, height, estimateRowHeight, rowKey, loading |
112
+ | EDescriptions | 描述列表 | items(DescriptionItem[]), columns, bordered, title |
113
+ | ETimeline | 时间线 | items(TimelineItem[]), reverse |
114
+ | EStatistic | 统计数值 | title, value, prefix, suffix, precision |
115
+ | ETree | 树形控件 | data(TreeNode[]), v-model, checkable, expandedKeys, defaultExpandAll |
116
+ | ECalendar | 日历 | v-model, multiple, disabled, locale, minValue, maxValue |
117
+ | ERangeCalendar | 日期范围日历 | v-model({start, end}), disabled, locale, minValue, maxValue |
118
+ | EPagination | 分页 | v-model, total, pageSize, showSizeChanger, pageSizes, showTotal |
119
+ | EBreadcrumb | 面包屑 | items(BreadcrumbItem[]), separator |
120
+
121
+ ### 弹层与反馈
122
+
123
+ | 组件 | 用途 | 关键 Props |
124
+ |------|------|-----------|
125
+ | EDialog | 对话框 | v-model:open, title, description, showClose, maxWidth |
126
+ | EDrawer | 抽屉 | v-model:open, title, description, direction(top/right/bottom/left), showClose |
127
+ | ESheet | 侧边面板 | v-model:open, title, description, side(top/right/bottom/left), showClose |
128
+ | EAlertDialog | 确认对话框 | v-model:open, title, description, confirmText, cancelText |
129
+ | ETooltip | 提示气泡 | content, side, sideOffset, delayDuration, disabled |
130
+ | EPopover | 弹出卡片 | v-model:open, side, sideOffset, align |
131
+ | EHoverCard | 悬停卡片 | openDelay, closeDelay |
132
+ | EToast | 轻提示 | position, expand, richColors(配合 `toast()` 函数使用) |
133
+ | EContextMenu | 右键菜单 | items(ContextMenuItem[]) |
134
+
135
+ ### 导航与布局
136
+
137
+ | 组件 | 用途 | 关键 Props |
138
+ |------|------|-----------|
139
+ | ETabs | 标签页 | v-model, items(TabItem[]) |
140
+ | EMenu | 侧边菜单 | v-model, items(MenuItem[]), collapsed |
141
+ | EDropdown | 下拉菜单 | items(DropdownMenuItem[]), side, align, sideOffset |
142
+ | EAccordion | 手风琴 | v-model, items(AccordionItemOption[]), multiple, collapsible |
143
+ | ECollapsible | 折叠面板 | v-model(boolean), disabled |
144
+ | EStepper | 步骤条 | v-model, items(StepperItem[]), direction(horizontal/vertical) |
145
+ | ECarousel | 轮播 | loop, direction, showArrows, showDots, autoplay, interval。子组件 ECarouselItem |
146
+ | EScrollArea | 自定义滚动区 | type(auto/always/scroll/hover) |
147
+ | EInfiniteScroll | 无限滚动 | loading, disabled, distance |
148
+
149
+ ### 复合组件
150
+
151
+ 以下组件由多个子组件组合使用,通过 `provide/inject` 共享状态:
152
+
153
+ | 组件 | 子组件 | 用途 |
154
+ |------|--------|------|
155
+ | ESidebar | ESidebarProvider, ESidebar, ESidebarTrigger, ESidebarContent, ESidebarHeader, ESidebarFooter, ESidebarGroup, ESidebarGroupLabel, ESidebarGroupContent, ESidebarMenu, ESidebarMenuItem, ESidebarMenuButton | 侧边栏。Provider(defaultOpen, v-model:open) |
156
+ | ECommand | ECommandInput, ECommandList, ECommandEmpty, ECommandGroup, ECommandItem, ECommandSeparator, ECommandShortcut | 命令面板(配合 useCommand) |
157
+ | ENavigationMenu | ENavigationMenuList, ENavigationMenuItem, ENavigationMenuTrigger, ENavigationMenuContent, ENavigationMenuLink, ENavigationMenuViewport | 导航菜单 |
158
+ | EMenubar | EMenubarMenu, EMenubarTrigger, EMenubarContent, EMenubarItem, EMenubarSeparator, EMenubarCheckboxItem, EMenubarRadioGroup, EMenubarRadioItem, EMenubarSub, EMenubarSubTrigger, EMenubarSubContent | 菜单栏 |
159
+ | EResizable | EResizablePanelGroup(direction), EResizablePanel(defaultSize, minSize, maxSize), EResizableHandle(withHandle) | 可调整大小面板 |
160
+
161
+ ### 其他
162
+
163
+ | 组件 | 用途 | 关键 Props |
164
+ |------|------|-----------|
165
+ | EConfigProvider | 全局配置注入 | size, zIndex, locale(Record) |
166
+ | EToggle | 切换按钮 | v-model, variant(default/outline), size, disabled |
167
+ | EToggleGroup | 切换按钮组 | v-model, type(single/multiple), variant, size, disabled |
168
+ | EButtonGroup | 按钮组 | orientation(horizontal/vertical) |
169
+ | EItem | 列表项 | variant(default/outline/muted), size(default/sm), as |
170
+ | EAspectRatio | 宽高比容器 | ratio |
171
+ | EAffix | 固钉 | offset, position(top/bottom) |
172
+ | EWatermark | 水印 | content, fontSize, rotate, gap, opacity |
173
+ | EChartContainer | 图表容器 | config(ChartConfig), id |
174
+
175
+ ### 业务组件
176
+
177
+ | 组件 | 用途 | 关键 Props |
178
+ |------|------|-----------|
179
+ | EDataPage | 数据列表页 | columns(TableColumn[]), fetchFn(async), searchable, pageSize。内置表格 + 分页 + 搜索 |
180
+ | EFormDialog | 表单弹窗 | v-model:open, title, size, loading, confirmText, cancelText |
181
+ | EConfirmDialog | 确认弹窗 | v-model:open, title, message, type(info/warning/error), confirmText, cancelText |
182
+ | EAdminLayout | 后台管理布局 | logo, title, menuItems(MenuItem[]), v-model:activeKey, v-model:collapsed, headerHeight, sidebarWidth |
183
+ | ELoginLayout | 登录页布局 | title, logo, background |
184
+ | EAIChat | AI 对话 | messages(ChatMessage[]), loading, placeholder |
185
+ | ETimetableGrid | 课程表/时间表 | data(TimetableItem[]), startHour, endHour, days |
186
+
187
+ ---
188
+
189
+ ## Composables
190
+
191
+ | Composable | 用途 | 返回值 |
192
+ |------------|------|--------|
193
+ | useMessage() | 全局消息提示 | { success, warning, error, info } — 每个接收 string 或 MessageOptions |
194
+ | useNotification() | 通知 | { success, warning, error, info } — 接收 NotificationOptions(title, message, position, duration) |
195
+ | useMessageBox() | 对话确认 | { alert, confirm, prompt } — 返回 Promise<'confirm' \| 'cancel'> |
196
+ | useTheme() | 主题管理 | { isDark, theme, setTheme, toggleDark, colorTheme, setColorTheme } |
197
+ | useConfigProvider() | 读取全局配置 | { size, zIndex, locale } |
198
+ | useFormField() | 表单字段上下文 | { id, name, formItemId, valid, isDirty, isTouched, error }(需在 FormField 内使用) |
199
+
200
+ ## 主题系统
201
+
202
+ - **Design Tokens**: CSS 自定义属性,定义在 `tokens.css`,前缀 `--`(如 `--primary`, `--background`)
203
+ - **暗色模式**: `dark.css`,通过 `<html class="dark">` 切换
204
+ - **色彩主题**: 7 种(neutral/blue/green/rose/orange/violet/slate),通过 `useTheme().setColorTheme()` 切换
205
+ - **样式工具**: 使用 `cn()` 合并 Tailwind 类名,自动解决冲突
206
+
207
+ ```ts
208
+ import { cn } from '@danweiyuan/eui'
209
+ cn('px-2 py-1', isActive && 'bg-primary text-white', className)
210
+ ```
211
+
212
+ ## 通用类型
213
+
214
+ ```ts
215
+ type Size = 'sm' | 'default' | 'lg'
216
+
217
+ interface Option {
218
+ label: string
219
+ value: string | number
220
+ disabled?: boolean
221
+ }
222
+
223
+ interface GroupedOption {
224
+ label: string
225
+ options: Option[]
226
+ }
227
+
228
+ interface MenuItem {
229
+ key: string
230
+ label: string
231
+ icon?: string
232
+ children?: MenuItem[]
233
+ disabled?: boolean
234
+ }
235
+
236
+ interface TableColumn<T = any> {
237
+ key: string
238
+ title: string
239
+ width?: number | string
240
+ minWidth?: number
241
+ sortable?: boolean
242
+ fixed?: 'left' | 'right'
243
+ align?: 'left' | 'center' | 'right'
244
+ render?: (row: T, index: number) => any
245
+ }
246
+ ```