create-dwy 0.5.1 → 0.6.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 (156) hide show
  1. package/bin/index.js +1 -1
  2. package/package.json +2 -2
  3. package/src/create.js +61 -37
  4. package/src/sync.js +212 -61
  5. package/templates/claude-global/CLAUDE.md +92 -0
  6. package/templates/claude-global/rules/dwy-android-core.md +610 -0
  7. package/templates/claude-global/rules/dwy-api-design.md +110 -0
  8. package/templates/claude-global/rules/dwy-db-migration.md +46 -0
  9. package/templates/claude-global/rules/dwy-flutter-core.md +564 -0
  10. package/templates/claude-global/rules/dwy-flutter-network.md +222 -0
  11. package/templates/claude-global/rules/dwy-flutter-router.md +97 -0
  12. package/templates/claude-global/rules/dwy-flutter-testing.md +94 -0
  13. package/templates/claude-global/rules/dwy-git-commit-convention.md +82 -0
  14. package/templates/claude-global/rules/dwy-git-security.md +53 -0
  15. package/templates/claude-global/rules/dwy-payload-limits.md +178 -0
  16. package/templates/claude-global/rules/{prefer-open-source.md → dwy-prefer-open-source.md} +4 -0
  17. package/templates/claude-global/rules/dwy-project-structure.md +181 -0
  18. package/templates/claude-global/rules/{python-code-style.md → dwy-python-core.md} +292 -358
  19. package/templates/claude-global/rules/dwy-python-fastapi.md +272 -0
  20. package/templates/claude-global/rules/dwy-python-orm.md +61 -0
  21. package/templates/claude-global/rules/{backend-security.md → dwy-python-security.md} +99 -72
  22. package/templates/claude-global/rules/dwy-python-testing.md +53 -0
  23. package/templates/claude-global/rules/{server-security.md → dwy-server-security.md} +1 -2
  24. package/templates/claude-global/rules/dwy-tdd-development.md +70 -0
  25. package/templates/claude-global/rules/dwy-tech-stack.md +60 -0
  26. package/templates/claude-global/rules/dwy-vue-api.md +146 -0
  27. package/templates/claude-global/rules/{vue-code-style.md → dwy-vue-core.md} +338 -152
  28. package/templates/claude-global/rules/dwy-vue-pinia.md +75 -0
  29. package/templates/claude-global/rules/dwy-vue-testing.md +102 -0
  30. package/templates/claude-global/skills/dwy-deploy-audit/SKILL.md +481 -0
  31. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-data.md +117 -0
  32. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +74 -0
  33. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-runtime.md +184 -0
  34. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_base.sh +39 -0
  35. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_capacity.sh +278 -0
  36. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_db.sh +122 -0
  37. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_docker.sh +249 -0
  38. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_env.sh +64 -0
  39. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_https.sh +60 -0
  40. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_logs.sh +209 -0
  41. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +92 -0
  42. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_resilience.sh +157 -0
  43. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_secrets.sh +327 -0
  44. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_services.sh +77 -0
  45. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_ssh.sh +56 -0
  46. package/templates/claude-global/skills/dwy-deploy-audit/scripts/run_all.sh +105 -0
  47. package/templates/claude-global/skills/dwy-docker/SKILL.md +187 -0
  48. package/templates/claude-global/skills/dwy-docker/references/ask-templates.md +142 -0
  49. package/templates/claude-global/skills/dwy-docker/references/templates.md +479 -0
  50. package/templates/claude-global/skills/dwy-docker/references/version-rules.md +76 -0
  51. package/templates/claude-global/skills/dwy-docker/scripts/query_dockerhub.py +204 -0
  52. package/templates/claude-global/skills/dwy-docker/scripts/scan_images.sh +185 -0
  53. package/templates/claude-global/skills/dwy-eapi/SKILL.md +433 -0
  54. package/templates/claude-global/skills/dwy-eapi/references/tasks-integration-guide.md +387 -0
  55. package/templates/claude-global/skills/dwy-ekit/SKILL.md +85 -0
  56. package/templates/claude-global/skills/dwy-ekit/references/data.md +135 -0
  57. package/templates/claude-global/skills/dwy-ekit/references/form.md +68 -0
  58. package/templates/claude-global/skills/dwy-ekit/references/request.md +183 -0
  59. package/templates/claude-global/skills/dwy-ekit/references/ui.md +109 -0
  60. package/templates/claude-global/skills/{dwy-frontend-eui → dwy-eui}/SKILL.md +129 -15
  61. package/templates/claude-global/skills/dwy-eui/references/eui-design-guide.md +257 -0
  62. package/templates/claude-global/skills/dwy-eui/references/eui-integration-guide.md +302 -0
  63. package/templates/claude-global/skills/dwy-eui/references/eui-landing-design-guide.md +437 -0
  64. package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +214 -0
  65. package/templates/claude-global/skills/dwy-mirror-source/SKILL.md +275 -0
  66. package/templates/claude-global/skills/dwy-mirror-source/references/dockerfile-snippets.md +228 -0
  67. package/templates/claude-global/skills/dwy-mirror-source/references/mirror-providers.md +150 -0
  68. package/templates/claude-global/skills/dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc +0 -0
  69. package/templates/claude-global/skills/dwy-mirror-source/scripts/apply_mirrors.py +666 -0
  70. package/templates/claude-global/skills/dwy-mirror-source/scripts/check_mirrors.py +868 -0
  71. package/templates/claude-global/skills/dwy-pentest/SKILL.md +344 -0
  72. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__init__.py +1 -0
  73. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc +0 -0
  74. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc +0 -0
  75. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc +0 -0
  76. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc +0 -0
  77. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc +0 -0
  78. package/templates/claude-global/skills/dwy-pentest/scripts/lib/config.py +109 -0
  79. package/templates/claude-global/skills/dwy-pentest/scripts/lib/executor.py +157 -0
  80. package/templates/claude-global/skills/dwy-pentest/scripts/lib/severity.py +96 -0
  81. package/templates/claude-global/skills/dwy-pentest/scripts/lib/validator.py +113 -0
  82. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_auth.py +289 -0
  83. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_recon.py +217 -0
  84. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_report.py +343 -0
  85. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_scan.py +205 -0
  86. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_ssl.py +208 -0
  87. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_vuln.py +205 -0
  88. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_web.py +330 -0
  89. package/templates/claude-global/skills/dwy-pentest/scripts/pyproject.toml +14 -0
  90. package/templates/claude-global/skills/dwy-pentest/scripts/setup_tools.sh +97 -0
  91. package/templates/claude-global/skills/dwy-pentest/scripts/uv.lock +415 -0
  92. package/templates/claude-global/skills/{dwy-release → dwy-publish}/SKILL.md +42 -18
  93. package/templates/claude-global/skills/dwy-vue-core/SKILL.md +890 -0
  94. package/templates/project/backend/.dwy.hbs +5 -0
  95. package/templates/project/backend/.python-version +1 -0
  96. package/templates/project/backend/CLAUDE.md.hbs +40 -0
  97. package/templates/project/{web/backend → backend}/alembic/script.py.mako +2 -0
  98. package/templates/project/{web/backend → backend}/alembic.ini.hbs +0 -1
  99. package/templates/project/backend/deploy.sh.hbs +18 -0
  100. package/templates/project/{web → backend}/docker-compose.prod.yml.hbs +1 -1
  101. package/templates/project/{web/backend → backend}/pyproject.toml.hbs +4 -5
  102. package/templates/project/{web/backend → backend}/src/app/config.py.hbs +1 -1
  103. package/templates/project/backend/src/app/database.py +10 -0
  104. package/templates/project/backend/src/app/dependencies.py +5 -0
  105. package/templates/project/{web/backend → backend}/src/app/main.py.hbs +3 -3
  106. package/templates/project/backend/src/app/models/base.py +5 -0
  107. package/templates/project/web/CLAUDE.md.hbs +29 -36
  108. package/templates/project/web/{frontend → apps/web}/index.html.hbs +1 -0
  109. package/templates/project/web/{frontend → apps/web}/package.json.hbs +4 -3
  110. package/templates/project/web/apps/web/src/App.vue +9 -0
  111. package/templates/project/web/apps/web/src/api/client.ts +5 -0
  112. package/templates/project/web/apps/web/src/composables/.gitkeep +0 -0
  113. package/templates/project/web/apps/web/src/layouts/DefaultLayout.vue +8 -0
  114. package/templates/project/web/{frontend → apps/web}/src/router/index.ts +1 -1
  115. package/templates/project/web/{frontend → apps/web}/src/stores/auth.ts +4 -2
  116. package/templates/project/web/apps/web/src/style.css +2 -0
  117. package/templates/project/web/apps/web/src/types/router.d.ts +8 -0
  118. package/templates/project/web/apps/web/src/views/HomeView.vue +8 -0
  119. package/templates/project/web/apps/web/tsconfig.app.json +18 -0
  120. package/templates/project/web/apps/web/tsconfig.json +6 -0
  121. package/templates/project/web/{frontend → apps/web}/vite.config.ts.hbs +7 -1
  122. package/templates/project/web/package.json.hbs +16 -0
  123. package/templates/project/web/packages/.gitkeep +0 -0
  124. package/templates/project/web/pnpm-workspace.yaml +3 -0
  125. package/templates/claude-global/hooks/post-sync-claude-global.sh +0 -72
  126. package/templates/claude-global/rules/agent-team-execution.md +0 -138
  127. package/templates/claude-global/rules/agent-team-patterns.md +0 -157
  128. package/templates/claude-global/rules/git-commit-convention.md +0 -106
  129. package/templates/claude-global/rules/git-security.md +0 -144
  130. package/templates/claude-global/skills/dwy-backend-base/SKILL.md +0 -279
  131. package/templates/claude-global/skills/dwy-frontend-core/SKILL.md +0 -181
  132. package/templates/project/web/.claude/commands/deploy.md +0 -4
  133. package/templates/project/web/backend/src/app/models/base.py +0 -5
  134. package/templates/project/web/deploy.sh.hbs +0 -113
  135. package/templates/project/web/frontend/src/App.vue +0 -3
  136. package/templates/project/web/frontend/src/api/client.ts +0 -13
  137. package/templates/project/web/frontend/src/style.css +0 -1
  138. package/templates/project/web/frontend/src/views/HomeView.vue +0 -5
  139. package/templates/project/web/frontend/tsconfig.app.json +0 -14
  140. package/templates/project/web/frontend/tsconfig.json +0 -4
  141. /package/templates/{project/web/backend → claude-global/skills/dwy-pentest/scripts}/.python-version +0 -0
  142. /package/templates/project/{web/backend → backend}/Dockerfile +0 -0
  143. /package/templates/project/{web/backend → backend}/alembic/env.py.hbs +0 -0
  144. /package/templates/{claude-global/skills → project/backend/alembic/versions}/.gitkeep +0 -0
  145. /package/templates/project/{web → backend}/docker-compose.dev.yml.hbs +0 -0
  146. /package/templates/project/{web/backend → backend}/src/app/__init__.py +0 -0
  147. /package/templates/project/{web/backend → backend}/src/app/models/__init__.py +0 -0
  148. /package/templates/project/{web/backend → backend}/src/app/routers/__init__.py +0 -0
  149. /package/templates/project/{web/backend → backend}/src/app/routers/health.py +0 -0
  150. /package/templates/project/{web/backend → backend}/src/app/schemas/__init__.py +0 -0
  151. /package/templates/project/{web/backend → backend}/src/app/services/__init__.py +0 -0
  152. /package/templates/project/{web/backend → backend}/tests/__init__.py +0 -0
  153. /package/templates/project/{web/backend → backend}/tests/conftest.py.hbs +0 -0
  154. /package/templates/project/{web/backend/alembic/versions → mobile}/.gitkeep +0 -0
  155. /package/templates/project/web/{frontend → apps/web}/src/components/.gitkeep +0 -0
  156. /package/templates/project/web/{frontend → apps/web}/src/main.ts +0 -0
