@deepstorm/cli 0.1.0 → 0.2.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 (25) hide show
  1. package/dist/config-schema.json +1 -1
  2. package/dist/registry.json +65 -5
  3. package/dist/skills/reef-gen-frontend/variants/react/steps.md +31 -0
  4. package/dist/skills/reef-gen-frontend/variants/vue/steps.md +34 -0
  5. package/dist/skills/reef-style-frontend/fragments/test/vitest-react/examples/testing.md +283 -0
  6. package/dist/skills/reef-style-frontend/fragments/test/vitest-react/quick-reference.md +152 -0
  7. package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/examples/testing.md +457 -0
  8. package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/quick-reference.md +186 -0
  9. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/examples/ui-components.md +257 -0
  10. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/quick-reference.md +166 -0
  11. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/examples/ui-components.md +353 -0
  12. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/quick-reference.md +267 -0
  13. package/dist/skills/reef-style-frontend/variants/react/examples/code-wrapping.md +124 -0
  14. package/dist/skills/reef-style-frontend/variants/react/examples/component-types-pipes.md +195 -0
  15. package/dist/skills/reef-style-frontend/variants/react/examples/entity-types.md +127 -0
  16. package/dist/skills/reef-style-frontend/variants/react/examples/forms-layer.md +251 -0
  17. package/dist/skills/reef-style-frontend/variants/react/examples/service-routing.md +226 -0
  18. package/dist/skills/reef-style-frontend/variants/react/quick-reference.md +118 -0
  19. package/dist/skills/reef-style-frontend/variants/vue/examples/code-wrapping.md +162 -0
  20. package/dist/skills/reef-style-frontend/variants/vue/examples/component-types-pipes.md +270 -0
  21. package/dist/skills/reef-style-frontend/variants/vue/examples/entity-types.md +145 -0
  22. package/dist/skills/reef-style-frontend/variants/vue/examples/forms-layer.md +231 -0
  23. package/dist/skills/reef-style-frontend/variants/vue/examples/service-routing.md +224 -0
  24. package/dist/skills/reef-style-frontend/variants/vue/quick-reference.md +145 -0
  25. package/package.json +3 -1
