Commit 21cf9227236f32054d7f6071cb301ff777468999

Authored by xingyu
1 parent 2bf09cbf

perf: refresh token && delete console

yudao-ui-admin-vue3/src/api/system/user/profile/index.ts
... ... @@ -24,6 +24,6 @@ export const updateUserPwdApi = (oldPassword: string, newPassword: string) => {
24 24 }
25 25  
26 26 // 用户头像上传
27   -export const uploadAvatarApi = (params) => {
28   - return request.upload({ url: '/system/user/profile/update-avatar', params })
  27 +export const uploadAvatarApi = (data) => {
  28 + return request.upload({ url: '/system/user/profile/update-avatar', data: data })
29 29 }
... ...
yudao-ui-admin-vue3/src/components/Verifition/src/Verify.vue
... ... @@ -102,7 +102,6 @@ export default {
102 102 * @description 刷新
103 103 * */
104 104 const refresh = () => {
105   - console.log(instance.value)
106 105 if (instance.value.refresh) {
107 106 instance.value.refresh()
108 107 }
... ... @@ -271,7 +270,6 @@ export default {
271 270 -moz-box-sizing: content-box;
272 271 box-sizing: content-box;
273 272 border: 1px solid #ddd;
274   - -webkit-border-radius: 4px;
275 273 }
276 274  
277 275 .verify-bar-area .verify-move-block {
... ... @@ -284,7 +282,6 @@ export default {
284 282 -moz-box-sizing: content-box;
285 283 box-sizing: content-box;
286 284 box-shadow: 0 0 2px #888888;
287   - -webkit-border-radius: 1px;
288 285 }
289 286  
290 287 .verify-bar-area .verify-move-block:hover {
... ...
yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue
... ... @@ -242,7 +242,6 @@ export default {
242 242 //兼容移动端
243 243 var x = e.touches[0].pageX
244 244 }
245   - console.log(barArea)
246 245 startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left)
247 246 startMoveTime.value = +new Date() //开始滑动的时间
248 247 if (isEnd.value == false) {
... ...
yudao-ui-admin-vue3/src/config/axios/index.ts
1 1 import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
2   -import { ElMessage, ElNotification } from 'element-plus'
  2 +import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
3 3 import qs from 'qs'
4 4 import { config } from '@/config/axios/config'
5   -import { getAccessToken, getTenantId, removeToken } from '@/utils/auth'
  5 +import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
6 6 import errorCode from './errorCode'
7 7 import { useI18n } from '@/hooks/web/useI18n'
  8 +import { resetRouter } from '@/router'
8 9  
9 10 const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE
10 11 const BASE_URL = import.meta.env.VITE_BASE_URL
... ... @@ -20,9 +21,9 @@ const ignoreMsgs = [
20 21 export const isRelogin = { show: false }
21 22 // Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
22 23 // 请求队列
23   -// const requestList = []
  24 +let requestList: any[] = []
24 25 // 是否正在刷新中
25   -// const isRefreshToken = false
  26 +let isRefreshToken = false
26 27  
27 28 export const PATH_URL = base_url[import.meta.env.VITE_API_BASEPATH]
28 29  
... ... @@ -91,6 +92,7 @@ service.interceptors.request.use(
91 92 service.interceptors.response.use(
92 93 async (response: AxiosResponse<Recordable>) => {
93 94 const { data } = response
  95 + console.info(data)
94 96 if (!data) {
95 97 // 返回“[HTTP]请求没有返回值”;
96 98 throw new Error()
... ... @@ -112,16 +114,38 @@ service.interceptors.response.use(
112 114 return Promise.reject(msg)
113 115 } else if (code === 401) {
114 116 // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
115   - return handleAuthorized()
116   - // if (!isRefreshToken) {
117   - // isRefreshToken = true
118   - // // 1. 如果获取不到刷新令牌,则只能执行登出操作
119   - // if (!getRefreshToken()) {
120   - // return handleAuthorized()
121   - // }
122   - // // 2. 进行刷新访问令牌
123   - // // TODO: 引入refreshToken会循环依赖报错
124   - // }
  117 + if (!isRefreshToken) {
  118 + isRefreshToken = true
  119 + // 1. 如果获取不到刷新令牌,则只能执行登出操作
  120 + if (!getRefreshToken()) {
  121 + return handleAuthorized()
  122 + }
  123 + // 2. 进行刷新访问令牌
  124 + try {
  125 + const refreshTokenRes = await refreshToken()
  126 + // 2.1 刷新成功,则回放队列的请求 + 当前请求
  127 + setToken(refreshTokenRes.data)
  128 + requestList.forEach((cb: any) => cb())
  129 + return service(response.config)
  130 + } catch (e) {
  131 + // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
  132 + // 2.2 刷新失败,只回放队列的请求
  133 + requestList.forEach((cb: any) => cb())
  134 + // 提示是否要登出。即不回放当前请求!不然会形成递归
  135 + return handleAuthorized()
  136 + } finally {
  137 + requestList = []
  138 + isRefreshToken = false
  139 + }
  140 + } else {
  141 + // 添加到队列,等待刷新获取到新的令牌
  142 + return new Promise((resolve) => {
  143 + requestList.push(() => {
  144 + ;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
  145 + resolve(service(response.config))
  146 + })
  147 + })
  148 + }
125 149 } else if (code === 500) {
126 150 ElMessage.error(t('sys.api.errMsg500'))
127 151 return Promise.reject(new Error(msg))
... ... @@ -165,14 +189,32 @@ service.interceptors.response.use(
165 189 return Promise.reject(error)
166 190 }
167 191 )
  192 +
  193 +const refreshToken = async () => {
  194 + return await service({
  195 + url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken(),
  196 + method: 'post'
  197 + })
  198 +}
168 199 const handleAuthorized = () => {
169 200 const { t } = useI18n()
170 201 if (!isRelogin.show) {
171   - removeToken()
172 202 isRelogin.show = true
173   - ElNotification.error(t('sys.api.timeoutMessage'))
  203 + ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
  204 + confirmButtonText: t('login.relogin'),
  205 + cancelButtonText: t('common.cancel'),
  206 + type: 'warning'
  207 + })
  208 + .then(() => {
  209 + removeToken()
  210 + resetRouter() // 重置静态路由表
  211 + isRelogin.show = false
  212 + location.href = '/'
  213 + })
  214 + .catch(() => {
  215 + isRelogin.show = false
  216 + })
174 217 }
175   - location.href = '/'
176 218 return Promise.reject(t('sys.api.timeoutMessage'))
177 219 }
178 220 export { service }
... ...
yudao-ui-admin-vue3/src/router/index.ts
... ... @@ -10,6 +10,7 @@ import { createRouter, createWebHashHistory } from &#39;vue-router&#39;
10 10 import { usePermissionStoreWithOut } from '@/store/modules/permission'
11 11 import { useDictStoreWithOut } from '@/store/modules/dict'
12 12 import { listSimpleDictDataApi } from '@/api/system/dict/dict.data'
  13 +import { isRelogin } from '@/config/axios'
13 14  
14 15 const permissionStore = usePermissionStoreWithOut()
15 16  
... ... @@ -48,14 +49,14 @@ router.beforeEach(async (to, from, next) =&gt; {
48 49 next({ path: '/' })
49 50 } else {
50 51 if (!dictStore.getIsSetDict) {
  52 + isRelogin.show = true
51 53 // 获取所有字典
52 54 const res = await listSimpleDictDataApi()
53   - if (res) {
54   - dictStore.setDictMap(res)
55   - dictStore.setIsSetDict(true)
56   - }
  55 + dictStore.setDictMap(res)
  56 + dictStore.setIsSetDict(true)
57 57 }
58 58 if (permissionStore.getIsAddRouters) {
  59 + isRelogin.show = false
59 60 next()
60 61 return
61 62 }
... ... @@ -77,7 +78,7 @@ router.beforeEach(async (to, from, next) =&gt; {
77 78 if (whiteList.indexOf(to.path) !== -1) {
78 79 next()
79 80 } else {
80   - next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
  81 + next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
81 82 }
82 83 }
83 84 })
... ...
yudao-ui-admin-vue3/src/views/Profile/components/UserAvatar.vue
... ... @@ -6,12 +6,12 @@ import { ElRow, ElCol, ElUpload, ElMessage, ElDialog } from &#39;element-plus&#39;
6 6 import { propTypes } from '@/utils/propTypes'
7 7 import { uploadAvatarApi } from '@/api/system/user/profile'
8 8 const cropper = ref()
  9 +const dialogVisible = ref(false)
  10 +const cropperVisible = ref(false)
9 11 const props = defineProps({
10 12 img: propTypes.string.def('')
11 13 })
12   -const state = reactive({
13   - dialogVisible: false,
14   - cropperVisible: false,
  14 +const options = reactive({
15 15 dialogTitle: '编辑头像',
16 16 options: {
17 17 img: props.img, //裁剪图片的地址
... ... @@ -27,8 +27,11 @@ const state = reactive({
27 27 })
28 28 /** 编辑头像 */
29 29 const editCropper = () => {
30   - state.dialogVisible = true
31   - state.cropperVisible = true
  30 + dialogVisible.value = true
  31 +}
  32 +// 打开弹出层结束时的回调
  33 +const modalOpened = () => {
  34 + cropperVisible.value = true
32 35 }
33 36 /** 向左旋转 */
34 37 const rotateLeft = () => {
... ... @@ -44,7 +47,7 @@ const changeScale = (num: number) =&gt; {
44 47 cropper.value.changeScale(num)
45 48 }
46 49 // 覆盖默认的上传行为
47   -const requestUpload = () => {}
  50 +const requestUpload: any = () => {}
48 51 /** 上传预处理 */
49 52 const beforeUpload = (file: Blob) => {
50 53 if (file.type.indexOf('image/') == -1) {
... ... @@ -54,64 +57,65 @@ const beforeUpload = (file: Blob) =&gt; {
54 57 reader.readAsDataURL(file)
55 58 reader.onload = () => {
56 59 if (reader.result) {
57   - state.options.img = reader.result as string
  60 + options.options.img = reader.result as string
58 61 }
59 62 }
60 63 }
61 64 }
62 65 /** 上传图片 */
63 66 const uploadImg = () => {
64   - cropper.value.getCropBlob((data) => {
  67 + cropper.value.getCropBlob((data: any) => {
65 68 let formData = new FormData()
66 69 formData.append('avatarfile', data)
67 70 uploadAvatarApi(formData)
68 71 })
69 72 }
70 73 /** 实时预览 */
71   -const realTime = (data) => {
72   - state.previews = data
  74 +const realTime = (data: any) => {
  75 + options.previews = data
73 76 }
74 77 watch(
75 78 () => props.img,
76 79 () => {
77 80 if (props.img) {
78   - state.options.img = props.img
79   - state.previews.img = props.img
80   - state.previews.url = props.img
  81 + options.options.img = props.img
  82 + options.previews.img = props.img
  83 + options.previews.url = props.img
81 84 }
82 85 }
83 86 )
84 87 </script>
85 88 <template>
86 89 <div class="user-info-head" @click="editCropper()">
87   - <img :src="state.options.img" title="点击上传头像" class="img-circle img-lg" alt="" />
  90 + <img :src="options.options.img" title="点击上传头像" class="img-circle img-lg" alt="" />
88 91 </div>
89 92 <el-dialog
90   - v-model="state.dialogVisible"
91   - :title="state.dialogTitle"
92   - width="50%"
93   - :maxHeight="350"
  93 + v-model="dialogVisible"
  94 + :title="options.dialogTitle"
  95 + width="800px"
  96 + append-to-body
94 97 style="padding: 30px 20px"
  98 + @opened="modalOpened"
95 99 >
96 100 <el-row>
97 101 <el-col :xs="24" :md="12" style="height: 350px">
98 102 <VueCropper
99 103 ref="cropper"
100   - :img="state.options.img"
  104 + :img="options.options.img"
101 105 :info="true"
102   - :autoCrop="state.options.autoCrop"
103   - :autoCropWidth="state.options.autoCropWidth"
104   - :autoCropHeight="state.options.autoCropHeight"
105   - :fixedBox="state.options.fixedBox"
  106 + :autoCrop="options.options.autoCrop"
  107 + :autoCropWidth="options.options.autoCropWidth"
  108 + :autoCropHeight="options.options.autoCropHeight"
  109 + :fixedBox="options.options.fixedBox"
106 110 @real-time="realTime"
107   - v-if="state.cropperVisible"
  111 + v-if="cropperVisible"
108 112 />
109 113 </el-col>
110 114 <el-col :xs="24" :md="12" style="height: 350px">
111 115 <div class="avatar-upload-preview">
112 116 <img
113   - :src="state.previews.url"
114   - :style="state.previews.img"
  117 + :src="options.previews.url"
  118 + :style="options.previews.img"
115 119 style="!max-width: 100%"
116 120 alt=""
117 121 />
... ...
yudao-ui-admin-vue3/src/views/system/menu/index.vue
... ... @@ -47,7 +47,6 @@ const getTree = async () =&gt; {
47 47 const res = await MenuApi.listSimpleMenusApi()
48 48 const menu = { id: 0, name: '主类目', children: [] as any[] }
49 49 menu.children = handleTree(res)
50   - console.info(menu)
51 50 menuOptions.value = menu
52 51 }
53 52 // ========== 查询 ==========
... ...
yudao-ui-admin-vue3/src/views/system/oauth2/client/index.vue
... ... @@ -155,7 +155,7 @@ getList()
155 155 </Table>
156 156 </ContentWrap>
157 157  
158   - <Dialog v-model="dialogVisible" :title="dialogTitle">
  158 + <Dialog v-model="dialogVisible" :title="dialogTitle" width="60%" maxHeight="420px">
159 159 <!-- 对话框(添加 / 修改) -->
160 160 <Form
161 161 v-if="['create', 'update'].includes(actionType)"
... ...
yudao-ui-admin-vue3/src/views/system/role/index.vue
... ... @@ -110,7 +110,7 @@ const defaultProps = {
110 110 label: 'name',
111 111 value: 'id'
112 112 }
113   -const treeOptions = ref([]) // 菜单树形结构
  113 +const treeOptions = ref<any[]>([]) // 菜单树形结构
114 114 const treeRef = ref<InstanceType<typeof ElTree>>()
115 115 const dialogScopeVisible = ref(false)
116 116 const dialogScopeTitle = ref('数据权限')
... ... @@ -133,7 +133,6 @@ const handleScope = async (type: string, row: RoleVO) =&gt; {
133 133 const menuRes = await listSimpleMenusApi()
134 134 treeOptions.value = handleTree(menuRes)
135 135 const role = await PermissionApi.listRoleMenusApi(row.id)
136   - console.info(role)
137 136 if (role) {
138 137 // treeRef.value!.setCheckedKeys(role as unknown as Array<number>)
139 138 defaultCheckedKeys.value = role
... ... @@ -142,7 +141,6 @@ const handleScope = async (type: string, row: RoleVO) =&gt; {
142 141 const deptRes = await listSimpleDeptApi()
143 142 treeOptions.value = handleTree(deptRes)
144 143 const role = await RoleApi.getRoleApi(row.id)
145   - console.info(role)
146 144 dataScopeForm.dataScope = role.dataScope
147 145 if (role.dataScopeDeptIds) {
148 146 // treeRef.value!.setCheckedKeys(role.dataScopeDeptIds as unknown as Array<number>, false)
... ... @@ -155,7 +153,6 @@ const handleScope = async (type: string, row: RoleVO) =&gt; {
155 153 // 保存权限
156 154 const submitScope = async () => {
157 155 const keys = treeRef.value!.getCheckedKeys(false) as unknown as Array<number>
158   - console.info(keys)
159 156 if ('data' === actionScopeType.value) {
160 157 const data = ref<PermissionAssignRoleDataScopeReqVO>({
161 158 roleId: dataScopeForm.id,
... ...
yudao-ui-admin-vue3/src/views/system/tenantPackage/index.vue
... ... @@ -19,7 +19,7 @@ const defaultProps = {
19 19 value: 'id'
20 20 }
21 21 // ========== 创建菜单树结构 ==========
22   -const menuOptions = ref([]) // 树形结构
  22 +const menuOptions = ref<any[]>([]) // 树形结构
23 23 const treeRef = ref<InstanceType<typeof ElTree>>()
24 24 const treeNodeAll = ref(false)
25 25 // 全选/全不选
... ... @@ -84,11 +84,9 @@ const submitForm = async () =&gt; {
84 84 if (actionType.value === 'create') {
85 85 await TenantPackageApi.createTenantPackageTypeApi(data)
86 86 ElMessage.success(t('common.createSuccess'))
87   - console.log('new data')
88 87 } else {
89 88 await TenantPackageApi.updateTenantPackageTypeApi(data)
90 89 ElMessage.success(t('common.updateSuccess'))
91   - console.log('edit data')
92 90 }
93 91 // 操作成功,重新加载列表
94 92 dialogVisible.value = false
... ...