@@ -0,0 +1,183 @@
1
+ # request — HTTP 客户端(防腐层)
2
+
3
+ > 何时读这份:当用户使用本模块的 API 或问法涉及本模块功能时读取(HTTP 请求、createRequest、内置插件、refreshToken、dwyeapi 响应契约对接、业务错误捕获)。
4
+
5
+ ```ts
6
+ import {
7
+ createRequest, tokenPlugin, headerPlugin, unwrapPlugin, refreshTokenPlugin,
8
+ SUCCESS_CODE, extractValidationErrors, isApiBusinessError,
9
+ } from '@dwydev/ekit'
10
+ import type {
11
+ HttpClient, HttpConfig, HttpResponse, HttpError, HttpPlugin,
12
+ HttpMethod, HttpResponseType, CreateRequestOptions,
13
+ ApiResponse, PageData, ValidationErrorData, ValidationFieldError,
14
+ CommonBusinessCode, BusinessCode, ApiBusinessError,
15
+ } from '@dwydev/ekit'
16
+ ```
17
+
18
+ ## createRequest(options?) → HttpClient
19
+
20
+ 底层是 axios,但**返回 `HttpClient` 而不是 `AxiosInstance`**。
21
+
22
+ ```ts
23
+ const http: HttpClient = createRequest({
24
+ baseURL: '/api', // 默认 '/api'
25
+ timeout: 30000, // 默认 30000
26
+ headers: { 'X-Client': 'web' }, // 共享默认请求头
27
+ plugins: [
28
+ tokenPlugin({ getToken: () => localStorage.getItem('token') }),
29
+ unwrapPlugin(),
30
+ ],
31
+ })
32
+
33
+ // HttpClient 接口
34
+ const res = await http.get<User>('/users/1') // res: HttpResponse<User>
35
+ const res = await http.post<User>('/users', { ... })
36
+ const res = await http.request({ method: 'PUT', url: '/users/1', data })
37
+ // 还有 put / delete / patch / head
38
+ ```
39
+
40
+ ## HttpConfig / HttpResponse / HttpError
41
+
42
+ ```ts
43
+ interface HttpConfig {
44
+ url?: string
45
+ method?: HttpMethod // 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'
46
+ baseURL?: string
47
+ headers?: Record<string, string>
48
+ params?: Record<string, any>
49
+ data?: any
50
+ timeout?: number
51
+ responseType?: HttpResponseType // 'json' | 'blob' | 'text' | 'arraybuffer'
52
+ extra?: Record<string, unknown> // 逃生舱:透传 axios 特有配置
53
+ }
54
+
55
+ interface HttpResponse<T = any> {
56
+ data: T
57
+ status: number
58
+ statusText: string
59
+ headers: Record<string, string>
60
+ config: HttpConfig
61
+ }
62
+
63
+ interface HttpError<T = any> extends Error {
64
+ config?: HttpConfig
65
+ response?: HttpResponse<T>
66
+ code?: string
67
+ businessCode?: string // v0.7.0 新增:后端业务错误码(如 "NOT_FOUND")
68
+ apiResponse?: ApiResponse<unknown> // v0.7.0 新增:原始 dwyeapi 响应信封
69
+ }
70
+ ```
71
+
72
+ ## HttpPlugin 接口
73
+
74
+ ```ts
75
+ interface HttpPlugin {
76
+ onRequest?: (config: HttpConfig) => HttpConfig | Promise<HttpConfig>
77
+ onResponse?: (response: HttpResponse) => HttpResponse | Promise<HttpResponse>
78
+ // 返回非 undefined 视为已恢复,终止后续插件
79
+ onResponseError?: (error: HttpError) => any
80
+ }
81
+ ```
82
+
83
+ > 旧版的 `RequestPlugin` 已重命名为 `HttpPlugin`,所有钩子参数类型从 axios 类型改为 ekit 自有契约。
84
+
85
+ ## 内置插件
86
+
87
+ | 插件 | 用途 | 参数 |
88
+ |------|------|------|
89
+ | `tokenPlugin` | 注入 `Authorization: Bearer <token>` | `{ getToken: () => string \| null }` |
90
+ | `headerPlugin` | 注入自定义动态 header | `{ name: string, getValue: () => string \| null }` |
91
+ | `unwrapPlugin` | 解包 dwyeapi `{ code, message, data, timestamp }`,code !== `"SUCCESS"` 时 reject(附带 `businessCode` / `apiResponse`);非该格式原样透传 | 无 |
92
+ | `refreshTokenPlugin` | 401 自动刷新并重放 | 见下 |
93
+
94
+ ## refreshTokenPlugin(带 retry 注入)
95
+
96
+ 新版需要传 `retry`,因为插件不再持有 client 引用:
97
+
98
+ ```ts
99
+ let http: HttpClient
100
+ http = createRequest({
101
+ plugins: [
102
+ tokenPlugin({ getToken: () => localStorage.getItem('token') }),
103
+ refreshTokenPlugin({
104
+ getRefreshToken: () => localStorage.getItem('refreshToken'),
105
+ refreshFn: async (refreshToken) => {
106
+ const res = await http.post<{ token: string }>('/auth/refresh', { refreshToken })
107
+ localStorage.setItem('token', res.data.token)
108
+ return res.data.token
109
+ },
110
+ onRefreshFail: () => {
111
+ localStorage.clear()
112
+ window.location.href = '/login'
113
+ },
114
+ retry: (cfg) => http.request(cfg), // 必填:重放请求
115
+ isLoginUrl: (url) => url.includes('/auth/login'), // 可选
116
+ }),
117
+ unwrapPlugin(),
118
+ ],
119
+ })
120
+ ```
121
+
122
+ ## 响应契约对齐 dwyeapi v0.7.0
123
+
124
+ 所有接口统一信封 `{ code, message, data, timestamp }`,`code === "SUCCESS"` 表示业务成功。unwrapPlugin 自动按此契约解包,业务代码拿到的 `response.data` 就是纯业务数据或 `PageData<T>`。
125
+
126
+ ```ts
127
+ import { SUCCESS_CODE } from '@dwydev/ekit'
128
+ import type { ApiResponse, PageData } from '@dwydev/ekit'
129
+
130
+ // 单体响应
131
+ const res = await http.get<User>('/users/1')
132
+ // res.data: User(unwrap 后是 User 不是 ApiResponse<User>)
133
+
134
+ // 分页响应
135
+ const list = await http.get<PageData<User>>('/users?page=1&page_size=20')
136
+ list.data.items // User[]
137
+ list.data.total // number
138
+ list.data.page // number
139
+ list.data.page_size // number(保持 snake_case 对齐后端 JSON)
140
+ ```
141
+
142
+ **内置业务错误码(`CommonBusinessCode`):**
143
+
144
+ | code | HTTP | 含义 |
145
+ |------|------|------|
146
+ | `SUCCESS` | 200 | 业务成功,unwrap 自动解包 data |
147
+ | `NOT_FOUND` | 404 | 资源不存在 |
148
+ | `BUSINESS_ERROR` | 422 | 业务规则不允许(基础码;业务常自定义为 `INSUFFICIENT_BALANCE` 等) |
149
+ | `PERMISSION_DENIED` | 403 | 无权限 |
150
+ | `AUTHENTICATION_FAILED` | 401 | 认证失败 |
151
+ | `VALIDATION_ERROR` | 422 | 请求参数校验失败,`data.errors` 为字段错误数组 |
152
+ | `INTERNAL_ERROR` | 500 | 服务器错误 |
153
+ | `HTTP_{status_code}` | 原状态 | 第三方 HTTPException 透传(如 OAuth2 的 `HTTP_401`) |
154
+
155
+ `BusinessCode = CommonBusinessCode | (string & {})`:既自动补全常见码,又允许业务 `BusinessError(code="CUSTOM")` 自定义。
156
+
157
+ ## 捕获业务错误:isApiBusinessError + extractValidationErrors
158
+
159
+ unwrapPlugin 失败时抛 `HttpError`,附带 `businessCode`(业务错误码)和 `apiResponse`(原始信封)。业务 catch 直接按 code 分支:
160
+
161
+ ```ts
162
+ import { isApiBusinessError, extractValidationErrors } from '@dwydev/ekit'
163
+
164
+ try {
165
+ await http.post('/orders', body)
166
+ } catch (err) {
167
+ if (!isApiBusinessError(err)) {
168
+ message.error('网络异常')
169
+ return
170
+ }
171
+ switch (err.businessCode) {
172
+ case 'INSUFFICIENT_BALANCE': showRecharge(); break
173
+ case 'NOT_FOUND': message.warning('订单不存在'); break
174
+ case 'VALIDATION_ERROR':
175
+ // err.apiResponse.data.errors: [{ field: "body.email", message: "..." }, ...]
176
+ form.setErrors(extractValidationErrors(err))
177
+ break
178
+ default: message.error(err.message)
179
+ }
180
+ }
181
+ ```
182
+
183
+ `extractValidationErrors` 自动剥离 `body. / query. / path. / header. / cookie.` 前缀,返回 `{ field: message }` 扁平对象,与 vee-validate 的 `form.setErrors` 直接兼容。嵌套字段路径(如 `body.items.0.name`)保留为 `items.0.name`。输入可以是 HttpError、完整 ApiResponse、或裸 `ValidationErrorData`,非校验错误时返回空对象。
@@ -0,0 +1,109 @@
1
+ # UI 交互(copy / file / hooks)
2
+
3
+ > 何时读这份:当用户使用本模块的 API 或问法涉及本模块功能时读取(剪贴板、文件下载/Blob/文件大小、Vue Composables 工具)。
4
+
5
+ ## copy — 剪贴板
6
+
7
+ ```ts
8
+ import { copyText, useClipboard } from '@dwydev/ekit'
9
+ ```
10
+
11
+ ### copyText(text)
12
+
13
+ 非组件场景一次性复制:
14
+
15
+ ```ts
16
+ await copyText('hello')
17
+ ```
18
+
19
+ ### useClipboard — 直接再导出 @vueuse/core
20
+
21
+ ```ts
22
+ const { text, copy, copied, isSupported } = useClipboard({ source: '', copiedDuring: 1500 })
23
+ copy('hello')
24
+ // copied: ComputedRef<boolean>,1500ms 后自动复位
25
+ // isSupported: ComputedRef<boolean>,注意是 computed 不是 ref
26
+ ```
27
+
28
+ > 旧版 ekit 自写的 `useClipboard` 返回结构略有不同,迁移时需对照新签名。
29
+
30
+ ---
31
+
32
+ ## file — 文件下载 / Blob / 文件大小
33
+
34
+ ```ts
35
+ import { downloadFile, saveBlob, formatFileSize } from '@dwydev/ekit'
36
+ import type { DownloadOptions, FileRequester } from '@dwydev/ekit' // requester 不是 requestInstance
37
+ ```
38
+
39
+ ### downloadFile(url, options?)
40
+
41
+ ```ts
42
+ await downloadFile('/api/export.xlsx') // GET,自动从 Content-Disposition 解析文件名
43
+ await downloadFile('/api/export', {
44
+ filename: '报表.xlsx',
45
+ method: 'POST',
46
+ data: { dateRange: [start, end] },
47
+ headers: { Authorization: `Bearer ${token}` },
48
+ })
49
+
50
+ // 用自己的 HTTP 客户端发请求(不传则动态 import axios)
51
+ await downloadFile('/api/export', { requester: myAxiosInstance })
52
+ ```
53
+
54
+ ### FileRequester 契约
55
+
56
+ 任何满足以下结构的对象都可以作为 requester(axios 实例天然满足):
57
+
58
+ ```ts
59
+ interface FileRequester {
60
+ request(config: {
61
+ url: string
62
+ method?: 'GET' | 'POST'
63
+ data?: any
64
+ headers?: Record<string, string>
65
+ responseType: 'blob'
66
+ }): Promise<{
67
+ data: Blob | ArrayBuffer
68
+ headers?: Record<string, string | undefined>
69
+ }>
70
+ }
71
+ ```
72
+
73
+ > 旧版字段 `requestInstance: AxiosInstance` 已改名 `requester: FileRequester`,类型不再泄露 axios。
74
+
75
+ ### 其他
76
+
77
+ ```ts
78
+ saveBlob(blob, 'data.json') // 直接保存已有 Blob
79
+ formatFileSize(1234567) // '1.18 MB'
80
+ formatFileSize(1234567, 0) // '1 MB'
81
+ ```
82
+
83
+ ---
84
+
85
+ ## hooks — Vue Composables
86
+
87
+ **全部再导出 `@vueuse/core`**,ekit 不再自写。从 `@dwydev/ekit` 主入口导入即可:
88
+
89
+ ```ts
90
+ import {
91
+ // src/hooks/index.ts
92
+ useDebounce, useClickOutside, useEventListener,
93
+ // src/hooks/vueuse.ts
94
+ useThrottle, useWindowSize, useMediaQuery, useIntersectionObserver, useResizeObserver,
95
+ } from '@dwydev/ekit'
96
+ ```
97
+
98
+ | ekit 名 | VueUse 实现 | 说明 |
99
+ |---------|-------------|------|
100
+ | `useDebounce(value, delay=300)` | `refDebounced` | 默认 300ms(VueUse 默认 200ms) |
101
+ | `useClickOutside(target, handler)` | `onClickOutside` | 点击元素外部触发 |
102
+ | `useEventListener(target, event, handler, options?)` | 同名 | 自动绑/解绑,支持 `Ref<EventTarget>` |
103
+ | `useThrottle(fn, ms)` | `useThrottleFn` | 节流函数 |
104
+ | `useWindowSize()` | 同名 | `{ width, height }` |
105
+ | `useMediaQuery(query)` | 同名 | `useMediaQuery('(max-width: 768px)')` |
106
+ | `useIntersectionObserver(target, cb)` | 同名 | 监听元素出现/消失 |
107
+ | `useResizeObserver(target, cb)` | 同名 | 监听元素尺寸变化 |
108
+
109
+ > 旧版 ekit 自写的 `useDebounce` 返回普通 `Ref<T>`,新版返回 VueUse 的 `Readonly<Ref<T>>`,**不能** `debouncedSearch.value = '...'`。
@@ -1,34 +1,146 @@
1
1
  ---
