mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 10:18:42 +08:00 
			
		
		
		
	perf: refresh token && delete console
This commit is contained in:
		| @@ -24,6 +24,6 @@ export const updateUserPwdApi = (oldPassword: string, newPassword: string) => { | ||||
| } | ||||
|  | ||||
| // 用户头像上传 | ||||
| export const uploadAvatarApi = (params) => { | ||||
|   return request.upload({ url: '/system/user/profile/update-avatar', params }) | ||||
| export const uploadAvatarApi = (data) => { | ||||
|   return request.upload({ url: '/system/user/profile/update-avatar', data: data }) | ||||
| } | ||||
|   | ||||
| @@ -102,7 +102,6 @@ export default { | ||||
|      * @description 刷新 | ||||
|      * */ | ||||
|     const refresh = () => { | ||||
|       console.log(instance.value) | ||||
|       if (instance.value.refresh) { | ||||
|         instance.value.refresh() | ||||
|       } | ||||
| @@ -271,7 +270,6 @@ export default { | ||||
|   -moz-box-sizing: content-box; | ||||
|   box-sizing: content-box; | ||||
|   border: 1px solid #ddd; | ||||
|   -webkit-border-radius: 4px; | ||||
| } | ||||
|  | ||||
| .verify-bar-area .verify-move-block { | ||||
| @@ -284,7 +282,6 @@ export default { | ||||
|   -moz-box-sizing: content-box; | ||||
|   box-sizing: content-box; | ||||
|   box-shadow: 0 0 2px #888888; | ||||
|   -webkit-border-radius: 1px; | ||||
| } | ||||
|  | ||||
| .verify-bar-area .verify-move-block:hover { | ||||
|   | ||||
| @@ -242,7 +242,6 @@ export default { | ||||
|         //兼容移动端 | ||||
|         var x = e.touches[0].pageX | ||||
|       } | ||||
|       console.log(barArea) | ||||
|       startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left) | ||||
|       startMoveTime.value = +new Date() //开始滑动的时间 | ||||
|       if (isEnd.value == false) { | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios' | ||||
| import { ElMessage, ElNotification } from 'element-plus' | ||||
| import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' | ||||
| import qs from 'qs' | ||||
| import { config } from '@/config/axios/config' | ||||
| import { getAccessToken, getTenantId, removeToken } from '@/utils/auth' | ||||
| import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth' | ||||
| import errorCode from './errorCode' | ||||
| import { useI18n } from '@/hooks/web/useI18n' | ||||
| import { resetRouter } from '@/router' | ||||
|  | ||||
| const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE | ||||
| const BASE_URL = import.meta.env.VITE_BASE_URL | ||||
| @@ -20,9 +21,9 @@ const ignoreMsgs = [ | ||||
| export const isRelogin = { show: false } | ||||
| // Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现 | ||||
| // 请求队列 | ||||
| // const requestList = [] | ||||
| let requestList: any[] = [] | ||||
| // 是否正在刷新中 | ||||
| // const isRefreshToken = false | ||||
| let isRefreshToken = false | ||||
|  | ||||
| export const PATH_URL = base_url[import.meta.env.VITE_API_BASEPATH] | ||||
|  | ||||
| @@ -91,6 +92,7 @@ service.interceptors.request.use( | ||||
| service.interceptors.response.use( | ||||
|   async (response: AxiosResponse<Recordable>) => { | ||||
|     const { data } = response | ||||
|     console.info(data) | ||||
|     if (!data) { | ||||
|       // 返回“[HTTP]请求没有返回值”; | ||||
|       throw new Error() | ||||
| @@ -112,16 +114,38 @@ service.interceptors.response.use( | ||||
|       return Promise.reject(msg) | ||||
|     } else if (code === 401) { | ||||
|       // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了 | ||||
|       if (!isRefreshToken) { | ||||
|         isRefreshToken = true | ||||
|         // 1. 如果获取不到刷新令牌,则只能执行登出操作 | ||||
|         if (!getRefreshToken()) { | ||||
|           return handleAuthorized() | ||||
|       // if (!isRefreshToken) { | ||||
|       //   isRefreshToken = true | ||||
|       //   // 1. 如果获取不到刷新令牌,则只能执行登出操作 | ||||
|       //   if (!getRefreshToken()) { | ||||
|       //     return handleAuthorized() | ||||
|       //   } | ||||
|       //   // 2. 进行刷新访问令牌 | ||||
|       //   // TODO: 引入refreshToken会循环依赖报错 | ||||
|       // } | ||||
|         } | ||||
|         // 2. 进行刷新访问令牌 | ||||
|         try { | ||||
|           const refreshTokenRes = await refreshToken() | ||||
|           // 2.1 刷新成功,则回放队列的请求 + 当前请求 | ||||
|           setToken(refreshTokenRes.data) | ||||
|           requestList.forEach((cb: any) => cb()) | ||||
|           return service(response.config) | ||||
|         } catch (e) { | ||||
|           // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。 | ||||
|           // 2.2 刷新失败,只回放队列的请求 | ||||
|           requestList.forEach((cb: any) => cb()) | ||||
|           // 提示是否要登出。即不回放当前请求!不然会形成递归 | ||||
|           return handleAuthorized() | ||||
|         } finally { | ||||
|           requestList = [] | ||||
|           isRefreshToken = false | ||||
|         } | ||||
|       } else { | ||||
|         // 添加到队列,等待刷新获取到新的令牌 | ||||
|         return new Promise((resolve) => { | ||||
|           requestList.push(() => { | ||||
|             ;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改 | ||||
|             resolve(service(response.config)) | ||||
|           }) | ||||
|         }) | ||||
|       } | ||||
|     } else if (code === 500) { | ||||
|       ElMessage.error(t('sys.api.errMsg500')) | ||||
|       return Promise.reject(new Error(msg)) | ||||
| @@ -165,14 +189,32 @@ service.interceptors.response.use( | ||||
|     return Promise.reject(error) | ||||
|   } | ||||
| ) | ||||
|  | ||||
| const refreshToken = async () => { | ||||
|   return await service({ | ||||
|     url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken(), | ||||
|     method: 'post' | ||||
|   }) | ||||
| } | ||||
| const handleAuthorized = () => { | ||||
|   const { t } = useI18n() | ||||
|   if (!isRelogin.show) { | ||||
|     removeToken() | ||||
|     isRelogin.show = true | ||||
|     ElNotification.error(t('sys.api.timeoutMessage')) | ||||
|   } | ||||
|     ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), { | ||||
|       confirmButtonText: t('login.relogin'), | ||||
|       cancelButtonText: t('common.cancel'), | ||||
|       type: 'warning' | ||||
|     }) | ||||
|       .then(() => { | ||||
|         removeToken() | ||||
|         resetRouter() // 重置静态路由表 | ||||
|         isRelogin.show = false | ||||
|         location.href = '/' | ||||
|       }) | ||||
|       .catch(() => { | ||||
|         isRelogin.show = false | ||||
|       }) | ||||
|   } | ||||
|   return Promise.reject(t('sys.api.timeoutMessage')) | ||||
| } | ||||
| export { service } | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import { createRouter, createWebHashHistory } from 'vue-router' | ||||
| import { usePermissionStoreWithOut } from '@/store/modules/permission' | ||||
| import { useDictStoreWithOut } from '@/store/modules/dict' | ||||
| import { listSimpleDictDataApi } from '@/api/system/dict/dict.data' | ||||
| import { isRelogin } from '@/config/axios' | ||||
|  | ||||
| const permissionStore = usePermissionStoreWithOut() | ||||
|  | ||||
| @@ -48,14 +49,14 @@ router.beforeEach(async (to, from, next) => { | ||||
|       next({ path: '/' }) | ||||
|     } else { | ||||
|       if (!dictStore.getIsSetDict) { | ||||
|         isRelogin.show = true | ||||
|         // 获取所有字典 | ||||
|         const res = await listSimpleDictDataApi() | ||||
|         if (res) { | ||||
|         dictStore.setDictMap(res) | ||||
|         dictStore.setIsSetDict(true) | ||||
|       } | ||||
|       } | ||||
|       if (permissionStore.getIsAddRouters) { | ||||
|         isRelogin.show = false | ||||
|         next() | ||||
|         return | ||||
|       } | ||||
| @@ -77,7 +78,7 @@ router.beforeEach(async (to, from, next) => { | ||||
|     if (whiteList.indexOf(to.path) !== -1) { | ||||
|       next() | ||||
|     } else { | ||||
|       next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页 | ||||
|       next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 | ||||
|     } | ||||
|   } | ||||
| }) | ||||
|   | ||||
| @@ -6,12 +6,12 @@ import { ElRow, ElCol, ElUpload, ElMessage, ElDialog } from 'element-plus' | ||||
| import { propTypes } from '@/utils/propTypes' | ||||
| import { uploadAvatarApi } from '@/api/system/user/profile' | ||||
| const cropper = ref() | ||||
| const dialogVisible = ref(false) | ||||
| const cropperVisible = ref(false) | ||||
| const props = defineProps({ | ||||
|   img: propTypes.string.def('') | ||||
| }) | ||||
| const state = reactive({ | ||||
|   dialogVisible: false, | ||||
|   cropperVisible: false, | ||||
| const options = reactive({ | ||||
|   dialogTitle: '编辑头像', | ||||
|   options: { | ||||
|     img: props.img, //裁剪图片的地址 | ||||
| @@ -27,8 +27,11 @@ const state = reactive({ | ||||
| }) | ||||
| /** 编辑头像 */ | ||||
| const editCropper = () => { | ||||
|   state.dialogVisible = true | ||||
|   state.cropperVisible = true | ||||
|   dialogVisible.value = true | ||||
| } | ||||
| // 打开弹出层结束时的回调 | ||||
| const modalOpened = () => { | ||||
|   cropperVisible.value = true | ||||
| } | ||||
| /** 向左旋转 */ | ||||
| const rotateLeft = () => { | ||||
| @@ -44,7 +47,7 @@ const changeScale = (num: number) => { | ||||
|   cropper.value.changeScale(num) | ||||
| } | ||||
| // 覆盖默认的上传行为 | ||||
| const requestUpload = () => {} | ||||
| const requestUpload: any = () => {} | ||||
| /** 上传预处理 */ | ||||
| const beforeUpload = (file: Blob) => { | ||||
|   if (file.type.indexOf('image/') == -1) { | ||||
| @@ -54,64 +57,65 @@ const beforeUpload = (file: Blob) => { | ||||
|     reader.readAsDataURL(file) | ||||
|     reader.onload = () => { | ||||
|       if (reader.result) { | ||||
|         state.options.img = reader.result as string | ||||
|         options.options.img = reader.result as string | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| /** 上传图片 */ | ||||
| const uploadImg = () => { | ||||
|   cropper.value.getCropBlob((data) => { | ||||
|   cropper.value.getCropBlob((data: any) => { | ||||
|     let formData = new FormData() | ||||
|     formData.append('avatarfile', data) | ||||
|     uploadAvatarApi(formData) | ||||
|   }) | ||||
| } | ||||
| /** 实时预览 */ | ||||
| const realTime = (data) => { | ||||
|   state.previews = data | ||||
| const realTime = (data: any) => { | ||||
|   options.previews = data | ||||
| } | ||||
| watch( | ||||
|   () => props.img, | ||||
|   () => { | ||||
|     if (props.img) { | ||||
|       state.options.img = props.img | ||||
|       state.previews.img = props.img | ||||
|       state.previews.url = props.img | ||||
|       options.options.img = props.img | ||||
|       options.previews.img = props.img | ||||
|       options.previews.url = props.img | ||||
|     } | ||||
|   } | ||||
| ) | ||||
| </script> | ||||
| <template> | ||||
|   <div class="user-info-head" @click="editCropper()"> | ||||
|     <img :src="state.options.img" title="点击上传头像" class="img-circle img-lg" alt="" /> | ||||
|     <img :src="options.options.img" title="点击上传头像" class="img-circle img-lg" alt="" /> | ||||
|   </div> | ||||
|   <el-dialog | ||||
|     v-model="state.dialogVisible" | ||||
|     :title="state.dialogTitle" | ||||
|     width="50%" | ||||
|     :maxHeight="350" | ||||
|     v-model="dialogVisible" | ||||
|     :title="options.dialogTitle" | ||||
|     width="800px" | ||||
|     append-to-body | ||||
|     style="padding: 30px 20px" | ||||
|     @opened="modalOpened" | ||||
|   > | ||||
|     <el-row> | ||||
|       <el-col :xs="24" :md="12" style="height: 350px"> | ||||
|         <VueCropper | ||||
|           ref="cropper" | ||||
|           :img="state.options.img" | ||||
|           :img="options.options.img" | ||||
|           :info="true" | ||||
|           :autoCrop="state.options.autoCrop" | ||||
|           :autoCropWidth="state.options.autoCropWidth" | ||||
|           :autoCropHeight="state.options.autoCropHeight" | ||||
|           :fixedBox="state.options.fixedBox" | ||||
|           :autoCrop="options.options.autoCrop" | ||||
|           :autoCropWidth="options.options.autoCropWidth" | ||||
|           :autoCropHeight="options.options.autoCropHeight" | ||||
|           :fixedBox="options.options.fixedBox" | ||||
|           @real-time="realTime" | ||||
|           v-if="state.cropperVisible" | ||||
|           v-if="cropperVisible" | ||||
|         /> | ||||
|       </el-col> | ||||
|       <el-col :xs="24" :md="12" style="height: 350px"> | ||||
|         <div class="avatar-upload-preview"> | ||||
|           <img | ||||
|             :src="state.previews.url" | ||||
|             :style="state.previews.img" | ||||
|             :src="options.previews.url" | ||||
|             :style="options.previews.img" | ||||
|             style="!max-width: 100%" | ||||
|             alt="" | ||||
|           /> | ||||
|   | ||||
| @@ -47,7 +47,6 @@ const getTree = async () => { | ||||
|   const res = await MenuApi.listSimpleMenusApi() | ||||
|   const menu = { id: 0, name: '主类目', children: [] as any[] } | ||||
|   menu.children = handleTree(res) | ||||
|   console.info(menu) | ||||
|   menuOptions.value = menu | ||||
| } | ||||
| // ========== 查询 ========== | ||||
|   | ||||
| @@ -155,7 +155,7 @@ getList() | ||||
|     </Table> | ||||
|   </ContentWrap> | ||||
|  | ||||
|   <Dialog v-model="dialogVisible" :title="dialogTitle"> | ||||
|   <Dialog v-model="dialogVisible" :title="dialogTitle" width="60%" maxHeight="420px"> | ||||
|     <!-- 对话框(添加 / 修改) --> | ||||
|     <Form | ||||
|       v-if="['create', 'update'].includes(actionType)" | ||||
|   | ||||
| @@ -110,7 +110,7 @@ const defaultProps = { | ||||
|   label: 'name', | ||||
|   value: 'id' | ||||
| } | ||||
| const treeOptions = ref([]) // 菜单树形结构 | ||||
| const treeOptions = ref<any[]>([]) // 菜单树形结构 | ||||
| const treeRef = ref<InstanceType<typeof ElTree>>() | ||||
| const dialogScopeVisible = ref(false) | ||||
| const dialogScopeTitle = ref('数据权限') | ||||
| @@ -133,7 +133,6 @@ const handleScope = async (type: string, row: RoleVO) => { | ||||
|     const menuRes = await listSimpleMenusApi() | ||||
|     treeOptions.value = handleTree(menuRes) | ||||
|     const role = await PermissionApi.listRoleMenusApi(row.id) | ||||
|     console.info(role) | ||||
|     if (role) { | ||||
|       // treeRef.value!.setCheckedKeys(role as unknown as Array<number>) | ||||
|       defaultCheckedKeys.value = role | ||||
| @@ -142,7 +141,6 @@ const handleScope = async (type: string, row: RoleVO) => { | ||||
|     const deptRes = await listSimpleDeptApi() | ||||
|     treeOptions.value = handleTree(deptRes) | ||||
|     const role = await RoleApi.getRoleApi(row.id) | ||||
|     console.info(role) | ||||
|     dataScopeForm.dataScope = role.dataScope | ||||
|     if (role.dataScopeDeptIds) { | ||||
|       // treeRef.value!.setCheckedKeys(role.dataScopeDeptIds as unknown as Array<number>, false) | ||||
| @@ -155,7 +153,6 @@ const handleScope = async (type: string, row: RoleVO) => { | ||||
| // 保存权限 | ||||
| const submitScope = async () => { | ||||
|   const keys = treeRef.value!.getCheckedKeys(false) as unknown as Array<number> | ||||
|   console.info(keys) | ||||
|   if ('data' === actionScopeType.value) { | ||||
|     const data = ref<PermissionAssignRoleDataScopeReqVO>({ | ||||
|       roleId: dataScopeForm.id, | ||||
|   | ||||
| @@ -19,7 +19,7 @@ const defaultProps = { | ||||
|   value: 'id' | ||||
| } | ||||
| // ========== 创建菜单树结构 ========== | ||||
| const menuOptions = ref([]) // 树形结构 | ||||
| const menuOptions = ref<any[]>([]) // 树形结构 | ||||
| const treeRef = ref<InstanceType<typeof ElTree>>() | ||||
| const treeNodeAll = ref(false) | ||||
| // 全选/全不选 | ||||
| @@ -84,11 +84,9 @@ const submitForm = async () => { | ||||
|     if (actionType.value === 'create') { | ||||
|       await TenantPackageApi.createTenantPackageTypeApi(data) | ||||
|       ElMessage.success(t('common.createSuccess')) | ||||
|       console.log('new data') | ||||
|     } else { | ||||
|       await TenantPackageApi.updateTenantPackageTypeApi(data) | ||||
|       ElMessage.success(t('common.updateSuccess')) | ||||
|       console.log('edit data') | ||||
|     } | ||||
|     // 操作成功,重新加载列表 | ||||
|     dialogVisible.value = false | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 xingyu
					xingyu