@deepstorm/cli 0.10.2 → 0.11.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/README.md +8 -8
- package/dist/build-registry.js +11 -5
- package/dist/cli.js +1256 -634
- package/dist/mcp/code-hosting/github.json +14 -1
- package/dist/mcp/docs-reference/context7.json +1 -10
- package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +8 -5
- package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +10 -8
- package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +21 -20
- package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +17 -17
- package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +20 -20
- package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +8 -8
- package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +13 -12
- package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +10 -10
- package/dist/registry.json +10 -0
- package/dist/skills/atoll-ops/SKILL.md +4 -0
- package/dist/skills/reef-commit/SKILL.md +9 -5
- package/dist/skills/reef-commit/scripts/branch-check.mjs +5 -11
- package/dist/skills/reef-commit/scripts/check-openspec-status.mjs +6 -12
- package/dist/skills/reef-commit/scripts/collect-git-context.mjs +13 -11
- package/dist/skills/reef-gen-backend/variants/java/steps.md +7 -7
- package/dist/skills/reef-gen-backend/variants/nodejs/steps.md +7 -7
- package/dist/skills/reef-gen-backend/variants/python/steps.md +7 -7
- package/dist/skills/reef-gen-frontend/variants/angular/steps.md +5 -5
- package/dist/skills/reef-gen-frontend/variants/react/steps.md +6 -6
- package/dist/skills/reef-gen-frontend/variants/vue/steps.md +6 -6
- package/dist/skills/reef-harden/EXAMPLES.md +12 -10
- package/dist/skills/reef-harden/SKILL.md +6 -0
- package/dist/skills/reef-harden/scripts/find-change-dir.mjs +13 -13
- package/dist/skills/reef-pr/SKILL.md +7 -0
- package/dist/skills/reef-pr/scripts/create-pr.mjs +31 -29
- package/dist/skills/reef-scope/SKILL.md +6 -6
- package/dist/skills/reef-start/references/jira-start-subagent.md +7 -7
- package/dist/skills/reef-start/references/risk-routing-card.md +28 -25
- package/dist/skills/reef-start/references/stage-4-implementation.md +33 -21
- package/dist/skills/reef-start/references/superpowers-gate.md +9 -9
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +2 -2
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +11 -0
- package/dist/skills/reef-style-backend/fragments/java/api-spec/jackson-polymorphism.md +36 -40
- package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +11 -10
- package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +7 -7
- package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +24 -21
- package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +7 -7
- package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +10 -8
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +2 -2
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +5 -5
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +1 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +12 -12
- package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +13 -12
- package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +11 -9
- package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +7 -7
- package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +6 -6
- package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +8 -8
- package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +8 -7
- package/dist/skills/reef-style-backend/fragments/nodejs/eslint-config.json +1 -4
- package/dist/skills/reef-style-backend/fragments/nodejs/nestjs-structure.md +9 -9
- package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +2 -1
- package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +10 -10
- package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +24 -22
- package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +10 -9
- package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +3 -0
- package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +10 -7
- package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +2 -0
- package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +2 -0
- package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +11 -9
- package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +3 -0
- package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +3 -4
- package/dist/skills/reef-style-backend/variants/java/quick-reference.md +31 -29
- package/dist/skills/reef-style-backend/variants/nodejs/examples/module-example.md +2 -8
- package/dist/skills/reef-style-backend/variants/nodejs/quick-reference.md +35 -36
- package/dist/skills/reef-style-backend/variants/python/quick-reference.md +32 -31
- package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +9 -7
- package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +9 -4
- package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +4 -4
- package/dist/skills/reef-style-frontend/fragments/test/vitest-react/examples/testing.md +135 -140
- package/dist/skills/reef-style-frontend/fragments/test/vitest-react/quick-reference.md +32 -32
- package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/examples/testing.md +161 -162
- package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/quick-reference.md +42 -42
- package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +16 -10
- package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/examples/ui-components.md +72 -87
- package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/quick-reference.md +14 -10
- package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/examples/ui-components.md +93 -111
- package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/quick-reference.md +29 -33
- package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +14 -23
- package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +15 -17
- package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +22 -5
- package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +52 -22
- package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +17 -20
- package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +8 -11
- package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +27 -27
- package/dist/skills/reef-style-frontend/variants/react/examples/code-wrapping.md +14 -27
- package/dist/skills/reef-style-frontend/variants/react/examples/component-types-pipes.md +45 -52
- package/dist/skills/reef-style-frontend/variants/react/examples/entity-types.md +44 -44
- package/dist/skills/reef-style-frontend/variants/react/examples/forms-layer.md +56 -85
- package/dist/skills/reef-style-frontend/variants/react/examples/service-routing.md +114 -88
- package/dist/skills/reef-style-frontend/variants/react/quick-reference.md +48 -42
- package/dist/skills/reef-style-frontend/variants/vue/examples/code-wrapping.md +24 -43
- package/dist/skills/reef-style-frontend/variants/vue/examples/component-types-pipes.md +48 -49
- package/dist/skills/reef-style-frontend/variants/vue/examples/entity-types.md +45 -45
- package/dist/skills/reef-style-frontend/variants/vue/examples/forms-layer.md +64 -68
- package/dist/skills/reef-style-frontend/variants/vue/examples/service-routing.md +85 -84
- package/dist/skills/reef-style-frontend/variants/vue/quick-reference.md +46 -51
- package/dist/skills/reef-testcase/SKILL.md +19 -19
- package/dist/skills/reef-testcase/references/coverage-dimensions.md +8 -0
- package/dist/skills/reef-testcase/references/test-case-template.md +16 -16
- package/dist/skills/sweep-explore/SKILL.md +824 -0
- package/dist/skills/sweep-explore/references/explore-flow-template.md +61 -0
- package/dist/skills/sweep-init/SKILL.md +4 -0
- package/dist/skills/sweep-init/scripts/flow-selector.mjs +6 -11
- package/dist/skills/sweep-init/scripts/init-project.mjs +68 -51
- package/dist/skills/sweep-plan/references/test-flow-template.md +12 -6
- package/dist/skills/sweep-record/SKILL.md +358 -0
- package/dist/skills/sweep-run/SKILL.md +59 -50
- package/dist/skills/sweep-run/scripts/env-manager.mjs +4 -5
- package/dist/skills/sweep-run/scripts/flow-parser.mjs +7 -6
- package/dist/skills/sweep-run/scripts/flow-selector.mjs +4 -15
- package/dist/skills/sweep-run/scripts/generate-report.mjs +1 -2
- package/dist/skills/sweep-run/scripts/mcp-manager.mjs +107 -14
- package/dist/skills/sweep-run/scripts/spec-compiler.mjs +19 -9
- package/dist/skills/tide-discuss/references/checklists.md +32 -32
- package/dist/skills/tide-discuss/references/data-format.md +85 -77
- package/dist/skills/tide-discuss/references/prd-template.md +21 -19
- package/dist/skills/tide-discuss/references/publish-flow.md +30 -11
- package/dist/skills/tide-discuss/references/role-prompts.md +5 -5
- package/package.json +5 -3
|
@@ -8,34 +8,35 @@ Vue 3 中不直接存在 Pipe 的概念,通过 computed 或工具函数实现
|
|
|
8
8
|
<script setup lang="ts">
|
|
9
9
|
// ✅ 方案1: 纯工具函数(无状态转换)
|
|
10
10
|
function formatDate(date: string | Date, style: 'short' | 'long' = 'short'): string {
|
|
11
|
-
const d = typeof date === 'string' ? new Date(date) : date
|
|
11
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
12
12
|
if (style === 'short') {
|
|
13
|
-
return d.toLocaleDateString('zh-CN')
|
|
13
|
+
return d.toLocaleDateString('zh-CN');
|
|
14
14
|
}
|
|
15
15
|
return d.toLocaleDateString('zh-CN', {
|
|
16
|
-
year: 'numeric',
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
year: 'numeric',
|
|
17
|
+
month: 'long',
|
|
18
|
+
day: 'numeric',
|
|
19
|
+
hour: '2-digit',
|
|
20
|
+
minute: '2-digit',
|
|
21
|
+
});
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
function formatCurrency(amount: number, currency = 'CNY'): string {
|
|
22
25
|
return new Intl.NumberFormat('zh-CN', {
|
|
23
26
|
style: 'currency',
|
|
24
27
|
currency,
|
|
25
|
-
}).format(amount)
|
|
28
|
+
}).format(amount);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
// ✅ 方案2: computed 属性(有状态转换 — 如搜索/过滤)
|
|
29
|
-
const props = defineProps<{ users: User[] }>()
|
|
30
|
-
const search = ref('')
|
|
32
|
+
const props = defineProps<{ users: User[] }>();
|
|
33
|
+
const search = ref('');
|
|
31
34
|
|
|
32
35
|
const filteredUsers = computed(() => {
|
|
33
|
-
if (!search.value.trim()) return props.users
|
|
34
|
-
const q = search.value.toLowerCase()
|
|
35
|
-
return props.users.filter(
|
|
36
|
-
|
|
37
|
-
)
|
|
38
|
-
})
|
|
36
|
+
if (!search.value.trim()) return props.users;
|
|
37
|
+
const q = search.value.toLowerCase();
|
|
38
|
+
return props.users.filter((u) => u.username.toLowerCase().includes(q) || u.email.toLowerCase().includes(q));
|
|
39
|
+
});
|
|
39
40
|
</script>
|
|
40
41
|
|
|
41
42
|
<template>
|
|
@@ -76,12 +77,12 @@ const filteredUsers = computed(() => {
|
|
|
76
77
|
<!-- components/DataList.vue -->
|
|
77
78
|
<script setup lang="ts">
|
|
78
79
|
interface Item {
|
|
79
|
-
id: number
|
|
80
|
-
name: string
|
|
81
|
-
email: string
|
|
80
|
+
id: number;
|
|
81
|
+
name: string;
|
|
82
|
+
email: string;
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
defineProps<{ items: Item[] }>()
|
|
85
|
+
defineProps<{ items: Item[] }>();
|
|
85
86
|
</script>
|
|
86
87
|
|
|
87
88
|
<template>
|
|
@@ -110,16 +111,16 @@ defineProps<{ items: Item[] }>()
|
|
|
110
111
|
|
|
111
112
|
```vue
|
|
112
113
|
<script setup lang="ts">
|
|
113
|
-
import { shallowRef } from 'vue'
|
|
114
|
-
import UserList from '@/views/UserList.vue'
|
|
115
|
-
import UserDetail from '@/views/UserDetail.vue'
|
|
116
|
-
import Settings from '@/views/Settings.vue'
|
|
114
|
+
import { shallowRef } from 'vue';
|
|
115
|
+
import UserList from '@/views/UserList.vue';
|
|
116
|
+
import UserDetail from '@/views/UserDetail.vue';
|
|
117
|
+
import Settings from '@/views/Settings.vue';
|
|
117
118
|
|
|
118
|
-
const currentView = shallowRef(UserList)
|
|
119
|
+
const currentView = shallowRef(UserList);
|
|
119
120
|
|
|
120
121
|
function switchView(view: 'list' | 'detail' | 'settings') {
|
|
121
|
-
const views = { list: UserList, detail: UserDetail, settings: Settings }
|
|
122
|
-
currentView.value = views[view]
|
|
122
|
+
const views = { list: UserList, detail: UserDetail, settings: Settings };
|
|
123
|
+
currentView.value = views[view];
|
|
123
124
|
}
|
|
124
125
|
</script>
|
|
125
126
|
|
|
@@ -150,9 +151,7 @@ function switchView(view: 'list' | 'detail' | 'settings') {
|
|
|
150
151
|
</a-button>
|
|
151
152
|
|
|
152
153
|
<Transition name="fade" mode="out-in">
|
|
153
|
-
<div v-if="show" key="content">
|
|
154
|
-
内容区域
|
|
155
|
-
</div>
|
|
154
|
+
<div v-if="show" key="content">内容区域</div>
|
|
156
155
|
</Transition>
|
|
157
156
|
</template>
|
|
158
157
|
|
|
@@ -172,15 +171,15 @@ function switchView(view: 'list' | 'detail' | 'settings') {
|
|
|
172
171
|
|
|
173
172
|
```typescript
|
|
174
173
|
// components/AsyncWidget.ts
|
|
175
|
-
import { defineAsyncComponent } from 'vue'
|
|
176
|
-
import { Spin } from 'ant-design-vue'
|
|
174
|
+
import { defineAsyncComponent } from 'vue';
|
|
175
|
+
import { Spin } from 'ant-design-vue';
|
|
177
176
|
|
|
178
177
|
export default defineAsyncComponent({
|
|
179
178
|
loader: () => import('./HeavyWidget.vue'),
|
|
180
179
|
loadingComponent: Spin,
|
|
181
180
|
delay: 200,
|
|
182
181
|
timeout: 8000,
|
|
183
|
-
})
|
|
182
|
+
});
|
|
184
183
|
|
|
185
184
|
// 路由中的异步组件 — 通过动态 import 实现
|
|
186
185
|
// router/index.ts
|
|
@@ -189,34 +188,34 @@ const routes = [
|
|
|
189
188
|
path: '/dashboard',
|
|
190
189
|
component: () => import('@/views/Dashboard.vue'),
|
|
191
190
|
},
|
|
192
|
-
]
|
|
191
|
+
];
|
|
193
192
|
```
|
|
194
193
|
|
|
195
194
|
## Provide/Inject 模式
|
|
196
195
|
|
|
197
196
|
```typescript
|
|
198
197
|
// injection-keys.ts
|
|
199
|
-
import type { InjectionKey, Ref } from 'vue'
|
|
198
|
+
import type { InjectionKey, Ref } from 'vue';
|
|
200
199
|
|
|
201
200
|
export const ThemeKey: InjectionKey<{
|
|
202
|
-
theme: Ref<'light' | 'dark'
|
|
203
|
-
toggleTheme: () => void
|
|
204
|
-
}> = Symbol('theme')
|
|
201
|
+
theme: Ref<'light' | 'dark'>;
|
|
202
|
+
toggleTheme: () => void;
|
|
203
|
+
}> = Symbol('theme');
|
|
205
204
|
```
|
|
206
205
|
|
|
207
206
|
```vue
|
|
208
207
|
<!-- 祖先组件:provide -->
|
|
209
208
|
<script setup lang="ts">
|
|
210
|
-
import { provide, ref } from 'vue'
|
|
211
|
-
import { ThemeKey } from '@/injection-keys'
|
|
209
|
+
import { provide, ref } from 'vue';
|
|
210
|
+
import { ThemeKey } from '@/injection-keys';
|
|
212
211
|
|
|
213
|
-
const theme = ref<'light' | 'dark'>('light')
|
|
212
|
+
const theme = ref<'light' | 'dark'>('light');
|
|
214
213
|
|
|
215
214
|
function toggleTheme() {
|
|
216
|
-
theme.value = theme.value === 'light' ? 'dark' : 'light'
|
|
215
|
+
theme.value = theme.value === 'light' ? 'dark' : 'light';
|
|
217
216
|
}
|
|
218
217
|
|
|
219
|
-
provide(ThemeKey, { theme, toggleTheme })
|
|
218
|
+
provide(ThemeKey, { theme, toggleTheme });
|
|
220
219
|
</script>
|
|
221
220
|
|
|
222
221
|
<template>
|
|
@@ -227,10 +226,10 @@ provide(ThemeKey, { theme, toggleTheme })
|
|
|
227
226
|
```vue
|
|
228
227
|
<!-- 后代组件:inject -->
|
|
229
228
|
<script setup lang="ts">
|
|
230
|
-
import { inject } from 'vue'
|
|
231
|
-
import { ThemeKey } from '@/injection-keys'
|
|
229
|
+
import { inject } from 'vue';
|
|
230
|
+
import { ThemeKey } from '@/injection-keys';
|
|
232
231
|
|
|
233
|
-
const { theme, toggleTheme } = inject(ThemeKey)
|
|
232
|
+
const { theme, toggleTheme } = inject(ThemeKey)!;
|
|
234
233
|
</script>
|
|
235
234
|
|
|
236
235
|
<template>
|
|
@@ -246,14 +245,14 @@ const { theme, toggleTheme } = inject(ThemeKey)!
|
|
|
246
245
|
```vue
|
|
247
246
|
<!-- components/AuthGuard.vue -->
|
|
248
247
|
<script setup lang="ts">
|
|
249
|
-
import { useAuthStore } from '@/stores/auth'
|
|
250
|
-
import { useRouter } from 'vue-router'
|
|
248
|
+
import { useAuthStore } from '@/stores/auth';
|
|
249
|
+
import { useRouter } from 'vue-router';
|
|
251
250
|
|
|
252
|
-
const auth = useAuthStore()
|
|
253
|
-
const router = useRouter()
|
|
251
|
+
const auth = useAuthStore();
|
|
252
|
+
const router = useRouter();
|
|
254
253
|
|
|
255
254
|
if (!auth.isAuthenticated) {
|
|
256
|
-
router.replace({ name: 'login', query: { redirect: router.currentRoute.value.fullPath } })
|
|
255
|
+
router.replace({ name: 'login', query: { redirect: router.currentRoute.value.fullPath } });
|
|
257
256
|
}
|
|
258
257
|
</script>
|
|
259
258
|
|
|
@@ -5,22 +5,22 @@
|
|
|
5
5
|
```typescript
|
|
6
6
|
// shared/types/entity.ts
|
|
7
7
|
export interface ImmutableEntity {
|
|
8
|
-
readonly id: string
|
|
9
|
-
readonly createdAt: string
|
|
10
|
-
readonly updatedAt: string
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly createdAt: string;
|
|
10
|
+
readonly updatedAt: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export interface Entity extends ImmutableEntity {
|
|
14
|
-
version: number
|
|
14
|
+
version: number;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export interface Auditable<T> extends Entity {
|
|
18
|
-
createdBy: T
|
|
19
|
-
updatedBy: T
|
|
18
|
+
createdBy: T;
|
|
19
|
+
updatedBy: T;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export interface ImmutableAuditable<T> extends ImmutableEntity {
|
|
23
|
-
createdBy: T
|
|
23
|
+
createdBy: T;
|
|
24
24
|
}
|
|
25
25
|
```
|
|
26
26
|
|
|
@@ -29,19 +29,19 @@ export interface ImmutableAuditable<T> extends ImmutableEntity {
|
|
|
29
29
|
```typescript
|
|
30
30
|
// shared/types/api.ts
|
|
31
31
|
export interface PageResponse<T> {
|
|
32
|
-
content: T[]
|
|
33
|
-
totalElements: number
|
|
34
|
-
totalPages: number
|
|
35
|
-
number: number
|
|
36
|
-
size: number
|
|
37
|
-
first: boolean
|
|
38
|
-
last: boolean
|
|
32
|
+
content: T[];
|
|
33
|
+
totalElements: number;
|
|
34
|
+
totalPages: number;
|
|
35
|
+
number: number;
|
|
36
|
+
size: number;
|
|
37
|
+
first: boolean;
|
|
38
|
+
last: boolean;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export interface ApiError {
|
|
42
|
-
code: string
|
|
43
|
-
message: string
|
|
44
|
-
details?: Record<string, string[]
|
|
42
|
+
code: string;
|
|
43
|
+
message: string;
|
|
44
|
+
details?: Record<string, string[]>;
|
|
45
45
|
}
|
|
46
46
|
```
|
|
47
47
|
|
|
@@ -50,12 +50,12 @@ export interface ApiError {
|
|
|
50
50
|
```typescript
|
|
51
51
|
// shared/types/user.ts
|
|
52
52
|
export interface User extends Auditable<string> {
|
|
53
|
-
username: string
|
|
54
|
-
email: string
|
|
55
|
-
displayName: string
|
|
56
|
-
avatar?: string
|
|
57
|
-
role: UserRole
|
|
58
|
-
active: boolean
|
|
53
|
+
username: string;
|
|
54
|
+
email: string;
|
|
55
|
+
displayName: string;
|
|
56
|
+
avatar?: string;
|
|
57
|
+
role: UserRole;
|
|
58
|
+
active: boolean;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export enum UserRole {
|
|
@@ -65,16 +65,16 @@ export enum UserRole {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
export interface CreateUserRequest {
|
|
68
|
-
username: string
|
|
69
|
-
email: string
|
|
70
|
-
displayName: string
|
|
71
|
-
role: UserRole
|
|
68
|
+
username: string;
|
|
69
|
+
email: string;
|
|
70
|
+
displayName: string;
|
|
71
|
+
role: UserRole;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export interface UpdateUserRequest {
|
|
75
|
-
email?: string
|
|
76
|
-
displayName?: string
|
|
77
|
-
role?: UserRole
|
|
75
|
+
email?: string;
|
|
76
|
+
displayName?: string;
|
|
77
|
+
role?: UserRole;
|
|
78
78
|
}
|
|
79
79
|
```
|
|
80
80
|
|
|
@@ -83,25 +83,25 @@ export interface UpdateUserRequest {
|
|
|
83
83
|
```vue
|
|
84
84
|
<!-- components/UserAvatar.vue -->
|
|
85
85
|
<script setup lang="ts">
|
|
86
|
-
import type { User } from '@/shared/types/user'
|
|
86
|
+
import type { User } from '@/shared/types/user';
|
|
87
87
|
|
|
88
88
|
interface UserAvatarProps {
|
|
89
|
-
user: Pick<User, 'username' | 'avatar'
|
|
90
|
-
size?: 'sm' | 'md' | 'lg'
|
|
91
|
-
onClick?: () => void
|
|
89
|
+
user: Pick<User, 'username' | 'avatar'>;
|
|
90
|
+
size?: 'sm' | 'md' | 'lg';
|
|
91
|
+
onClick?: () => void;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
const props = withDefaults(defineProps<UserAvatarProps>(), {
|
|
95
95
|
size: 'md',
|
|
96
|
-
})
|
|
96
|
+
});
|
|
97
97
|
|
|
98
98
|
const emit = defineEmits<{
|
|
99
|
-
click: [user: User]
|
|
100
|
-
}>()
|
|
99
|
+
click: [user: User];
|
|
100
|
+
}>();
|
|
101
101
|
|
|
102
102
|
function handleClick() {
|
|
103
103
|
if (props.onClick) {
|
|
104
|
-
props.onClick()
|
|
104
|
+
props.onClick();
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
</script>
|
|
@@ -120,9 +120,9 @@ function handleClick() {
|
|
|
120
120
|
```vue
|
|
121
121
|
<!-- views/UserListPage.vue — 页面组件:默认导出 -->
|
|
122
122
|
<script setup lang="ts">
|
|
123
|
-
import { useUsers } from '@/composables/useUsers'
|
|
123
|
+
import { useUsers } from '@/composables/useUsers';
|
|
124
124
|
|
|
125
|
-
const { users, loading } = useUsers()
|
|
125
|
+
const { users, loading } = useUsers();
|
|
126
126
|
</script>
|
|
127
127
|
|
|
128
128
|
<template>
|
|
@@ -134,12 +134,12 @@ const { users, loading } = useUsers()
|
|
|
134
134
|
|
|
135
135
|
```typescript
|
|
136
136
|
// 用于多状态组件
|
|
137
|
-
export type NotificationType = 'success' | 'error' | 'warning' | 'info'
|
|
137
|
+
export type NotificationType = 'success' | 'error' | 'warning' | 'info';
|
|
138
138
|
|
|
139
139
|
export interface NotificationConfig {
|
|
140
|
-
type: NotificationType
|
|
141
|
-
title: string
|
|
142
|
-
message: string
|
|
143
|
-
duration?: number
|
|
140
|
+
type: NotificationType;
|
|
141
|
+
title: string;
|
|
142
|
+
message: string;
|
|
143
|
+
duration?: number;
|
|
144
144
|
}
|
|
145
145
|
```
|
|
@@ -5,30 +5,30 @@
|
|
|
5
5
|
```vue
|
|
6
6
|
<!-- components/CreateUserForm.vue -->
|
|
7
7
|
<script setup lang="ts">
|
|
8
|
-
import { reactive, ref } from 'vue'
|
|
9
|
-
import { message } from 'ant-design-vue'
|
|
10
|
-
import type { UserRole } from '@/shared/types/user'
|
|
8
|
+
import { reactive, ref } from 'vue';
|
|
9
|
+
import { message } from 'ant-design-vue';
|
|
10
|
+
import type { UserRole } from '@/shared/types/user';
|
|
11
11
|
|
|
12
12
|
interface FormState {
|
|
13
|
-
username: string
|
|
14
|
-
email: string
|
|
15
|
-
displayName: string
|
|
16
|
-
role: UserRole | undefined
|
|
13
|
+
username: string;
|
|
14
|
+
email: string;
|
|
15
|
+
displayName: string;
|
|
16
|
+
role: UserRole | undefined;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const emit = defineEmits<{
|
|
20
|
-
success: [userId: string]
|
|
21
|
-
}>()
|
|
20
|
+
success: [userId: string];
|
|
21
|
+
}>();
|
|
22
22
|
|
|
23
23
|
const formState = reactive<FormState>({
|
|
24
24
|
username: '',
|
|
25
25
|
email: '',
|
|
26
26
|
displayName: '',
|
|
27
27
|
role: undefined,
|
|
28
|
-
})
|
|
28
|
+
});
|
|
29
29
|
|
|
30
|
-
const loading = ref(false)
|
|
31
|
-
const formRef = ref()
|
|
30
|
+
const loading = ref(false);
|
|
31
|
+
const formRef = ref();
|
|
32
32
|
|
|
33
33
|
// 校验规则
|
|
34
34
|
const rules = {
|
|
@@ -40,42 +40,38 @@ const rules = {
|
|
|
40
40
|
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
|
41
41
|
{ type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur' },
|
|
42
42
|
],
|
|
43
|
-
displayName: [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
role: [
|
|
47
|
-
{ required: true, message: '请选择角色', trigger: 'change' },
|
|
48
|
-
],
|
|
49
|
-
}
|
|
43
|
+
displayName: [{ required: true, message: '请输入显示名称', trigger: 'blur' }],
|
|
44
|
+
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
|
|
45
|
+
};
|
|
50
46
|
|
|
51
47
|
async function handleSubmit() {
|
|
52
48
|
try {
|
|
53
|
-
await formRef.value?.validate()
|
|
54
|
-
loading.value = true
|
|
49
|
+
await formRef.value?.validate();
|
|
50
|
+
loading.value = true;
|
|
55
51
|
|
|
56
52
|
const res = await fetch('/api/v1/users', {
|
|
57
53
|
method: 'POST',
|
|
58
54
|
headers: { 'Content-Type': 'application/json' },
|
|
59
55
|
body: JSON.stringify(formState),
|
|
60
|
-
})
|
|
56
|
+
});
|
|
61
57
|
|
|
62
|
-
if (!res.ok) throw new Error('创建失败')
|
|
63
|
-
const user = await res.json()
|
|
58
|
+
if (!res.ok) throw new Error('创建失败');
|
|
59
|
+
const user = await res.json();
|
|
64
60
|
|
|
65
|
-
message.success('用户创建成功')
|
|
66
|
-
emit('success', user.id)
|
|
67
|
-
formRef.value?.resetFields()
|
|
61
|
+
message.success('用户创建成功');
|
|
62
|
+
emit('success', user.id);
|
|
63
|
+
formRef.value?.resetFields();
|
|
68
64
|
} catch (e) {
|
|
69
65
|
if (e instanceof Error) {
|
|
70
|
-
message.error(e.message)
|
|
66
|
+
message.error(e.message);
|
|
71
67
|
}
|
|
72
68
|
} finally {
|
|
73
|
-
loading.value = false
|
|
69
|
+
loading.value = false;
|
|
74
70
|
}
|
|
75
71
|
}
|
|
76
72
|
|
|
77
73
|
function handleReset() {
|
|
78
|
-
formRef.value?.resetFields()
|
|
74
|
+
formRef.value?.resetFields();
|
|
79
75
|
}
|
|
80
76
|
</script>
|
|
81
77
|
|
|
@@ -112,9 +108,7 @@ function handleReset() {
|
|
|
112
108
|
<a-button type="primary" html-type="submit" :loading="loading">
|
|
113
109
|
{{ loading ? '创建中...' : '创建' }}
|
|
114
110
|
</a-button>
|
|
115
|
-
<a-button style="margin-left: 8px" @click="handleReset">
|
|
116
|
-
重置
|
|
117
|
-
</a-button>
|
|
111
|
+
<a-button style="margin-left: 8px" @click="handleReset"> 重置 </a-button>
|
|
118
112
|
</a-form-item>
|
|
119
113
|
</a-form>
|
|
120
114
|
</template>
|
|
@@ -125,58 +119,60 @@ function handleReset() {
|
|
|
125
119
|
```vue
|
|
126
120
|
<!-- components/EditUserModal.vue -->
|
|
127
121
|
<script setup lang="ts">
|
|
128
|
-
import { reactive, ref, watch } from 'vue'
|
|
129
|
-
import { message, Modal } from 'ant-design-vue'
|
|
130
|
-
import type { User } from '@/shared/types/user'
|
|
122
|
+
import { reactive, ref, watch } from 'vue';
|
|
123
|
+
import { message, Modal } from 'ant-design-vue';
|
|
124
|
+
import type { User } from '@/shared/types/user';
|
|
131
125
|
|
|
132
126
|
interface EditForm {
|
|
133
|
-
email: string
|
|
134
|
-
displayName: string
|
|
135
|
-
active: boolean
|
|
127
|
+
email: string;
|
|
128
|
+
displayName: string;
|
|
129
|
+
active: boolean;
|
|
136
130
|
}
|
|
137
131
|
|
|
138
132
|
const props = defineProps<{
|
|
139
|
-
visible: boolean
|
|
140
|
-
user: User | null
|
|
141
|
-
}>()
|
|
133
|
+
visible: boolean;
|
|
134
|
+
user: User | null;
|
|
135
|
+
}>();
|
|
142
136
|
|
|
143
137
|
const emit = defineEmits<{
|
|
144
|
-
close: []
|
|
145
|
-
saved: []
|
|
146
|
-
}>()
|
|
138
|
+
close: [];
|
|
139
|
+
saved: [];
|
|
140
|
+
}>();
|
|
147
141
|
|
|
148
142
|
const formState = reactive<EditForm>({
|
|
149
143
|
email: '',
|
|
150
144
|
displayName: '',
|
|
151
145
|
active: true,
|
|
152
|
-
})
|
|
146
|
+
});
|
|
153
147
|
|
|
154
|
-
const formRef = ref()
|
|
155
|
-
const loading = ref(false)
|
|
148
|
+
const formRef = ref();
|
|
149
|
+
const loading = ref(false);
|
|
156
150
|
|
|
157
151
|
// 监听 user 变化填充表单
|
|
158
|
-
watch(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
152
|
+
watch(
|
|
153
|
+
() => props.user,
|
|
154
|
+
(user) => {
|
|
155
|
+
if (user) {
|
|
156
|
+
formState.email = user.email;
|
|
157
|
+
formState.displayName = user.displayName;
|
|
158
|
+
formState.active = user.active;
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{ immediate: true },
|
|
162
|
+
);
|
|
165
163
|
|
|
166
164
|
const rules = {
|
|
167
165
|
email: [
|
|
168
166
|
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
|
169
167
|
{ type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur' },
|
|
170
168
|
],
|
|
171
|
-
displayName: [
|
|
172
|
-
|
|
173
|
-
],
|
|
174
|
-
}
|
|
169
|
+
displayName: [{ required: true, message: '请输入显示名称', trigger: 'blur' }],
|
|
170
|
+
};
|
|
175
171
|
|
|
176
172
|
async function handleOk() {
|
|
177
173
|
try {
|
|
178
|
-
await formRef.value?.validate()
|
|
179
|
-
loading.value = true
|
|
174
|
+
await formRef.value?.validate();
|
|
175
|
+
loading.value = true;
|
|
180
176
|
|
|
181
177
|
const res = await fetch(`/api/v1/users/${props.user!.id}`, {
|
|
182
178
|
method: 'PUT',
|
|
@@ -185,22 +181,22 @@ async function handleOk() {
|
|
|
185
181
|
email: formState.email,
|
|
186
182
|
displayName: formState.displayName,
|
|
187
183
|
}),
|
|
188
|
-
})
|
|
184
|
+
});
|
|
189
185
|
|
|
190
|
-
if (!res.ok) throw new Error('更新失败')
|
|
191
|
-
message.success('更新成功')
|
|
192
|
-
emit('saved')
|
|
186
|
+
if (!res.ok) throw new Error('更新失败');
|
|
187
|
+
message.success('更新成功');
|
|
188
|
+
emit('saved');
|
|
193
189
|
} catch (e) {
|
|
194
190
|
if (e instanceof Error) {
|
|
195
|
-
message.error(e.message)
|
|
191
|
+
message.error(e.message);
|
|
196
192
|
}
|
|
197
193
|
} finally {
|
|
198
|
-
loading.value = false
|
|
194
|
+
loading.value = false;
|
|
199
195
|
}
|
|
200
196
|
}
|
|
201
197
|
|
|
202
198
|
function handleCancel() {
|
|
203
|
-
emit('close')
|
|
199
|
+
emit('close');
|
|
204
200
|
}
|
|
205
201
|
</script>
|
|
206
202
|
|