2
- name: dwy-frontend-eui
3
- description: "@danweiyuan/eui Vue 3 组件库速查。触发条件:使用 Vue 3 构建页面、选择 UI 组件、查询 eui 组件 API 时。"
2
+ name: dwy-eui
3
+ description: "@dwydev/eui Vue 3 组件库速查。触发条件:使用 Vue 3 构建页面、选择 UI 组件、查询 eui 组件 API 时。"
4
4
  ---
5
5
 
6
- # @danweiyuan/eui 组件速查
6
+ # @dwydev/eui 组件速查
7
7
 
8
8
  基于 Reka-ui 原语层 + shadcn-vue 设计风格 + Element Plus 式 API 的 Vue 3 组件库。
9
9
 
10
+ ## 集成指南
11
+
12
+ 新项目集成 EUI 时,读取 `references/eui-integration-guide.md`,包含完整的安装、Vite 配置、样式引入、组件注册、主题切换等步骤。
13
+
14
+ ## 设计风格规范
15
+
16
+ - **中后台页面**:读取 `references/eui-design-guide.md`,包含颜色系统、布局规范、排版规范、组件使用规范、交互规范等。
17
+ - **落地页/营销页**:读取 `references/eui-landing-design-guide.md`,包含 Hero、功能展示、社会证明、定价、FAQ 等 Section 模板和动画规范。
18
+
19
+ ---
20
+
10
21
  ## 安装与引入
