-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathvue3_dev-spec.html
More file actions
352 lines (338 loc) · 114 KB
/
vue3_dev-spec.html
File metadata and controls
352 lines (338 loc) · 114 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>开发规范 | ruoyi-vue-pro 开发指南</title>
<meta name="generator" content="VuePress 1.9.2">
<link rel="icon" href="img_favicon.ico.html">
<script ></script>
<script ></script>
<script ></script>
<link rel="stylesheet" href="https://static.iocoder.cn/answer.css">
<meta name="description" content="RuoYi-Vue 全新 Pro 版本,优化重构所有功能。基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 微信小程序,支持 RBAC 动态权限、数据权限、SaaS 多租户、Activiti + Flowable 工作流、三方登录、支付、短信、商城等功能。">
<meta name="keywords" content="ruoyi-vue,权限,数据权限,SaaS,多租户,Activiti,Flowable,工作流,商城">
<meta name="theme-color" content="#11a8cd">
<link rel="preload" >
<link rel="stylesheet" href="https://doc.iocoder.cn/assets/css/0.styles.7c54dcda.css">
</head>
<body class="theme-mode-light">
<div id="app" data-server-rendered="true"><div class="theme-container sidebar-open have-rightmenu"><header class="navbar blur"><div title="目录" class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="intro.html" class="home-link router-link-active"><img src="https://static.iocoder.cn/ruoyi-vue-pro-logo.png" alt="ruoyi-vue-pro 开发指南" class="logo"> <span class="site-name can-hide">ruoyi-vue-pro 开发指南(更新时间:2025-2-26)</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="开发指南" class="dropdown-title"><a href="intro.html" class="link-title">开发指南</a> <span class="title" style="display:none;">开发指南</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="intro.html" class="nav-link">萌新必读</a></li><li class="dropdown-item"><!----> <a href="module-new.html" class="nav-link">后端手册</a></li><li class="dropdown-item"><!----> <a href="message-queue_event.html" class="nav-link">中间件手册</a></li><li class="dropdown-item"><!----> <a href="bpm-preview.html" class="nav-link">工作流手册</a></li><li class="dropdown-item"><!----> <a href="report.html" class="nav-link">大屏手册</a></li><li class="dropdown-item"><!----> <a href="pay_build.html" class="nav-link">支付手册</a></li><li class="dropdown-item"><!----> <a href="member_build.html" class="nav-link">会员手册</a></li><li class="dropdown-item"><!----> <a href="mall_build.html" class="nav-link">商城手册</a></li><li class="dropdown-item"><!----> <a href="erp_build.html" class="nav-link">ERP 手册</a></li><li class="dropdown-item"><!----> <a href="crm_build.html" class="nav-link">CRM 手册</a></li><li class="dropdown-item"><!----> <a href="ai-preview.html" class="nav-link">AI 大模型手册</a></li><li class="dropdown-item"><!----> <a href="mp_build.html" class="nav-link">公众号手册</a></li><li class="dropdown-item"><!----> <a href="sms.html" class="nav-link">系统手册</a></li><li class="dropdown-item"><!----> <a href="dev-env.html" class="nav-link">运维手册</a></li><li class="dropdown-item"><!----> <a href="vue2_dev-spec.html" class="nav-link">前端手册 Vue 2.x</a></li><li class="dropdown-item"><!----> <a href="vue3_dev-spec.html" aria-current="page" class="nav-link router-link-exact-active router-link-active">前端手册 Vue 3.x</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="项目实战" class="dropdown-title"><a href="bpm.html" class="link-title">项目实战</a> <span class="title" style="display:none;">项目实战</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="bpm-preview.html" class="nav-link">工作流手册</a></li><li class="dropdown-item"><!----> <a href="report.html" class="nav-link">大屏手册</a></li><li class="dropdown-item"><!----> <a href="pay_build.html" class="nav-link">支付手册</a></li><li class="dropdown-item"><!----> <a href="member_build.html" class="nav-link">会员手册</a></li><li class="dropdown-item"><!----> <a href="mall_build.html" class="nav-link">商城手册</a></li><li class="dropdown-item"><!----> <a href="erp_build.html" class="nav-link">ERP 手册</a></li><li class="dropdown-item"><!----> <a href="crm_build.html" class="nav-link">CRM 手册</a></li><li class="dropdown-item"><!----> <a href="ai-preview.html" class="nav-link">AI 大模型手册</a></li><li class="dropdown-item"><!----> <a href="mp_build.html" class="nav-link">公众号手册</a></li><li class="dropdown-item"><!----> <a href="sms.html" class="nav-link">系统手册</a></li></ul></div></div><div class="nav-item"><a href="video.html" class="nav-link">视频教程</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="在线体验" class="dropdown-title"><!----> <span class="title" style="display:;">在线体验</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="http://dashboard-vue3.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue3 + element-plus
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://dashboard-vben.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue3 + vben(ant-design-vue)
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://dashboard.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue2 + element-ui
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div><div class="nav-item"><a href="https://github.com/YunaiV/yudao-cloud" target="_blank" rel="noopener noreferrer" class="nav-link external">
微服务版
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="https://www.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
作者博客
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <a href="https://github.com/YunaiV/ruoyi-vue-pro" target="_blank" rel="noopener noreferrer" class="repo-link">
GitHub
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <div class="sidebar-hover-trigger"></div> <aside class="sidebar"><!----> <nav class="nav-links"><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="开发指南" class="dropdown-title"><a href="intro.html" class="link-title">开发指南</a> <span class="title" style="display:none;">开发指南</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="intro.html" class="nav-link">萌新必读</a></li><li class="dropdown-item"><!----> <a href="module-new.html" class="nav-link">后端手册</a></li><li class="dropdown-item"><!----> <a href="message-queue_event.html" class="nav-link">中间件手册</a></li><li class="dropdown-item"><!----> <a href="bpm-preview.html" class="nav-link">工作流手册</a></li><li class="dropdown-item"><!----> <a href="report.html" class="nav-link">大屏手册</a></li><li class="dropdown-item"><!----> <a href="pay_build.html" class="nav-link">支付手册</a></li><li class="dropdown-item"><!----> <a href="member_build.html" class="nav-link">会员手册</a></li><li class="dropdown-item"><!----> <a href="mall_build.html" class="nav-link">商城手册</a></li><li class="dropdown-item"><!----> <a href="erp_build.html" class="nav-link">ERP 手册</a></li><li class="dropdown-item"><!----> <a href="crm_build.html" class="nav-link">CRM 手册</a></li><li class="dropdown-item"><!----> <a href="ai-preview.html" class="nav-link">AI 大模型手册</a></li><li class="dropdown-item"><!----> <a href="mp_build.html" class="nav-link">公众号手册</a></li><li class="dropdown-item"><!----> <a href="sms.html" class="nav-link">系统手册</a></li><li class="dropdown-item"><!----> <a href="dev-env.html" class="nav-link">运维手册</a></li><li class="dropdown-item"><!----> <a href="vue2_dev-spec.html" class="nav-link">前端手册 Vue 2.x</a></li><li class="dropdown-item"><!----> <a href="vue3_dev-spec.html" aria-current="page" class="nav-link router-link-exact-active router-link-active">前端手册 Vue 3.x</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="项目实战" class="dropdown-title"><a href="bpm.html" class="link-title">项目实战</a> <span class="title" style="display:none;">项目实战</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="bpm-preview.html" class="nav-link">工作流手册</a></li><li class="dropdown-item"><!----> <a href="report.html" class="nav-link">大屏手册</a></li><li class="dropdown-item"><!----> <a href="pay_build.html" class="nav-link">支付手册</a></li><li class="dropdown-item"><!----> <a href="member_build.html" class="nav-link">会员手册</a></li><li class="dropdown-item"><!----> <a href="mall_build.html" class="nav-link">商城手册</a></li><li class="dropdown-item"><!----> <a href="erp_build.html" class="nav-link">ERP 手册</a></li><li class="dropdown-item"><!----> <a href="crm_build.html" class="nav-link">CRM 手册</a></li><li class="dropdown-item"><!----> <a href="ai-preview.html" class="nav-link">AI 大模型手册</a></li><li class="dropdown-item"><!----> <a href="mp_build.html" class="nav-link">公众号手册</a></li><li class="dropdown-item"><!----> <a href="sms.html" class="nav-link">系统手册</a></li></ul></div></div><div class="nav-item"><a href="video.html" class="nav-link">视频教程</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="在线体验" class="dropdown-title"><!----> <span class="title" style="display:;">在线体验</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="http://dashboard-vue3.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue3 + element-plus
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://dashboard-vben.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue3 + vben(ant-design-vue)
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://dashboard.yudao.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
Vue2 + element-ui
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div><div class="nav-item"><a href="https://github.com/YunaiV/yudao-cloud" target="_blank" rel="noopener noreferrer" class="nav-link external">
微服务版
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="https://www.iocoder.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
作者博客
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <a href="https://github.com/YunaiV/ruoyi-vue-pro" target="_blank" rel="noopener noreferrer" class="repo-link">
GitHub
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav> <ul class="sidebar-links"><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>萌新必读</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="intro.html" class="sidebar-link">简介</a></li><li><a href="qun.html" class="sidebar-link">交流群</a></li><li><a href="video.html" class="sidebar-link">视频教程</a></li><li><a href="feature.html" class="sidebar-link">功能列表</a></li><li><a href="quick-start.html" class="sidebar-link">快速启动(后端项目)</a></li><li><a href="quick-start-front.html" class="sidebar-link">快速启动(前端项目)</a></li><li><a href="api-doc.html" class="sidebar-link">接口文档</a></li><li><a href="technology.html" class="sidebar-link">技术选型</a></li><li><a href="project-intro.html" class="sidebar-link">项目结构</a></li><li><a href="dev-hot-swap.html" class="sidebar-link">代码热加载</a></li><li><a href="project-rename.html" class="sidebar-link">一键改包</a></li><li><a href="migrate-module.html" class="sidebar-link">迁移模块(适合新项目)</a></li><li><a href="delete-code.html" class="sidebar-link">删除功能(以租户为例)</a></li><li><a href="sql-update.html" class="sidebar-link">表结构变更(版本升级)</a></li><li><a href="xinchuang-db.html" class="sidebar-link">国产信创数据库(DM 达梦、大金、OpenGauss)</a></li><li><a href="remove-redis.html" class="sidebar-link">如何去除 Redis 缓存</a></li><li><a href="natapp.html" class="sidebar-link">内网穿透</a></li><li><a href="interview.html" class="sidebar-link">面试题、简历模版、简历优化</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>后端手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="module-new.html" class="sidebar-link">新建模块</a></li><li><a href="new-feature.html" class="sidebar-link">代码生成【单表】(新增功能)</a></li><li><a href="new-feature_master-sub.html" class="sidebar-link">代码生成【主子表】</a></li><li><a href="new-feature_tree.html" class="sidebar-link">代码生成(树表)</a></li><li><a href="resource-permission.html" class="sidebar-link">功能权限</a></li><li><a href="data-permission.html" class="sidebar-link">数据权限</a></li><li><a href="user-center.html" class="sidebar-link">用户体系</a></li><li><a href="social-user.html" class="sidebar-link">三方登录</a></li><li><a href="oauth2.html" class="sidebar-link">OAuth 2.0(SSO 单点登录)</a></li><li><a href="saas-tenant.html" class="sidebar-link">SaaS 多租户【字段隔离】</a></li><li><a href="saas-tenant_dynamic.html" class="sidebar-link">SaaS 多租户【数据库隔离】</a></li><li><a href="websocket.html" class="sidebar-link">WebSocket 实时通信</a></li><li><a href="exception.html" class="sidebar-link">异常处理(错误码)</a></li><li><a href="validator.html" class="sidebar-link">参数校验、时间传参</a></li><li><a href="page-feature.html" class="sidebar-link">分页实现</a></li><li><a href="vo.html" class="sidebar-link">VO 对象转换、数据翻译</a></li><li><a href="file.html" class="sidebar-link">文件存储(上传下载)</a></li><li><a href="excel-import-and-export.html" class="sidebar-link">Excel 导入导出</a></li><li><a href="system-log.html" class="sidebar-link">操作日志、访问日志、异常日志</a></li><li><a href="mybatis.html" class="sidebar-link">MyBatis 数据库</a></li><li><a href="mybatis-pro.html" class="sidebar-link">MyBatis 联表&分页查询</a></li><li><a href="dynamic-datasource.html" class="sidebar-link">多数据源(读写分离)、事务</a></li><li><a href="redis-cache.html" class="sidebar-link">Redis 缓存</a></li><li><a href="local-cache.html" class="sidebar-link">本地缓存</a></li><li><a href="async-task.html" class="sidebar-link">异步任务</a></li><li><a href="distributed-lock.html" class="sidebar-link">分布式锁</a></li><li><a href="idempotent.html" class="sidebar-link">幂等性(防重复提交)</a></li><li><a href="rate-limiter.html" class="sidebar-link">请求限流(RateLimiter)</a></li><li><a href="http-sign.html" class="sidebar-link">HTTP 接口签名(防篡改)</a></li><li><a href="unit-test.html" class="sidebar-link">单元测试</a></li><li><a href="captcha.html" class="sidebar-link">验证码</a></li><li><a href="util.html" class="sidebar-link">工具类 Util</a></li><li><a href="config-center.html" class="sidebar-link">配置管理</a></li><li><a href="db-doc.html" class="sidebar-link">数据库文档</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>中间件手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="job.html" class="sidebar-link">定时任务</a></li><li><a href="message-queue_event.html" class="sidebar-link">消息队列(内存)</a></li><li><a href="message-queue_redis.html" class="sidebar-link">消息队列(Redis)</a></li><li><a href="message-queue_rocketmq.html" class="sidebar-link">消息队列(RocketMQ)</a></li><li><a href="message-queue_rabbitmq.html" class="sidebar-link">消息队列(RabbitMQ)</a></li><li><a href="message-queue_kafka.html" class="sidebar-link">消息队列(Kafka)</a></li><li><a href="server-protection.html" class="sidebar-link">限流熔断</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>工作流手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="bpm-preview.html" class="sidebar-link">工作流演示</a></li><li><a href="bpm.html" class="sidebar-link">功能开启</a></li><li><a href="bpm_dameng.html" class="sidebar-link">工作流(达梦适配)</a></li><li><a href="bpm_use-bpm-form.html" class="sidebar-link">审批接入(流程表单)</a></li><li><a href="bpm_use-business-form.html" class="sidebar-link">审批接入(业务表单)</a></li><li><a href="bpm_model-designer-bpmn.html" class="sidebar-link">流程设计器(BPMN)</a></li><li><a href="bpm_model-designer-dingding.html" class="sidebar-link">流程设计器(钉钉、飞书)</a></li><li><a href="bpm_assignee.html" class="sidebar-link">选择审批人、发起人自选</a></li><li><a href="bpm_multi-instance.html" class="sidebar-link">会签、或签、依次审批</a></li><li><a href="bpm_process-instance.html" class="sidebar-link">流程发起、取消、重新发起</a></li><li><a href="bpm_task-todo-done.html" class="sidebar-link">审批通过、不通过、驳回</a></li><li><a href="bpm_sign.html" class="sidebar-link">审批加签、减签</a></li><li><a href="bpm_task-delegation-and-cc.html" class="sidebar-link">审批转办、委派、抄送</a></li><li><a href="bpm_listener.html" class="sidebar-link">执行监听器、任务监听器</a></li><li><a href="bpm_expression.html" class="sidebar-link">流程表达式</a></li><li><a href="bpm_message.html" class="sidebar-link">流程审批通知</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>大屏手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="report.html" class="sidebar-link">报表设计器</a></li><li><a href="report_screen.html" class="sidebar-link">大屏设计器</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>支付手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="pay_build.html" class="sidebar-link">功能开启</a></li><li><a href="pay_alipay-pay-demo.html" class="sidebar-link">支付宝支付接入</a></li><li><a href="pay_wx-pub-pay-demo.html" class="sidebar-link">微信公众号支付接入</a></li><li><a href="pay_wx-lite-pay-demo.html" class="sidebar-link">微信小程序支付接入</a></li><li><a href="pay_refund-demo.html" class="sidebar-link">支付宝、微信退款接入</a></li><li><a href="pay_wallet.html" class="sidebar-link">钱包充值、支付、退款</a></li><li><a href="pay_mock.html" class="sidebar-link">模拟支付、退款</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>会员手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="member_build.html" class="sidebar-link">功能开启</a></li><li><a href="member_weixin-mp-login.html" class="sidebar-link">微信公众号登录</a></li><li><a href="member_weixin-lite-login.html" class="sidebar-link">微信小程序登录</a></li><li><a href="member_weixin-lite-subscribe-message.html" class="sidebar-link">微信小程序订阅消息</a></li><li><a href="member_weixin-lite-qrcode.html" class="sidebar-link">微信小程序码</a></li><li><a href="member_user.html" class="sidebar-link">会员用户、标签、分组</a></li><li><a href="member_level.html" class="sidebar-link">会员等级、积分、签到</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>商城手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="mall-preview.html" class="sidebar-link">商城演示</a></li><li><a href="mall_build.html" class="sidebar-link">功能开启</a></li><li><a href="mall_diy.html" class="sidebar-link">商城装修</a></li><li><a href="mall_kefu.html" class="sidebar-link">在线客服</a></li><li><a href="mall_product-category.html" class="sidebar-link">【商品】商品分类</a></li><li><a href="mall_product-property.html" class="sidebar-link">【商品】商品属性</a></li><li><a href="mall_product-spu-sku.html" class="sidebar-link">【商品】商品 SPU 与 SKU</a></li><li><a href="mall_product-comment.html" class="sidebar-link">【商品】商品评价</a></li><li><a href="mall_trade-cart.html" class="sidebar-link">【交易】购物车</a></li><li><a href="mall_trade-order.html" class="sidebar-link">【交易】交易订单</a></li><li><a href="mall_trade-aftersale.html" class="sidebar-link">【交易】售后退款</a></li><li><a href="mall_trade-delivery-express.html" class="sidebar-link">【交易】快递发货</a></li><li><a href="mall_trade-delivery-pickup.html" class="sidebar-link">【交易】门店自提</a></li><li><a href="mall_trade-brokerage.html" class="sidebar-link">【交易】分销返佣</a></li><li><a href="mall_promotion-coupon.html" class="sidebar-link">【营销】优惠劵</a></li><li><a href="mall_point-activity.html" class="sidebar-link">【营销】积分商城</a></li><li><a href="mall_promotion-combination.html" class="sidebar-link">【营销】拼团活动</a></li><li><a href="mall_promotion-seckill.html" class="sidebar-link">【营销】秒杀活动</a></li><li><a href="mall_promotion-bargain.html" class="sidebar-link">【营销】砍价活动</a></li><li><a href="mall_promotion-record.html" class="sidebar-link">【营销】满减送活动</a></li><li><a href="mall_promotion-discount.html" class="sidebar-link">【营销】限时折扣</a></li><li><a href="mall_promotion-content.html" class="sidebar-link">【营销】内容管理</a></li><li><a href="mall_statistics.html" class="sidebar-link">【统计】会员、商品、交易统计</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>ERP手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="erp-preview.html" class="sidebar-link">ERP 演示</a></li><li><a href="erp_build.html" class="sidebar-link">功能开启</a></li><li><a href="erp_product.html" class="sidebar-link">【产品】产品信息、分类、单位</a></li><li><a href="erp_stock.html" class="sidebar-link">【库存】产品库存、库存明细</a></li><li><a href="erp_stock-in-out.html" class="sidebar-link">【库存】其它入库、其它出库</a></li><li><a href="erp_stock-move-check.html" class="sidebar-link">【库存】库存调拨、库存盘点</a></li><li><a href="erp_purchase.html" class="sidebar-link">【采购】采购订单、入库、退货</a></li><li><a href="erp_sale.html" class="sidebar-link">【销售】销售订单、出库、退货</a></li><li><a href="sale_finance-payment-receipt.html" class="sidebar-link">【财务】采购付款、销售收款</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>CRM手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="crm-preview.html" class="sidebar-link">CRM 演示</a></li><li><a href="crm_build.html" class="sidebar-link">功能开启</a></li><li><a href="crm_clue.html" class="sidebar-link">【线索】线索管理</a></li><li><a href="crm_customer.html" class="sidebar-link">【客户】客户管理、公海客户</a></li><li><a href="crm_business.html" class="sidebar-link">【商机】商机管理、商机状态</a></li><li><a href="crm_contract.html" class="sidebar-link">【合同】合同管理、合同提醒</a></li><li><a href="crm_receivable.html" class="sidebar-link">【回款】回款管理、回款计划</a></li><li><a href="crm_product.html" class="sidebar-link">【产品】产品管理、产品分类</a></li><li><a href="crm_permission.html" class="sidebar-link">【通用】数据权限</a></li><li><a href="crm_follow-up.html" class="sidebar-link">【通用】跟进记录、待办事项</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>AI大模型手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="ai-preview.html" class="sidebar-link">AI 大模型演示</a></li><li><a href="ai_build.html" class="sidebar-link">功能开启</a></li><li><a href="ai_chat.html" class="sidebar-link">AI 聊天对话</a></li><li><a href="ai_image.html" class="sidebar-link">AI 绘画创作</a></li><li><a href="ai_music.html" class="sidebar-link">AI 音乐创作</a></li><li><a href="ai_write.html" class="sidebar-link">AI 写作助手</a></li><li><a href="ai_mindmap.html" class="sidebar-link">AI 思维导图</a></li><li><a href="ai_openai.html" class="sidebar-link">【模型接入】OpenAI</a></li><li><a href="ai_tongyi.html" class="sidebar-link">【模型接入】通义千问</a></li><li><a href="ai_deep-seek.html" class="sidebar-link">【模型接入】DeepSeek</a></li><li><a href="ai_doubao.html" class="sidebar-link">【模型接入】字节豆包</a></li><li><a href="ai_hunyuan.html" class="sidebar-link">【模型接入】腾讯混元</a></li><li><a href="ai_siliconflow.html" class="sidebar-link">【模型接入】硅基流动</a></li><li><a href="ai_yiyan.html" class="sidebar-link">【模型接入】文心一言</a></li><li><a href="ai_llama.html" class="sidebar-link">【模型接入】LLAMA</a></li><li><a href="ai_glm.html" class="sidebar-link">【模型接入】智谱 GLM</a></li><li><a href="ai_xinghuo.html" class="sidebar-link">【模型接入】讯飞星火</a></li><li><a href="ai_azure-openai.html" class="sidebar-link">【模型接入】微软 OpenAI</a></li><li><a href="ai_gemini.html" class="sidebar-link">【模型接入】谷歌 Gemini</a></li><li><a href="ai_stable-diffusion.html" class="sidebar-link">【模型接入】Stable Diffusion</a></li><li><a href="ai_midjourney.html" class="sidebar-link">【模型接入】Midjourney</a></li><li><a href="ai_suno.html" class="sidebar-link">【模型接入】Suno</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>公众号手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="mp_build.html" class="sidebar-link">功能开启</a></li><li><a href="mp_account.html" class="sidebar-link">公众号接入</a></li><li><a href="mp_user.html" class="sidebar-link">公众号粉丝</a></li><li><a href="mp_tag.html" class="sidebar-link">公众号标签</a></li><li><a href="mp_message.html" class="sidebar-link">公众号消息</a></li><li><a href="mp_auto-reply.html" class="sidebar-link">自动回复</a></li><li><a href="mp_menu.html" class="sidebar-link">公众号菜单</a></li><li><a href="mp_material.html" class="sidebar-link">公众号素材</a></li><li><a href="mp_article.html" class="sidebar-link">公众号图文</a></li><li><a href="mp_statistics.html" class="sidebar-link">公众号统计</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>系统手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="sms.html" class="sidebar-link">短信配置</a></li><li><a href="mail.html" class="sidebar-link">邮件配置</a></li><li><a href="notify.html" class="sidebar-link">站内信配置</a></li><li><a href="desensitize.html" class="sidebar-link">数据脱敏、字段权限</a></li><li><a href="sensitive-word.html" class="sidebar-link">敏感词</a></li><li><a href="area-and-ip.html" class="sidebar-link">地区 & IP 库</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>运维手册</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="dev-env.html" class="sidebar-link">开发环境</a></li><li><a href="deployment-linux.html" class="sidebar-link">Linux 部署</a></li><li><a href="deployment-docker.html" class="sidebar-link">Docker 部署</a></li><li><a href="deployment-jenkins.html" class="sidebar-link">Jenkins 部署</a></li><li><a href="deployment-baota.html" class="sidebar-link">宝塔部署</a></li><li><a href="https.html" class="sidebar-link">HTTPS 证书</a></li><li><a href="server-monitor.html" class="sidebar-link">服务监控</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>前端手册 Vue 3.x</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="vue3_dev-spec.html" aria-current="page" class="active sidebar-link">开发规范</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level2"><a href="vue3/dev-spec_#_0-实战案例.html" class="sidebar-link">0. 实战案例</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="vue3/dev-spec_#_0-1-普通列表.html" class="sidebar-link">0.1 普通列表</a></li><li class="sidebar-sub-header level3"><a href="vue3/dev-spec_#_0-2-树形列表.html" class="sidebar-link">0.2 树形列表</a></li><li class="sidebar-sub-header level3"><a href="vue3/dev-spec_#_0-3-高性能列表.html" class="sidebar-link">0.3 高性能列表</a></li><li class="sidebar-sub-header level3"><a href="vue3/dev-spec_#_0-4-详情弹窗.html" class="sidebar-link">0.4 详情弹窗</a></li></ul></li><li class="sidebar-sub-header level2"><a href="vue3/dev-spec_#_1-view-页面.html" class="sidebar-link">1. view 页面</a></li><li class="sidebar-sub-header level2"><a href="vue3/dev-spec_#_2-api-请求.html" class="sidebar-link">2. api 请求</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="vue3/dev-spec_#_2-1-请求封装.html" class="sidebar-link">2.1 请求封装</a></li><li class="sidebar-sub-header level3"><a href="vue3/dev-spec_#_2-2-交互流程.html" class="sidebar-link">2.2 交互流程</a></li></ul></li><li class="sidebar-sub-header level2"><a href="vue3/dev-spec_#_3-component-组件.html" class="sidebar-link">3. component 组件</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="vue3/dev-spec_#_3-1-全局组件.html" class="sidebar-link">3.1 全局组件</a></li><li class="sidebar-sub-header level3"><a href="vue3/dev-spec_#_3-2-模块内组件.html" class="sidebar-link">3.2 模块内组件</a></li></ul></li><li class="sidebar-sub-header level2"><a href="vue3/dev-spec_#_4-style-样式.html" class="sidebar-link">4. style 样式</a></li><li class="sidebar-sub-header level2"><a href="vue3/dev-spec_#_5-项目规范.html" class="sidebar-link">5. 项目规范</a></li></ul></li><li><a href="vue3_route.html" class="sidebar-link">菜单路由</a></li><li><a href="vue3_icon.html" class="sidebar-link">Icon 图标</a></li><li><a href="vue3_dict.html" class="sidebar-link">字典数据</a></li><li><a href="vue3_components.html" class="sidebar-link">系统组件</a></li><li><a href="vue3_util.html" class="sidebar-link">通用方法</a></li><li><a href="vue3_config-center.html" class="sidebar-link">配置读取</a></li><li><a href="vue3_crud-schema.html" class="sidebar-link">CRUD 组件</a></li><li><a href="vue3_i18n.html" class="sidebar-link">国际化</a></li><li><a href="vue3_debugger.html" class="sidebar-link">IDE 调试</a></li><li><a href="vue3_format.html" class="sidebar-link">代码格式化</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>前端手册 Vue 2.x</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="vue2_dev-spec.html" class="sidebar-link">开发规范</a></li><li><a href="vue2_route.html" class="sidebar-link">菜单路由</a></li><li><a href="vue2_icon.html" class="sidebar-link">Icon 图标</a></li><li><a href="vue2_dict.html" class="sidebar-link">字典数据</a></li><li><a href="vue2_components.html" class="sidebar-link">系统组件</a></li><li><a href="vue2_util.html" class="sidebar-link">通用方法</a></li><li><a href="vue2_config-center.html" class="sidebar-link">配置读取</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>更新日志</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="changelog_2.4.2.html" class="sidebar-link">【v2.4.2】开发中</a></li><li><a href="changelog_2.4.1.html" class="sidebar-link">【v2.4.1】2025-02-09</a></li><li><a href="changelog_2.4.0.html" class="sidebar-link">【v2.4.0】2024-12-31</a></li><li><a href="changelog_2.3.0.html" class="sidebar-link">【v2.3.0】2024-10-07</a></li><li><a href="changelog_2.2.0.html" class="sidebar-link">【v2.2.0】2024-08-02</a></li><li><a href="changelog_2.1.0.html" class="sidebar-link">【v2.1.0】2024-05-05</a></li><li><a href="changelog_2.0.1.html" class="sidebar-link">【v2.0.1】2024-03-01</a></li><li><a href="changelog_2.0.0.html" class="sidebar-link">【v2.0.0】2024-01-26</a></li></ul></section></li></ul> </aside> <div><main class="page"><div class="theme-vdoing-wrapper "><div class="articleInfo-wrap" data-v-06225672><div class="articleInfo" data-v-06225672><ul class="breadcrumbs" data-v-06225672><li data-v-06225672><a href="intro.html" title="首页" class="iconfont icon-home router-link-active" data-v-06225672></a></li> <li data-v-06225672><span data-v-06225672>开发指南</span></li><li data-v-06225672><span data-v-06225672>前端手册 Vue 3.x</span></li></ul> <div class="info" data-v-06225672><div title="作者" class="author iconfont icon-touxiang" data-v-06225672><a href="https://www.iocoder.cn" target="_blank" title="作者" class="beLink" data-v-06225672>芋道源码</a></div> <div title="创建时间" class="date iconfont icon-riqi" data-v-06225672><a href="javascript:;" data-v-06225672>2022-04-17</a></div> <!----></div></div></div> <!----> <div class="content-wrapper"><div class="right-menu-wrapper"><div class="right-menu-margin"><div class="right-menu-title">目录</div> <div class="right-menu-content"></div></div></div> <h1><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABH1JREFUSA3tVl1oHFUUPmdmd2ltklqbpJDiNnXFmgbFktho7YMPNiJSSZM0+CAYSkUELVhM6YuwIPpgoOKDqOBDC0XE2CQoNtQXBUFTTcCi+Wlh1V2TQExsUzcltd3M9Tt3ZjZzZ2fT+OJTL8yeM+eee757fmeJbq//KQL8X3DUSFOcfr7cRsRtxNQMWueeVzOkaITIGqQHNg5y8+jNW9ldM7A6nTpAjuolUikAwq7CE3WcM2RRDz+XGVgN3FptU/aUSlvq9Pa3iZ1+sgAqJyyAFqkipd9dqiwHF3P65YycLWc/6sqGrvoEoIp6DOFaX5h6+dnfjkWprwqsPk0dUGq5vySwDImC10KxFHgGL1SWoc92O3eVht09qdXNH11I2SsTsJYqMWzihqGMi+A+Garf3BAuuLI5oGlULyNfyB/HYNujwktOfRrMr5t77NmevqaUopx0grnKAyvVpmwUDB4x6FPXuGvYLTDwWsejwgtgkYKPqRJg8SV6xaiZ3ZTppGneS4yfH5/66fZSDHv+QZci/+h5c5UHtpy67JUqGppM0sh0Nc1dW6/N1W5Yoqat8/TU/VnadmdeW2PLLSyh0cvxBs3KbqTmwYPpxN4do/mzE8nEpvX/UMu2Wbp74zUAK5q6WkHns7V0eWkdPbPzd3rxkTGybadYySumVzhcaJFbs5UrEkQ/+CK8gF5dnh/6ciIZ73gwQ927L1IitoxKLXYP3SjYdOrHHfTZhRRlFyrorafPk20B3HPD1y2G3qKZME5Jcf3t/HUC13/8tSd++vqFveMUTwAUxSUFI1QekR1+bIze3D9MF2aq6cPvG72CgnldWCFqyRw3lwH8ZMerjTD9ElRO7Gv44wNpC90aASqGfVlz/Rx17srQ57/UU26hkhQqUB7dBR71WmzQhHUnblGmVOEw0jhbV1n9OlXUDCIRGaNV5Jp43N516fN7JmnTHdfp7Hgy0luO4aMhtkLL8Bi3bUWYvzh5Mn1dTxrL6QmGuRhGL/TiTTxRoEdTszSaq9GR0NGA3KdkOz3hqSV3MIDhQ5IVX/Ivx3umBti2es2h4eZby7x8br1rkf7Mo90AqC8aQ3sJeNzqFRu+vSANAQe3PL7l0HGOAdwDCeZYvNKeoZp1Qfs6Aipndh86HmFRi0LAnEO47wsqM6cdfjh3jBPUzhZy7nvlUfFsamED1VQt6aISHVymXZ/B2aCtIG8AI8xfobj2d3en1wWVhOeHELKmLQ1s211s88comkv4UCwWyF787mJdYXtNfhKAXVqnKTq8QZvGAGGOfaTo5pGZ/PwbUCr5+DPr/1J92JNHr9aOl/F3iI5+O1nfybsGxoimvZ3ViWSluDITw3P37mypheDIPY0tw7+O/5ApbkYw+zpfaUVu32Pi98+defdUhEpZkRFq0aqyNh9FuL9hpYbEm6iwi0z2REd09ZmyENEbuhjDWzKvZXTqKYaBIr3tt5kuPtQBZFvEUwHt60vfCNu41XsksH9Ij1BMMz1Y0OOunHNShFIP5868g5zeXmuLwL9T4b6Q2+KejgAAAABJRU5ErkJggg==">开发规范<!----></h1> <div class="theme-vdoing-content content__default"><h2 id="_0-实战案例"><a href="#_0-实战案例" class="header-anchor">#</a> 0. 实战案例</h2> <p>本小节,提供大家开发管理后台的功能时,最常用的普通列表、树形列表、新增与修改的表单弹窗、详情表单弹窗的实战案例。</p> <h3 id="_0-1-普通列表"><a href="#_0-1-普通列表" class="header-anchor">#</a> 0.1 普通列表</h3> <p>可参考 [系统管理 -> 岗位管理] 菜单:</p> <ul><li>API 接口:<a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/api/system/post/index.ts" target="_blank" rel="noopener noreferrer"><code>/src/api/system/post/index.ts</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li>列表界面:<a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/system/post/index.vue" target="_blank" rel="noopener noreferrer"><code>/src/views/system/post/index.vue</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li>表单界面:<a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/system/post/PostForm.vue" target="_blank" rel="noopener noreferrer"><code>/src/views/system/post/PostForm.vue</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul> <div class="custom-block tip"><p class="custom-block-title">为什么界面拆成列表和表单两个 Vue 文件?</p> <p>每个 Vue 文件,只实现一个功能,更简洁,维护性更好,Git 代码冲突概率低。</p></div> <h3 id="_0-2-树形列表"><a href="#_0-2-树形列表" class="header-anchor">#</a> 0.2 树形列表</h3> <p>可参考 [系统管理 -> 部门管理] 菜单:</p> <ul><li>API 接口:<a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/api/system/dept/index.ts" target="_blank" rel="noopener noreferrer"><code>/src/api/system/dept/index.ts</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li>列表界面:<a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/system/dept/index.vue" target="_blank" rel="noopener noreferrer"><code>/src/views/system/dept/index.vue</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li>表单界面:<a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/system/dept/DeptForm.vue" target="_blank" rel="noopener noreferrer"><code>/src/views/system/dept/DeptForm.vue</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul> <h3 id="_0-3-高性能列表"><a href="#_0-3-高性能列表" class="header-anchor">#</a> 0.3 高性能列表</h3> <p>可参考 [系统管理 -> 地区管理] 菜单,对应 <a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/system/area/index.vue" target="_blank" rel="noopener noreferrer"><code>/src/views/system/area/index.vue</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 列表界面</p> <p>基于 <a href="https://element-plus.org/zh-CN/component_table-v2.html" target="_blank" rel="noopener noreferrer">Virtualized Table 虚拟化表格<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 实现,解决一屏里超过 1000 条数据记录时,就会出现卡顿等性能问题。</p> <h3 id="_0-4-详情弹窗"><a href="#_0-4-详情弹窗" class="header-anchor">#</a> 0.4 详情弹窗</h3> <p>可参考 [基础设施 -> API 日志 -> 访问日志] 菜单,对应 <a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/infra/apiAccessLog/ApiAccessLogDetail.vue" target="_blank" rel="noopener noreferrer"><code>/src/views/infra/apiAccessLog/ApiAccessLogDetail.vue</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 详情弹窗</p> <h2 id="_1-view-页面"><a href="#_1-view-页面" class="header-anchor">#</a> 1. view 页面</h2> <p>在 <a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/" target="_blank" rel="noopener noreferrer"><code>@views</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 目录下,每个模块对应一个目录,它的所有功能的 <code>.vue</code> 都放在该目录里。</p> <p><img src="https://doc.iocoder.cn/img/Vue3/%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83/01.png" alt=" 目录"></p> <p>一般来说,一个路由对应一个 <code>index.vue</code> 文件。</p> <h2 id="_2-api-请求"><a href="#_2-api-请求" class="header-anchor">#</a> 2. api 请求</h2> <p>在 <a href="https://github.com/yudaocode/yudao-ui-admin-vue3/tree/master/src/api" target="_blank" rel="noopener noreferrer"><code>@/api</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 目录下,每个模块对应一个 <code>index.ts</code> API 文件。</p> <p><img src="https://doc.iocoder.cn/img/Vue3/%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83/02.png" alt=" 目录"></p> <ul><li>API 方法:会调用 <code>request</code> 方法,发起对后端 RESTful API 的调用。</li> <li><code>interface</code> 类型:定义了 API 的请求参数和返回结果的类型,对应后端的 VO 类型。</li></ul> <h3 id="_2-1-请求封装"><a href="#_2-1-请求封装" class="header-anchor">#</a> 2.1 请求封装</h3> <p><a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/config/axios/service.ts" target="_blank" rel="noopener noreferrer"><code>/src/config/axios/index.ts</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 基于 <a href="http://axios-js.com/zh-cn/docs_index.html" target="_blank" rel="noopener noreferrer">axios<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 封装,统一处理 GET、POST 方法的请求参数、请求头,以及错误提示信息等。</p> <p><img src="https://doc.iocoder.cn/img/Vue3/%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83/03-01.png" alt="axios/index.ts"></p> <h4 id="_2-1-1-创建-axios-实例"><a href="#_2-1-1-创建-axios-实例" class="header-anchor">#</a> 2.1.1 创建 axios 实例</h4> <ul><li><code>baseURL</code> 基础路径</li> <li><code>timeout</code> 超时时间,默认为 30000 毫秒</li></ul> <details class="custom-block details"><summary>实现代码 /src/config/axios/service.ts</summary> <div class="language-TypeScript extra-class"><pre class="language-typescript"><code><span class="token keyword">import</span> axios <span class="token keyword">from</span> <span class="token string">'axios'</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> result_code<span class="token punctuation">,</span> base_url<span class="token punctuation">,</span> request_timeout <span class="token punctuation">}</span> <span class="token operator">=</span> config
<span class="token comment">// 创建 axios 实例</span>
<span class="token keyword">const</span> service<span class="token operator">:</span> AxiosInstance <span class="token operator">=</span> axios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
baseURL<span class="token operator">:</span> base_url<span class="token punctuation">,</span> <span class="token comment">// api 的 base_url</span>
timeout<span class="token operator">:</span> request_timeout<span class="token punctuation">,</span> <span class="token comment">// 请求超时时间</span>
withCredentials<span class="token operator">:</span> <span class="token boolean">false</span> <span class="token comment">// 禁用 Cookie 等信息</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div></details> <h4 id="_2-1-2-request-拦截器"><a href="#_2-1-2-request-拦截器" class="header-anchor">#</a> 2.1.2 Request 拦截器</h4> <ul><li>【重点】<code>Authorization</code>、<code>tenant-id</code> 请求头</li> <li>GET 请求参数的拼接</li></ul> <details class="custom-block details"><summary>实现代码 /src/config/axios/service.ts</summary> <div class="language-TypeScript extra-class"><pre class="language-typescript"><code><span class="token keyword">import</span> axios<span class="token punctuation">,</span> <span class="token punctuation">{</span>
AxiosInstance<span class="token punctuation">,</span>
AxiosRequestHeaders<span class="token punctuation">,</span>
AxiosResponse<span class="token punctuation">,</span>
AxiosError<span class="token punctuation">,</span>
InternalAxiosRequestConfig
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'axios'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> getAccessToken<span class="token punctuation">,</span> getTenantId <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@/utils/auth'</span>
<span class="token keyword">const</span> tenantEnable <span class="token operator">=</span> <span class="token keyword">import</span><span class="token punctuation">.</span>meta<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">VITE_APP_TENANT_ENABLE</span>
service<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>
<span class="token punctuation">(</span>config<span class="token operator">:</span> InternalAxiosRequestConfig<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">// 是否需要设置 token</span>
<span class="token keyword">let</span> isToken <span class="token operator">=</span> <span class="token punctuation">(</span>config<span class="token operator">!</span><span class="token punctuation">.</span>headers <span class="token operator">||</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>isToken <span class="token operator">===</span> <span class="token boolean">false</span>
whiteList<span class="token punctuation">.</span><span class="token function">some</span><span class="token punctuation">(</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>config<span class="token punctuation">.</span>url<span class="token punctuation">)</span> <span class="token punctuation">{</span>
config<span class="token punctuation">.</span>url<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span>
<span class="token keyword">return</span> <span class="token punctuation">(</span>isToken <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">getAccessToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token operator">!</span>isToken<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token punctuation">(</span>config <span class="token keyword">as</span> Recordable<span class="token punctuation">)</span><span class="token punctuation">.</span>headers<span class="token punctuation">.</span>Authorization <span class="token operator">=</span> <span class="token string">'Bearer '</span> <span class="token operator">+</span> <span class="token function">getAccessToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 让每个请求携带自定义token</span>
<span class="token punctuation">}</span>
<span class="token comment">// 设置租户</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>tenantEnable <span class="token operator">&&</span> tenantEnable <span class="token operator">===</span> <span class="token string">'true'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> tenantId <span class="token operator">=</span> <span class="token function">getTenantId</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>tenantId<span class="token punctuation">)</span> <span class="token punctuation">(</span>config <span class="token keyword">as</span> Recordable<span class="token punctuation">)</span><span class="token punctuation">.</span>headers<span class="token punctuation">[</span><span class="token string">'tenant-id'</span><span class="token punctuation">]</span> <span class="token operator">=</span> tenantId
<span class="token punctuation">}</span>
<span class="token keyword">const</span> params <span class="token operator">=</span> config<span class="token punctuation">.</span>params <span class="token operator">||</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">const</span> data <span class="token operator">=</span> config<span class="token punctuation">.</span>data <span class="token operator">||</span> <span class="token boolean">false</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>
config<span class="token punctuation">.</span>method<span class="token operator">?.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token string">'POST'</span> <span class="token operator">&&</span>
<span class="token punctuation">(</span>config<span class="token punctuation">.</span>headers <span class="token keyword">as</span> AxiosRequestHeaders<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token string">'Content-Type'</span><span class="token punctuation">]</span> <span class="token operator">===</span>
<span class="token string">'application/x-www-form-urlencoded'</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
config<span class="token punctuation">.</span>data <span class="token operator">=</span> qs<span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token comment">// get参数编码</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>config<span class="token punctuation">.</span>method<span class="token operator">?.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token string">'GET'</span> <span class="token operator">&&</span> params<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> url <span class="token operator">=</span> config<span class="token punctuation">.</span>url <span class="token operator">+</span> <span class="token string">'?'</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> propName <span class="token keyword">of</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> value <span class="token operator">=</span> params<span class="token punctuation">[</span>propName<span class="token punctuation">]</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>value <span class="token operator">!==</span> <span class="token keyword">void</span> <span class="token number">0</span> <span class="token operator">&&</span> value <span class="token operator">!==</span> <span class="token keyword">null</span> <span class="token operator">&&</span> <span class="token keyword">typeof</span> value <span class="token operator">!==</span> <span class="token string">'undefined'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> value <span class="token operator">===</span> <span class="token string">'object'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> val <span class="token keyword">of</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> params <span class="token operator">=</span> propName <span class="token operator">+</span> <span class="token string">'['</span> <span class="token operator">+</span> val <span class="token operator">+</span> <span class="token string">']'</span>
<span class="token keyword">const</span> subPart <span class="token operator">=</span> <span class="token function">encodeURIComponent</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'='</span>
url <span class="token operator">+=</span> subPart <span class="token operator">+</span> <span class="token function">encodeURIComponent</span><span class="token punctuation">(</span>value<span class="token punctuation">[</span>val<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'&'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
url <span class="token operator">+=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>propName<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">encodeURIComponent</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&</span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 给 get 请求加上时间戳参数,避免从缓存中拿数据</span>
<span class="token comment">// const now = new Date().getTime()</span>
<span class="token comment">// params = params.substring(0, url.length - 1) + `?_t=${now}`</span>
url <span class="token operator">=</span> url<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span>
config<span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
config<span class="token punctuation">.</span>url <span class="token operator">=</span> url
<span class="token punctuation">}</span>
<span class="token keyword">return</span> config
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">(</span>error<span class="token operator">:</span> AxiosError<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">// Do something with request error</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token comment">// for debug</span>
<span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span>
</code></pre></div></details> <h4 id="_2-1-3-response-拦截器"><a href="#_2-1-3-response-拦截器" class="header-anchor">#</a> 2.1.3 Response 拦截器</h4> <ul><li>访问令牌 AccessToken 过期时,使用刷新令牌 RefreshToken 刷新,获得新的访问令牌</li> <li>刷新令牌失败(过期)时,跳回首页进行登录</li> <li>请求失败,Message 错误提示</li></ul> <details class="custom-block details"><summary>实现代码 /src/config/axios/service.ts</summary> <div class="language-TypeScript extra-class"><pre class="language-typescript"><code><span class="token keyword">import</span> axios<span class="token punctuation">,</span> <span class="token punctuation">{</span>
AxiosInstance<span class="token punctuation">,</span>
AxiosRequestHeaders<span class="token punctuation">,</span>
AxiosResponse<span class="token punctuation">,</span>
AxiosError<span class="token punctuation">,</span>
InternalAxiosRequestConfig
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'axios'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> ElMessage<span class="token punctuation">,</span> ElMessageBox<span class="token punctuation">,</span> ElNotification <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'element-plus'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> getAccessToken<span class="token punctuation">,</span> getRefreshToken<span class="token punctuation">,</span> removeToken<span class="token punctuation">,</span> setToken <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@/utils/auth'</span>
<span class="token comment">// 需要忽略的提示。忽略后,自动 Promise.reject('error')</span>
<span class="token keyword">const</span> ignoreMsgs <span class="token operator">=</span> <span class="token punctuation">[</span>
<span class="token string">'无效的刷新令牌'</span><span class="token punctuation">,</span> <span class="token comment">// 刷新令牌被删除时,不用提示</span>
<span class="token string">'刷新令牌已过期'</span> <span class="token comment">// 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面</span>
<span class="token punctuation">]</span>
<span class="token comment">// 是否显示重新登录</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> isRelogin <span class="token operator">=</span> <span class="token punctuation">{</span> show<span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span>
<span class="token keyword">import</span> errorCode <span class="token keyword">from</span> <span class="token string">'./errorCode'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> resetRouter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@/router'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useCache <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@/hooks/web/useCache'</span>
service<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>response<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>
<span class="token keyword">async</span> <span class="token punctuation">(</span>response<span class="token operator">:</span> AxiosResponse<span class="token operator"><</span><span class="token builtin">any</span><span class="token operator">></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> data <span class="token punctuation">}</span> <span class="token operator">=</span> response
<span class="token keyword">const</span> config <span class="token operator">=</span> response<span class="token punctuation">.</span>config
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>data<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// 返回“[HTTP]请求没有返回值”;</span>
<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> t <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useI18n</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 未设置状态码则默认成功状态</span>
<span class="token keyword">const</span> code <span class="token operator">=</span> data<span class="token punctuation">.</span>code <span class="token operator">||</span> result_code
<span class="token comment">// 二进制数据则直接返回</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>
response<span class="token punctuation">.</span>request<span class="token punctuation">.</span>responseType <span class="token operator">===</span> <span class="token string">'blob'</span> <span class="token operator">||</span>
response<span class="token punctuation">.</span>request<span class="token punctuation">.</span>responseType <span class="token operator">===</span> <span class="token string">'arraybuffer'</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> response<span class="token punctuation">.</span>data
<span class="token punctuation">}</span>
<span class="token comment">// 获取错误信息</span>
<span class="token keyword">const</span> msg <span class="token operator">=</span> data<span class="token punctuation">.</span>msg <span class="token operator">||</span> errorCode<span class="token punctuation">[</span>code<span class="token punctuation">]</span> <span class="token operator">||</span> errorCode<span class="token punctuation">[</span><span class="token string">'default'</span><span class="token punctuation">]</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>ignoreMsgs<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// 如果是忽略的错误码,直接返回 msg 异常</span>
<span class="token keyword">return</span> <span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>code <span class="token operator">===</span> <span class="token number">401</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isRefreshToken<span class="token punctuation">)</span> <span class="token punctuation">{</span>
isRefreshToken <span class="token operator">=</span> <span class="token boolean">true</span>
<span class="token comment">// 1. 如果获取不到刷新令牌,则只能执行登出操作</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">getRefreshToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">handleAuthorized</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token comment">// 2. 进行刷新访问令牌</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> refreshTokenRes <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">refreshToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 2.1 刷新成功,则回放队列的请求 + 当前请求</span>
<span class="token function">setToken</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">await</span> refreshTokenRes<span class="token punctuation">)</span><span class="token punctuation">.</span>data<span class="token punctuation">.</span>data<span class="token punctuation">)</span>
config<span class="token punctuation">.</span>headers<span class="token operator">!</span><span class="token punctuation">.</span>Authorization <span class="token operator">=</span> <span class="token string">'Bearer '</span> <span class="token operator">+</span> <span class="token function">getAccessToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
requestList<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>cb<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">cb</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
requestList <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token keyword">return</span> <span class="token function">service</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。</span>
<span class="token comment">// 2.2 刷新失败,只回放队列的请求</span>
requestList<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>cb<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">cb</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">// 提示是否要登出。即不回放当前请求!不然会形成递归</span>
<span class="token keyword">return</span> <span class="token function">handleAuthorized</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
requestList <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
isRefreshToken <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token comment">// 添加到队列,等待刷新获取到新的令牌</span>
<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name"><span class="token builtin">Promise</span></span><span class="token punctuation">(</span><span class="token punctuation">(</span>resolve<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
requestList<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
config<span class="token punctuation">.</span>headers<span class="token operator">!</span><span class="token punctuation">.</span>Authorization <span class="token operator">=</span> <span class="token string">'Bearer '</span> <span class="token operator">+</span> <span class="token function">getAccessToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 让每个请求携带自定义token 请根据实际情况自行修改</span>
<span class="token function">resolve</span><span class="token punctuation">(</span><span class="token function">service</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>code <span class="token operator">===</span> <span class="token number">500</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
ElMessage<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'sys.api.errMsg500'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token keyword">return</span> <span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>code <span class="token operator">===</span> <span class="token number">901</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
ElMessage<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
offset<span class="token operator">:</span> <span class="token number">300</span><span class="token punctuation">,</span>
dangerouslyUseHTMLString<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
message<span class="token operator">:</span>
<span class="token string">'<div>'</span> <span class="token operator">+</span>
<span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'sys.api.errMsg901'</span><span class="token punctuation">)</span> <span class="token operator">+</span>
<span class="token string">'</div>'</span> <span class="token operator">+</span>
<span class="token string">'<div> &nbsp; </div>'</span> <span class="token operator">+</span>
<span class="token string">'<div>参考 https://doc.iocoder.cn/ 教程</div>'</span> <span class="token operator">+</span>
<span class="token string">'<div> &nbsp; </div>'</span> <span class="token operator">+</span>
<span class="token string">'<div>5 分钟搭建本地环境</div>'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">return</span> <span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>code <span class="token operator">!==</span> <span class="token number">200</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>msg <span class="token operator">===</span> <span class="token string">'无效的刷新令牌'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// hard coding:忽略这个提示,直接登出</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
ElNotification<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token punctuation">{</span> title<span class="token operator">:</span> msg <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span><span class="token string">'error'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> data
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">(</span>error<span class="token operator">:</span> AxiosError<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'err'</span> <span class="token operator">+</span> error<span class="token punctuation">)</span> <span class="token comment">// for debug</span>
<span class="token keyword">let</span> <span class="token punctuation">{</span> message <span class="token punctuation">}</span> <span class="token operator">=</span> error
<span class="token keyword">const</span> <span class="token punctuation">{</span> t <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useI18n</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>message <span class="token operator">===</span> <span class="token string">'Network Error'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
message <span class="token operator">=</span> <span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'sys.api.errorMessage'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>message<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span><span class="token string">'timeout'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
message <span class="token operator">=</span> <span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'sys.api.apiTimeoutMessage'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>message<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span><span class="token string">'Request failed with status code'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
message <span class="token operator">=</span> <span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'sys.api.apiRequestFailed'</span><span class="token punctuation">)</span> <span class="token operator">+</span> message<span class="token punctuation">.</span><span class="token function">substr</span><span class="token punctuation">(</span>message<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">3</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
ElMessage<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span>
<span class="token keyword">return</span> <span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span>
<span class="token keyword">const</span> <span class="token function-variable function">refreshToken</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
axios<span class="token punctuation">.</span>defaults<span class="token punctuation">.</span>headers<span class="token punctuation">.</span>common<span class="token punctuation">[</span><span class="token string">'tenant-id'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">getTenantId</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">return</span> <span class="token keyword">await</span> axios<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span>base_url <span class="token operator">+</span> <span class="token string">'/system/auth/refresh-token?refreshToken='</span> <span class="token operator">+</span> <span class="token function">getRefreshToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> <span class="token function-variable function">handleAuthorized</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> t <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useI18n</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isRelogin<span class="token punctuation">.</span>show<span class="token punctuation">)</span> <span class="token punctuation">{</span>
isRelogin<span class="token punctuation">.</span>show <span class="token operator">=</span> <span class="token boolean">true</span>
ElMessageBox<span class="token punctuation">.</span><span class="token function">confirm</span><span class="token punctuation">(</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'sys.api.timeoutMessage'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'common.confirmTitle'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
confirmButtonText<span class="token operator">:</span> <span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'login.relogin'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
cancelButtonText<span class="token operator">:</span> <span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'common.cancel'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
type<span class="token operator">:</span> <span class="token string">'warning'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> wsCache <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token function">resetRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 重置静态路由表</span>
wsCache<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token function">removeToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
isRelogin<span class="token punctuation">.</span>show <span class="token operator">=</span> <span class="token boolean">false</span>
window<span class="token punctuation">.</span>location<span class="token punctuation">.</span>href <span class="token operator">=</span> <span class="token string">'/'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
isRelogin<span class="token punctuation">.</span>show <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string">'sys.api.timeoutMessage'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div></details> <h3 id="_2-2-交互流程"><a href="#_2-2-交互流程" class="header-anchor">#</a> 2.2 交互流程</h3> <p>一个完整的前端 UI 交互到服务端处理流程,如下图所示:</p> <p><img src="https://doc.iocoder.cn/img/Vue3/%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83/03-02.png" alt="交互流程"></p> <p>继续以 [系统管理 -> 岗位管理] 菜单为例,查看它是如何读取岗位列表的。代码如下:</p> <div class="language-TypeScript extra-class"><pre class="language-typescript"><code><span class="token comment">// ① api/system/post/index.ts</span>
<span class="token keyword">import</span> request <span class="token keyword">from</span> <span class="token string">'@/config/axios'</span>
<span class="token comment">// 查询岗位列表</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">getPostPage</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>params<span class="token operator">:</span> PageParam<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">await</span> request<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">{</span> url<span class="token operator">:</span> <span class="token string">'/system/post/page'</span><span class="token punctuation">,</span> params <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token comment">// ② views/system/post/index.vue</span>
<span class="token operator"><</span>script setup lang<span class="token operator">=</span><span class="token string">"tsx"</span><span class="token operator">></span>
<span class="token keyword">const</span> loading <span class="token operator">=</span> <span class="token function">ref</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token comment">// 列表的加载中</span>
<span class="token keyword">const</span> total <span class="token operator">=</span> <span class="token function">ref</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">// 列表的总页数</span>
<span class="token keyword">const</span> list <span class="token operator">=</span> <span class="token function">ref</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token comment">// 列表的数据</span>
<span class="token keyword">const</span> queryParams <span class="token operator">=</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
pageNo<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
pageSize<span class="token operator">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
code<span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
name<span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
status<span class="token operator">:</span> <span class="token keyword">undefined</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">/** 查询岗位列表 */</span>
<span class="token keyword">const</span> <span class="token function-variable function">getList</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
loading<span class="token punctuation">.</span>value <span class="token operator">=</span> <span class="token boolean">true</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword">await</span> PostApi<span class="token punctuation">.</span><span class="token function">getPostPage</span><span class="token punctuation">(</span>queryParams<span class="token punctuation">)</span>
list<span class="token punctuation">.</span>value <span class="token operator">=</span> data<span class="token punctuation">.</span>list
total<span class="token punctuation">.</span>value <span class="token operator">=</span> data<span class="token punctuation">.</span>total
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
loading<span class="token punctuation">.</span>value <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>
</code></pre></div><h2 id="_3-component-组件"><a href="#_3-component-组件" class="header-anchor">#</a> 3. component 组件</h2> <h3 id="_3-1-全局组件"><a href="#_3-1-全局组件" class="header-anchor">#</a> 3.1 全局组件</h3> <p>在 <a href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/components/" target="_blank" rel="noopener noreferrer"><code>@/components</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 目录下,实现<strong>全局</strong>组件,被所有模块所公用。</p> <p>例如说,富文本编辑器、各种各搜索组件、封装的分页组件等等。</p> <p><img src="https://doc.iocoder.cn/img/Vue3/%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83/04.png" alt="全局组件"></p> <h3 id="_3-2-模块内组件"><a href="#_3-2-模块内组件" class="header-anchor">#</a> 3.2 模块内组件</h3> <p>每个模块的业务组件,可实现在 <code>views</code> 目录下,自己模块的目录的 <code>components</code> 目录下,避免单个 <code>.vue</code> 文件过大,降低维护成功。</p> <p>例如说,<code>@/views/pay/app/components/xxx.vue</code>:</p> <p><img src="https://doc.iocoder.cn/img/Vue3/%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83/05.png" alt="业务组件"></p> <h2 id="_4-style-样式"><a href="#_4-style-样式" class="header-anchor">#</a> 4. style 样式</h2> <p>① 在 <a href="https://github.com/yudaocode/yudao-ui-admin-vue3/tree/master/src/styles" target="_blank" rel="noopener noreferrer"><code>@/styles</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 目录下,实现<strong>全局</strong>样式,被所有页面所公用。</p> <p><img src="https://doc.iocoder.cn/img/Vue3/%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83/06.png" alt="全局样式"></p> <p>② 每个 <code>.vue</code> 页面,可在 <code><style /></code> 标签中添加样式,注意需要添加 <code>scoped</code> 表示只作用在当前页面里,避免造成全局的样式污染。</p> <p><img src="https://doc.iocoder.cn/img/Vue3/%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83/07.png" alt="业务样式"></p> <p>更多也可以看看如下两篇文档:</p> <ul><li><a href="https://element-plus-admin-doc.cn/guide/settings.html#%E6%A0%B7%E5%BC%8F%E9%85%8D%E7%BD%AE" target="_blank" rel="noopener noreferrer">《vue-element-plus-admin —— 项目配置「样式配置」》<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://element-plus-admin-doc.cn/guide_design.html" target="_blank" rel="noopener noreferrer">《vue-element-plus-admin —— 样式》<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul> <h2 id="_5-项目规范"><a href="#_5-项目规范" class="header-anchor">#</a> 5. 项目规范</h2> <p>可参考 <a href="https://element-plus-admin-doc.cn/dep_lint.html" target="_blank" rel="noopener noreferrer">《vue-element-plus-admin —— 项目规范》<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 文档。</p></div></div> <div class="page-slot page-slot-bottom">
<div class="wwads-cn wwads-horizontal pageB" data-id="136" style="width:100%;max-height:80px;min-height:auto;"></div>
<style>
.pageB img{width:80px!important;}
.wwads-horizontal .wwads-text, .wwads-content .wwads-text{line-height:1;}
</style>
</div> <div class="page-edit"><!----> <!----> <!----></div> <div class="page-nav-wapper"><div class="page-nav-centre-wrap"><a href="server-monitor.html" class="page-nav-centre page-nav-centre-prev"><div class="tooltip">服务监控</div></a> <a href="vue3_route.html" class="page-nav-centre page-nav-centre-next"><div class="tooltip">菜单路由</div></a></div> <div class="page-nav"><p class="inner"><span class="prev">
←
<a href="server-monitor.html" class="prev">服务监控</a></span> <span class="next"><a href="vue3_route.html">菜单路由</a>→
</span></p></div></div></div> <!----></main></div> <div class="footer"><div class="icons"><a href="mailto:7685413@qq.com" title="发邮件" target="_blank" class="iconfont icon-youjian"></a><a href="https://github.com/YunaiV" title="GitHub" target="_blank" class="iconfont icon-github"></a><a href="https://www.iocoder.cn/?yudao" title="博客" target="_blank" class="iconfont icon-erji"></a></div>
Theme by
<a href="https://github.com/xugaoyi/vuepress-theme-vdoing" target="_blank" title="本站主题">Vdoing</a>
| Copyright © 2019-2025
<span>芋道源码 | MIT License</span></div> <div class="buttons"><div title="返回顶部" class="button blur go-to-top iconfont icon-fanhuidingbu" style="display:none;"></div> <div title="去评论" class="button blur go-to-comment iconfont icon-pinglun" style="display:none;"></div> <div title="主题模式" class="button blur theme-mode-but iconfont icon-zhuti"><ul class="select-box" style="display:none;"><li class="iconfont icon-zidong">
跟随系统
</li><li class="iconfont icon-rijianmoshi">
浅色模式
</li><li class="iconfont icon-yejianmoshi">
深色模式
</li><li class="iconfont icon-yuedu">
阅读模式
</li></ul></div></div> <!----> <!----> <div class="custom-html-window custom-html-window-rb" style="display:;"><div class="custom-wrapper"><span class="close-but">×</span> <div>
<div class="wwads-cn wwads-vertical windowRB" data-id="136" style="max-width:160px;
min-width: auto;min-height:auto;"></div>
<style>
.windowRB{ padding: 0;}
.windowRB .wwads-img{margin-top: 10px;}
.windowRB .wwads-content{margin: 0 10px 10px 10px;}
.custom-html-window-rb .close-but{
display: none;
}
</style>
</div></div></div></div><div class="global-ui"></div></div>
<script defer></script>
</body>
</html>