feat: 保存主机分组数据.

This commit is contained in:
lijiahang
2023-11-14 15:05:47 +08:00
parent b07d1d1d19
commit 4202890d07
13 changed files with 148 additions and 85 deletions

View File

@@ -36,9 +36,6 @@ public class HostGroupTreeVO implements Serializable {
@Schema(description = "组名称")
private String name;
@Schema(description = "排序")
private Integer sort;
@Schema(description = "子节点")
private List<HostGroupTreeVO> children;

View File

@@ -179,7 +179,7 @@ public class DataGroupRelServiceImpl implements DataGroupRelService {
@Override
public List<DataGroupRelCacheDTO> getGroupRelListByCache(String type, Long groupId) {
return this.getGroupRelListByCache(
DataGroupCacheKeyDefine.DATA_GROUP_REL_GROUP.format(type),
DataGroupCacheKeyDefine.DATA_GROUP_REL_GROUP.format(groupId),
DataGroupCacheKeyDefine.DATA_GROUP_REL_GROUP,
() -> dataGroupRelDAO.of()
.createWrapper()
@@ -203,11 +203,11 @@ public class DataGroupRelServiceImpl implements DataGroupRelService {
Supplier<List<DataGroupRelCacheDTO>> valueSupplier) {
// 查询缓存
List<DataGroupRelCacheDTO> list = RedisStrings.getJsonArray(key, define);
if (list.isEmpty()) {
if (Lists.isEmpty(list)) {
// 查询数据库
list = valueSupplier.get();
// 添加默认值 防止穿透
if (list.isEmpty()) {
if (Lists.isEmpty(list)) {
list.add(DataGroupRelCacheDTO.builder()
.id(Const.NONE_ID)
.build());

View File

@@ -39,7 +39,7 @@ export interface HostGroupQueryResponse {
*/
export interface HostGroupRelUpdateRequest {
groupId?: number;
relIdList?: Array<number>;
relIdList?: Array<string>;
}
/**
@@ -80,8 +80,8 @@ export function deleteHostGroup(id: number) {
/**
* 查询分组内主机
*/
export function getHostGroupRelList() {
return axios.get<Array<number>>('/asset/host-group/rel-list');
export function getHostGroupRelList(groupId: number) {
return axios.get<Array<number>>('/asset/host-group/rel-list', { params: { groupId } });
}
/**

View File

@@ -3,13 +3,13 @@ import { Message } from '@arco-design/web-vue';
export default function useCopy() {
const { isSupported, copy: c, text, copied } = useClipboard();
const copy = async (value: string, tips = `${ value } 已复制`) => {
const copy = async (value: string, tips = `${value} 已复制`) => {
try {
await c(value);
if (tips) {
Message.success(tips);
}
} catch {
} catch (e) {
Message.error('复制失败');
}
};

View File

@@ -1,13 +1,16 @@
import type { CacheState } from './types';
import { defineStore } from 'pinia';
export type CacheType = 'users' | 'menus' | 'roles' | 'hostTags' | 'hostKeys' | 'hostIdentities' | 'dictKeys' | string
export type CacheType = 'users' | 'menus' | 'roles'
| 'host' | 'hostTags' | 'hostKeys' | 'hostIdentities'
| 'dictKeys' | string
export default defineStore('cache', {
state: (): CacheState => ({
users: [],
menus: [],
roles: [],
hosts: [],
hostTags: [],
hostKeys: [],
hostIdentities: [],

View File

@@ -5,12 +5,14 @@ import type { TagQueryResponse } from '@/api/meta/tag';
import type { HostKeyQueryResponse } from '@/api/asset/host-key';
import type { HostIdentityQueryResponse } from '@/api/asset/host-identity';
import type { DictKeyQueryResponse } from '@/api/system/dict-key';
import type { HostQueryResponse } from '@/api/asset/host';
export interface CacheState {
users: UserQueryResponse[];
menus: MenuQueryResponse[];
roles: RoleQueryResponse[];
hostTags: TagQueryResponse[];
hosts: HostQueryResponse[];
hostKeys: HostKeyQueryResponse[];
hostIdentities: HostIdentityQueryResponse[];
dictKeys: DictKeyQueryResponse[];

View File

@@ -4,6 +4,5 @@ import type { NodeData } from './global';
declare module '@arco-design/web-vue' {
interface TreeNodeData extends NodeData {
[key: string]: any;
}
}

View File

@@ -36,7 +36,7 @@
<!-- 名称 -->
<span v-else
class="node-title-wrapper"
@click="() => emits('selectKey', node.key)">
@click="() => emits('selectNode', node)">
{{ node.title }}
</span>
</template>
@@ -94,7 +94,7 @@
const props = defineProps({
loading: Boolean
});
const emits = defineEmits(['loading', 'selectKey']);
const emits = defineEmits(['loading', 'selectNode']);
const tree = ref();
const modCount = ref(0);
@@ -278,7 +278,14 @@
emits('loading', true);
const { data } = await getHostGroupTree();
treeData.value = data;
} catch {
// 未选择则选择首个
if (!tree.value?.getSelectedNodes()?.length && data.length) {
await nextTick(() => {
tree.value?.selectNode(data[0].key);
emits('selectNode', data[0]);
});
}
} catch (e) {
} finally {
emits('loading', false);
}
@@ -308,7 +315,17 @@
color: var(--color-text-3);
}
:deep(.arco-tree-node) {
cursor: unset;
.arco-tree-node-switcher {
margin-left: 8px;
}
}
:deep(.arco-tree-node-selected) {
background-color: var(--color-fill-2);
.arco-tree-node-title {
&:hover {
background-color: var(--color-fill-2);
@@ -317,7 +334,8 @@
}
:deep(.arco-tree-node-title) {
padding-right: 48px;
padding: 0 68px 0 0;
height: 32px;
&:hover {
background-color: var(--color-fill-1);
@@ -325,17 +343,19 @@
.arco-tree-node-title-text {
width: 100%;
height: 100%;
display: flex;
align-items: center;
cursor: pointer;
}
}
:deep(.arco-tree-node-selected) {
background-color: var(--color-fill-2);
}
.node-title-wrapper {
width: 100%;
height: 100%;
display: flex;
align-items: center;
padding-left: 8px;
}
.tree-icon {

View File

@@ -28,13 +28,15 @@
<host-group-tree ref="tree"
:loading="treeLoading"
@loading="setTreeLoading"
@select-key="selectGroup" />
@select-node="selectGroup" />
</div>
</a-spin>
<!-- 身体部分 -->
<a-spin class="simple-card view-body"
:loading="dataLoading">
<host-transfer ref="transfer" />
<host-transfer ref="transfer"
:group="currentGroup"
@loading="setDataLoading" />
</a-spin>
</template>
@@ -45,6 +47,7 @@
</script>
<script lang="ts" setup>
import type { TreeNodeData } from '@arco-design/web-vue';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import HostGroupTree from './host-group-tree.vue';
@@ -55,6 +58,7 @@
const tree = ref();
const transfer = ref();
const currentGroup = ref();
// 添加根节点
const addRootNode = () => {
@@ -67,28 +71,8 @@
};
// 选中分组
const selectGroup = (key: number) => {
console.log(key);
};
const { loading: treeLoading, setLoading: setTreeLoading } = useLoading();
const { loading: dataLoading, setLoading: setDataLoading } = useLoading();
const tree = ref();
// 添加根节点
const addRootNode = () => {
tree.value.addRootNode();
};
// 刷新树
const refreshTree = () => {
tree.value.fetchTreeData();
};
// 选中分组
const selectGroup = (key: number) => {
console.log(key);
const selectGroup = (group: TreeNodeData) => {
currentGroup.value = group;
};
</script>
@@ -127,10 +111,14 @@
display: flex;
.handler-icon-wrapper {
margin-left: 8px;
color: rgb(var(--primary-6));
margin-left: 2px;
padding: 4px;
font-size: 16px;
background: unset;
&:hover {
background: var(--color-fill-3);
}
}
}

View File

@@ -3,10 +3,21 @@
<!-- 头部 -->
<div class="transfer-header">
<!-- 提示 -->
<a-alert class="alert-wrapper">123123</a-alert>
<a-alert class="alert-wrapper">
<!-- 已选中分组 -->
<template v-if="group.key">
<span>当前编辑的分组为 <span class="span-blue">{{ group.title }}</span></span>
</template>
<!-- 未选中分组 -->
<template v-else>
<span>点击左侧分组即可加载组内数据</span>
</template>
</a-alert>
<!-- 保存按钮 -->
<a-button class="save-button"
<a-button v-permission="['asset:host-group:update-rel']"
class="save-button"
type="primary"
:disabled="!group.key"
@click="save">
保存
<template #icon>
@@ -19,10 +30,11 @@
:data="data"
:source-input-search-props="{ placeholder:'请输入主机名称/编码/IP' }"
:target-input-search-props="{ placeholder:'请输入主机名称/编码/IP' }"
:disabled="!group.key"
show-search
one-way>
<!-- 主机列表 -->
<template #source-title="{ countTotal, countSelected, checked, indeterminate, onSelectAllChange}">
<template #source-title="{ countTotal, countSelected, checked, indeterminate, onSelectAllChange }">
<!-- 左侧标题 -->
<div class="source-title-container">
<a-checkbox style="margin-right: 8px;"
@@ -45,6 +57,10 @@
</span>
</div>
</template>
<!-- 内容 -->
<template #item="{ label }">
<span v-html="renderLabel(label)" />
</template>
</a-transfer>
</div>
</template>
@@ -57,25 +73,79 @@
<script lang="ts" setup>
import type { TransferItem } from '@arco-design/web-vue/es/transfer/interface';
import { onMounted, ref } from 'vue';
import type { TreeNodeData } from '@arco-design/web-vue';
import type { PropType } from 'vue';
import { onMounted, ref, watch } from 'vue';
import { useCacheStore } from '@/store';
import { getHostGroupRelList, updateHostGroupRel } from '@/api/asset/host-group';
import { Message } from '@arco-design/web-vue';
const props = defineProps({
group: {
type: Object as PropType<TreeNodeData>,
default: () => {
return {};
}
}
});
const emits = defineEmits(['loading']);
const cacheStore = useCacheStore();
const data = ref<Array<TransferItem>>([]);
const value = ref([]);
const value = ref<Array<string>>([]);
// 渲染 label
const renderLabel = (label: string) => {
const last = label.lastIndexOf('-');
const prefix = label.substring(0, last);
const ip = label.substring(last + 1, label.length);
return `${prefix} - <span class="span-blue">${ip}</span>`;
};
// 保存
const save = () => {
console.log(value.value);
const save = async () => {
try {
emits('loading', true);
await updateHostGroupRel({
groupId: props.group?.key as number,
relIdList: value.value
});
Message.success('保存成功');
} catch (e) {
} finally {
emits('loading', false);
}
};
// 查询组内数据
watch(() => props.group?.key, async (groupId) => {
if (groupId) {
// 加载组内数据
try {
emits('loading', true);
const { data } = await getHostGroupRelList(groupId as number);
value.value = data.map(String);
} catch (e) {
} finally {
emits('loading', false);
}
} else {
// 重置
value.value = [];
}
});
// 加载主机
onMounted(() => {
const cacheStore = useCacheStore();
data.value = Array(200).fill(undefined).map((_, index) => ({
value: `option${index + 1}`,
label: `Option ${index + 1}`,
disabled: false
}));
data.value = cacheStore.hosts.map(s => {
return {
value: String(s.id),
label: `${s.name}(${s.code})-${s.address}`,
disabled: false
};
});
});
</script>

View File

@@ -16,41 +16,25 @@
import { computed, ref, onBeforeMount, onUnmounted } from 'vue';
import { useAppStore, useCacheStore, useDictStore } from '@/store';
import HostGroupView from './components/host-group-view.vue';
import { getHostList } from '@/api/asset/host';
const render = ref(false);
const table = ref();
const card = ref();
const modal = ref();
const config = ref();
const appStore = useAppStore();
const cacheStore = useCacheStore();
// 添加回调
const modalAddCallback = () => {
table.value.addedCallback();
};
// 修改回调
const modalUpdateCallback = () => {
table.value.updatedCallback();
};
// 加载 tags
const loadTags = async () => {
// 加载主机列表
const loadHostList = async () => {
try {
const { data } = await getHostList();
// 设置到缓存
// cacheStore.set('hostTags', data);
} catch {
cacheStore.set('hosts', data);
} catch (e) {
Message.error('tag加载失败');
}
};
onBeforeMount(async () => {
// 加载角色列表
// 加载用户列表
// 加载主机列表
await loadHostList();
render.value = true;
});

View File

@@ -73,7 +73,7 @@
const { data } = await getTagList('HOST');
// 设置到缓存
cacheStore.set('hostTags', data);
} catch {
} catch (e) {
Message.error('tag加载失败');
}
};

View File

@@ -61,7 +61,7 @@
const { data } = await getDictKeyList();
// 设置到缓存
cacheStore.set('dictKeys', data);
} catch {
} catch (e) {
Message.error('配置项加载失败');
}
};