vue2server7 7.0.105 → 7.0.107

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2server7",
3
- "version": "7.0.105",
3
+ "version": "7.0.107",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "dev": "nodemon --watch src --ext ts --exec \"ts-node src/app.ts\"",
package/test/231231 ADDED
@@ -0,0 +1,128 @@
1
+ <template>
2
+ <div class="error-page">
3
+ <div class="error-content">
4
+ <div class="illustration">
5
+ <svg viewBox="0 0 420 260" fill="none" xmlns="http://www.w3.org/2000/svg">
6
+ <path d="M120 75H310L292 155H95L120 75Z" stroke="#5B83F7" stroke-width="2" />
7
+ <path d="M135 90H290" stroke="#5B83F7" stroke-width="14" />
8
+ <path d="M104 110H150" stroke="#5B83F7" stroke-width="14" />
9
+ <path d="M292 110H345" stroke="#5B83F7" stroke-width="8" />
10
+ <path d="M306 135H335" stroke="#5B83F7" stroke-width="5" />
11
+ <path d="M260 175H355" stroke="#5B83F7" stroke-width="14" />
12
+ <path d="M85 168H250" stroke="#5B83F7" stroke-width="2" />
13
+ <circle cx="245" cy="122" r="43" fill="#6B8DFF" />
14
+ <path d="M230 107L260 137M260 107L230 137" stroke="white" stroke-width="10" stroke-linecap="round" />
15
+ <circle cx="65" cy="155" r="24" stroke="#5B83F7" stroke-width="2" />
16
+ <circle cx="95" cy="158" r="34" stroke="#5B83F7" stroke-width="2" />
17
+ <circle cx="95" cy="158" r="22" fill="#87A4FF" />
18
+ <path d="M48 155C20 150 25 130 50 132C74 134 85 143 92 154" stroke="#5B83F7" stroke-width="2" />
19
+ <path d="M30 185H118C124 185 129 190 129 196C129 202 124 207 118 207H82C76 207 71 212 71 218C71 224 76 229 82 229H52" stroke="#5B83F7" stroke-width="2" />
20
+ <path d="M40 196H155" stroke="#5B83F7" stroke-width="18" stroke-linecap="round" />
21
+ <path d="M92 190V222" stroke="#5B83F7" stroke-width="18" stroke-linecap="round" />
22
+ <path d="M58 223H115" stroke="#5B83F7" stroke-width="16" stroke-linecap="round" />
23
+ <path d="M270 223H330" stroke="#5B83F7" stroke-width="2" />
24
+ <path d="M280 210H310C318 210 325 217 325 225H275C275 217 276 210 280 210Z" fill="#5B83F7" />
25
+ <path d="M318 210H333" stroke="#5B83F7" stroke-width="2" />
26
+ <path d="M130 62L140 30H315L300 75" stroke="#5B83F7" stroke-width="2" />
27
+ <path d="M320 40L305 100" stroke="#5B83F7" stroke-width="2" />
28
+ <path d="M115 112L92 175" stroke="#5B83F7" stroke-width="2" />
29
+ <path d="M305 78H340" stroke="#476AD7" stroke-width="8" />
30
+ <circle cx="148" cy="48" r="3" fill="#5B83F7" />
31
+ <circle cx="162" cy="48" r="3" fill="#5B83F7" />
32
+ </svg>
33
+ </div>
34
+
35
+ <div class="text-box">
36
+ <h2>抱歉,服务器出错了</h2>
37
+ <el-button type="primary" class="back-btn" @click="goHome">
38
+ 返回首页
39
+ </el-button>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </template>
44
+
45
+ <script setup lang="ts">
46
+ import { useRouter } from 'vue-router'
47
+
48
+ const router = useRouter()
49
+
50
+ const goHome = (): void => {
51
+ router.push('/')
52
+ }
53
+ </script>
54
+
55
+ <style scoped>
56
+ .error-page {
57
+ width: 100%;
58
+ min-height: 100vh;
59
+ background: #f7f8fa;
60
+ display: flex;
61
+ align-items: center;
62
+ justify-content: center;
63
+ }
64
+
65
+ .error-content {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: 110px;
69
+ transform: translateY(-20px);
70
+ }
71
+
72
+ .illustration {
73
+ width: 420px;
74
+ height: 260px;
75
+ }
76
+
77
+ .illustration svg {
78
+ width: 100%;
79
+ height: 100%;
80
+ display: block;
81
+ }
82
+
83
+ .text-box {
84
+ padding-top: 10px;
85
+ }
86
+
87
+ .text-box h2 {
88
+ margin: 0 0 28px;
89
+ font-size: 26px;
90
+ font-weight: 600;
91
+ color: #71809a;
92
+ letter-spacing: 1px;
93
+ }
94
+
95
+ .back-btn {
96
+ width: 118px;
97
+ height: 48px;
98
+ border-radius: 6px;
99
+ font-size: 16px;
100
+ background: #5b83f7;
101
+ border-color: #5b83f7;
102
+ }
103
+
104
+ .back-btn:hover,
105
+ .back-btn:focus {
106
+ background: #6b8dff;
107
+ border-color: #6b8dff;
108
+ }
109
+
110
+ @media (max-width: 768px) {
111
+ .error-content {
112
+ flex-direction: column;
113
+ gap: 24px;
114
+ }
115
+
116
+ .illustration {
117
+ width: 320px;
118
+ }
119
+
120
+ .text-box {
121
+ text-align: center;
122
+ }
123
+
124
+ .text-box h2 {
125
+ font-size: 22px;
126
+ }
127
+ }
128
+ </style>
@@ -0,0 +1,5 @@
1
+ <svg
2
+ viewBox="0 0 400 300"
3
+ fill="none"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ ><mask id="a" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="47" y="38" width="307" height="224"><path d="M353.3 38H47.5v223.8h305.8V38Z" fill="#fff"/></mask><g mask="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M299.2 200.6H61.6v5.1h240.3l-2.7-5.1Z" fill="#C7DEFF"/><path d="m308.9 185.8-6.5 20H183.7M332.3 127.6h10.6l-5 16.7-14.8-.1-7.2 21.1M328.8 127.4l13.6-39.6M307.6 166 337 84.7H180.6l-9.8 26.9h-10.5M296.6 196l4.3-11.8M157.2 149.2l6.4-17.7" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M324.8 93.1H188.5l-34.8 95.8h136.4l34.7-95.8ZM169.9 166.2l5-13.6-5 13.6Z" fill="#fff"/><path d="m169.9 166.2 5-13.6" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M324.8 93.1H188.5l-4 11.7h135.8l4.5-11.7Z" fill="#006EFF"/><path fill-rule="evenodd" clip-rule="evenodd" d="M102.6 159.5h38.3l2.7 36.6h-38.4c-10.1 0-20.9-8.2-20.9-18.3 0-10.1 8.2-18.3 18.3-18.3Z" fill="#DEEBFC"/><path fill-rule="evenodd" clip-rule="evenodd" d="M84.3 174.102c2.5 3.4 10 5 17.9 2.8 16.6-6.5 23.8-3.9 23.8-3.9s.5-3.4 1.3-5c-5.8-3-15.4.3-26.1 3.1-10.7 2.8-15.8-2.5-15.8-2.5-.4 0-1.1 2.8-1.1 5.5Z" fill="#fff"/><path d="M96.5 194.2c-7.2-3.3-12.2-10.5-12.2-19m0 0c0-11.5 9.3-20.8 20.8-20.8h29.4" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M140.3 195.1c-8.4-2.7-14.5-10.6-14.5-19.8l14.5 19.8Zm-14.5-19.8c0-11.5 9.3-20.8 20.8-20.8l-20.8 20.8Zm20.8-20.8c11.5 0 20.8 9.3 20.8 20.8l-20.8-20.8Zm20.8 20.8c0 8.4-5 15.6-12.1 18.9l12.1-18.9Z" fill="#fff"/><path d="M140.3 195.1c-8.4-2.7-14.5-10.6-14.5-19.8m0 0c0-11.5 9.3-20.8 20.8-20.8m0 0c11.5 0 20.8 9.3 20.8 20.8m0 0c0 8.4-5 15.6-12.1 18.9" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M161.5 177.2c0-7.7-6.3-14-14-14s-14 6.3-14 14c0 5.8 3.5 10.8 8.6 12.9.1 0 5.8 1.6 10.7 0 5.3-1.7 8.7-7.1 8.7-12.9Z" fill="#00E4E5"/><path d="M140.5 190.1c-5.8-2.4-9.9-8.2-9.9-14.9 0-8.9 7.2-16.1 16.1-16.1 8.9 0 16.1 7.2 16.1 16.1 0 6.8-4.2 12.5-10.1 14.9M88.4 170.604c2.9 1.3 7.7 2.6 13.6.3 14.7-5.7 22.3-4.3 24.6-3.5M84.5 174.599s5.9 6.5 19 1.7c9.2-3.4 15.3-3.9 18.8-3.8" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M340.6 112.3h-55.2l-2.7 6.2H338l2.6-6.2Z" fill="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M236.8 117.9c-16.13 0-29.2 13.07-29.2 29.2s13.07 29.2 29.2 29.2 29.2-13.07 29.2-29.2-13.07-29.2-29.2-29.2Z" fill="#00E4E5"/><path d="M265 123.3c13.1 13.1 13.1 34.4 0 47.6M306 205.9h19.2M61.7 205.9h32.9M181.2 196.2h115.2M47.5 205.9h10v-9.7h73.8" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M146.7 179.2c-2.49 0-4.5 2.01-4.5 4.5s2.01 4.5 4.5 4.5 4.5-2.01 4.5-4.5-2.01-4.5-4.5-4.5Z" fill="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M169.5 196.2c3.9 0 7.1 3.2 7.1 7.1 0 3.9-3.2 7.1-7.1 7.1H144c-2.1 0-3.9 1.7-3.9 3.9v1c0 2.1 1.7 3.9 3.9 3.9h48c5.1 0 9.2 4.1 9.2 9.2s-4.1 9.3-9.2 9.2h-33.8c-2.3 0-4.1 1.8-4.1 4.1s1.8 4.1 4.1 4.1h4.2c4.4 0 8 3.6 8 8s-3.6 8-8 8H111c-3.7 0-6.8-3-6.8-6.8 0-3.7 3-6.8 6.8-6.8h.3c2.3 0 4.1-1.8 4.1-4.1s-1.8-4.1-4.1-4.1H79c-4.5 0-8.1-3.6-8.1-8.1s3.6-8.1 8.1-8.1h37.7c2.1 0 3.9-1.7 3.9-3.9 0-2.1-1.7-3.9-3.9-3.9h-7.9c-4.4 0-7.9-3.5-7.9-7.9s3.5-7.9 7.9-7.9h30.4c2.2 0 3.9-1.8 3.9-3.9V187c0-1.9 1.6-3.5 3.5-3.5s3.5 1.6 3.5 3.5v5.3c0 2.2 1.8 3.9 3.9 3.9h15.5Z" fill="#006EFF"/><path d="m227.8 138.5 18.7 18.7M227.8 157.2l18.7-18.7" stroke="#fff" stroke-width="6"/><path fill-rule="evenodd" clip-rule="evenodd" d="M194.8 96.9c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8ZM202.9 96.9c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8Z" fill="#fff"/><path d="m291.7 184.3-1.6 4.6h-121M298.1 166.7l22.5-61.9" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="m193 134.1 2.2-5.1h-19.4l-2.3 5.1H193ZM313.2 123.5l2.2-5.1h-24.5l-2.3 5.1h24.6Z" fill="#DEEBFC"/><path d="m164.5 159.2 19.8-54.6" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M199.6 119.8h-53.2l-4.4 9.3h53.2l4.4-9.3Z" fill="#00E4E5"/><path d="M151.3 129.1H142l4.4-9.3h16.9" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M353.3 169.4h-67.4l-4.8 12.2h67.3l4.9-12.2Z" fill="#006EFF"/><path d="M332.4 169.4h20.9l-4.9 12.2h-39.7M242.7 235.5v-4.8c0-3.8 3.1-7 7-7h20.2c3.8 0 7 3.1 7 7" stroke="#071F4D"/><path d="M261.1 235.5v-4.8c0-3.8 3.1-7 7-7h13.7c3.8 0 7 3.1 7 7v4.8M242.6 230.7h13.7M235.2 237.7h63.3M224 237.7h6.7" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M324.1 141.3H335l3.3-10.7h-10.2l-4 10.7Z" fill="#C7DEFF"/><path fill-rule="evenodd" clip-rule="evenodd" d="M288.3 230.4c0-3.6-2.9-6.5-6.5-6.5h-14.2c-3.6 0-6.5 2.9-6.5 6.5v5.3h27.2v-5.3Z" fill="#071F4D"/><path d="M80.4 228.5H83M87.7 228.5h19.2M146.3 195.8v2c0 3.6-2.9 6.6-6.6 6.6H138M133.4 204.3h1.5M154 249.9h9.4" stroke="#DEEBFC"/><path d="m299.4 141.9 5.1-13.9" stroke="#071F4D"/></g></svg>
@@ -0,0 +1,871 @@
1
+ # Vue3 项目开发规范
2
+
3
+ ## 1. 文档说明
4
+
5
+ 本文档用于规范 Vue3 项目的目录结构、代码风格、组件开发、路由管理、状态管理、接口请求、样式编写、Git 提交以及团队协作流程。
6
+
7
+ 适用技术栈:
8
+
9
+ - Vue 3
10
+ - Vite
11
+ - TypeScript
12
+ - Vue Router
13
+ - Pinia
14
+ - Axios
15
+ - Element Plus / TDesign Vue Next
16
+ - ESLint
17
+ - Prettier
18
+ - Stylelint
19
+
20
+ ---
21
+
22
+ ## 2. 项目目录规范
23
+
24
+ 推荐目录结构:
25
+
26
+ ```bash
27
+ src
28
+ ├── api # 接口请求模块
29
+ ├── assets # 静态资源
30
+ │ ├── images
31
+ │ ├── icons
32
+ │ └── fonts
33
+ ├── components # 公共组件
34
+ ├── composables # 组合式函数
35
+ ├── constants # 常量配置
36
+ ├── directives # 自定义指令
37
+ ├── hooks # 业务 hooks
38
+ ├── layouts # 页面布局
39
+ ├── plugins # 插件注册
40
+ ├── router # 路由配置
41
+ │ ├── index.ts
42
+ │ └── modules
43
+ ├── stores # Pinia 状态管理
44
+ ├── styles # 全局样式
45
+ ├── types # TypeScript 类型声明
46
+ ├── utils # 工具函数
47
+ ├── views # 页面模块
48
+ ├── App.vue
49
+ └── main.ts
50
+ ```
51
+
52
+ ---
53
+
54
+ ## 3. 文件命名规范
55
+
56
+ ### 3.1 Vue 组件命名
57
+
58
+ 组件文件使用大驼峰命名:
59
+
60
+ ```bash
61
+ UserCard.vue
62
+ UserForm.vue
63
+ BaseTable.vue
64
+ SearchPanel.vue
65
+ ```
66
+
67
+ ### 3.2 页面文件命名
68
+
69
+ 页面文件建议使用大驼峰命名:
70
+
71
+ ```bash
72
+ views/user/UserList.vue
73
+ views/user/UserDetail.vue
74
+ views/system/RoleManage.vue
75
+ ```
76
+
77
+ ### 3.3 工具文件命名
78
+
79
+ 工具文件使用小驼峰命名:
80
+
81
+ ```bash
82
+ formatDate.ts
83
+ storage.ts
84
+ request.ts
85
+ validate.ts
86
+ ```
87
+
88
+ ### 3.4 类型文件命名
89
+
90
+ 类型文件使用 `.type.ts` 后缀:
91
+
92
+ ```bash
93
+ user.type.ts
94
+ order.type.ts
95
+ api.type.ts
96
+ ```
97
+
98
+ ### 3.5 路由文件命名
99
+
100
+ 路由文件使用 `.route.ts` 后缀:
101
+
102
+ ```bash
103
+ user.route.ts
104
+ order.route.ts
105
+ system.route.ts
106
+ ```
107
+
108
+ ---
109
+
110
+ ## 4. 代码风格规范
111
+
112
+ ### 4.1 统一使用 `<script setup>`
113
+
114
+ ```vue
115
+ <script setup lang="ts">
116
+ import { ref } from 'vue'
117
+
118
+ const count = ref(0)
119
+
120
+ function handleAdd() {
121
+ count.value++
122
+ }
123
+ </script>
124
+
125
+ <template>
126
+ <button @click="handleAdd">
127
+ {{ count }}
128
+ </button>
129
+ </template>
130
+ ```
131
+
132
+ ### 4.2 使用 TypeScript
133
+
134
+ 所有 Vue 文件推荐使用:
135
+
136
+ ```vue
137
+ <script setup lang="ts">
138
+ </script>
139
+ ```
140
+
141
+ ### 4.3 禁止滥用 any
142
+
143
+ 不推荐:
144
+
145
+ ```ts
146
+ const user: any = {}
147
+ ```
148
+
149
+ 推荐:
150
+
151
+ ```ts
152
+ interface UserInfo {
153
+ id: number
154
+ username: string
155
+ avatar?: string
156
+ }
157
+
158
+ const user = ref<UserInfo | null>(null)
159
+ ```
160
+
161
+ ---
162
+
163
+ ## 5. 组件开发规范
164
+
165
+ ### 5.1 组件职责单一
166
+
167
+ 一个组件只负责一个明确的功能,例如:
168
+
169
+ - 用户表格组件只负责展示用户列表
170
+ - 搜索组件只负责搜索条件输入
171
+ - 弹窗组件只负责表单录入和提交
172
+
173
+ ### 5.2 Props 定义规范
174
+
175
+ 必须定义 Props 类型。
176
+
177
+ ```ts
178
+ interface Props {
179
+ title: string
180
+ loading?: boolean
181
+ }
182
+
183
+ const props = withDefaults(defineProps<Props>(), {
184
+ loading: false
185
+ })
186
+ ```
187
+
188
+ 禁止使用:
189
+
190
+ ```ts
191
+ defineProps(['title', 'loading'])
192
+ ```
193
+
194
+ ### 5.3 Emits 定义规范
195
+
196
+ 事件名建议使用 kebab-case。
197
+
198
+ ```ts
199
+ const emit = defineEmits<{
200
+ 'submit-form': [value: FormData]
201
+ 'update:modelValue': [value: string]
202
+ }>()
203
+ ```
204
+
205
+ ### 5.4 不直接修改 Props
206
+
207
+ 错误写法:
208
+
209
+ ```ts
210
+ props.visible = false
211
+ ```
212
+
213
+ 正确写法:
214
+
215
+ ```ts
216
+ const emit = defineEmits<{
217
+ 'update:visible': [value: boolean]
218
+ }>()
219
+
220
+ emit('update:visible', false)
221
+ ```
222
+
223
+ ---
224
+
225
+ ## 6. 页面开发规范
226
+
227
+ 页面建议按以下顺序组织:
228
+
229
+ 1. import 引入
230
+ 2. 类型定义
231
+ 3. 响应式数据
232
+ 4. 计算属性
233
+ 5. 方法函数
234
+ 6. 生命周期
235
+ 7. 监听器
236
+
237
+ 示例:
238
+
239
+ ```vue
240
+ <script setup lang="ts">
241
+ import { onMounted, ref } from 'vue'
242
+ import { getUserListApi } from '@/api/user.api'
243
+ import type { UserInfo } from '@/types/user.type'
244
+
245
+ const loading = ref(false)
246
+ const userList = ref<UserInfo[]>([])
247
+
248
+ async function getUserList() {
249
+ loading.value = true
250
+
251
+ try {
252
+ const res = await getUserListApi()
253
+ userList.value = res.data
254
+ } finally {
255
+ loading.value = false
256
+ }
257
+ }
258
+
259
+ onMounted(() => {
260
+ getUserList()
261
+ })
262
+ </script>
263
+ ```
264
+
265
+ ---
266
+
267
+ ## 7. 路由规范
268
+
269
+ ### 7.1 路由目录
270
+
271
+ ```bash
272
+ router
273
+ ├── index.ts
274
+ └── modules
275
+ ├── user.route.ts
276
+ ├── order.route.ts
277
+ └── system.route.ts
278
+ ```
279
+
280
+ ### 7.2 路由配置示例
281
+
282
+ ```ts
283
+ import type { RouteRecordRaw } from 'vue-router'
284
+
285
+ const userRoutes: RouteRecordRaw[] = [
286
+ {
287
+ path: '/user',
288
+ name: 'User',
289
+ component: () => import('@/views/user/UserList.vue'),
290
+ meta: {
291
+ title: '用户管理',
292
+ requiresAuth: true,
293
+ permissions: ['user:list']
294
+ }
295
+ }
296
+ ]
297
+
298
+ export default userRoutes
299
+ ```
300
+
301
+ ### 7.3 路由 meta 规范
302
+
303
+ ```ts
304
+ meta: {
305
+ title: '页面标题',
306
+ requiresAuth: true,
307
+ keepAlive: false,
308
+ permissions: ['user:list']
309
+ }
310
+ ```
311
+
312
+ 常用字段说明:
313
+
314
+ | 字段 | 说明 |
315
+ | --- | --- |
316
+ | title | 页面标题 |
317
+ | requiresAuth | 是否需要登录 |
318
+ | keepAlive | 是否缓存页面 |
319
+ | permissions | 页面权限标识 |
320
+
321
+ ---
322
+
323
+ ## 8. 状态管理规范
324
+
325
+ ### 8.1 Store 文件命名
326
+
327
+ ```bash
328
+ stores/user.store.ts
329
+ stores/app.store.ts
330
+ stores/permission.store.ts
331
+ ```
332
+
333
+ ### 8.2 Pinia 示例
334
+
335
+ ```ts
336
+ import { ref } from 'vue'
337
+ import { defineStore } from 'pinia'
338
+
339
+ export const useUserStore = defineStore('user', () => {
340
+ const token = ref('')
341
+ const username = ref('')
342
+
343
+ function setToken(value: string) {
344
+ token.value = value
345
+ }
346
+
347
+ function clearUser() {
348
+ token.value = ''
349
+ username.value = ''
350
+ }
351
+
352
+ return {
353
+ token,
354
+ username,
355
+ setToken,
356
+ clearUser
357
+ }
358
+ })
359
+ ```
360
+
361
+ ---
362
+
363
+ ## 9. API 请求规范
364
+
365
+ ### 9.1 API 目录结构
366
+
367
+ ```bash
368
+ api
369
+ ├── request.ts
370
+ ├── user.api.ts
371
+ ├── order.api.ts
372
+ └── system.api.ts
373
+ ```
374
+
375
+ ### 9.2 接口命名规范
376
+
377
+ 接口函数建议使用动词开头:
378
+
379
+ ```ts
380
+ getUserList()
381
+ getUserDetail()
382
+ createUser()
383
+ updateUser()
384
+ deleteUser()
385
+ ```
386
+
387
+ ### 9.3 API 示例
388
+
389
+ ```ts
390
+ import request from './request'
391
+ import type { CreateUserDTO, UserListParams } from '@/types/user.type'
392
+
393
+ export function getUserListApi(params: UserListParams) {
394
+ return request.get('/users', { params })
395
+ }
396
+
397
+ export function createUserApi(data: CreateUserDTO) {
398
+ return request.post('/users', data)
399
+ }
400
+
401
+ export function updateUserApi(id: number, data: CreateUserDTO) {
402
+ return request.put(`/users/${id}`, data)
403
+ }
404
+
405
+ export function deleteUserApi(id: number) {
406
+ return request.delete(`/users/${id}`)
407
+ }
408
+ ```
409
+
410
+ ---
411
+
412
+ ## 10. Axios 封装规范
413
+
414
+ ```ts
415
+ import axios from 'axios'
416
+ import type { AxiosError, AxiosResponse } from 'axios'
417
+
418
+ const request = axios.create({
419
+ baseURL: import.meta.env.VITE_API_BASE_URL,
420
+ timeout: 10000
421
+ })
422
+
423
+ request.interceptors.request.use(config => {
424
+ const token = localStorage.getItem('token')
425
+
426
+ if (token) {
427
+ config.headers.Authorization = `Bearer ${token}`
428
+ }
429
+
430
+ return config
431
+ })
432
+
433
+ request.interceptors.response.use(
434
+ (response: AxiosResponse) => {
435
+ return response.data
436
+ },
437
+ (error: AxiosError) => {
438
+ return Promise.reject(error)
439
+ }
440
+ )
441
+
442
+ export default request
443
+ ```
444
+
445
+ ---
446
+
447
+ ## 11. TypeScript 类型规范
448
+
449
+ ### 11.1 通用响应类型
450
+
451
+ ```ts
452
+ export interface ApiResponse<T = unknown> {
453
+ code: number
454
+ message: string
455
+ data: T
456
+ }
457
+ ```
458
+
459
+ ### 11.2 分页类型
460
+
461
+ ```ts
462
+ export interface PageParams {
463
+ page: number
464
+ pageSize: number
465
+ }
466
+
467
+ export interface PageResult<T> {
468
+ list: T[]
469
+ total: number
470
+ page: number
471
+ pageSize: number
472
+ }
473
+ ```
474
+
475
+ ### 11.3 用户类型示例
476
+
477
+ ```ts
478
+ export interface UserInfo {
479
+ id: number
480
+ username: string
481
+ nickname: string
482
+ avatar?: string
483
+ status: number
484
+ createdAt: string
485
+ }
486
+
487
+ export interface UserListParams extends PageParams {
488
+ keyword?: string
489
+ status?: number
490
+ }
491
+
492
+ export interface CreateUserDTO {
493
+ username: string
494
+ password: string
495
+ nickname: string
496
+ }
497
+ ```
498
+
499
+ ---
500
+
501
+ ## 12. 样式规范
502
+
503
+ ### 12.1 样式目录
504
+
505
+ ```bash
506
+ styles
507
+ ├── index.scss
508
+ ├── reset.scss
509
+ ├── variables.scss
510
+ ├── mixin.scss
511
+ └── transition.scss
512
+ ```
513
+
514
+ ### 12.2 组件样式
515
+
516
+ 组件样式默认使用 scoped。
517
+
518
+ ```vue
519
+ <style scoped lang="scss">
520
+ .user-card {
521
+ padding: 16px;
522
+ border-radius: 8px;
523
+ background-color: #fff;
524
+ }
525
+ </style>
526
+ ```
527
+
528
+ ### 12.3 CSS 命名规范
529
+
530
+ 推荐使用 BEM 命名。
531
+
532
+ ```scss
533
+ .user-card {
534
+ &__header {
535
+ display: flex;
536
+ align-items: center;
537
+ }
538
+
539
+ &__title {
540
+ font-size: 18px;
541
+ font-weight: 600;
542
+ }
543
+
544
+ &--active {
545
+ border-color: #409eff;
546
+ }
547
+ }
548
+ ```
549
+
550
+ ---
551
+
552
+ ## 13. 环境变量规范
553
+
554
+ ### 13.1 文件命名
555
+
556
+ ```bash
557
+ .env.development
558
+ .env.test
559
+ .env.production
560
+ ```
561
+
562
+ ### 13.2 变量命名
563
+
564
+ Vite 项目环境变量必须使用 `VITE_` 前缀。
565
+
566
+ ```env
567
+ VITE_APP_TITLE=Vue3后台管理系统
568
+ VITE_API_BASE_URL=https://api.example.com
569
+ VITE_UPLOAD_URL=https://upload.example.com
570
+ ```
571
+
572
+ ### 13.3 使用方式
573
+
574
+ ```ts
575
+ const baseURL = import.meta.env.VITE_API_BASE_URL
576
+ ```
577
+
578
+ ---
579
+
580
+ ## 14. 权限规范
581
+
582
+ 权限建议分为:
583
+
584
+ - 登录权限
585
+ - 路由权限
586
+ - 菜单权限
587
+ - 按钮权限
588
+ - 接口权限
589
+
590
+ ### 14.1 路由权限
591
+
592
+ ```ts
593
+ meta: {
594
+ requiresAuth: true,
595
+ permissions: ['user:list']
596
+ }
597
+ ```
598
+
599
+ ### 14.2 按钮权限
600
+
601
+ ```vue
602
+ <el-button v-if="hasPermission('user:create')">
603
+ 新增用户
604
+ </el-button>
605
+ ```
606
+
607
+ ---
608
+
609
+ ## 15. 表单开发规范
610
+
611
+ ### 15.1 表单数据
612
+
613
+ ```ts
614
+ const formData = reactive({
615
+ username: '',
616
+ password: '',
617
+ status: 1
618
+ })
619
+ ```
620
+
621
+ ### 15.2 表单校验
622
+
623
+ ```ts
624
+ const rules = {
625
+ username: [
626
+ { required: true, message: '请输入用户名', trigger: 'blur' }
627
+ ],
628
+ password: [
629
+ { required: true, message: '请输入密码', trigger: 'blur' }
630
+ ]
631
+ }
632
+ ```
633
+
634
+ ### 15.3 提交规范
635
+
636
+ ```ts
637
+ async function handleSubmit() {
638
+ try {
639
+ await formRef.value?.validate()
640
+ await createUserApi(formData)
641
+ } catch (error) {
642
+ console.error(error)
643
+ }
644
+ }
645
+ ```
646
+
647
+ ---
648
+
649
+ ## 16. 列表页面规范
650
+
651
+ 列表页面通常包含:
652
+
653
+ - 搜索区域
654
+ - 操作按钮区域
655
+ - 表格区域
656
+ - 分页区域
657
+
658
+ 推荐状态命名:
659
+
660
+ ```ts
661
+ const loading = ref(false)
662
+ const tableData = ref([])
663
+ const total = ref(0)
664
+ const queryParams = reactive({
665
+ page: 1,
666
+ pageSize: 10,
667
+ keyword: ''
668
+ })
669
+ ```
670
+
671
+ ---
672
+
673
+ ## 17. 工具函数规范
674
+
675
+ 工具函数必须保持职责单一。
676
+
677
+ ```ts
678
+ export function formatDate(date: string | Date): string {
679
+ const d = new Date(date)
680
+ return d.toLocaleDateString()
681
+ }
682
+ ```
683
+
684
+ 不建议一个工具函数同时处理多个不相关功能。
685
+
686
+ ---
687
+
688
+ ## 18. 常量规范
689
+
690
+ 常量统一放在 `constants` 目录。
691
+
692
+ ```ts
693
+ export const USER_STATUS = {
694
+ ENABLED: 1,
695
+ DISABLED: 0
696
+ } as const
697
+
698
+ export const USER_STATUS_TEXT = {
699
+ [USER_STATUS.ENABLED]: '启用',
700
+ [USER_STATUS.DISABLED]: '禁用'
701
+ }
702
+ ```
703
+
704
+ ---
705
+
706
+ ## 19. 代码提交规范
707
+
708
+ 推荐使用 Conventional Commits。
709
+
710
+ ```bash
711
+ feat: 新增用户管理页面
712
+ fix: 修复登录状态失效问题
713
+ docs: 更新项目开发文档
714
+ style: 调整页面样式
715
+ refactor: 重构接口请求封装
716
+ perf: 优化列表加载性能
717
+ test: 新增单元测试
718
+ chore: 修改构建配置
719
+ ```
720
+
721
+ 提交类型说明:
722
+
723
+ | 类型 | 说明 |
724
+ | --- | --- |
725
+ | feat | 新功能 |
726
+ | fix | 修复问题 |
727
+ | docs | 文档修改 |
728
+ | style | 样式或格式调整 |
729
+ | refactor | 代码重构 |
730
+ | perf | 性能优化 |
731
+ | test | 测试相关 |
732
+ | chore | 构建或工具配置 |
733
+
734
+ ---
735
+
736
+ ## 20. ESLint 与 Prettier 规范
737
+
738
+ ### 20.1 Prettier 配置
739
+
740
+ ```json
741
+ {
742
+ "semi": false,
743
+ "singleQuote": true,
744
+ "printWidth": 100,
745
+ "trailingComma": "none"
746
+ }
747
+ ```
748
+
749
+ ### 20.2 ESLint 规则示例
750
+
751
+ ```js
752
+ export default [
753
+ {
754
+ rules: {
755
+ 'vue/multi-word-component-names': 'off',
756
+ 'no-console': 'warn',
757
+ 'no-debugger': 'warn'
758
+ }
759
+ }
760
+ ]
761
+ ```
762
+
763
+ ---
764
+
765
+ ## 21. 性能优化规范
766
+
767
+ ### 21.1 路由懒加载
768
+
769
+ ```ts
770
+ component: () => import('@/views/user/UserList.vue')
771
+ ```
772
+
773
+ ### 21.2 组件按需加载
774
+
775
+ 大型组件、弹窗、图表组件建议按需加载。
776
+
777
+ ### 21.3 避免不必要的响应式
778
+
779
+ 大数据列表可以使用 `shallowRef`。
780
+
781
+ ```ts
782
+ const list = shallowRef([])
783
+ ```
784
+
785
+ ### 21.4 图片优化
786
+
787
+ - 图片压缩后再上传
788
+ - 小图标优先使用 SVG
789
+ - 大图使用懒加载
790
+ - 静态资源放入 CDN
791
+
792
+ ---
793
+
794
+ ## 22. 安全规范
795
+
796
+ - 不在前端代码中写死密钥
797
+ - 不在仓库中提交 `.env.production`
798
+ - 接口请求必须携带必要鉴权信息
799
+ - 用户输入内容需要校验
800
+ - 富文本内容需要防止 XSS
801
+ - 文件上传需要限制类型和大小
802
+
803
+ ---
804
+
805
+ ## 23. 开发注意事项
806
+
807
+ - 组件不要过大,复杂页面要拆分组件
808
+ - 接口请求统一放到 `api` 目录
809
+ - 公共逻辑抽离到 `composables` 或 `hooks`
810
+ - 不要直接操作 DOM,优先使用 Vue 响应式能力
811
+ - 列表渲染必须添加唯一 `key`
812
+ - 异步请求必须处理 loading 状态
813
+ - 删除、提交、重置等危险操作需要二次确认
814
+ - 禁止在组件中硬编码接口地址
815
+ - 禁止将大量业务逻辑写在模板中
816
+
817
+ ---
818
+
819
+ ## 24. 推荐 package.json scripts
820
+
821
+ ```json
822
+ {
823
+ "scripts": {
824
+ "dev": "vite",
825
+ "build": "vue-tsc -b && vite build",
826
+ "preview": "vite preview",
827
+ "lint": "eslint .",
828
+ "format": "prettier --write src"
829
+ }
830
+ }
831
+ ```
832
+
833
+ ---
834
+
835
+ ## 25. 项目启动流程
836
+
837
+ ```bash
838
+ pnpm install
839
+ pnpm dev
840
+ ```
841
+
842
+ 生产构建:
843
+
844
+ ```bash
845
+ pnpm build
846
+ ```
847
+
848
+ 本地预览:
849
+
850
+ ```bash
851
+ pnpm preview
852
+ ```
853
+
854
+ ---
855
+
856
+ ## 26. 总结
857
+
858
+ Vue3 项目开发应重点关注以下几点:
859
+
860
+ - 目录结构清晰
861
+ - 命名风格统一
862
+ - 组件职责单一
863
+ - 类型定义完善
864
+ - 接口统一封装
865
+ - 状态管理规范
866
+ - 样式风格一致
867
+ - 权限设计清晰
868
+ - Git 提交规范
869
+ - 构建部署流程稳定
870
+
871
+ 良好的开发规范可以提升团队协作效率,降低维护成本,并让项目在长期迭代中保持稳定和可扩展。
@@ -0,0 +1,16 @@
1
+ <!-- 500页面 -->
2
+ <template>
3
+ <ArtException
4
+ :data="{
5
+ title: '500',
6
+ desc: $t('exceptionPage.500'),
7
+ btnText: $t('exceptionPage.gohome'),
8
+ imgUrl
9
+ }"
10
+ />
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import imgUrl from '@imgs/svg/500.svg'
15
+ defineOptions({ name: 'Exception500' })
16
+ </script>