refactor: 优化数据加载逻辑.

This commit is contained in:
lijiahangmax
2023-12-02 23:36:02 +08:00
parent 9f35fb855d
commit a22f30a8b4
19 changed files with 155 additions and 117 deletions

View File

@@ -10,7 +10,7 @@
<span title="当前版本">v{{ version }}</span> <span title="当前版本">v{{ version }}</span>
</a-space> </a-space>
<span class="copyright"> <span class="copyright">
Copyright<icon-copyright /> 2023 By OrionOpsPro Copyright<icon-copyright /> 2023 By lijiahang
</span> </span>
</a-space> </a-space>
</a-layout-footer> </a-layout-footer>

View File

@@ -206,12 +206,12 @@
import { useDark, useFullscreen, useToggle } from '@vueuse/core'; import { useDark, useFullscreen, useToggle } from '@vueuse/core';
import { useAppStore, useTabBarStore, useTipsStore, useUserStore } from '@/store'; import { useAppStore, useTabBarStore, useTipsStore, useUserStore } from '@/store';
import { LOCALE_OPTIONS } from '@/locale'; import { LOCALE_OPTIONS } from '@/locale';
import { triggerMouseEvent } from '@/utils'; import { triggerMouseEvent } from '@/utils/event';
import { openAppSettingKey, toggleDrawerMenuKey } from '@/types/symbol'; import { openAppSettingKey, toggleDrawerMenuKey } from '@/types/symbol';
import { preferenceTipsKey } from './const'; import { preferenceTipsKey } from './const';
import { REDIRECT_ROUTE_NAME, routerToTag } from '@/router/constants'; import { REDIRECT_ROUTE_NAME, routerToTag } from '@/router/constants';
import Menu from '@/components/system/menu/tree/index.vue'; import Menu from '@/components/system/menu/tree/index.vue';
import UpdatePasswordModal from '@/components/user/role/update-password-modal.vue'; import UpdatePasswordModal from '@/components/user/user/update-password-modal.vue';
import MessageBox from '@/components/system/message-box/index.vue'; import MessageBox from '@/components/system/message-box/index.vue';
const tipsStore = useTipsStore(); const tipsStore = useTipsStore();

View File

@@ -45,12 +45,14 @@
// //
onBeforeMount(() => { onBeforeMount(() => {
optionData.value = cacheStore.roles.map(s => { cacheStore.loadRoles().then(roles => {
return { optionData.value = roles.map(s => {
label: `${s.name} (${s.code})`, return {
disabled: s.status === RoleStatus.DISABLED, label: `${s.name} (${s.code})`,
value: s.id, disabled: s.status === RoleStatus.DISABLED,
}; value: s.id,
};
});
}); });
}); });

View File

@@ -18,7 +18,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import type { SelectOptionData } from '@arco-design/web-vue'; import type { SelectOptionData } from '@arco-design/web-vue';
import { computed, ref, onBeforeMount } from 'vue'; import { computed, ref, onMounted } from 'vue';
import { useCacheStore } from '@/store'; import { useCacheStore } from '@/store';
import { labelFilter } from '@/types/form'; import { labelFilter } from '@/types/form';
@@ -43,12 +43,15 @@
const optionData = ref<Array<SelectOptionData>>([]); const optionData = ref<Array<SelectOptionData>>([]);
// //
onBeforeMount(() => { onMounted(() => {
optionData.value = cacheStore.users.map(s => { //
return { cacheStore.loadUsers().then(users => {
label: `${s.nickname} (${s.username})`, optionData.value = users.map(s => {
value: s.id, return {
}; label: `${s.nickname} (${s.username})`,
value: s.id,
};
});
}); });
}); });

View File

