Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
# .claude/settings.local.json
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | --- |
| | | name: add-language-rules |
| | | description: Workflow command scaffold for add-language-rules in everything-claude-code. |
| | | allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] |
| | | --- |
| | | |
| | | # /add-language-rules |
| | | |
| | | Use this workflow when working on **add-language-rules** in `everything-claude-code`. |
| | | |
| | | ## Goal |
| | | |
| | | Adds a new programming language to the rules system, including coding style, hooks, patterns, security, and testing guidelines. |
| | | |
| | | ## Common Files |
| | | |
| | | - `rules/*/coding-style.md` |
| | | - `rules/*/hooks.md` |
| | | - `rules/*/patterns.md` |
| | | - `rules/*/security.md` |
| | | - `rules/*/testing.md` |
| | | |
| | | ## Suggested Sequence |
| | | |
| | | 1. Understand the current state and failure mode before editing. |
| | | 2. Make the smallest coherent change that satisfies the workflow goal. |
| | | 3. Run the most relevant verification for touched files. |
| | | 4. Summarize what changed and what still needs review. |
| | | |
| | | ## Typical Commit Signals |
| | | |
| | | - Create a new directory under rules/{language}/ |
| | | - Add coding-style.md, hooks.md, patterns.md, security.md, and testing.md files with language-specific content |
| | | - Optionally reference or link to related skills |
| | | |
| | | ## Notes |
| | | |
| | | - Treat this as a scaffold, not a hard-coded script. |
| | | - Update the command if the workflow evolves materially. |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | --- |
| | | name: api-service |
| | | description: å建æ°ç API æå¡æ¨¡åï¼å
å« TypeScript ç±»åå®ä¹ |
| | | allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] |
| | | --- |
| | | |
| | | # /api-service |
| | | |
| | | å建æ°ç API æå¡æ¨¡åæ¶ä½¿ç¨æ¤å·¥ä½æµã |
| | | |
| | | ## ç®æ |
| | | |
| | | å建ä¸ä¸ªç±»åå®å
¨ç API æå¡æ¨¡åï¼å
嫿£ç¡®ç请æ±/ååºç±»åå®ä¹ã |
| | | |
| | | ## å¸¸ç¨æä»¶ |
| | | |
| | | - `src/api/**/*.ts` |
| | | - `src/services/**/*.ts` |
| | | |
| | | ## 建议æ¥éª¤ |
| | | |
| | | 1. å®ä¹è¯·æ±/ååºç TypeScript æ¥å£ |
| | | 2. åå»ºå¸¦ç±»åæ¹æ³ç API æå¡å¯¹è±¡ |
| | | 3. 导åºç±»ååæå¡ |
| | | 4. å¦éè¦ï¼æ·»å é误å¤ç |
| | | |
| | | ## API æå¡æ¨¡æ¿ |
| | | |
| | | ```typescript |
| | | // types.ts |
| | | export interface User { |
| | | id: number; |
| | | name: string; |
| | | email: string; |
| | | createdAt: string; |
| | | } |
| | | |
| | | export interface UserQuery { |
| | | page?: number; |
| | | pageSize?: number; |
| | | name?: string; |
| | | } |
| | | |
| | | export interface CreateUserDTO { |
| | | name: string; |
| | | email: string; |
| | | } |
| | | |
| | | export interface UserListResponse { |
| | | list: User[]; |
| | | total: number; |
| | | } |
| | | |
| | | // user.ts |
| | | import { request } from '#/utils/request'; |
| | | import type { User, UserQuery, CreateUserDTO, UserListResponse } from './types'; |
| | | |
| | | export const userApi = { |
| | | // è·åç¨æ·å表 |
| | | getList: (params?: UserQuery) => |
| | | request.get<UserListResponse>('/users', { params }), |
| | | |
| | | // æ ¹æ®IDè·åç¨æ· |
| | | getById: (id: number) => |
| | | request.get<User>(`/users/${id}`), |
| | | |
| | | // åå»ºç¨æ· |
| | | create: (data: CreateUserDTO) => |
| | | request.post<User>('/users', data), |
| | | |
| | | // æ´æ°ç¨æ· |
| | | update: (id: number, data: Partial<CreateUserDTO>) => |
| | | request.put<User>(`/users/${id}`, data), |
| | | |
| | | // å é¤ç¨æ· |
| | | delete: (id: number) => |
| | | request.delete<void>(`/users/${id}`), |
| | | }; |
| | | ``` |
| | | |
| | | ## å
¸åæäº¤ä¿¡æ¯ |
| | | |
| | | - feat: add [模åå] API service |
| | | - feat: implement [模åå] API with types |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | --- |
| | | name: database-migration |
| | | description: Workflow command scaffold for database-migration in everything-claude-code. |
| | | allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] |
| | | --- |
| | | |
| | | # /database-migration |
| | | |
| | | Use this workflow when working on **database-migration** in `everything-claude-code`. |
| | | |
| | | ## Goal |
| | | |
| | | Database schema changes with migration files |
| | | |
| | | ## Common Files |
| | | |
| | | - `**/schema.*` |
| | | - `migrations/*` |
| | | |
| | | ## Suggested Sequence |
| | | |
| | | 1. Understand the current state and failure mode before editing. |
| | | 2. Make the smallest coherent change that satisfies the workflow goal. |
| | | 3. Run the most relevant verification for touched files. |
| | | 4. Summarize what changed and what still needs review. |
| | | |
| | | ## Typical Commit Signals |
| | | |
| | | - Create migration file |
| | | - Update schema definitions |
| | | - Generate/update types |
| | | |
| | | ## Notes |
| | | |
| | | - Treat this as a scaffold, not a hard-coded script. |
| | | - Update the command if the workflow evolves materially. |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | --- |
| | | name: feature-development |
| | | description: Workflow command scaffold for feature-development in everything-claude-code. |
| | | allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] |
| | | --- |
| | | |
| | | # /feature-development |
| | | |
| | | Use this workflow when working on **feature-development** in `everything-claude-code`. |
| | | |
| | | ## Goal |
| | | |
| | | Standard feature implementation workflow |
| | | |
| | | ## Common Files |
| | | |
| | | - `manifests/*` |
| | | - `schemas/*` |
| | | - `**/*.test.*` |
| | | - `**/api/**` |
| | | |
| | | ## Suggested Sequence |
| | | |
| | | 1. Understand the current state and failure mode before editing. |
| | | 2. Make the smallest coherent change that satisfies the workflow goal. |
| | | 3. Run the most relevant verification for touched files. |
| | | 4. Summarize what changed and what still needs review. |
| | | |
| | | ## Typical Commit Signals |
| | | |
| | | - Add feature implementation |
| | | - Add tests for feature |
| | | - Update documentation |
| | | |
| | | ## Notes |
| | | |
| | | - Treat this as a scaffold, not a hard-coded script. |
| | | - Update the command if the workflow evolves materially. |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | --- |
| | | name: pinia-store |
| | | description: å建æ°ç Pinia ç¶æåå¨ï¼å
å« TypeScript ç±»å |
| | | allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] |
| | | --- |
| | | |
| | | # /pinia-store |
| | | |
| | | å建æ°ç Pinia ç¶æå卿¶ä½¿ç¨æ¤å·¥ä½æµã |
| | | |
| | | ## ç®æ |
| | | |
| | | ä½¿ç¨ Composition API 飿 ¼å建类åå®å
¨ç Pinia ç¶æåå¨ã |
| | | |
| | | ## å¸¸ç¨æä»¶ |
| | | |
| | | - `src/stores/**/*.ts` |
| | | - `src/**/store.ts` |
| | | |
| | | ## 建议æ¥éª¤ |
| | | |
| | | 1. å®ä¹ç¶ææ¥å£ |
| | | 2. ä½¿ç¨ setup è¯æ³å建 store |
| | | 3. æ·»å 计ç®å±æ§ï¼gettersï¼ |
| | | 4. æ·»å æä½æ¹æ³ï¼actionsï¼ |
| | | 5. å¦éè¦ï¼é
ç½®æä¹
å |
| | | |
| | | ## Pinia Store æ¨¡æ¿ |
| | | |
| | | ```typescript |
| | | // stores/user.ts |
| | | import { ref, computed } from 'vue'; |
| | | import { defineStore } from 'pinia'; |
| | | import type { User } from '#/api/types'; |
| | | |
| | | interface UserState { |
| | | user: User | null; |
| | | token: string | null; |
| | | } |
| | | |
| | | export const useUserStore = defineStore( |
| | | 'user', |
| | | () => { |
| | | // ç¶æ |
| | | const user = ref<User | null>(null); |
| | | const token = ref<string | null>(null); |
| | | |
| | | // 计ç®å±æ§ (Getters) |
| | | const isLoggedIn = computed(() => !!token.value && !!user.value); |
| | | const userName = computed(() => user.value?.name ?? ''); |
| | | const userRoles = computed(() => user.value?.roles ?? []); |
| | | |
| | | // æä½æ¹æ³ (Actions) |
| | | const setUser = (newUser: User) => { |
| | | user.value = newUser; |
| | | }; |
| | | |
| | | const setToken = (newToken: string) => { |
| | | token.value = newToken; |
| | | }; |
| | | |
| | | const logout = () => { |
| | | user.value = null; |
| | | token.value = null; |
| | | }; |
| | | |
| | | const hasRole = (role: string) => { |
| | | return userRoles.value.includes(role); |
| | | }; |
| | | |
| | | return { |
| | | // ç¶æ |
| | | user, |
| | | token, |
| | | // 计ç®å±æ§ |
| | | isLoggedIn, |
| | | userName, |
| | | userRoles, |
| | | // æä½æ¹æ³ |
| | | setUser, |
| | | setToken, |
| | | logout, |
| | | hasRole, |
| | | }; |
| | | }, |
| | | { |
| | | persist: { |
| | | key: 'user-store', |
| | | storage: localStorage, |
| | | pick: ['token'], // åªæä¹
å tokenï¼ä¸æä¹
åç¨æ·å¯¹è±¡ |
| | | }, |
| | | } |
| | | ); |
| | | ``` |
| | | |
| | | ## å
¸åæäº¤ä¿¡æ¯ |
| | | |
| | | - feat: add [Storeå] store |
| | | - feat: implement [Storeå] state management |
| | |
| | | { |
| | | "permissions": { |
| | | "allow": [ |
| | | "Bash(pnpm install *)", |
| | | "Bash(pnpm view *)", |
| | | "Bash(pnpm approve-builds *)", |
| | | "Bash(pnpm ls *)" |
| | | "WebFetch(domain:github.com)", |
| | | "Bash(gh repo *)", |
| | | "WebSearch" |
| | | ] |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | --- |
| | | name: antd-vue-components |
| | | description: Ant Design Vue ç»ä»¶å¼å模å¼åæä½³å®è·µ |
| | | --- |
| | | |
| | | # Ant Design Vue ç»ä»¶æè½ |
| | | |
| | | ## 使¶ä½¿ç¨ |
| | | |
| | | å¨ä»¥ä¸åºæ¯æ¿æ´»æ¤æè½ï¼ |
| | | - ä½¿ç¨ Ant Design Vue ç»ä»¶æå»ºè¡¨å |
| | | - å建 Vxe-Table æ Ant Design è¡¨æ ¼ |
| | | - å®ç°æ¨¡ææ¡åæ½å±ç»ä»¶ |
| | | - ä½¿ç¨ Ant Design 表åéªè¯ |
| | | - èªå®ä¹ Ant Design ä¸»é¢ |
| | | |
| | | ## å·¥ä½åç |
| | | |
| | | æ¤æè½ä¸º Ant Design Vue 4.x å¼åæ¨¡å¼æä¾æå¯¼ã |
| | | |
| | | ## ææ¯æ |
| | | |
| | | - **Ant Design Vue**: 4.2.x |
| | | - **Vxe-Table**: 4.19.xï¼ç¨äºå¤æè¡¨æ ¼ï¼ |
| | | - **表åéªè¯**: VeeValidate + Zod schema |
| | | - **徿 **: @iconify/vue å @lucide/vue |
| | | |
| | | ## å¸¸ç¨æ¨¡å¼ |
| | | |
| | | ### 带éªè¯ç表å |
| | | |
| | | ```vue |
| | | <script setup lang="ts"> |
| | | import { useForm } from 'vee-validate'; |
| | | import { toTypedSchema } from '@vee-validate/zod'; |
| | | import { z } from 'zod'; |
| | | |
| | | const schema = toTypedSchema( |
| | | z.object({ |
| | | username: z.string().min(3, 'ç¨æ·åè³å°3个å符'), |
| | | email: z.string().email('é®ç®±æ ¼å¼ä¸æ£ç¡®'), |
| | | role: z.enum(['admin', 'user', 'guest']), |
| | | }) |
| | | ); |
| | | |
| | | const { handleSubmit, setFieldValue, values } = useForm({ |
| | | validationSchema: schema, |
| | | initialValues: { |
| | | username: '', |
| | | email: '', |
| | | role: 'user', |
| | | }, |
| | | }); |
| | | |
| | | const onSubmit = handleSubmit(async (values) => { |
| | | // ç±»åå®å
¨ç values |
| | | console.log(values); |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <a-form @submit="onSubmit"> |
| | | <a-form-item label="ç¨æ·å" name="username"> |
| | | <a-input v-model:value="values.username" /> |
| | | </a-form-item> |
| | | <a-form-item label="é®ç®±" name="email"> |
| | | <a-input v-model:value="values.email" type="email" /> |
| | | </a-form-item> |
| | | <a-form-item label="è§è²" name="role"> |
| | | <a-select v-model:value="values.role"> |
| | | <a-select-option value="admin">管çå</a-select-option> |
| | | <a-select-option value="user">ç¨æ·</a-select-option> |
| | | <a-select-option value="guest">访客</a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | <a-button type="primary" html-type="submit">æäº¤</a-button> |
| | | </a-form> |
| | | </template> |
| | | ``` |
| | | |
| | | ### 带æä½åçè¡¨æ ¼ |
| | | |
| | | ```vue |
| | | <script setup lang="ts"> |
| | | import { ref, computed } from 'vue'; |
| | | import type { TableColumn } from '#/types'; |
| | | |
| | | interface User { |
| | | id: number; |
| | | name: string; |
| | | email: string; |
| | | status: 'active' | 'inactive'; |
| | | } |
| | | |
| | | const columns: TableColumn<User>[] = [ |
| | | { title: 'å§å', dataIndex: 'name', key: 'name' }, |
| | | { title: 'é®ç®±', dataIndex: 'email', key: 'email' }, |
| | | { |
| | | title: 'ç¶æ', |
| | | dataIndex: 'status', |
| | | key: 'status', |
| | | customRender: ({ text }) => text === 'active' ? 'å¯ç¨' : 'ç¦ç¨', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | key: 'actions', |
| | | width: 150, |
| | | }, |
| | | ]; |
| | | |
| | | const dataSource = ref<User[]>([]); |
| | | const loading = ref(false); |
| | | |
| | | const handleEdit = (record: User) => { |
| | | // ç¼è¾é»è¾ |
| | | }; |
| | | |
| | | const handleDelete = (id: number) => { |
| | | // å é¤é»è¾ |
| | | }; |
| | | </script> |
| | | |
| | | <template> |
| | | <a-table |
| | | :columns="columns" |
| | | :data-source="dataSource" |
| | | :loading="loading" |
| | | row-key="id" |
| | | > |
| | | <template #bodyCell="{ column, record }"> |
| | | <template v-if="column.key === 'actions'"> |
| | | <a-space> |
| | | <a-button type="link" @click="handleEdit(record)">ç¼è¾</a-button> |
| | | <a-popconfirm |
| | | title="ç¡®å®è¦å é¤åï¼" |
| | | @confirm="handleDelete(record.id)" |
| | | > |
| | | <a-button type="link" danger>å é¤</a-button> |
| | | </a-popconfirm> |
| | | </a-space> |
| | | </template> |
| | | </template> |
| | | </a-table> |
| | | </template> |
| | | ``` |
| | | |
| | | ### æ¨¡ææ¡ |
| | | |
| | | ```vue |
| | | <script setup lang="ts"> |
| | | import { ref } from 'vue'; |
| | | |
| | | const visible = ref(false); |
| | | const loading = ref(false); |
| | | |
| | | const open = () => { |
| | | visible.value = true; |
| | | }; |
| | | |
| | | const handleOk = async () => { |
| | | loading.value = true; |
| | | try { |
| | | // ä¿åé»è¾ |
| | | visible.value = false; |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const handleCancel = () => { |
| | | visible.value = false; |
| | | }; |
| | | |
| | | defineExpose({ open }); |
| | | </script> |
| | | |
| | | <template> |
| | | <a-modal |
| | | v-model:open="visible" |
| | | title="å¯¹è¯æ¡æ é¢" |
| | | :confirm-loading="loading" |
| | | @ok="handleOk" |
| | | @cancel="handleCancel" |
| | | > |
| | | <p>æ¨¡ææ¡å
容</p> |
| | | </a-modal> |
| | | </template> |
| | | ``` |
| | | |
| | | ## æä½³å®è·µ |
| | | |
| | | ### æ¨è |
| | | |
| | | - å¤æè¡¨åéªè¯ä½¿ç¨ VeeValidate + Zod |
| | | - Ant Design è¾å
¥æ¡ä½¿ç¨ `v-model:value` |
| | | - 卿¨¡æ¿å¤è§£æè¡¨æ ¼åå®ä¹ |
| | | - å±é©æä½ä½¿ç¨ `a-popconfirm` 确认 |
| | | - è®¾ç½®è¡¨æ ¼ `row-key` æåæ§è½ |
| | | |
| | | ### é¿å
|
| | | |
| | | - ä¸è¦æ··ç¨ Ant Design 3.x å 4.x API |
| | | - ä¸è¦å¿è®°å¤çå è½½ç¶æ |
| | | - ä¸è¦ä½¿ç¨å
èæ ·å¼ - ä½¿ç¨ Tailwind æ scoped CSS |
| | | |
| | | ## Vxe-Table å¤æè¡¨æ ¼ |
| | | |
| | | 以ä¸åºæ¯ä½¿ç¨ Vxe-Tableï¼ |
| | | - å¤§æ°æ®éèææ»å¨ |
| | | - Excel 飿 ¼ç¼è¾ |
| | | - 夿åå
æ ¼åå¹¶ |
| | | - æ å½¢æ°æ®å±å¼/æå |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | --- |
| | | name: frontend-code-review |
| | | description: Vue 3 + TypeScript 项ç®å端代ç å®¡æ¥æå |
| | | --- |
| | | |
| | | # å端代ç å®¡æ¥æè½ |
| | | |
| | | ## 使¶ä½¿ç¨ |
| | | |
| | | å¨ä»¥ä¸åºæ¯æ¿æ´»æ¤æè½ï¼ |
| | | - å®¡æ¥ Pull Request |
| | | - è¿è¡ Vue ç»ä»¶ä»£ç å®¡æ¥ |
| | | - æ£æ¥å®å
¨æ¼æ´ |
| | | - éªè¯ TypeScript ç±»åå®å
¨ |
| | | - å®¡æ¥æ§è½ä¼å |
| | | |
| | | ## å·¥ä½åç |
| | | |
| | | æ¤æè½æä¾å端代ç 审æ¥çæ£æ¥æ¸
ååæåã |
| | | |
| | | ## å®¡æ¥æ¸
å |
| | | |
| | | ### TypeScript åç±»å |
| | | |
| | | - [ ] 没æä¸åçç `any` ç±»å |
| | | - [ ] Props å Emits ææ£ç¡®ç TypeScript æ¥å£ |
| | | - [ ] API ååºæç±»åæ¥å£ |
| | | - [ ] æ£ç¡®ä½¿ç¨æ³å |
| | | - [ ] ç±»å导å
¥ä½¿ç¨ `import type` è¯æ³ |
| | | |
| | | ### Vue ç»ä»¶ |
| | | |
| | | - [ ] ä½¿ç¨ `<script setup>` è¯æ³ |
| | | - [ ] å¯é Props æé»è®¤å¼ |
| | | - [ ] Emits ä½¿ç¨ TypeScript ç±»å |
| | | - [ ] 没æç´æ¥ä¿®æ¹ props |
| | | - [ ] æ£ç¡®ä½¿ç¨ `computed` vs `watch` |
| | | - [ ] å¨ `onUnmounted` 䏿¸
çå¯ä½ç¨ |
| | | |
| | | ### å®å
¨æ§ |
| | | |
| | | - [ ] 没æ XSS æ¼æ´ï¼ç¨æ·å
å®¹ä½¿ç¨ `v-dompurify-html`ï¼ |
| | | - [ ] 没æç¡¬ç¼ç çå¯é¥æ API Key |
| | | - [ ] ç¨æ·è¾å
¥å·²è¿æ»¤ |
| | | - [ ] æææ°æ®ä¸å¨ localStorageï¼ä½¿ç¨ secure-ls æ sessionStorageï¼ |
| | | - [ ] CSRF token æ£ç¡®å¤ç |
| | | |
| | | ### æ§è½ |
| | | |
| | | - [ ] 大å表使ç¨èææ»å¨ |
| | | - [ ] å¾çå·²ä¼å |
| | | - [ ] è·¯ç±æå è½½ |
| | | - [ ] 没æä¸å¿
è¦ç鿏²æ |
| | | - [ ] æ£ç¡®ä½¿ç¨ `v-memo` å `v-once` |
| | | |
| | | ### å¯è®¿é®æ§ |
| | | |
| | | - [ ] æ£ç¡®ç ARIA 屿§ |
| | | - [ ] é®ç导èªå¯ç¨ |
| | | - [ ] é¢è²å¯¹æ¯åº¦è¶³å¤ |
| | | - [ ] æ¨¡ææ¡ç¦ç¹ç®¡ç |
| | | |
| | | ### 代ç è´¨é |
| | | |
| | | - [ ] 彿°èè´£åä¸ |
| | | - [ ] 没æéå¤ä»£ç ï¼æåå°ç»åå¼å½æ°ï¼ |
| | | - [ ] é误å¤çå®å |
| | | - [ ] å è½½ç¶æå·²å¤ç |
| | | - [ ] è¾¹çæ
åµå·²èè |
| | | |
| | | ## 常è§é®é¢æ è®° |
| | | |
| | | ### 1. ç´æ¥ä¿®æ¹ Props |
| | | |
| | | ```typescript |
| | | // é误 |
| | | props.value = newValue; |
| | | |
| | | // æ£ç¡® |
| | | emit('update:value', newValue); |
| | | ``` |
| | | |
| | | ### 2. å
åæ³æ¼ |
| | | |
| | | ```typescript |
| | | // é误 - æ²¡ææ¸
ç |
| | | onMounted(() => { |
| | | window.addEventListener('resize', handleResize); |
| | | }); |
| | | |
| | | // æ£ç¡® - ç»ä»¶å¸è½½æ¶æ¸
ç |
| | | onMounted(() => { |
| | | window.addEventListener('resize', handleResize); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | window.removeEventListener('resize', handleResize); |
| | | }); |
| | | ``` |
| | | |
| | | ### 3. ä¸å¿
è¦çååºå¼ |
| | | |
| | | ```typescript |
| | | // é误 - éææ°æ®ä¸éè¦ååºå¼ |
| | | const menuItems = reactive([ |
| | | { label: 'é¦é¡µ', path: '/' }, |
| | | { label: 'å
³äº', path: '/about' }, |
| | | ]); |
| | | |
| | | // æ£ç¡® - éææ°æ®ä¸éè¦ååºå¼ |
| | | const menuItems = [ |
| | | { label: 'é¦é¡µ', path: '/' }, |
| | | { label: 'å
³äº', path: '/about' }, |
| | | ]; |
| | | ``` |
| | | |
| | | ### 4. 缺å°é误边ç |
| | | |
| | | ```vue |
| | | <!-- é误 - 没æé误å¤ç --> |
| | | <template> |
| | | <ExpensiveComponent /> |
| | | </template> |
| | | |
| | | <!-- æ£ç¡® - æé误边ç --> |
| | | <template> |
| | | <ErrorBoundary> |
| | | <ExpensiveComponent /> |
| | | </ErrorBoundary> |
| | | </template> |
| | | ``` |
| | | |
| | | ## å®å
¨çº¢çº¿ |
| | | |
| | | 1. **XSS é£é©** |
| | | - ä½¿ç¨ `v-html` 使²¡æè¿æ»¤ |
| | | - 渲æç¨æ·çæçå
容 |
| | | - éè¿ `window.location` è¿è¡ URL 注å
¥ |
| | | |
| | | 2. **æ°æ®æ³é²** |
| | | - æ§å¶å°è¾åºæææ°æ® |
| | | - Token å¨ localStorage 䏿ªå å¯ |
| | | - æºä»£ç 䏿 API Key |
| | | |
| | | 3. **注å
¥é£é©** |
| | | - 卿ç»ä»¶åæ¥èªç¨æ·è¾å
¥ |
| | | - ä½¿ç¨ `eval()` æ `Function()` |
| | | - API è°ç¨ä¸ç SQL 注å
¥ |
| | | |
| | | ## å®¡æ¥æè§æ ¼å¼ |
| | | |
| | | 使ç¨å»ºè®¾æ§çè¯è®ºï¼ |
| | | |
| | | ```markdown |
| | | **建议**: è¿éèèä½¿ç¨ `computed` æ¿ä»£ `watch` 以è·å¾æ´å¥½çæ§è½ã |
| | | |
| | | ```typescript |
| | | // å½å代ç |
| | | watch(() => props.value, (val) => { |
| | | doubled.value = val * 2; |
| | | }); |
| | | |
| | | // å»ºè®®ä¿®æ¹ |
| | | const doubled = computed(() => props.value * 2); |
| | | ``` |
| | | |
| | | åå : 计ç®å±æ§æç¼åï¼åªæä¾èµååæ¶æéæ°è®¡ç®ã |
| | | ``` |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | --- |
| | | name: vue3-development |
| | | description: Vue 3 + TypeScript + Composition API å¼åè§èåæä½³å®è·µ |
| | | --- |
| | | |
| | | # Vue 3 å¼åæè½ |
| | | |
| | | ## 使¶ä½¿ç¨ |
| | | |
| | | å¨ä»¥ä¸åºæ¯æ¿æ´»æ¤æè½ï¼ |
| | | - ä½¿ç¨ Composition API å¼å Vue 3 ç»ä»¶ |
| | | - 为 Vue åºç¨ç¼å TypeScript 代ç |
| | | - 设置 Pinia ç¶æå卿 Vue Router |
| | | - 使ç¨ç»åå¼å½æ°åååºå¼ç¶æ |
| | | - ä¼å Vue ç»ä»¶æ§è½ |
| | | |
| | | ## å·¥ä½åç |
| | | |
| | | æ¤æè½ä¸º Vue 3 å¼åæ¨¡å¼æä¾æå¯¼ã |
| | | |
| | | ## ææ¯æ |
| | | |
| | | - **Vue**: 3.5.x ä½¿ç¨ Composition API |
| | | - **TypeScript**: 宿´ç±»åå®å
¨ |
| | | - **ç¶æç®¡ç**: Pinia + persistedstate |
| | | - **è·¯ç±**: Vue Router 5.x |
| | | - **表åéªè¯**: VeeValidate + Zod |
| | | - **HTTP 客æ·ç«¯**: Axios |
| | | |
| | | ## 代ç 飿 ¼ |
| | | |
| | | ### ç»ä»¶ç»æ |
| | | |
| | | ```vue |
| | | <script setup lang="ts"> |
| | | import { ref, computed, onMounted } from 'vue'; |
| | | import type { PropType } from 'vue'; |
| | | |
| | | // ä½¿ç¨ TypeScript å®ä¹ Props |
| | | interface Props { |
| | | title: string; |
| | | items?: string[]; |
| | | } |
| | | |
| | | const props = withDefaults(defineProps<Props>(), { |
| | | items: () => [], |
| | | }); |
| | | |
| | | // ä½¿ç¨ TypeScript å®ä¹ Emits |
| | | interface Emits { |
| | | (e: 'update', value: string): void; |
| | | (e: 'delete', id: number): void; |
| | | } |
| | | |
| | | const emit = defineEmits<Emits>(); |
| | | |
| | | // ååºå¼ç¶æ |
| | | const isLoading = ref(false); |
| | | const localData = ref<string | null>(null); |
| | | |
| | | // 计ç®å±æ§ |
| | | const formattedTitle = computed(() => props.title.toUpperCase()); |
| | | |
| | | // æ¹æ³ |
| | | const handleSubmit = async () => { |
| | | isLoading.value = true; |
| | | try { |
| | | // ... |
| | | } finally { |
| | | isLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | // åå§å |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="component-wrapper"> |
| | | <h1>{{ formattedTitle }}</h1> |
| | | <slot /> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .component-wrapper { |
| | | /* scoped æ ·å¼ */ |
| | | } |
| | | </style> |
| | | ``` |
| | | |
| | | ### ç»åå¼å½æ°æ¨¡å¼ |
| | | |
| | | ```typescript |
| | | // composables/useUser.ts |
| | | import { ref, computed } from 'vue'; |
| | | import { defineStore } from 'pinia'; |
| | | |
| | | export const useUserStore = defineStore('user', () => { |
| | | const user = ref<User | null>(null); |
| | | const isLoggedIn = computed(() => !!user.value); |
| | | |
| | | const login = async (credentials: LoginCredentials) => { |
| | | // å®ç° |
| | | }; |
| | | |
| | | const logout = () => { |
| | | user.value = null; |
| | | }; |
| | | |
| | | return { user, isLoggedIn, login, logout }; |
| | | }); |
| | | ``` |
| | | |
| | | ### API éæ |
| | | |
| | | ```typescript |
| | | // api/user.ts |
| | | import { request } from '#/utils/request'; |
| | | |
| | | export const userApi = { |
| | | getList: (params: UserQuery) => request.get<UserList>('/users', { params }), |
| | | getById: (id: number) => request.get<User>(`/users/${id}`), |
| | | create: (data: CreateUserDTO) => request.post<User>('/users', data), |
| | | update: (id: number, data: UpdateUserDTO) => request.put<User>(`/users/${id}`, data), |
| | | delete: (id: number) => request.delete(`/users/${id}`), |
| | | }; |
| | | ``` |
| | | |
| | | ## æä½³å®è·µ |
| | | |
| | | ### æ¨è |
| | | |
| | | - ææç»ä»¶ä½¿ç¨ `<script setup>` è¯æ³ |
| | | - ä½¿ç¨ TypeScript æ¥å£å®ä¹ props å emits |
| | | - 使ç¨ç»åå¼å½æ°å¤ç¨é»è¾ |
| | | - å°½å¯è½ä½¿ç¨ `computed` èé `watch` |
| | | - è·¯ç±çº§ç»ä»¶ä½¿ç¨ `defineAsyncComponent` æå è½½ |
| | | - ä¿æç»ä»¶èè´£åä¸ |
| | | |
| | | ### é¿å
|
| | | |
| | | - ä¸è¦ä½¿ç¨ Options APIï¼æ¨è Composition APIï¼ |
| | | - ä¸è¦ç´æ¥ä¿®æ¹ props |
| | | - ä¸è¦ä½¿ç¨ `any` ç±»å - å®ä¹æ£ç¡®çæ¥å£ |
| | | - ä¸è¦å¿è®°å¨ `onUnmounted` 䏿¸
çå¯ä½ç¨ |
| | | |
| | | ## æ§è½ä¼å建议 |
| | | |
| | | 1. 对äºä¸éè¦æ·±åº¦ååºç大åå¯¹è±¡ä½¿ç¨ `shallowRef` |
| | | 2. å¯¹äºæè´µçå表渲æä½¿ç¨ `v-memo` |
| | | 3. ä½¿ç¨ `defineAsyncComponent` æå è½½ç»ä»¶ |
| | | 4. 对äºéæå
å®¹ä½¿ç¨ `v-once` |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | --- |
| | | name: vue-component |
| | | description: å建æ°ç Vue 3 ç»ä»¶ï¼å
å« TypeScript ç±»åå®ä¹åæä½³å®è·µ |
| | | allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] |
| | | --- |
| | | |
| | | # /vue-component |
| | | |
| | | å建æ°ç Vue 3 ç»ä»¶æ¶ä½¿ç¨æ¤å·¥ä½æµã |
| | | |
| | | ## ç®æ |
| | | |
| | | å建ä¸ä¸ªç»æè¯å¥½ç Vue 3 ç»ä»¶ï¼å
å« TypeScript ç±»åãæ£ç¡®ç props/emit å®ä¹ï¼å¹¶éµå¾ªé¡¹ç®è§èã |
| | | |
| | | ## å¸¸ç¨æä»¶ |
| | | |
| | | - `src/components/**/*.vue` |
| | | - `src/views/**/*.vue` |
| | | - `src/**/components/*.vue` |
| | | |
| | | ## 建议æ¥éª¤ |
| | | |
| | | 1. æ ¹æ®ä½ç¨åç¡®å®ç»ä»¶ä½ç½®ï¼å
¨å±ç»ä»¶ vs åè½ç»ä»¶ï¼ |
| | | 2. å建 `.vue` æä»¶ï¼ä½¿ç¨ `<script setup lang="ts">` |
| | | 3. å®ä¹ Props å Emits ç TypeScript æ¥å£ |
| | | 4. ä½¿ç¨ Composition API å®ç°ç»ä»¶é»è¾ |
| | | 5. æ·»å æ¨¡æ¿åæ£ç¡®çç»å® |
| | | 6. å¦éè¦ï¼æ·»å scoped æ ·å¼ |
| | | 7. 导åºç»ä»¶ä¾ä½¿ç¨ |
| | | |
| | | ## ç»ä»¶æ¨¡æ¿ |
| | | |
| | | ```vue |
| | | <script setup lang="ts"> |
| | | import { ref, computed } from 'vue'; |
| | | |
| | | // Props å®ä¹ |
| | | interface Props { |
| | | title: string; |
| | | disabled?: boolean; |
| | | } |
| | | |
| | | const props = withDefaults(defineProps<Props>(), { |
| | | disabled: false, |
| | | }); |
| | | |
| | | // Emits å®ä¹ |
| | | interface Emits { |
| | | (e: 'click', value: string): void; |
| | | } |
| | | |
| | | const emit = defineEmits<Emits>(); |
| | | |
| | | // ååºå¼ç¶æ |
| | | const isActive = ref(false); |
| | | |
| | | // æ¹æ³ |
| | | const handleClick = () => { |
| | | if (!props.disabled) { |
| | | isActive.value = !isActive.value; |
| | | emit('click', props.title); |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <template> |
| | | <div |
| | | class="component-name" |
| | | :class="{ 'is-active': isActive, 'is-disabled': disabled }" |
| | | @click="handleClick" |
| | | > |
| | | <span>{{ title }}</span> |
| | | <slot /> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .component-name { |
| | | /* æ ·å¼ */ |
| | | } |
| | | </style> |
| | | ``` |
| | | |
| | | ## å
¸åæäº¤ä¿¡æ¯ |
| | | |
| | | - feat: add [ç»ä»¶å] component |
| | | - feat: implement [ç»ä»¶å] for [åè½å] |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | --- |
| | | name: vue-page |
| | | description: å建æ°ç页é¢/è§å¾ï¼å
å« Vue Router è·¯ç±é
ç½® |
| | | allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] |
| | | --- |
| | | |
| | | # /vue-page |
| | | |
| | | å建æ°ç页é¢/è§å¾æ¶ä½¿ç¨æ¤å·¥ä½æµã |
| | | |
| | | ## ç®æ |
| | | |
| | | å建æ°ç页é¢ç»ä»¶å¹¶å¨ Vue Router 䏿³¨åè·¯ç±ã |
| | | |
| | | ## å¸¸ç¨æä»¶ |
| | | |
| | | - `src/views/**/*.vue` |
| | | - `src/router/routes/**/*.ts` |
| | | |
| | | ## 建议æ¥éª¤ |
| | | |
| | | 1. å¨ `src/views/` ä¸å建页é¢ç»ä»¶ |
| | | 2. å®ä¹è·¯ç±é
ç½® |
| | | 3. å°è·¯ç±æ·»å å°è·¯ç±å¨ |
| | | 4. å¦éè¦ï¼æ·»å 页颿 é¢å meta ä¿¡æ¯ |
| | | |
| | | ## 页颿¨¡æ¿ |
| | | |
| | | ```vue |
| | | <script setup lang="ts"> |
| | | import { ref, onMounted } from 'vue'; |
| | | import { useRoute } from 'vue-router'; |
| | | |
| | | defineOptions({ |
| | | name: 'PageName', |
| | | }); |
| | | |
| | | const route = useRoute(); |
| | | const loading = ref(false); |
| | | |
| | | onMounted(() => { |
| | | // åå§å页颿°æ® |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="page-container"> |
| | | <a-spin :spinning="loading"> |
| | | <!-- 页é¢å
容 --> |
| | | <h1>页颿 é¢</h1> |
| | | </a-spin> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .page-container { |
| | | padding: 16px; |
| | | } |
| | | </style> |
| | | ``` |
| | | |
| | | ## è·¯ç±é
ç½® |
| | | |
| | | ```typescript |
| | | // router/routes/module.ts |
| | | import type { RouteRecordRaw } from 'vue-router'; |
| | | |
| | | const routes: RouteRecordRaw[] = [ |
| | | { |
| | | path: '/module', |
| | | name: 'ModuleList', |
| | | component: () => import('#/views/module/index.vue'), |
| | | meta: { |
| | | title: '模åå表', |
| | | icon: 'mdi:list', |
| | | }, |
| | | }, |
| | | { |
| | | path: '/module/:id', |
| | | name: 'ModuleDetail', |
| | | component: () => import('#/views/module/detail.vue'), |
| | | meta: { |
| | | title: '模å详æ
', |
| | | hideMenu: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | |
| | | export default routes; |
| | | ``` |
| | | |
| | | ## å
¸åæäº¤ä¿¡æ¯ |
| | | |
| | | - feat: add [页é¢å] page |
| | | - feat: implement [页é¢å] view |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # é¡¹ç®æå |
| | | |
| | | è¿æ¯åºäº **Vue Vben Admin** çå端项ç®ï¼ä½¿ç¨ Vue 3 + TypeScript + Vite + Ant Design Vueã |
| | | |
| | | ## ææ¯æ |
| | | |
| | | - **æ¡æ¶**: Vue 3.5 + TypeScript 6.0 |
| | | - **æå»ºå·¥å
·**: Vite 8.0 |
| | | - **UI ç»ä»¶**: Ant Design Vue 4.2 |
| | | - **ç¶æç®¡ç**: Pinia 3.0 + persistedstate |
| | | - **è·¯ç±**: Vue Router 5.1 |
| | | - **表åéªè¯**: VeeValidate + Zod |
| | | - **HTTP 客æ·ç«¯**: Axios |
| | | - **æ ·å¼**: Tailwind CSS 4.3 + SCSS |
| | | - **å¾è¡¨**: ECharts 6.1 |
| | | - **坿æ¬ç¼è¾å¨**: TinyMCE 7.9, Tiptap 3.26 |
| | | - **æµç¨å¾**: BPMN.js 18.16 |
| | | |
| | | ## 项ç®ç»æ |
| | | |
| | | ``` |
| | | src/ |
| | | âââ api/ # API æ¥å£å®ä¹ |
| | | âââ components/ # å
Œ
±ç»ä»¶ |
| | | âââ composables/ # ç»åå¼å½æ° |
| | | âââ layouts/ # å¸å±ç»ä»¶ |
| | | âââ locales/ # å½é
å |
| | | âââ router/ # è·¯ç±é
ç½® |
| | | âââ stores/ # Pinia ç¶æç®¡ç |
| | | âââ styles/ # å
¨å±æ ·å¼ |
| | | âââ utils/ # å·¥å
·å½æ° |
| | | âââ views/ # 页é¢ç»ä»¶ |
| | | ``` |
| | | |
| | | ## å¼åå½ä»¤ |
| | | |
| | | ```bash |
| | | # å¼å |
| | | pnpm dev |
| | | |
| | | # æå»º |
| | | pnpm build |
| | | |
| | | # ç±»åæ£æ¥ |
| | | pnpm typecheck |
| | | |
| | | # é¢è§æå»ºç»æ |
| | | pnpm preview |
| | | ``` |
| | | |
| | | ## 代ç è§è |
| | | |
| | | ### ç»ä»¶å½å |
| | | |
| | | - ç»ä»¶æä»¶ä½¿ç¨ PascalCase: `UserList.vue` |
| | | - ç»ä»¶ name ä½¿ç¨ PascalCase: `defineOptions({ name: 'UserList' })` |
| | | |
| | | ### TypeScript |
| | | |
| | | - ç¦æ¢ä½¿ç¨ `any` ç±»å |
| | | - Props å Emits å¿
é¡»å®ä¹ TypeScript æ¥å£ |
| | | - ä½¿ç¨ `import type` 导å
¥ç±»å |
| | | |
| | | ### Vue ç»ä»¶ |
| | | |
| | | - ç»ä¸ä½¿ç¨ `<script setup lang="ts">` è¯æ³ |
| | | - Props ä½¿ç¨ `withDefaults` + `defineProps<T>()` |
| | | - Emits ä½¿ç¨ `defineEmits<T>()` |
| | | |
| | | ### æ ·å¼ |
| | | |
| | | - ä¼å
ä½¿ç¨ Tailwind CSS ç±» |
| | | - ç»ä»¶æ ·å¼ä½¿ç¨ `scoped` |
| | | - é¢è²ä½¿ç¨ CSS åé |
| | | |
| | | ## å¯ç¨å½ä»¤ |
| | | |
| | | - `/vue-component` - å建æ°ç Vue ç»ä»¶ |
| | | - `/vue-page` - å建æ°çé¡µé¢ |
| | | - `/api-service` - å建 API æå¡æ¨¡å |
| | | - `/pinia-store` - å建 Pinia ç¶æåå¨ |
| | | |
| | | ## å¯ç¨æè½ |
| | | |
| | | - `vue3-development` - Vue 3 å¼åè§è |
| | | - `antd-vue-components` - Ant Design Vue ç»ä»¶æ¨¡å¼ |
| | | - `frontend-code-review` - å端代ç å®¡æ¥æå |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | 1. è·¯å¾å«å `#/*` æå `./src/*` |
| | | 2. ä½¿ç¨ VeeValidate + Zod è¿è¡è¡¨åéªè¯ |
| | | 3. ä½¿ç¨ `v-dompurify-html` å¤çç¨æ·è¾å
¥ç HTML |
| | | 4. æææ°æ®ä½¿ç¨ `secure-ls` å å¯åå¨ |