11
22
 
12
23
  ```bash
13
- pnpm add @danweiyuan/eui
24
+ pnpm add @dwydev/eui
14
25
  ```
15
26
 
16
27
  ```ts
17
28
  // 按需导入(推荐)
18
- import { EButton, EInput, ETable } from '@danweiyuan/eui'
29
+ import { EButton, EInput, ETable } from '@dwydev/eui'
19
30
 
20
31
  // 全局注册(Vue plugin)
21
- import EUI from '@danweiyuan/eui'
32
+ import EUI from '@dwydev/eui'
22
33
  app.use(EUI)
23
34
  ```
24
35
 
25
36
  ```ts
26
37
  // 主题 CSS(必须引入)
27
- import '@danweiyuan/eui/theme' // 基础样式 + tokens
28
- import '@danweiyuan/eui/theme/dark' // 暗色模式(可选)
29
- import '@danweiyuan/eui/theme/tokens' // 仅 design tokens(可选)
38
+ import '@dwydev/eui/theme' // 基础样式 + tokens
39
+ import '@dwydev/eui/theme/dark' // 暗色模式(可选)
40
+ import '@dwydev/eui/theme/tokens' // 仅 design tokens(可选)
41
+ ```
42
+
43
+ ### 全局配置 EConfigProvider(必须)
44
+
45
+ **EConfigProvider 是使用 EUI 的前置条件。** 必须在 App 根组件用 `<EConfigProvider>` 包裹整个应用,否则以下功能不生效:
46
+
47
+ - 国际化(日期选择器、月份/年份选择器等显示为英文而非中文)
48
+ - 全局组件尺寸统一控制
49
+ - 弹层 z-index 统一管理
50
+ - 组件内部 UI 文案(确定/取消/暂无数据等)
51
+
52
+ #### 基础用法
53
+
54
+ ```vue
55
+ <!-- App.vue -->
56
+ <script setup lang="ts">
57
+ import { EConfigProvider } from '@dwydev/eui'
58
+ </script>
59
+
60
+ <template>
61
+ <EConfigProvider>
62
+ <RouterView />
63
+ </EConfigProvider>
64
+ </template>
30
65
  ```