@@ -14,8 +14,8 @@
v-model:page-size="(pagination as PaginationProps).pageSize" v-model:page-size="(pagination as PaginationProps).pageSize"
v-bind="pagination" v-bind="pagination"
:auto-adjust="false" :auto-adjust="false"
@change="page => emits('emitter', HeaderEmitter.PAGE_CHANGE, page, (pagination as PaginationProps).pageSize)" @change="page => bubblesEmitter(HeaderEmitter.PAGE_CHANGE, page, (pagination as PaginationProps).pageSize)"
@page-size-change="limit => emits('emitter', HeaderEmitter.PAGE_CHANGE, 1, limit)" /> @page-size-change="limit => bubblesEmitter(HeaderEmitter.PAGE_CHANGE, 1, limit)" />
</div> </div>
</div> </div>
<!-- 操作部分 --> <!-- 操作部分 -->
@@ -28,7 +28,7 @@
v-if="!handleVisible?.disableAdd" v-if="!handleVisible?.disableAdd"
class="click-icon-wrapper card-header-icon-wrapper" class="click-icon-wrapper card-header-icon-wrapper"
title="创建" title="创建"
@click="emits('emitter', HeaderEmitter.ADD)"> @click="bubblesEmitter(HeaderEmitter.ADD)">
<icon-plus /> <icon-plus />
</div> </div>
<!-- 左侧侧操作槽位 --> <!-- 左侧侧操作槽位 -->
@@ -48,9 +48,9 @@
:placeholder="searchInputPlaceholder as string" :placeholder="searchInputPlaceholder as string"
size="small" size="small"
allow-clear allow-clear
@input="e => emits('emitter', HeaderEmitter.UPDATE_SEARCH_VALUE, e)" @input="e => bubblesEmitter(HeaderEmitter.UPDATE_SEARCH_VALUE, e)"
@change="e => emits('emitter', HeaderEmitter.UPDATE_SEARCH_VALUE, e)" @change="e => bubblesEmitter(HeaderEmitter.UPDATE_SEARCH_VALUE, e)"
@keyup.enter="emits('emitter', HeaderEmitter.SEARCH)" /> @keyup.enter="bubblesEmitter(HeaderEmitter.SEARCH)" />
</div> </div>
<!-- 过滤条件 --> <!-- 过滤条件 -->
<a-popover position="br" trigger="click" content-class="card-filter-wrapper"> <a-popover position="br" trigger="click" content-class="card-filter-wrapper">
@@ -78,14 +78,14 @@
<div v-if="!handleVisible?.disableSearch" <div v-if="!handleVisible?.disableSearch"
class="click-icon-wrapper card-header-icon-wrapper" class="click-icon-wrapper card-header-icon-wrapper"
title="搜索" title="搜索"
@click="emits('emitter', HeaderEmitter.SEARCH)"> @click="bubblesEmitter(HeaderEmitter.SEARCH)">
<icon-search /> <icon-search />
</div> </div>
<!-- 重置 --> <!-- 重置 -->
<div v-if="!handleVisible?.disableReset" <div v-if="!handleVisible?.disableReset"
class="click-icon-wrapper card-header-icon-wrapper" class="click-icon-wrapper card-header-icon-wrapper"
title="重置" title="重置"
@click="emits('emitter', HeaderEmitter.RESET)"> @click="bubblesEmitter(HeaderEmitter.RESET)">
<icon-refresh /> <icon-refresh />
</div> </div>
</a-space> </a-space>
@@ -106,12 +106,15 @@
import type { CardProps } from '../types/props'; import type { CardProps } from '../types/props';
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { useAppStore } from '@/store'; import { useAppStore } from '@/store';
import { triggerMouseEvent } from '@/utils'; import { triggerMouseEvent } from '@/utils/event';
import { HeaderEmitter } from '../types/emits'; import { HeaderEmitter } from '../types/emits';
import useEmitter from '@/hooks/emitter';
const props = defineProps<CardProps>(); const props = defineProps<CardProps>();
const emits = defineEmits(['emitter']); const emits = defineEmits(['emitter']);
const { bubblesEmitter } = useEmitter(emits);
const appStore = useAppStore(); const appStore = useAppStore();
const filterRef = ref(); const filterRef = ref();
@@ -130,21 +133,21 @@
}, },
set(e) { set(e) {
if (e) { if (e) {
emits('emitter', HeaderEmitter.UPDATE_SEARCH_VALUE, e); bubblesEmitter(HeaderEmitter.UPDATE_SEARCH_VALUE, e);
} else { } else {
emits('emitter', HeaderEmitter.UPDATE_SEARCH_VALUE, null); bubblesEmitter(HeaderEmitter.UPDATE_SEARCH_VALUE, null);
} }
} }
}); });
// 重置过滤 // 重置过滤
const filterReset = () => { const filterReset = () => {
emits('emitter', HeaderEmitter.RESET); bubblesEmitter(HeaderEmitter.RESET);
}; };
// 搜索 // 搜索
const filterSearch = () => { const filterSearch = () => {
emits('emitter', HeaderEmitter.SEARCH); bubblesEmitter(HeaderEmitter.SEARCH);
triggerMouseEvent(filterRef); triggerMouseEvent(filterRef);
}; };
@@ -156,7 +159,7 @@
} }
.card-list-header { .card-list-header {
margin: -17px -16px 0 -16px; margin: -17px -16px 0 -15px;
padding: 16px 16px 12px 16px; padding: 16px 16px 12px 16px;
position: fixed; position: fixed;
background: var(--color-fill-2); background: var(--color-fill-2);
@@ -165,7 +168,7 @@
transition: none; transition: none;
&-wrapper { &-wrapper {
background: var(--color-bg-4); background: var(--color-bg-2);
padding: 0 12px; padding: 0 12px;
border-radius: 4px; border-radius: 4px;

View File

@@ -10,8 +10,8 @@
:hoverable="true" :hoverable="true"
:body-style="cardBodyStyle as Record<string, any>" :body-style="cardBodyStyle as Record<string, any>"
@contextmenu.prevent="() => false" @contextmenu.prevent="() => false"
@click="emits('emitter', CardEmitter.CLICK, item, index)" @click="bubblesEmitter(CardEmitter.CLICK, item, index)"
@dblclick="emits('emitter', CardEmitter.DBL_CLICK, item, index)"> @dblclick="bubblesEmitter(CardEmitter.DBL_CLICK, item, index)">
<!-- 标题 --> <!-- 标题 -->
<template #title> <template #title>
<slot name="title" /> <slot name="title" />
@@ -83,14 +83,16 @@
import type { CardRecord } from '@/types/card'; import type { CardRecord } from '@/types/card';
import type { CardProps } from '../types/props'; import type { CardProps } from '../types/props';
import { CardEmitter } from '../types/emits'; import { CardEmitter } from '../types/emits';
import useEmitter from '@/hooks/emitter';
const props = defineProps<CardProps & { const props = defineProps<CardProps & {
index: number, index: number,
item: CardRecord item: CardRecord
}>(); }>();
const emits = defineEmits(['emitter']); const emits = defineEmits(['emitter']);
const { bubblesEmitter } = useEmitter(emits);
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@@ -1,8 +1,7 @@
<template> <template>
<div class="card-list-layout"> <div class="card-list-layout">
<!-- 头部部分-固定 --> <!-- 头部部分-固定 -->
<card-header v-bind="props" <card-header v-bind="props" @emitter="dispatchEmitter">
@emitter="dispatchEmitter">
<!-- 左侧侧操作槽位 --> <!-- 左侧侧操作槽位 -->
<template #leftHandle> <template #leftHandle>
<slot name="leftHandle" /> <slot name="leftHandle" />
@@ -80,8 +79,7 @@
import CardItem from './components/card-item.vue'; import CardItem from './components/card-item.vue';
import { Emitter } from './types/emits'; import { Emitter } from './types/emits';
import { CardProps } from './types/props'; import { CardProps } from './types/props';
import useEmitter from '@/hooks/emitter';
const emits = defineEmits(Emitter);
const props = withDefaults(defineProps<CardProps>(), { const props = withDefaults(defineProps<CardProps>(), {
key: 'id', key: 'id',
@@ -107,10 +105,8 @@
list: () => [] list: () => []
}); });
// 调度事件 const emits = defineEmits(Emitter);
const dispatchEmitter = (event: string, ...args: any) => { const { dispatchEmitter } = useEmitter(emits);
emits(event, ...args);
};
</script> </script>

View File

@@ -0,0 +1,26 @@
const emitter = 'emitter';
export default function useEmitter(emits: any) {
// 事件向上冒泡
const bubblesEmitter = (event: string, ...args: any[]) => {
if (args) {
emits(emitter, event, ...args);
} else {
emits(emitter, event);
}
};
// 处理冒泡事件
const dispatchEmitter = (event: string, ...args: any[]) => {
if (args) {
emits(event, ...args);
} else {
emits(event);
}
};
return {
bubblesEmitter,
dispatchEmitter
};
}

View File

@@ -1,5 +1,8 @@
import type { CacheState } from './types'; import type { CacheState } from './types';
import type { AxiosResponse } from 'axios';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { getUserList } from '@/api/user/user';
import { getRoleList } from '@/api/user/role';
export type CacheType = 'users' | 'menus' | 'roles' export type CacheType = 'users' | 'menus' | 'roles'
| 'host' | 'hostGroups' | 'hostTags' | 'hostKeys' | 'hostIdentities' | 'host' | 'hostGroups' | 'hostTags' | 'hostKeys' | 'hostIdentities'
@@ -7,7 +10,6 @@ export type CacheType = 'users' | 'menus' | 'roles'
export default defineStore('cache', { export default defineStore('cache', {
state: (): CacheState => ({ state: (): CacheState => ({
users: [],
menus: [], menus: [],
roles: [], roles: [],
hosts: [], hosts: [],
@@ -29,13 +31,40 @@ export default defineStore('cache', {
// 清空 // 清空
reset(...names: CacheType[]) { reset(...names: CacheType[]) {
for (let name of names) { for (let name of names) {
this[name] = []; this[name] = undefined;
} }
}, },
// 清除全部 // 清除全部
clear() { clear() {
this.$reset(); this.$reset();
} },
},
// 加载数据
async load<T>(name: CacheType, loader: () => Promise<AxiosResponse<T>>, onErrorValue = []) {
// 尝试直接从缓存中获取数据
if (this[name]) {
return this[name] as T;
}
// 加载数据
try {
const { data } = await loader();
this[name] = data;
return this[name] as T;
} catch (e) {
return onErrorValue as T;
}
},
// 获取用户列表
async loadUsers() {
return await this.load('users', getUserList);
},
// 获取角色列表
async loadRoles() {
return await this.load('roles', getRoleList);
},
}
}); });

View File

@@ -9,15 +9,15 @@ import type { HostIdentityQueryResponse } from '@/api/asset/host-identity';
import type { DictKeyQueryResponse } from '@/api/system/dict-key'; import type { DictKeyQueryResponse } from '@/api/system/dict-key';
export interface CacheState { export interface CacheState {
users: UserQueryResponse[]; users?: UserQueryResponse[];
menus: MenuQueryResponse[]; menus?: MenuQueryResponse[];
roles: RoleQueryResponse[]; roles?: RoleQueryResponse[];
hostTags: TagQueryResponse[]; hostTags?: TagQueryResponse[];
hosts: HostQueryResponse[]; hosts?: HostQueryResponse[];
hostGroups: HostGroupQueryResponse[]; hostGroups?: HostGroupQueryResponse[];
hostKeys: HostKeyQueryResponse[]; hostKeys?: HostKeyQueryResponse[];
hostIdentities: HostIdentityQueryResponse[]; hostIdentities?: HostIdentityQueryResponse[];
dictKeys: DictKeyQueryResponse[]; dictKeys?: DictKeyQueryResponse[];
[key: string]: unknown; [key: string]: unknown;
} }

View File

@@ -1,3 +1,6 @@
// 添加事件监听器
import { Ref } from 'vue';
export function addEventListen( export function addEventListen(
target: Window | HTMLElement, target: Window | HTMLElement,
event: string, event: string,
@@ -12,6 +15,7 @@ export function addEventListen(
} }
} }
// 移除事件监听器
export function removeEventListen( export function removeEventListen(
target: Window | HTMLElement, target: Window | HTMLElement,
event: string, event: string,
@@ -25,3 +29,13 @@ export function removeEventListen(
target.removeEventListener(event, handler, capture); target.removeEventListener(event, handler, capture);
} }
} }
// 触发鼠标事件
export const triggerMouseEvent = (ref: Ref, e = 'click') => {
const event = new MouseEvent(e, {
view: window,
bubbles: true,
cancelable: true,
});
ref.value.dispatchEvent(event);
};

View File

@@ -1,4 +1,3 @@
import type { Ref } from 'vue';
import { Md5 } from 'ts-md5'; import { Md5 } from 'ts-md5';
type TargetContext = '_self' | '_parent' | '_blank' | '_top'; type TargetContext = '_self' | '_parent' | '_blank' | '_top';
@@ -31,18 +30,6 @@ export const regexUrl = new RegExp(
'i' 'i'
); );
/**
* 触发鼠标事件
*/
export const triggerMouseEvent = (ref: Ref, e = 'click') => {
const event = new MouseEvent(e, {
view: window,
bubbles: true,
cancelable: true,
});
ref.value.dispatchEvent(event);
};
/** /**
* md5 * md5
*/ */

View File

@@ -31,7 +31,6 @@
import type { TabRouterItem } from '@/components/view/tab-router/types'; import type { TabRouterItem } from '@/components/view/tab-router/types';
import { computed, onMounted, ref } from 'vue'; import { computed, onMounted, ref } from 'vue';
import { useCacheStore } from '@/store'; import { useCacheStore } from '@/store';
import { getRoleList } from '@/api/user/role';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
@@ -59,9 +58,14 @@
const loadRoleList = async () => { const loadRoleList = async () => {
setLoading(true); setLoading(true);
try { try {
const { data } = await getRoleList(); const roles = await cacheStore.loadRoles();
// 设置到缓存 rolesRouter.value = roles.map(s => {
cacheStore.set('roles', data); return {
key: s.id,
text: `${s.name} (${s.code})`,
code: s.code
};
});
} catch (e) { } catch (e) {
Message.error('角色列表加载失败'); Message.error('角色列表加载失败');
} finally { } finally {
@@ -69,18 +73,9 @@
} }
}; };
// 加载主机 // 加载角色列表
onMounted(async () => { onMounted(() => {
if (!cacheStore.roles.length) { loadRoleList();
await loadRoleList();
}
rolesRouter.value = cacheStore.roles.map(s => {
return {
key: s.id,
text: `${s.name} (${s.code})`,
code: s.code
};
});
}); });
</script> </script>

View File

@@ -31,7 +31,6 @@
import type { TabRouterItem } from '@/components/view/tab-router/types'; import type { TabRouterItem } from '@/components/view/tab-router/types';
import { computed, onMounted, ref } from 'vue'; import { computed, onMounted, ref } from 'vue';
import { useCacheStore } from '@/store'; import { useCacheStore } from '@/store';
import { getUserList } from '@/api/user/user';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
@@ -59,9 +58,13 @@
const loadUserList = async () => { const loadUserList = async () => {
setLoading(true); setLoading(true);
try { try {
const { data } = await getUserList(); const users = await cacheStore.loadUsers();
// 设置到缓存 usersRouter.value = users.map(s => {
cacheStore.set('users', data); return {
key: s.id,
text: `${s.nickname} (${s.username})`
};
});
} catch (e) { } catch (e) {
Message.error('用户列表加载失败'); Message.error('用户列表加载失败');
} finally { } finally {
@@ -69,17 +72,9 @@
} }
}; };
// 加载主机 // 加载用户列表
onMounted(async () => { onMounted(() => {
if (!cacheStore.users.length) { loadUserList();
await loadUserList();
}
usersRouter.value = cacheStore.users.map(s => {
return {
key: s.id,
text: `${s.nickname} (${s.username})`
};
});
}); });
</script> </script>

View File

@@ -69,7 +69,7 @@
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import { useDictStore } from '@/store'; import { useDictStore } from '@/store';
import UserSelector from '@/components/user/role/user-selector.vue'; import UserSelector from '@/components/user/user/user-selector.vue';
import { operatorLogModuleKey, operatorLogTypeKey, operatorRiskLevelKey, operatorLogResultKey } from '../types/const'; import { operatorLogModuleKey, operatorLogTypeKey, operatorRiskLevelKey, operatorLogResultKey } from '../types/const';
import { labelFilter } from '@/types/form'; import { labelFilter } from '@/types/form';

View File

@@ -34,7 +34,6 @@
import { ref, onBeforeMount, onUnmounted } from 'vue'; import { ref, onBeforeMount, onUnmounted } from 'vue';
import { useCacheStore, useDictStore } from '@/store'; import { useCacheStore, useDictStore } from '@/store';
import { dictKeys } from './types/const'; import { dictKeys } from './types/const';
import { getUserList } from '@/api/user/user';
import OperatorLogQueryHeader from './components/operator-log-query-header.vue'; import OperatorLogQueryHeader from './components/operator-log-query-header.vue';
import OperatorLogTable from './components/operator-log-table.vue'; import OperatorLogTable from './components/operator-log-table.vue';
import JsonViewModal from '@/components/view/json/json-view-modal.vue'; import JsonViewModal from '@/components/view/json/json-view-modal.vue';
@@ -45,18 +44,10 @@
const table = ref(); const table = ref();
const view = ref(); const view = ref();
// 加载全部用户列表
const fetchUserList = async () => {
const { data } = await getUserList();
cacheStore.set('users', data);
};
onBeforeMount(async () => { onBeforeMount(async () => {
// 加载字典值 // 加载字典值
const dictStore = useDictStore(); const dictStore = useDictStore();
await dictStore.loadKeys(dictKeys); await dictStore.loadKeys(dictKeys);
// 加载用户列表
await fetchUserList();
render.value = true; render.value = true;
}); });

View File

@@ -29,7 +29,7 @@
</a-form-item> </a-form-item>
<!-- 角色 --> <!-- 角色 -->
<a-form-item field="roles" label="角色"> <a-form-item field="roles" label="角色">
<user-role-selector v-model="formModel.roleIdList" <role-selector v-model="formModel.roleIdList"
:loading="roleLoading" :loading="roleLoading"
:multiple="true" /> :multiple="true" />
</a-form-item> </a-form-item>
@@ -50,8 +50,7 @@
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible'; import useVisible from '@/hooks/visible';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import UserRoleSelector from '@/components/user/role/user-role-selector.vue'; import RoleSelector from '@/components/user/role/role-selector.vue';
import { getRoleList } from '@/api/user/role';
import { useCacheStore } from '@/store'; import { useCacheStore } from '@/store';
import { getUserRoleIdList, grantUserRole } from '@/api/user/user'; import { getUserRoleIdList, grantUserRole } from '@/api/user/user';
@@ -84,11 +83,6 @@
const loadRoles = async () => { const loadRoles = async () => {
try { try {
setRoleLoading(true); setRoleLoading(true);
// 获取全部角色
if (!cacheStore.roles?.length) {
const { data } = await getRoleList();
cacheStore.set('roles', data);
}
// 加载用户角色 // 加载用户角色
const { data: roleIdList } = await getUserRoleIdList(formModel.value.id as number); const { data: roleIdList } = await getUserRoleIdList(formModel.value.id as number);
formModel.value.roleIdList = roleIdList; formModel.value.roleIdList = roleIdList;

View File

@@ -39,6 +39,7 @@
const grantRoleModal = ref(); const grantRoleModal = ref();
onBeforeMount(async () => { onBeforeMount(async () => {
// 加载字典值
const dictStore = useDictStore(); const dictStore = useDictStore();
await dictStore.loadKeys(dictKeys); await dictStore.loadKeys(dictKeys);
render.value = true; render.value = true;