From a003c9725a53e61497ef6acbe1b9643be857e9ac Mon Sep 17 00:00:00 2001 From: lijiahang Date: Thu, 2 Nov 2023 17:23:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9F=A5=E8=AF=A2=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BF=A1=E6=81=AF.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orion-ops-ui/src/api/interceptor.ts | 34 ++- orion-ops-ui/src/api/user/mine.ts | 12 +- orion-ops-ui/src/api/user/user.ts | 14 + .../src/components/app/navbar/index.vue | 4 +- .../host-identity/host-identity-selector.vue | 2 +- .../asset/host-key/host-key-selector.vue | 2 +- .../system/dict-key/dict-key-selector.vue | 9 +- .../menu/selector/menu-selector-tree.vue | 2 +- .../user/role/user-role-selector.vue | 9 +- .../components/user/role/user-selector.vue | 12 +- orion-ops-ui/src/store/modules/cache/index.ts | 5 + orion-ops-ui/src/store/modules/user/index.ts | 5 +- orion-ops-ui/src/types/form.ts | 4 + orion-ops-ui/src/types/symbol.ts | 3 + .../src/views/authentication/login/index.vue | 21 +- .../components/dict-value-table.vue | 2 +- .../menu/components/menu-tree-selector.vue | 2 +- .../user/info/components/login-history.vue | 46 ++- .../info/components/operator-log-list.vue | 274 ++++-------------- .../{user-info.vue => user-base-info.vue} | 42 ++- .../user/info/components/user-session.vue | 54 +++- orion-ops-ui/src/views/user/info/index.vue | 94 +++++- .../components/operator-log-query-header.vue | 6 +- .../components/operator-log-table.vue | 27 +- .../src/views/user/operator-log/index.vue | 1 - .../views/user/user/components/user-table.vue | 16 + .../views/user/user/types/table.columns.ts | 2 +- 27 files changed, 402 insertions(+), 302 deletions(-) create mode 100644 orion-ops-ui/src/types/form.ts rename orion-ops-ui/src/views/user/info/components/{user-info.vue => user-base-info.vue} (71%) diff --git a/orion-ops-ui/src/api/interceptor.ts b/orion-ops-ui/src/api/interceptor.ts index 55de6807..c4b491eb 100644 --- a/orion-ops-ui/src/api/interceptor.ts +++ b/orion-ops-ui/src/api/interceptor.ts @@ -1,8 +1,9 @@ import type { AxiosRequestConfig, AxiosResponse } from 'axios'; import axios from 'axios'; -import { Message, Notification } from '@arco-design/web-vue'; +import { Message } from '@arco-design/web-vue'; import { useUserStore } from '@/store'; import { getToken } from '@/utils/auth'; +import { reLoginTipsKey } from '@/types/symbol'; export interface HttpResponse { msg: string; @@ -45,25 +46,28 @@ axios.interceptors.response.use( if (code === 200) { return res; } - // 非 200 业务异常 - if (response.config.promptBizErrorMessage) { + // 异常判断 + if ([401, 700, 701, 702, 703].includes(code)) { + // 提示 Message.error({ content: res.msg || 'Error', duration: 5 * 1000, }); - } - // 业务判断 - if ([401, 700, 701, 702].includes(code)) { - Notification.error({ - closable: true, - content: res.msg, + // 认证异常 + setTimeout(async () => { + // 设置错误信息 登录页面重新提示 (重新加载会刷掉提示) + window.sessionStorage.setItem(reLoginTipsKey, res.msg); + // 登出 + await useUserStore().logout(); + // 重新加载自动跳转登录页面 + window.location.reload(); }); - // 非登录页面跳转登录页面 - if (response.config.url !== '/infra/auth/login') { - setTimeout(async () => { - // 登出 - await useUserStore().logout(); - window.location.reload(); + } else { + // 其他异常 判断是否弹出错误信息 + if (response.config.promptBizErrorMessage) { + Message.error({ + content: res.msg || 'Error', + duration: 5 * 1000, }); } } diff --git a/orion-ops-ui/src/api/user/mine.ts b/orion-ops-ui/src/api/user/mine.ts index c232c056..997a848b 100644 --- a/orion-ops-ui/src/api/user/mine.ts +++ b/orion-ops-ui/src/api/user/mine.ts @@ -1,5 +1,6 @@ -import type { LoginHistoryQueryResponse } from './operator-log'; -import type { UserQueryResponse, UserSessionQueryResponse, UserSessionOfflineRequest, UserUpdateRequest } from './user'; +import type { DataGrid } from '@/types/global'; +import type { LoginHistoryQueryResponse, OperatorLogQueryRequest, OperatorLogQueryResponse } from './operator-log'; +import type { UserQueryResponse, UserSessionOfflineRequest, UserSessionQueryResponse, UserUpdateRequest } from './user'; import axios from 'axios'; /** @@ -51,3 +52,10 @@ export function getCurrentUserSessionList() { export function offlineCurrentUserSession(request: UserSessionOfflineRequest) { return axios.put('/infra/mine/offline-session', request); } + +/** + * 查询当前用户操作日志 + */ +export function getCurrentUserOperatorLog(request: OperatorLogQueryRequest) { + return axios.post>('/infra/mine/query-operator-log', request); +} diff --git a/orion-ops-ui/src/api/user/user.ts b/orion-ops-ui/src/api/user/user.ts index a1bfc611..a9f95358 100644 --- a/orion-ops-ui/src/api/user/user.ts +++ b/orion-ops-ui/src/api/user/user.ts @@ -19,6 +19,7 @@ export interface UserCreateRequest { */ export interface UserUpdateRequest extends UserCreateRequest { id?: number; + status?: number; roleIdList?: Array; password?: string; } @@ -146,3 +147,16 @@ export function deleteUser(id: number) { return axios.delete('/infra/system-user/delete', { params: { id } }); } +/** + * 获取用户会话列表 + */ +export function getUserSessionList(id: number) { + return axios.get>('/infra/system-user/user-session', { params: { id } }); +} + +/** + * 下线用户会话 + */ +export function offlineUserSession(request: UserSessionOfflineRequest) { + return axios.put('/infra/system-user/offline-session', request); +} diff --git a/orion-ops-ui/src/components/app/navbar/index.vue b/orion-ops-ui/src/components/app/navbar/index.vue index 77dc8912..4986af5c 100644 --- a/orion-ops-ui/src/components/app/navbar/index.vue +++ b/orion-ops-ui/src/components/app/navbar/index.vue @@ -255,8 +255,8 @@ }; // 退出登录 - const handleLogout = () => { - logout(); + const handleLogout = async () => { + await logout(); }; // 关闭偏好提示 diff --git a/orion-ops-ui/src/components/asset/host-identity/host-identity-selector.vue b/orion-ops-ui/src/components/asset/host-identity/host-identity-selector.vue index dacc6451..a8302287 100644 --- a/orion-ops-ui/src/components/asset/host-identity/host-identity-selector.vue +++ b/orion-ops-ui/src/components/asset/host-identity/host-identity-selector.vue @@ -47,6 +47,6 @@ }; - diff --git a/orion-ops-ui/src/components/asset/host-key/host-key-selector.vue b/orion-ops-ui/src/components/asset/host-key/host-key-selector.vue index c9370868..059dd58b 100644 --- a/orion-ops-ui/src/components/asset/host-key/host-key-selector.vue +++ b/orion-ops-ui/src/components/asset/host-key/host-key-selector.vue @@ -47,6 +47,6 @@ }; - diff --git a/orion-ops-ui/src/components/system/dict-key/dict-key-selector.vue b/orion-ops-ui/src/components/system/dict-key/dict-key-selector.vue index 7ba09db3..6b359c33 100644 --- a/orion-ops-ui/src/components/system/dict-key/dict-key-selector.vue +++ b/orion-ops-ui/src/components/system/dict-key/dict-key-selector.vue @@ -4,7 +4,7 @@ :allow-search="true" :loading="loading" :disabled="loading" - :filter-option="filterOption" + :filter-option="labelFilter" :allow-create="allowCreate" placeholder="请选择配置项" /> @@ -19,6 +19,7 @@ import type { SelectOptionData } from '@arco-design/web-vue'; import { computed } from 'vue'; import { useCacheStore } from '@/store'; + import { labelFilter } from '@/types/form'; const props = defineProps({ modelValue: Number, @@ -65,12 +66,8 @@ }); }; - // 搜索 - const filterOption = (searchValue: string, option: { label: string; }) => { - return option.label.toLowerCase().indexOf(searchValue.toLowerCase()) > -1; - }; - diff --git a/orion-ops-ui/src/components/system/menu/selector/menu-selector-tree.vue b/orion-ops-ui/src/components/system/menu/selector/menu-selector-tree.vue index 5131a2ca..4627166f 100644 --- a/orion-ops-ui/src/components/system/menu/selector/menu-selector-tree.vue +++ b/orion-ops-ui/src/components/system/menu/selector/menu-selector-tree.vue @@ -143,6 +143,6 @@ -; diff --git a/orion-ops-ui/src/components/user/role/user-role-selector.vue b/orion-ops-ui/src/components/user/role/user-role-selector.vue index 78e169ad..dfd1d859 100644 --- a/orion-ops-ui/src/components/user/role/user-role-selector.vue +++ b/orion-ops-ui/src/components/user/role/user-role-selector.vue @@ -5,7 +5,7 @@ :multiple="multiple" :loading="loading" :disabled="loading" - :filter-option="filterOption" + :filter-option="labelFilter" placeholder="请选择角色" /> @@ -21,6 +21,7 @@ import { computed } from 'vue'; import { useCacheStore } from '@/store'; import { RoleStatus } from '@/views/user/role/types/const'; + import { labelFilter } from '@/types/form'; const props = defineProps({ modelValue: [Number, Array] as PropType>, @@ -51,12 +52,8 @@ }); }; - // 搜索 - const filterOption = (searchValue: string, option: { label: string; }) => { - return option.label.toLowerCase().indexOf(searchValue.toLowerCase()) > -1; - }; - diff --git a/orion-ops-ui/src/components/user/role/user-selector.vue b/orion-ops-ui/src/components/user/role/user-selector.vue index 13f47191..ce4c5364 100644 --- a/orion-ops-ui/src/components/user/role/user-selector.vue +++ b/orion-ops-ui/src/components/user/role/user-selector.vue @@ -5,7 +5,7 @@ :multiple="multiple" :loading="loading" :disabled="loading" - :filter-option="filterOption" + :filter-option="labelFilter" placeholder="请选择用户" /> @@ -20,7 +20,7 @@ import type { SelectOptionData } from '@arco-design/web-vue'; import { computed } from 'vue'; import { useCacheStore } from '@/store'; - import { RoleStatus } from '@/views/user/role/types/const'; + import { labelFilter } from '@/types/form'; const props = defineProps({ modelValue: [Number, Array] as PropType>, @@ -38,7 +38,6 @@ emits('update:modelValue', e); } }); - // 选项数据 const cacheStore = useCacheStore(); const optionData = (): SelectOptionData[] => { @@ -49,13 +48,8 @@ }; }); }; - - // 搜索 - const filterOption = (searchValue: string, option: { label: string; }) => { - return option.label.toLowerCase().indexOf(searchValue.toLowerCase()) > -1; - }; - diff --git a/orion-ops-ui/src/store/modules/cache/index.ts b/orion-ops-ui/src/store/modules/cache/index.ts index 126a23af..4ebb8be4 100644 --- a/orion-ops-ui/src/store/modules/cache/index.ts +++ b/orion-ops-ui/src/store/modules/cache/index.ts @@ -27,6 +27,11 @@ export default defineStore('cache', { for (let name of names) { this[name] = []; } + }, + + // 清除全部 + clear() { + this.$reset(); } }, }); diff --git a/orion-ops-ui/src/store/modules/user/index.ts b/orion-ops-ui/src/store/modules/user/index.ts index 81fe4c11..11853c24 100644 --- a/orion-ops-ui/src/store/modules/user/index.ts +++ b/orion-ops-ui/src/store/modules/user/index.ts @@ -5,7 +5,7 @@ import { getUserPermission, login as userLogin, logout as userLogout } from '@/a import { clearToken, setToken } from '@/utils/auth'; import { md5 } from '@/utils'; import { removeRouteListener } from '@/utils/route-listener'; -import { useAppStore, useMenuStore, useTabBarStore, useTipsStore } from '@/store'; +import { useAppStore, useCacheStore, useMenuStore, useTabBarStore, useTipsStore } from '@/store'; export default defineStore('user', { state: (): UserState => ({ @@ -89,6 +89,9 @@ export default defineStore('user', { // 清除 tabs const tabBarStore = useTabBarStore(); tabBarStore.resetTabList(); + // 清除缓存 + const cacheStore = useCacheStore(); + cacheStore.clear(); }, }, }); diff --git a/orion-ops-ui/src/types/form.ts b/orion-ops-ui/src/types/form.ts new file mode 100644 index 00000000..336256de --- /dev/null +++ b/orion-ops-ui/src/types/form.ts @@ -0,0 +1,4 @@ +// 通过 label 进行过滤 +export const labelFilter = (searchValue: string, option: { label: string; }) => { + return option.label.toLowerCase().indexOf(searchValue.toLowerCase()) > -1; +}; diff --git a/orion-ops-ui/src/types/symbol.ts b/orion-ops-ui/src/types/symbol.ts index b56626b2..db61d91d 100644 --- a/orion-ops-ui/src/types/symbol.ts +++ b/orion-ops-ui/src/types/symbol.ts @@ -3,3 +3,6 @@ export const toggleDrawerMenuKey = Symbol(); // 打开偏好设置 export const openAppSettingKey = Symbol(); + +// 重新登录的提示 +export const reLoginTipsKey = 're-login-tips'; diff --git a/orion-ops-ui/src/views/authentication/login/index.vue b/orion-ops-ui/src/views/authentication/login/index.vue index b77cde59..b5ece70b 100644 --- a/orion-ops-ui/src/views/authentication/login/index.vue +++ b/orion-ops-ui/src/views/authentication/login/index.vue @@ -1,14 +1,14 @@