review code.
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
};
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
};
|
||||||
|
|||||||
@@ -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();
|
||||||
// 加载主机身份
|
// 加载主机身份
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -26,3 +26,6 @@ export const AuthType = {
|
|||||||
|
|
||||||
// 主机验证方式 字典项
|
// 主机验证方式 字典项
|
||||||
export const authTypeKey = 'hostAuthTypeType';
|
export const authTypeKey = 'hostAuthTypeType';
|
||||||
|
|
||||||
|
// 加载的字典值
|
||||||
|
export const dictKeys = ['hostAuthTypeType'];
|
||||||
|
|||||||
@@ -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']);
|
||||||
|
|
||||||
|
|||||||
@@ -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[]>([]);
|
||||||
|
|||||||
@@ -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;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,3 @@ export const tagColor = [
|
|||||||
'pinkpurple',
|
'pinkpurple',
|
||||||
'magenta'
|
'magenta'
|
||||||
];
|
];
|
||||||
|
|
||||||
// 加载的字典值
|
|
||||||
export const dictKeys = ['hostAuthTypeType'];
|
|
||||||
|
|||||||
Reference in New Issue
Block a user