31
66
 
67
+ 使用默认配置即可满足中文项目需求,无需传任何 props。
68
+
69
+ #### Props
70
+
71
+ | Prop | 类型 | 默认值 | 说明 |
72
+ |------|------|--------|------|
73
+ | size | `'sm' \| 'default' \| 'lg'` | `'default'` | 所有组件的全局尺寸 |
74
+ | zIndex | `number` | `2000` | 弹层组件(Dialog/Drawer/Popover 等)的基准 z-index |
75
+ | locale | `Record<string, string>` | 见下方 | 国际化配置 |
76
+
77
+ #### locale 配置
78
+
79
+ locale 是一个扁平的键值对象,包含两部分内容:
80
+
81
+ | 字段 | 默认值 | 说明 |
82
+ |------|--------|------|
83
+ | name | `'zh-CN'` | **BCP 47 语言标签**,控制日期选择器(EDatePicker)、日历(ECalendar)等组件的本地化显示(月份名、星期名、年份格式等) |
84
+ | confirm | `'确定'` | 确认按钮文案 |
85
+ | cancel | `'取消'` | 取消按钮文案 |
86
+ | close | `'关闭'` | 关闭按钮文案 |
87
+ | loading | `'加载中...'` | 加载状态文案 |
88
+ | empty | `'暂无数据'` | 空状态文案 |
89
+ | search | `'搜索'` | 搜索占位文案 |
90
+ | selectPlaceholder | `'请选择'` | 选择器默认占位文案 |
91
+ | inputPlaceholder | `'请输入'` | 输入框默认占位文案 |
92
+
93
+ #### 自定义配置示例
94
+
95
+ ```vue
96
+ <!-- 英文项目 -->
97
+ <EConfigProvider
98
+ size="sm"
99
+ :z-index="3000"
100
+ :locale="{
101
+ name: 'en-US',
102
+ confirm: 'OK',
103
+ cancel: 'Cancel',
104
+ close: 'Close',
105
+ loading: 'Loading...',
106
+ empty: 'No data',
107
+ search: 'Search',
108
+ selectPlaceholder: 'Please select',
109
+ inputPlaceholder: 'Please input',
110
+ }"
111
+ >
112
+ <RouterView />
113
+ </EConfigProvider>
114
+ ```
115
+
116
+ #### 嵌套覆盖
117
+
118
+ EConfigProvider 支持嵌套,内层配置覆盖外层。适用于局部区域需要不同配置的场景:
119
+
120
+ ```vue
121
+ <EConfigProvider> <!-- 全局中文 -->
122
+ <EConfigProvider :locale="{ name: 'en-US', ... }"> <!-- 局部英文 -->
123
+ <EDatePicker type="month" /> <!-- 显示英文月份 -->
124
+ </EConfigProvider>
125
+ </EConfigProvider>
126
+ ```
127
+
128
+ #### 实现原理
129
+
130
+ EConfigProvider 内部做了两件事:
131
+ 1. 通过 Vue `provide/inject` 向子树注入 size、zIndex、locale 配置,EUI 组件通过 `useConfigProvider()` 读取
132
+ 2. 包裹 reka-ui 的 `ConfigProvider`,将 `locale.name` 透传为 reka-ui 的全局 locale,所有基于 reka-ui 的日期类组件自动继承
133
+
134
+ #### 不使用 EConfigProvider 的后果
135
+
136
+ | 功能 | 不包裹时的表现 |
137
+ |------|--------------|
138
+ | EDatePicker 月份/星期 | 显示英文(Jan、Feb、Mon、Tue) |
139
+ | ECalendar 月份/星期 | 显示英文 |
140
+ | 组件尺寸 | 各组件使用各自默认值,无法统一控制 |
141
+ | 弹层 z-index | 各组件使用各自默认值,可能层级混乱 |
142
+ | UI 文案 | useConfigProvider() 使用内部默认值(中文),但不保证一致性 |
143
+
32
144
  ## 命名约定
