review code.

This commit is contained in:
lijiahang
2023-12-01 17:29:42 +08:00
parent 351669a4f3
commit e8d99cb263
21 changed files with 235 additions and 132 deletions

View File

@@ -21,7 +21,7 @@ export interface AssetAuthorizedDataQueryRequest {
* 主机分组授权 * 主机分组授权
*/ */
export function grantHostGroup(request: AssetDataGrantRequest) { export function grantHostGroup(request: AssetDataGrantRequest) {
return axios.put('/asset/host-group/grant-host-group', request); return axios.put('/asset/data-grant/grant-host-group', request);
} }
/** /**

View File

@@ -4,12 +4,10 @@
<div class="tab-bar-box"> <div class="tab-bar-box">
<div class="tab-bar-scroll"> <div class="tab-bar-scroll">
<div class="tags-wrap"> <div class="tags-wrap">
<TabItem <TabItem v-for="(tag, index) in tagList"
v-for="(tag, index) in tagList" :key="tag.fullPath"
:key="tag.fullPath" :index="index"
:index="index" :item-data="tag" />
:item-data="tag"
/>
</div> </div>
</div> </div>
<div class="tag-bar-operation"></div> <div class="tag-bar-operation"></div>
@@ -48,7 +46,7 @@
listenerRouteChange((route: RouteLocationNormalized) => { listenerRouteChange((route: RouteLocationNormalized) => {
if ( if (
!route.meta.noAffix && !route.meta.noAffix &&
!tagList.value.some((tag) => tag.fullPath === route.fullPath) !tagList.value.some((tag) => tag.path === route.path)
) { ) {
// 固定并且没有此 tab 则添加 // 固定并且没有此 tab 则添加
tabBarStore.addTab(routerToTag(route), route.meta?.ignoreCache as unknown as boolean); tabBarStore.addTab(routerToTag(route), route.meta?.ignoreCache as unknown as boolean);

View File

@@ -1,18 +1,15 @@
<template> <template>
<a-dropdown <a-dropdown trigger="contextMenu"
trigger="contextMenu" :popup-max-height="false"
:popup-max-height="false" @select="actionSelect">
@select="actionSelect"> <span class="arco-tag arco-tag-size-medium arco-tag-checked"
<span :class="{ 'link-activated': itemData?.path === $route.path }"
class="arco-tag arco-tag-size-medium arco-tag-checked" @click="goto(itemData as TagProps)">
:class="{ 'link-activated': itemData?.fullPath === $route.fullPath }"
@click="goto(itemData as TagProps)">
<span class="tag-link"> <span class="tag-link">
{{ itemData.title }} {{ itemData.title }}
</span> </span>
<span <span class="arco-icon-hover arco-tag-icon-hover arco-icon-hover-size-medium arco-tag-close-btn"
class="arco-icon-hover arco-tag-icon-hover arco-icon-hover-size-medium arco-tag-close-btn" @click.stop="tagClose(itemData as TagProps, index)">
@click.stop="tagClose(itemData as TagProps, index)">
<icon-close /> <icon-close />
</span> </span>
</span> </span>
@@ -91,7 +88,7 @@
}); });
const disabledReload = computed(() => { const disabledReload = computed(() => {
return props.itemData.fullPath !== route.fullPath; return props.itemData.path !== route.path;
}); });
const disabledCurrent = computed(() => { const disabledCurrent = computed(() => {
@@ -106,22 +103,22 @@
return props.index === tagList.value.length - 1; return props.index === tagList.value.length - 1;
}); });
/** // 关闭 tag
* 关闭 tag
*/
const tagClose = (tag: TagProps, idx: number) => { const tagClose = (tag: TagProps, idx: number) => {
tabBarStore.deleteTab(idx, tag); tabBarStore.deleteTab(idx, tag);
if (props.itemData.fullPath === route.fullPath) { if (props.itemData.path === route.path) {
// 获取队列的前一个 tab // 获取队列的前一个 tab
const latest = tagList.value[idx - 1]; const latest = tagList.value[idx - 1];
router.push({ name: latest.name }); router.push({ name: latest.name });
} }
}; };
// 获取当前路由索引
const findCurrentRouteIndex = () => { const findCurrentRouteIndex = () => {
return tagList.value.findIndex((el) => el.fullPath === route.fullPath); return tagList.value.findIndex((el) => el.path === route.path);
}; };
// 选择操作
const actionSelect = async (value: any) => { const actionSelect = async (value: any) => {
const { itemData, index } = props; const { itemData, index } = props;
const copyTagList = [...tagList.value]; const copyTagList = [...tagList.value];

View File

@@ -2,11 +2,9 @@
<router-view v-slot="{ Component, route }"> <router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in" appear> <transition name="fade" mode="out-in" appear>
<!-- 渲染组件 --> <!-- 渲染组件 -->
<component <component :is="Component"
:is="Component" v-if="route.meta.ignoreCache"
v-if="route.meta.ignoreCache" :key="route.fullPath" />
:key="route.fullPath"
/>
<!-- 渲染缓存组件 --> <!-- 渲染缓存组件 -->
<keep-alive v-else :include="cacheList"> <keep-alive v-else :include="cacheList">
<component :is="Component" :key="route.fullPath" /> <component :is="Component" :key="route.fullPath" />

View File

@@ -35,6 +35,7 @@ export const STATUS_ROUTER_LIST = [
export const DEFAULT_TAB = { export const DEFAULT_TAB = {
title: '工作台', title: '工作台',
name: DEFAULT_ROUTE_NAME, name: DEFAULT_ROUTE_NAME,
path: DEFAULT_ROUTE_FULL_PATH,
fullPath: DEFAULT_ROUTE_FULL_PATH, fullPath: DEFAULT_ROUTE_FULL_PATH,
}; };
@@ -42,10 +43,11 @@ export const DEFAULT_TAB = {
* router 转 tag * router 转 tag
*/ */
export const routerToTag = (route: RouteLocationNormalized): TagProps => { export const routerToTag = (route: RouteLocationNormalized): TagProps => {
const { name, meta, fullPath, query } = route; const { name, meta, path, fullPath, query } = route;
return { return {
title: meta.locale || '', title: meta.locale || '',
name: String(name), name: String(name),
path,
fullPath, fullPath,
query, query,
ignoreCache: meta.ignoreCache, ignoreCache: meta.ignoreCache,

View File

@@ -1,6 +1,7 @@
export interface TagProps { export interface TagProps {
title: string; title: string;
name: string; name: string;
path: string;
fullPath: string; fullPath: string;
query?: any; query?: any;
ignoreCache?: boolean; ignoreCache?: boolean;

View File

@@ -6,3 +6,9 @@ export const TablePageSizeOptions = [10, 20, 30, 50, 100];
// 卡片视图分页数配置 // 卡片视图分页数配置
export const CardPageSizeOptions = [12, 18, 36, 48, 96]; export const CardPageSizeOptions = [12, 18, 36, 48, 96];
// 通用启用状态
export const EnabledStatus = {
DISABLED: 0,
ENABLED: 1
};

View File

@@ -1,14 +1,16 @@
<template> <template>
<a-spin :loading="loading" class="grant-container"> <a-spin :loading="loading" class="grant-container">
<!-- 角色列表 --> <!-- 角色列表 -->
<router-roles v-model="roleId" @change="fetchAuthorizedGroup" /> <router-roles outer-class="roles-router-wrapper"
v-model="roleId"
@change="fetchAuthorizedGroup" />
<!-- 分组列表 --> <!-- 分组列表 -->
<div class="group-container"> <div class="group-container">
<!-- 顶部 --> <!-- 顶部 -->
<div class="group-header"> <div class="group-header">
<!-- 提示信息 --> <!-- 提示信息 -->
<a-alert class="alert-wrapper" :show-icon="false"> <a-alert class="alert-wrapper" :show-icon="false">
<span v-if="currentRole"> <span v-if="currentRole" class="alert-message">
当前选择的角色为 <span class="span-blue mr4">{{ currentRole?.text }}</span> 当前选择的角色为 <span class="span-blue mr4">{{ currentRole?.text }}</span>
<span class="span-blue ml4" v-if="currentRole.code === AdminRoleCode">管理员拥有全部权限, 无需配置</span> <span class="span-blue ml4" v-if="currentRole.code === AdminRoleCode">管理员拥有全部权限, 无需配置</span>
</span> </span>
@@ -112,6 +114,12 @@
height: 100%; height: 100%;
display: flex; display: flex;
padding: 0 12px 12px 0; padding: 0 12px 12px 0;
position: absolute;
.roles-router-wrapper {
margin-right: 16px;
border-right: 1px var(--color-neutral-3) solid;
}
.group-container { .group-container {
position: relative; position: relative;
@@ -126,6 +134,11 @@
.alert-wrapper { .alert-wrapper {
padding: 4px 16px; padding: 4px 16px;
.alert-message {
display: block;
height: 22px;
}
} }
.grant-button { .grant-button {

View File

@@ -1,14 +1,16 @@
<template> <template>
<a-spin :loading="loading" class="grant-container"> <a-spin :loading="loading" class="grant-container">
<!-- 用户列表 --> <!-- 用户列表 -->
<router-users v-model="userId" @change="fetchAuthorizedGroup" /> <router-users outer-class="users-router-wrapper"
v-model="userId"
@change="fetchAuthorizedGroup" />
<!-- 分组列表 --> <!-- 分组列表 -->
<div class="group-container"> <div class="group-container">
<!-- 顶部 --> <!-- 顶部 -->
<div class="group-header"> <div class="group-header">
<!-- 提示信息 --> <!-- 提示信息 -->
<a-alert class="alert-wrapper" :show-icon="false"> <a-alert class="alert-wrapper" :show-icon="false">
<span v-if="currentUser"> <span v-if="currentUser" class="alert-message">
当前选择的用户为 <span class="span-blue mr4">{{ currentUser?.text }}</span> 当前选择的用户为 <span class="span-blue mr4">{{ currentUser?.text }}</span>
<span class="ml4">若当前选择的用户角色包含管理员则无需配置 (管理员拥有全部权限)</span> <span class="ml4">若当前选择的用户角色包含管理员则无需配置 (管理员拥有全部权限)</span>
</span> </span>
@@ -110,6 +112,12 @@
height: 100%; height: 100%;
display: flex; display: flex;
padding: 0 12px 12px 0; padding: 0 12px 12px 0;
position: absolute;
.users-router-wrapper {
margin-right: 16px;
border-right: 1px var(--color-neutral-3) solid;
}
.group-container { .group-container {
position: relative; position: relative;
@@ -124,6 +132,11 @@
.alert-wrapper { .alert-wrapper {
padding: 4px 16px; padding: 4px 16px;
.alert-message {
display: block;
height: 22px;
}
} }
.grant-button { .grant-button {

View File

@@ -66,13 +66,6 @@
const { data } = await getHostGroupRelList(groupId as number); const { data } = await getHostGroupRelList(groupId as number);
selectedGroupHosts.value = data.map(s => cacheStore.hosts.find(h => h.id === s) as HostQueryResponse) selectedGroupHosts.value = data.map(s => cacheStore.hosts.find(h => h.id === s) as HostQueryResponse)
.filter(Boolean); .filter(Boolean);
for (let i = 1800; i < 2000; i++) {
selectedGroupHosts.value.push({
id: i,
name: i + '',
code: i + '',
} as any);
}
} catch (e) { } catch (e) {
} finally { } finally {
setLoading(false); setLoading(false);

View File

@@ -1,18 +1,24 @@
<template> <template>
<div class="role-container"> <a-scrollbar>
<!-- 角色列表 --> <div class="role-container">
<tab-router v-if="rolesRouter.length" <!-- 角色列表 -->
class="role-router" <tab-router v-if="rolesRouter.length"
v-model="value" class="role-router"
:items="rolesRouter" v-model="value"
@change="(key, item) => emits('change', key, item)" /> :items="rolesRouter"
<!-- 暂无数据 --> @change="(key, item) => emits('change', key, item)" />
<a-empty v-else class="role-empty"> <!-- 加载中 -->
<div slot="description"> <a-skeleton v-else-if="loading" class="skeleton-wrapper">
暂无角色数据 <a-skeleton-line :rows="4" />
</div> </a-skeleton>
</a-empty> <!-- 暂无数据 -->
</div> <a-empty v-else class="role-empty">
<div slot="description">
暂无角色数据
</div>
</a-empty>
</div>
</a-scrollbar>
</template> </template>
<script lang="ts"> <script lang="ts">
@@ -27,6 +33,7 @@
import { useCacheStore } from '@/store'; import { useCacheStore } from '@/store';
import { getRoleList } from '@/api/user/role'; 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';
const props = defineProps({ const props = defineProps({
modelValue: Number modelValue: Number
@@ -34,6 +41,7 @@
const emits = defineEmits(['update:modelValue', 'change']); const emits = defineEmits(['update:modelValue', 'change']);
const { loading, setLoading } = useLoading();
const cacheStore = useCacheStore(); const cacheStore = useCacheStore();
const rolesRouter = ref<Array<TabRouterItem>>([]); const rolesRouter = ref<Array<TabRouterItem>>([]);
@@ -49,12 +57,15 @@
// 加载角色列表 // 加载角色列表
const loadRoleList = async () => { const loadRoleList = async () => {
setLoading(true);
try { try {
const { data } = await getRoleList(); const { data } = await getRoleList();
// 设置到缓存 // 设置到缓存
cacheStore.set('roles', data); cacheStore.set('roles', data);
} catch (e) { } catch (e) {
Message.error('角色列表加载失败'); Message.error('角色列表加载失败');
} finally {
setLoading(false);
} }
}; };
@@ -75,17 +86,27 @@
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@width: 198px;
@height: 198px;
:deep(.arco-scrollbar-container) {
height: 100%;
overflow-y: auto;
}
.role-container { .role-container {
margin-right: 16px; overflow: hidden;
.role-router { .role-router {
height: 100%; height: 100%;
min-width: max-content; min-width: @width;
border-right: 1px var(--color-neutral-3) solid; width: max-content;
} }
.role-empty { .skeleton-wrapper, .role-empty {
width: 198px; width: @width;
height: 200px;
padding: 8px;
} }
} }
</style> </style>

View File

@@ -1,18 +1,24 @@
<template> <template>
<div class="user-container"> <a-scrollbar>
<!-- 用户列表 --> <div class="user-container">
<tab-router v-if="usersRouter.length" <!-- 用户列表 -->
class="user-router" <tab-router v-if="usersRouter.length"
v-model="value" class="user-router"
:items="usersRouter" v-model="value"
@change="(key, item) => emits('change', key, item)" /> :items="usersRouter"
<!-- 暂无数据 --> @change="(key, item) => emits('change', key, item)" />
<a-empty v-else class="user-empty"> <!-- 加载中 -->
<div slot="description"> <a-skeleton v-else-if="loading" class="skeleton-wrapper">
暂无用户数据 <a-skeleton-line :rows="4" />
</div> </a-skeleton>
</a-empty> <!-- 暂无数据 -->
</div> <a-empty v-else class="user-empty">
<div slot="description">
暂无用户数据
</div>
</a-empty>
</div>
</a-scrollbar>
</template> </template>
<script lang="ts"> <script lang="ts">
@@ -27,6 +33,7 @@
import { useCacheStore } from '@/store'; import { useCacheStore } from '@/store';
import { getUserList } from '@/api/user/user'; 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';
const props = defineProps({ const props = defineProps({
modelValue: Number modelValue: Number
@@ -34,6 +41,7 @@
const emits = defineEmits(['update:modelValue', 'change']); const emits = defineEmits(['update:modelValue', 'change']);
const { loading, setLoading } = useLoading();
const cacheStore = useCacheStore(); const cacheStore = useCacheStore();
const usersRouter = ref<Array<TabRouterItem>>([]); const usersRouter = ref<Array<TabRouterItem>>([]);
@@ -49,12 +57,15 @@
// 加载用户列表 // 加载用户列表
const loadUserList = async () => { const loadUserList = async () => {
setLoading(true);
try { try {
const { data } = await getUserList(); const { data } = await getUserList();
// 设置到缓存 // 设置到缓存
cacheStore.set('users', data); cacheStore.set('users', data);
} catch (e) { } catch (e) {
Message.error('用户列表加载失败'); Message.error('用户列表加载失败');
} finally {
setLoading(false);
} }
}; };
@@ -74,17 +85,27 @@
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@width: 198px;
@height: 198px;
:deep(.arco-scrollbar-container) {
height: 100%;
overflow-y: auto;
}
.user-container { .user-container {
margin-right: 16px; overflow: hidden;
.user-router { .user-router {
height: 100%; height: 100%;
min-width: max-content; min-width: @width;
border-right: 1px var(--color-neutral-3) solid; width: max-content;
} }
.user-empty { .skeleton-wrapper, .user-empty {
width: 198px; width: @width;
height: @height;
padding: 8px;
} }
} }
</style> </style>

View File

@@ -1,18 +1,18 @@
<template> <template>
<div class="view-container"> <div class="view-container">
<a-tabs class="tabs-container simple-card" <a-tabs v-model:active-key="activeKey"
class="tabs-container simple-card"
size="large" size="large"
:destroy-on-hide="true"
:justify="true" :justify="true"
:lazy-load="true"> :lazy-load="true">
<!-- 主机分组授权(角色) --> <!-- 主机分组授权(角色) -->
<a-tab-pane :key="1" <a-tab-pane :key="GrantKey.HOST_GROUP_ROLE"
v-permission="['asset:host-group:grant']" v-permission="['asset:host-group:grant']"
title="主机分组授权(角色)"> title="主机分组授权(角色)">
<host-group-role-grant /> <host-group-role-grant />
</a-tab-pane> </a-tab-pane>
<!-- 主机分组授权(用户) --> <!-- 主机分组授权(用户) -->
<a-tab-pane :key="2" <a-tab-pane :key="GrantKey.HOST_GROUP_USER"
v-permission="['asset:host-group:grant']" v-permission="['asset:host-group:grant']"
title="主机分组授权(用户)"> title="主机分组授权(用户)">
<host-group-user-grant /> <host-group-user-grant />
@@ -28,33 +28,31 @@
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onBeforeMount, onUnmounted } from 'vue'; import { onBeforeMount, onUnmounted, ref } from 'vue';
import { useCacheStore } from '@/store'; import { useCacheStore } from '@/store';
import { Message } from '@arco-design/web-vue';
import { getUserList } from '@/api/user/user';
import { getRoleList } from '@/api/user/role';
import HostGroupRoleGrant from './components/host-group-role-grant.vue'; import HostGroupRoleGrant from './components/host-group-role-grant.vue';
import HostGroupUserGrant from './components/host-group-user-grant.vue'; import HostGroupUserGrant from './components/host-group-user-grant.vue';
import { GrantKey } from './types/const';
import { useRoute } from 'vue-router';
const render = ref(false); const route = useRoute();
const cacheStore = useCacheStore(); const cacheStore = useCacheStore();
// 加载用户列表 const activeKey = ref();
const loadUserList = async () => {
try {
const { data } = await getUserList();
// 设置到缓存
cacheStore.set('users', data);
} catch (e) {
Message.error('用户列表加载失败');
}
};
// 卸载时清除 cache // 卸载时清除 cache
onUnmounted(() => { onUnmounted(() => {
cacheStore.reset('users', 'roles', 'hosts', 'hostGroups'); cacheStore.reset('users', 'roles', 'hosts', 'hostGroups');
}); });
// 跳转到指定页
onBeforeMount(() => {
const key = route.query.key;
if (key) {
activeKey.value = Number(key);
}
});
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@@ -71,6 +69,12 @@
height: 100%; height: 100%;
} }
:deep(.arco-tabs-pane) {
width: 100%;
height: 100%;
position: relative;
}
:deep(.arco-tabs-tab-title) { :deep(.arco-tabs-tab-title) {
user-select: none; user-select: none;
} }

View File

@@ -1,5 +1,10 @@
// 创建前缀 // 路由
export const createGroupGroupPrefix = 'create-'; export const GrantRouteName = 'assetGrant';
// 根id // 授权 key
export const rootId = 0; export const GrantKey = {
// 主机分组-角色
HOST_GROUP_ROLE: 1,
// 主机分组-用户
HOST_GROUP_USER: 2
};

View File

@@ -35,9 +35,10 @@
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { getHostConfigAll } from '@/api/asset/host'; import { getHostConfigAll } from '@/api/asset/host';
import { useCacheStore } from '@/store'; import { useCacheStore, useDictStore } from '@/store';
import { getHostKeyList } from '@/api/asset/host-key'; import { getHostKeyList } from '@/api/asset/host-key';
import { getHostIdentityList } from '@/api/asset/host-identity'; import { getHostIdentityList } from '@/api/asset/host-identity';
import { dictKeys as sshDictKeys } from './ssh/types/const';
import SshConfigForm from './ssh/ssh-config-form.vue'; import SshConfigForm from './ssh/ssh-config-form.vue';
const { visible, setVisible } = useVisible(); const { visible, setVisible } = useVisible();
@@ -95,6 +96,9 @@
}; };
onBeforeMount(async () => { onBeforeMount(async () => {
// 加载字典值
const dictStore = useDictStore();
await dictStore.loadKeys([...sshDictKeys]);
// 加载主机秘钥 // 加载主机秘钥
await fetchHostKeys(); await fetchHostKeys();
// 加载主机身份 // 加载主机身份

View File

@@ -1,6 +1,6 @@
<template> <template>
<a-card class="general-card" <a-card class="general-card"
:body-style="{ padding: config.status === 1 ? '' : '0' }"> :body-style="{ padding: config.status === EnabledStatus.ENABLED ? '' : '0' }">
<!-- 标题 --> <!-- 标题 -->
<template #title> <template #title>
<div class="config-title-wrapper"> <div class="config-title-wrapper">
@@ -140,7 +140,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { FieldRule } from '@arco-design/web-vue'; import type { FieldRule } from '@arco-design/web-vue';
import type { HostSshConfig } from './types/const'; import type { HostSshConfig } from './types/const';
import { ref, watch } from 'vue'; import { reactive, ref, watch } from 'vue';
import { updateHostConfigStatus, updateHostConfig } from '@/api/asset/host'; import { updateHostConfigStatus, updateHostConfig } from '@/api/asset/host';
import { authTypeKey, AuthType } from './types/const'; import { authTypeKey, AuthType } from './types/const';
import rules from './types/form.rules'; import rules from './types/form.rules';
@@ -149,6 +149,7 @@
import { useDictStore } from '@/store'; import { useDictStore } from '@/store';
import HostKeySelector from '@/components/asset/host-key/host-key-selector.vue'; import HostKeySelector from '@/components/asset/host-key/host-key-selector.vue';
import HostIdentitySelector from '@/components/asset/host-identity/host-identity-selector.vue'; import HostIdentitySelector from '@/components/asset/host-identity/host-identity-selector.vue';
import { EnabledStatus } from '@/types/const';
const { loading, setLoading } = useLoading(); const { loading, setLoading } = useLoading();
const { toOptions } = useDictStore(); const { toOptions } = useDictStore();
@@ -159,7 +160,7 @@
const emits = defineEmits(['submitted']); const emits = defineEmits(['submitted']);
const config = ref({ const config = reactive({
status: undefined, status: undefined,
version: undefined, version: undefined,
}); });
@@ -182,8 +183,8 @@
// 监听数据变化 // 监听数据变化
watch(() => props.content, (v: any) => { watch(() => props.content, (v: any) => {
config.value.status = v?.status; config.status = v?.status;
config.value.version = v?.version; config.version = v?.version;
resetConfig(); resetConfig();
}); });
@@ -221,9 +222,9 @@
return updateHostConfigStatus({ return updateHostConfigStatus({
id: props?.content?.id, id: props?.content?.id,
status: e, status: e,
version: config.value.version version: config.version
}).then(({ data }) => { }).then(({ data }) => {
config.value.version = data; config.version = data;
setLoading(false); setLoading(false);
return true; return true;
}).catch(() => { }).catch(() => {
@@ -250,10 +251,10 @@
setLoading(true); setLoading(true);
const { data } = await updateHostConfig({ const { data } = await updateHostConfig({
id: props?.content?.id, id: props?.content?.id,
version: config.value.version, version: config.version,
config: JSON.stringify(formModel.value) config: JSON.stringify(formModel.value)
}); });
config.value.version = data; config.version = data;
setLoading(false); setLoading(false);
Message.success('修改成功'); Message.success('修改成功');
// 回调 props // 回调 props

View File

@@ -26,3 +26,6 @@ export const AuthType = {
// 主机验证方式 字典项 // 主机验证方式 字典项
export const authTypeKey = 'hostAuthTypeType'; export const authTypeKey = 'hostAuthTypeType';
// 加载的字典值
export const dictKeys = ['hostAuthTypeType'];

View File

@@ -8,7 +8,8 @@
:pagination="pagination" :pagination="pagination"
:card-layout-cols="cardColLayout" :card-layout-cols="cardColLayout"
:filter-count="filterCount" :filter-count="filterCount"
:handle-visible="{ disableAdd: true }" :add-permission="['asset:host:create']"
@add="emits('openAdd')"
@reset="reset" @reset="reset"
@search="fetchCardData" @search="fetchCardData"
@page-change="fetchCardData"> @page-change="fetchCardData">
@@ -16,22 +17,31 @@
<template #leftHandle> <template #leftHandle>
<!-- 主机分组 --> <!-- 主机分组 -->
<a-button v-permission="['asset:host-group:update']" <a-button v-permission="['asset:host-group:update']"
class="click-icon-wrapper card-header-icon-wrapper" class="card-header-icon-wrapper"
type="primary"
title="分组配置"
@click="emits('openHostGroup')"> @click="emits('openHostGroup')">
主机分组 主机分组
<template #icon> <template #icon>
<icon-layers /> <icon-layers />
</template> </template>
</a-button> </a-button>
<!-- 创建 --> <!-- 角色授权 -->
<div v-permission="['asset:host:create']" <a-button v-permission="['asset:host-group:grant']"
class="click-icon-wrapper card-header-icon-wrapper" class="card-header-icon-wrapper"
title="创建" @click="$router.push({ name: GrantRouteName, query: { key: GrantKey.HOST_GROUP_ROLE }})">
@click="emits('openAdd')"> 角色授权
<icon-plus /> <template #icon>
</div> <icon-user-group />
</template>
</a-button>
<!-- 用户授权 -->
<a-button v-permission="['asset:host-group:grant']"
class="card-header-icon-wrapper"
@click="$router.push({ name: GrantRouteName, query: { key: GrantKey.HOST_GROUP_USER }})">
用户授权
<template #icon>
<icon-user />
</template>
</a-button>
</template> </template>
<!-- 过滤条件 --> <!-- 过滤条件 -->
<template #filterContent> <template #filterContent>
@@ -173,6 +183,7 @@
import { tagColor } from '../types/const'; import { tagColor } from '../types/const';
import TagMultiSelector from '@/components/meta/tag/tag-multi-selector.vue'; import TagMultiSelector from '@/components/meta/tag/tag-multi-selector.vue';
import useCopy from '@/hooks/copy'; import useCopy from '@/hooks/copy';
import { GrantKey, GrantRouteName } from '@/views/asset/grant/types/const';
const emits = defineEmits(['openAdd', 'openUpdate', 'openUpdateConfig', 'openHostGroup']); const emits = defineEmits(['openAdd', 'openUpdate', 'openUpdateConfig', 'openHostGroup']);

View File

@@ -71,6 +71,24 @@
<icon-layers /> <icon-layers />
</template> </template>
</a-button> </a-button>
<!-- 角色授权 -->
<a-button type="primary"
v-permission="['asset:host-group:grant']"
@click="$router.push({ name: GrantRouteName, query: { key: GrantKey.HOST_GROUP_ROLE }})">
角色授权
<template #icon>
<icon-user-group />
</template>
</a-button>
<!-- 用户授权 -->
<a-button type="primary"
v-permission="['asset:host-group:grant']"
@click="$router.push({ name: GrantRouteName, query: { key: GrantKey.HOST_GROUP_USER }})">
用户授权
<template #icon>
<icon-user />
</template>
</a-button>
<!-- 新增 --> <!-- 新增 -->
<a-button type="primary" <a-button type="primary"
v-permission="['asset:host:create']" v-permission="['asset:host:create']"
@@ -183,6 +201,7 @@
import useFavorite from '@/hooks/favorite'; import useFavorite from '@/hooks/favorite';
import { dataColor } from '@/utils'; import { dataColor } from '@/utils';
import TagMultiSelector from '@/components/meta/tag/tag-multi-selector.vue'; import TagMultiSelector from '@/components/meta/tag/tag-multi-selector.vue';
import { GrantKey, GrantRouteName } from '@/views/asset/grant/types/const';
const tagSelector = ref(); const tagSelector = ref();
const tableRenderData = ref<HostQueryResponse[]>([]); const tableRenderData = ref<HostQueryResponse[]>([]);

View File

@@ -33,9 +33,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, onBeforeMount, onUnmounted } from 'vue'; import { computed, ref, onBeforeMount, onUnmounted } from 'vue';
import { useAppStore, useCacheStore, useDictStore } from '@/store'; import { useAppStore, useCacheStore } from '@/store';
import { getTagList } from '@/api/meta/tag'; import { getTagList } from '@/api/meta/tag';
import { dictKeys } from './types/const';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import HostTable from './components/host-table.vue'; import HostTable from './components/host-table.vue';
import HostCardList from './components/host-card-list.vue'; import HostCardList from './components/host-card-list.vue';
@@ -87,9 +86,6 @@
onBeforeMount(async () => { onBeforeMount(async () => {
// 加载 tags // 加载 tags
await loadTags(); await loadTags();
// 加载字典值
const dictStore = useDictStore();
await dictStore.loadKeys(dictKeys);
render.value = true; render.value = true;
}); });

View File

@@ -15,6 +15,3 @@ export const tagColor = [
'pinkpurple', 'pinkpurple',
'magenta' 'magenta'
]; ];
// 加载的字典值
export const dictKeys = ['hostAuthTypeType'];