@@ -0,0 +1,224 @@
1
+ # Vue 数据获取层 + 路由配置示例
2
+
3
+ ## 组合式函数(数据获取层)
4
+
5
+ ```typescript
6
+ // composables/useUsers.ts
7
+ import { ref, computed, watch } from 'vue'
8
+ import type { User, PageResponse } from '@/shared/types/user'
9
+
10
+ interface UseUsersOptions {
11
+ page?: number
12
+ size?: number
13
+ search?: string
14
+ role?: string
15
+ }
16
+
17
+ // 使用 URLSearchParams 构建查询参数
18
+ function buildQuery(params: UseUsersOptions): string {
19
+ const searchParams = new URLSearchParams()
20
+ if (params.page !== undefined) searchParams.set('page', String(params.page))
21
+ if (params.size !== undefined) searchParams.set('size', String(params.size))
22
+ if (params.search) searchParams.set('search', params.search)
23
+ if (params.role) searchParams.set('role', params.role)
24
+ const qs = searchParams.toString()
25
+ return qs ? `?${qs}` : ''
26
+ }
27
+
28
+ export function useUsers(options: UseUsersOptions = {}) {
29
+ const users = ref<User[]>([])
30
+ const total = ref(0)
31
+ const loading = ref(true)
32
+ const error = ref<Error | null>(null)
33
+ const refreshKey = ref(0)
34
+
35
+ async function fetchUsers() {
36
+ loading.value = true
37
+ error.value = null
38
+
39
+ try {
40
+ const res = await fetch(`/api/v1/users${buildQuery(options)}`)
41
+ if (!res.ok) throw new Error(`HTTP ${res.status}`)
42
+ const data: PageResponse<User> = await res.json()
43
+ users.value = data.content
44
+ total.value = data.totalElements
45
+ } catch (e) {
46
+ error.value = e as Error
47
+ } finally {
48
+ loading.value = false
49
+ }
50
+ }
51
+
52
+ watch([() => options.page, () => options.size, () => options.search, () => options.role, refreshKey], () => {
53
+ fetchUsers()
54
+ }, { immediate: true })
55
+
56
+ function refresh() {
57
+ refreshKey.value++
58
+ }
59
+
60
+ return { users, total, loading, error, refresh }
61
+ }
62
+
63
+ // 写操作组合式函数
64
+ export function useCreateUser() {
65
+ const loading = ref(false)
66
+
67
+ async function create(data: CreateUserRequest): Promise<User> {
68
+ loading.value = true
69
+ try {
70
+ const res = await fetch('/api/v1/users', {
71
+ method: 'POST',
72
+ headers: { 'Content-Type': 'application/json' },
73
+ body: JSON.stringify(data),
74
+ })
75
+ if (!res.ok) throw new Error(`HTTP ${res.status}`)
76
+ return await res.json()
77
+ } finally {
78
+ loading.value = false
79
+ }
80
+ }
81
+
82
+ return { create, loading }
83
+ }
84
+ ```
85
+
86
+ ## Pinia Store 示例
87
+
88
+ ```typescript
89
+ // stores/auth.ts
90
+ import { defineStore } from 'pinia'
91
+ import { ref, computed } from 'vue'
92
+ import type { User } from '@/shared/types/user'
93
+
94
+ export const useAuthStore = defineStore('auth', () => {
95
+ // state
96
+ const user = ref<User | null>(null)
97
+ const token = ref<string | null>(localStorage.getItem('auth_token'))
98
+ const loading = ref(false)
99
+
100
+ // getters
101
+ const isAuthenticated = computed(() => !!token.value)
102
+ const isAdmin = computed(() => user.value?.role === 'ADMIN')
103
+
104
+ // actions
105
+ async function login(username: string, password: string) {
106
+ loading.value = true
107
+ try {
108
+ const res = await fetch('/api/v1/auth/login', {
109
+ method: 'POST',
110
+ headers: { 'Content-Type': 'application/json' },
111
+ body: JSON.stringify({ username, password }),
112
+ })
113
+ const data = await res.json()
114
+ token.value = data.token
115
+ user.value = data.user
116
+ localStorage.setItem('auth_token', data.token)
117
+ } finally {
118
+ loading.value = false
119
+ }
120
+ }
121
+
122
+ function logout() {
123
+ user.value = null
124
+ token.value = null
125
+ localStorage.removeItem('auth_token')
126
+ }
127
+
128
+ return { user, token, loading, isAuthenticated, isAdmin, login, logout }
129
+ })
130
+ ```
131
+
132
+ ## 路由配置
133
+
134
+ ```typescript
135
+ // router/index.ts
136
+ import { createRouter, createWebHistory } from 'vue-router'
137
+ import { useAuthStore } from '@/stores/auth'
138
+
139
+ const router = createRouter({
140
+ history: createWebHistory(import.meta.env.BASE_URL),
141
+ routes: [
142
+ {
143
+ path: '/',
144
+ name: 'dashboard',
145
+ component: () => import('@/views/Dashboard.vue'),
146
+ meta: { requiresAuth: true },
147
+ },
148
+ {
149
+ path: '/users',
150
+ name: 'users',
151
+ component: () => import('@/views/UserList.vue'),
152
+ meta: { requiresAuth: true },
153
+ },
154
+ {
155
+ path: '/users/new',
156
+ name: 'create-user',
157
+ component: () => import('@/views/CreateUser.vue'),
158
+ meta: { requiresAuth: true },
159
+ },
160
+ {
161
+ path: '/users/:id',
162
+ name: 'user-detail',
163
+ component: () => import('@/views/UserDetail.vue'),
164
+ meta: { requiresAuth: true },
165
+ },
166
+ {
167
+ path: '/login',
168
+ name: 'login',
169
+ component: () => import('@/views/Login.vue'),
170
+ },
171
+ {
172
+ path: '/:pathMatch(.*)*',
173
+ name: 'not-found',
174
+ component: () => import('@/views/NotFound.vue'),
175
+ },
176
+ ],
177
+ })
178
+
179
+ // 导航守卫
180
+ router.beforeEach((to, from) => {
181
+ const auth = useAuthStore()
182
+ if (to.meta.requiresAuth && !auth.isAuthenticated) {
183
+ return { name: 'login', query: { redirect: to.fullPath } }
184
+ }
185
+ })
186
+
187
+ export default router
188
+ ```
189
+
190
+ ## 主应用入口
191
+
192
+ ```vue
193
+ <!-- App.vue -->
194
+ <script setup lang="ts">
195
+ import { ConfigProvider } from 'ant-design-vue'
196
+ import zhCN from 'ant-design-vue/es/locale/zh_CN'
197
+ import dayjs from 'dayjs'
198
+ import 'dayjs/locale/zh-cn'
199
+
200
+ dayjs.locale('zh-cn')
201
+ </script>
202
+
203
+ <template>
204
+ <ConfigProvider
205
+ :locale="zhCN"
206
+ :theme="{ token: { colorPrimary: '#1677ff', borderRadius: 6 } }"
207
+ >
208
+ <RouterView />
209
+ </ConfigProvider>
210
+ </template>
211
+ ```
212
+
213
+ ```typescript
214
+ // main.ts
215
+ import { createApp } from 'vue'
216
+ import { createPinia } from 'pinia'
217
+ import App from './App.vue'
218
+ import router from './router'
219
+
220
+ const app = createApp(App)
221
+ app.use(createPinia())
222
+ app.use(router)
223
+ app.mount('#app')
224
+ ```
@@ -0,0 +1,145 @@
1
+ # 前端编码快速参考 (Vue 3)
2
+
3
+ 按需加载。仅当你需要编写对应组件类型时阅读相关章节。
4
+
5
+ > **完整示例代码见 `examples/` 目录。** 已安装子维度的规范,通过该维度的 `{value}.md` 文件阅读。
6
+
7
+ ## 速查
8
+
9
+ | 场景 | 决策 |
10
+ | --- | --- |
11
+ | 新建组件 | `<script setup lang="ts">` + `defineProps<Props>()` + 组合式 API |
12
+ | 新建表单 | Ant Design Vue `<a-form>` + `v-model` + `:rules` 校验 |
13
+ | 读数据 | 组合式函数(`useUsers`)封装 fetch/axios |
14
+ | 写数据 | 组合式函数 + `ref()` loading/error/data 三态 |
15
+ | 组合式函数职责 | 只负责业务逻辑和数据获取,不管理 UI 渲染 |
16
+ | 路由 | 动态 `import()` + `createRouter` 懒加载 |
17
+
18
+ ## 新建组件
19
+
20
+ ```vue
21
+ <script setup lang="ts">
22
+ interface UserTableProps {
23
+ users: User[]
24
+ loading?: boolean
25
+ onSelect?: (user: User) => void
26
+ }
27
+
28
+ const props = defineProps<UserTableProps>()
29
+ const emit = defineEmits<{
30
+ select: [user: User]
31
+ }>()
32
+
33
+ function handleClick(user: User) {
34
+ emit('select', user)
35
+ }
36
+ </script>
37
+
38
+ <template>
39
+ <a-table
40
+ :data-source="props.users"
41
+ :loading="props.loading"
42
+ row-key="id"
43
+ @click="handleClick"
44
+ />
45
+ </template>
46
+ ```
47
+
48
+ | 规则 | 要求 |
49
+ | --- | --- |
50
+ | `<script setup lang="ts">` | 禁止 Options API,使用组合式 API |
51
+ | `defineProps<Type>()` | 使用泛型语法定义 Props,`type` 定义联合类型 |
52
+ | 状态提升 | 组件间共享状态提升到最近公共父组件,或使用 Pinia |
53
+ | 默认导出 | 页面级组件使用 `export default`(路由组件),通用组件使用具名导出 |
54
+
55
+ ## 组合式 API 规则
56
+
57
+ - **顶层调用**:`ref()`、`computed()`、`watch()` 等组合式 API 在 `<script setup>` 顶层调用,禁止在条件/循环中
58
+ - **ref 默认首选**:优先使用 `ref()`,`reactive()` 仅用于不需要整体替换的固定结构对象
59
+ - **生命周期**:注册的监听器和定时器必须在 `onUnmounted` 中清理
60
+ - **组合式函数**:以 `use` 开头命名,返回值为对象
61
+
62
+ ```vue
63
+ <script setup lang="ts">
64
+ import { ref, onMounted, onUnmounted } from 'vue'
65
+
66
+ const users = ref<User[]>([])
67
+ const loading = ref(true)
68
+ const error = ref<Error | null>(null)
69
+
70
+ onMounted(async () => {
71
+ try {
72
+ const res = await fetch('/api/v1/users')
73
+ users.value = await res.json()
74
+ } catch (e) {
75
+ error.value = e as Error
76
+ } finally {
77
+ loading.value = false
78
+ }
79
+ })
80
+ </script>
81
+ ```
82
+
83
+ ## Ant Design Vue 组件
84
+
85
+ - **自动按需导入**:通过 `unplugin-vue-components` 配合 `AntDesignVueResolver` 实现,模板中使用 `a-` 前缀(`<a-button>`、`<a-table>`)
86
+ - **Form**:`<a-form-item>` 的 `name` 必须与数据模型字段一致,使用 `:rules` 定义校验
87
+ - **Table**:必须指定 `row-key`,空数据时设置 `locale.emptyText`
88
+ - **Modal**:关闭时使用 `destroyOnClose` 或 `@afterClose` 重置内部状态
89
+ - **布局**:使用 `<a-row>` / `<a-col>` 栅格系统或 Flex 布局
90
+ - **程序化 API**:`message.success()`、`Modal.confirm()` 等从 `ant-design-vue` 手动导入
91
+
92
+ ## 错误处理
93
+
94
+ - 错误边界使用 `onErrorCaptured` 生命周期钩子捕获子组件异常
95
+ - API 错误在组合式函数层捕获并暴露错误状态,UI 层通过 Ant Design Vue 组件展示反馈
96
+ - 表单校验使用 Ant Design Vue Form 内置校验规则,非必要不自定义 validator
97
+ - Error Boundary 组件使用 `<script setup>` + `onErrorCaptured` 实现
98
+
99
+ ## 路由
100
+
101
+ - 所有页面组件使用动态 `import()` 实现懒加载
102
+ - 路由配置集中定义在路由文件中,不在组件内嵌路由
103
+ - 权限通过导航守卫(`beforeEach`)控制
104
+ - 组件内路由操作使用 `useRouter()` / `useRoute()` 组合式函数
105
+
106
+ ```vue
107
+ <script setup lang="ts">
108
+ import { createRouter, createWebHistory } from 'vue-router'
109
+
110
+ const router = createRouter({
111
+ history: createWebHistory(import.meta.env.BASE_URL),
112
+ routes: [
113
+ {
114
+ path: '/users',
115
+ name: 'users',
116
+ component: () => import('@/views/UserList.vue'),
117
+ },
118
+ {
119
+ path: '/users/:id',
120
+ name: 'user-detail',
121
+ component: () => import('@/views/UserDetail.vue'),
122
+ },
123
+ ],
124
+ })
125
+ </script>
126
+ ```
127
+
128
+ ## 代码风格
129
+
130
+ - 控制流语句必须使用大括号,禁止无大括号的早期返回
131
+ - 代码折行规则(90 列限制)详见 `examples/code-wrapping.md`
132
+ - 模板属性超过 3 个时换行排列
133
+ - 自闭合标签:无子元素时使用 `<Component />` 而非 `<Component></Component>`
134
+ - 模板表达式保持简洁,复杂逻辑提取到 `<script setup>` 中
135
+
136
+ ## 常见坑
137
+
138
+ | 场景 | 问题 | 正确做法 |
139
+ |------|------|---------|
140
+ | `reactive` 整体赋值 | 直接 `state = newVal` 破坏响应式 | 使用 `Object.assign()` 或改用 `ref()` |
141
+ | `v-for` 缺少 `:key` | 列表渲染异常/性能下降 | 绑定唯一 `:key="item.id"` |
142
+ | 响应式解构丢失 | `const { name } = reactive(obj)` 解构后 `name` 失去响应性 | 使用 `toRefs()` 或保持 `obj.name` 访问 |
143
+ | 生命周期清理遗漏 | 事件监听/定时器在组件卸载后仍然运行 | `onUnmounted` 中清理 |
144
+ | `watch` 旧值误用 | 以为 `watch` 第一个回调参数是新值 | 签名是 `watch(source, (newVal, oldVal) => ...)` |
145
+ | Ant Design Vue 手动导入遗漏 | `message` / `Modal` 等程序化 API 未导入 | 从 `ant-design-vue` 包手动导入 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstorm/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "DeepStorm CLI — 一键配置项目开发环境",
5
5
  "license": "MIT",
6
6
  "author": "billkang",
@@ -12,6 +12,7 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "build": "node scripts/build.mjs && node dist/build-registry.js",
15
+ "publish": "node scripts/publish.mjs",
15
16
  "test": "vitest run --coverage",
16
17
  "prepublishOnly": "npm run build",
17
18
  "prepack": "npm run build"
@@ -19,6 +20,7 @@
19
20
  "dependencies": {
20
21
  "@clack/prompts": "^0.9.0",
21
22
  "commander": "^12.0.0",
23
+ "dotenv": "^17.4.2",
22
24
  "handlebars": "^4.7.8",
23
25
  "js-yaml": "^4.1.0"
24
26
  },