33
145
 
34
146
  - 组件前缀 `E`:`EButton`, `EInput`, `EDialog`
@@ -78,7 +190,7 @@ frontend/eui/src/components/{component-name}/types.ts
78
190
  | EInput | 输入框 | v-model, type, placeholder, size, clearable, showPassword, disabled, readonly, maxlength, showWordLimit |
79
191
  | ETextarea | 多行输入 | v-model, placeholder, rows, autoResize, maxlength, disabled, showWordLimit, minRows, maxRows |
80
192
  | ENumberField | 数字输入 | v-model, min, max, step, size, disabled, precision, controlsPosition, placeholder, readonly |
81
- | ESelect | 下拉选择 | v-model, options(Option[]), placeholder, clearable, size, disabled, filterable, multiple, collapseTags, remote, remoteMethod, loading |
193
+ | ESelect | 下拉选择 | v-model, options(Option[]), placeholder, clearable, size, disabled, filterable, multiple, collapseTags, remote, remoteMethod, loading。**注意:option.value 不能为空字符串**,reka-ui 不支持 |
82
194
  | ENativeSelect | 原生 select | v-model, options(SelectOption[]), placeholder, disabled |
83
195
  | ECombobox | 组合框 | v-model, options(Option[]), placeholder, emptyText, disabled |
84
196
  | EAutocomplete | 自动补全 | v-model, fetchSuggestions(async fn), debounce, placeholder |
@@ -103,6 +215,10 @@ frontend/eui/src/components/{component-name}/types.ts
103
215
  | EField | 字段布局 | orientation(vertical/horizontal/responsive), invalid |
104
216
  | EInputGroup | 输入组 | 子组件 EInputGroupAddon(align) |
105
217
 
218
+ #### 表单强制约束
219
+
220
+ **禁止使用原生 `<form>` 标签,所有表单必须使用 `<EForm>` 组件。** EForm 内置 vee-validate 校验、provide/inject 上下文传递、统一的 label 布局和 reset/validate API,原生 `<form>` 无法与 EUI 表单体系集成。
221
+
106
222
  ### 数据展示
107
223
 
108
224
  | 组件 | 用途 | 关键 Props |
@@ -122,7 +238,7 @@ frontend/eui/src/components/{component-name}/types.ts
122
238
 
123
239
  | 组件 | 用途 | 关键 Props |
124
240
  |------|------|-----------|
125
- | EDialog | 对话框 | v-model:open, title, description, showClose, maxWidth, draggable, closeOnClickModal, closeOnPressEscape, fullscreen, destroyOnClose |
241
+ | EDialog | 对话框 | v-model:open, title, description, showClose, maxWidth, draggable, closeOnClickModal, closeOnPressEscape, fullscreen, destroyOnClose。内容超出视口时 body 自动滚动,header/footer 固定。**注意:body 区域有 `overflow-y-auto`,首尾位置的 input/select 等表单控件的 focus-ring 会被裁切,需在内容区域添加 `p-1` 或 `py-1` padding 规避** |
126
242
  | EDrawer | 抽屉 | v-model:open, title, description, direction(top/right/bottom/left), showClose |
127
243
  | ESheet | 侧边面板 | v-model:open, title, description, side(top/right/bottom/left), showClose |
128
244
  | EAlertDialog | 确认对话框 | v-model:open, title, description, confirmText, cancelText |
@@ -152,7 +268,6 @@ frontend/eui/src/components/{component-name}/types.ts
152
268
 
153
269
  | 组件 | 子组件 | 用途 |
154
270
  |------|--------|------|
155
- | ESidebar | ESidebarProvider, ESidebar, ESidebarTrigger, ESidebarContent, ESidebarHeader, ESidebarFooter, ESidebarGroup, ESidebarGroupLabel, ESidebarGroupContent, ESidebarMenu, ESidebarMenuItem, ESidebarMenuButton | 侧边栏。Provider(defaultOpen, v-model:open) |
156
271
  | ECommand | ECommandInput, ECommandList, ECommandEmpty, ECommandGroup, ECommandItem, ECommandSeparator, ECommandShortcut | 命令面板(配合 useCommand) |
157
272
  | ENavigationMenu | ENavigationMenuList, ENavigationMenuItem, ENavigationMenuTrigger, ENavigationMenuContent, ENavigationMenuLink, ENavigationMenuViewport | 导航菜单 |
158
273
  | EMenubar | EMenubarMenu, EMenubarTrigger, EMenubarContent, EMenubarItem, EMenubarSeparator, EMenubarCheckboxItem, EMenubarRadioGroup, EMenubarRadioItem, EMenubarSub, EMenubarSubTrigger, EMenubarSubContent | 菜单栏 |
@@ -162,7 +277,7 @@ frontend/eui/src/components/{component-name}/types.ts
162
277
 
163
278
  | 组件 | 用途 | 关键 Props |
164
279
  |------|------|-----------|
165
- | EConfigProvider | 全局配置注入 | size, zIndex, locale(Record) |
280
+ | EConfigProvider | **必须**包裹 App 根组件,提供国际化/尺寸/层级全局配置 | size, zIndex, locale(Record, 含 name 字段为 BCP 47 语言标签,默认 zh-CN) |
166
281
  | EToggle | 切换按钮 | v-model, variant(default/outline), size, disabled |
167
282
  | EToggleGroup | 切换按钮组 | v-model, type(single/multiple), variant, size, disabled |
168
283
  | EButtonGroup | 按钮组 | orientation(horizontal/vertical) |
@@ -180,7 +295,6 @@ frontend/eui/src/components/{component-name}/types.ts
180
295
  | EFormDialog | 表单弹窗 | v-model:open, title, size, loading, confirmText, cancelText |
181
296
  | EConfirmDialog | 确认弹窗 | v-model:open, title, message, type(info/warning/error), confirmText, cancelText |
182
297
  | EAdminLayout | 后台管理布局 | logo, title, menuItems(MenuItem[]), v-model:activeKey, v-model:collapsed, headerHeight, sidebarWidth |
183
- | ELoginLayout | 登录页布局 | title, logo, background |
184
298
  | EAIChat | AI 对话 | messages(ChatMessage[]), loading, placeholder |
185
299
  | ETimetableGrid | 课程表/时间表 | data(TimetableItem[]), startHour, endHour, days |
186
300
 
@@ -205,7 +319,7 @@ frontend/eui/src/components/{component-name}/types.ts
205
319
  - **样式工具**: 使用 `cn()` 合并 Tailwind 类名,自动解决冲突
206
320
 
207
321
  ```ts
208
- import { cn } from '@danweiyuan/eui'
322
+ import { cn } from '@dwydev/eui'
209
323
  cn('px-2 py-1', isActive && 'bg-primary text-white', className)
210
324
  ```
211
325