feat: 主机列表视图.

This commit is contained in:
lijiahang
2023-12-15 19:21:31 +08:00
parent 59c8a7fd2f
commit 5f485172e7
5 changed files with 221 additions and 131 deletions

View File

@@ -16,9 +16,9 @@
</a-scrollbar>
</div>
<!-- 主机列表 -->
<div class="host-list">
<host-list :hosts="hosts" />
</div>
<host-list class="host-list"
:hostList="hosts.hostList"
empty-value="当前分组内无授权主机!" />
</div>
</template>

View File

@@ -1,8 +1,8 @@
<template>
<div class="list-view-container">
<!-- 主机列表 -->
<host-list :hosts="hosts" />
</div>
<!-- 主机列表 -->
<host-list class="list-view-container"
:hostList="hostList"
:empty-value="emptyValue" />
</template>
<script lang="ts">
@@ -12,11 +12,12 @@
</script>
<script lang="ts" setup>
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
import type { HostQueryResponse } from '@/api/asset/host';
import HostList from './host-list.vue';
const props = defineProps<{
hosts: AuthorizedHostQueryResponse
hostList: Array<HostQueryResponse>,
emptyValue: string
}>();
</script>

View File

@@ -1,117 +1,116 @@
<template>
<a-list ref="listRel"
size="large"
max-height="100%"
:hoverable="true"
:data="hosts.hostList">
<!-- 空数据 -->
<template #empty>
<a-empty>
<template #image>
<icon-desktop />
</template>
当前分组内无授权主机!
</a-empty>
</template>
<!-- 数据 -->
<template #item="{ item }">
<a-list-item class="host-item-wrapper" @click="openTerminal(item)">
<div class="host-item">
<!-- 左侧图标-名称 -->
<div class="flex-center host-item-left">
<!-- 图标 -->
<span class="host-item-left-icon">
<div class="hosts-list-container">
<a-list size="large"
max-height="100%"
:hoverable="true"
:data="hostList">
<!-- 空数据 -->
<template #empty>
<a-empty>
<template #image>
<icon-desktop />
</template>
{{ emptyValue }}
</a-empty>
</template>
<!-- 数据 -->
<template #item="{ item }">
<a-list-item class="host-item-wrapper" @click="openTerminal(item)">
<div class="host-item">
<!-- 左侧图标-名称 -->
<div class="flex-center host-item-left">
<!-- 图标 -->
<span class="host-item-left-icon">
<icon-desktop />
</span>
<!-- 名称 -->
<a-tooltip position="top"
:mini="true"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
:content="`${item.name} (${item.code})`">
<!-- 名称 -->
<a-tooltip position="top"
:mini="true"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
:content="`${item.name} (${item.code})`">
<span class="host-item-text host-item-left-name">
{{ `${item.name} (${item.code})` }}
</span>
</a-tooltip>
</div>
<!-- 中间ip -->
<div class="flex-center host-item-center">
<!-- ip -->
<a-tooltip position="top"
:mini="true"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
:content="item.address">
</a-tooltip>
</div>
<!-- 中间ip -->
<div class="flex-center host-item-center">
<!-- ip -->
<a-tooltip position="top"
:mini="true"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
:content="item.address">
<span class="host-item-text host-item-center-address">
{{ item.address }}
</span>
</a-tooltip>
</div>
<!-- 右侧tag-操作 -->
<div class="flex-center host-item-right">
<!-- tags -->
<div class="host-item-right-tags">
<template v-if="item.tags?.length">
<a-tag v-for="(tag, i) in item.tags"
class="host-item-text"
:key="tag.id"
:style="{
maxWidth: `calc(${100 / item.tags.length}% - ${i !== item.tags.length - 1 ? '8px' : '0px'})`,
marginRight: `${i !== item.tags.length - 1 ? '8px' : '0'}`,
}"
:color="dataColor(tag.name, tagColor)">
{{ tag.name }}
</a-tag>
</template>
</div>
<!-- 操作 -->
<div class="host-item-right-actions">
<!-- 连接主机 -->
<a-tooltip position="top"
:mini="true"
:popup-container="listRel?.$el?.parent"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
content="连接主机">
<div class="terminal-sidebar-icon-wrapper">
<div class="terminal-sidebar-icon" @click.stop="openTerminal(item)">
<icon-thunderbolt />
</div>
</div>
</a-tooltip>
<!-- 连接设置 -->
<a-tooltip position="top"
:mini="true"
:popup-container="listRel?.$el?.parent"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
content="连接设置">
<div class="terminal-sidebar-icon-wrapper">
<div class="terminal-sidebar-icon" @click.stop="openSetting(item)">
<icon-settings />
</div>
</div>
</a-tooltip>
<!-- 收藏 -->
<a-tooltip position="top"
:mini="true"
:popup-container="listRel?.$el?.parent"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
content="收藏">
<div class="terminal-sidebar-icon-wrapper">
<div class="terminal-sidebar-icon" @click.stop="setFavorite(item)">
<icon-star-fill class="favorite" v-if="item.favorite" />
<icon-star v-else />
</div>
</div>
</a-tooltip>
</div>
<!-- 右侧tag-操作 -->
<div class="flex-center host-item-right">
<!-- tags -->
<div class="host-item-right-tags">
<template v-if="item.tags?.length">
<a-tag v-for="(tag, i) in item.tags"
class="host-item-text"
:key="tag.id"
:style="{
maxWidth: `calc(${100 / item.tags.length}% - ${i !== item.tags.length - 1 ? '8px' : '0px'})`,
marginRight: `${i !== item.tags.length - 1 ? '8px' : '0'}`,
}"
:color="dataColor(tag.name, tagColor)">
{{ tag.name }}
</a-tag>
</template>
</div>
<!-- 操作 -->
<div class="host-item-right-actions">
<!-- 连接主机 -->
<a-tooltip position="top"
:mini="true"
:popup-visible="true"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
content="连接主机">
<div class="terminal-sidebar-icon-wrapper">
<div class="terminal-sidebar-icon" @click.stop="openTerminal(item)">
<icon-thunderbolt />
</div>
</div>
</a-tooltip>
<!-- 连接设置 -->
<a-tooltip position="top"
:mini="true"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
content="连接设置">
<div class="terminal-sidebar-icon-wrapper">
<div class="terminal-sidebar-icon" @click.stop="openSetting(item)">
<icon-settings />
</div>
</div>
</a-tooltip>
<!-- 收藏 -->
<a-tooltip position="top"
:mini="true"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
content="收藏">
<div class="terminal-sidebar-icon-wrapper">
<div class="terminal-sidebar-icon" @click.stop="setFavorite(item)">
<icon-star-fill class="favorite" v-if="item.favorite" />
<icon-star v-else />
</div>
</div>
</a-tooltip>
</div>
</div>
</div>
</div>
</a-list-item>
</template>
</a-list>
</a-list-item>
</template>
</a-list>
</div>
</template>
<script lang="ts">
@@ -121,20 +120,18 @@
</script>
<script lang="ts" setup>
import { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
import type { HostQueryResponse } from '@/api/asset/host';
import useFavorite from '@/hooks/favorite';
import { ref } from 'vue';
import { dataColor } from '@/utils';
import { tagColor } from '@/views/asset/host-list/types/const';
const props = defineProps<{
hosts: AuthorizedHostQueryResponse
hostList: Array<HostQueryResponse>,
emptyValue: string
}>();
const { toggle: toggleFavorite, loading: favoriteLoading } = useFavorite('HOST');
const listRel = ref();
// 打开终端
const openTerminal = (item: any) => {
console.log('ter', item);

View File

@@ -12,10 +12,24 @@
:options="toOptions(NewConnectionTypeKey)"
@change="changeConnectionType" />
<!-- 过滤 -->
<a-input-search v-model="filterValue"
class="host-filter"
placeholder="输入名称/编码/IP @标签"
:allow-clear="true" />
<a-auto-complete v-model="filterValue"
class="host-filter"
placeholder="输入名称/编码/IP @标签"
:allow-clear="true"
:data="filterOptions"
:filter-option="searchFilter"
@change="shuffleHosts">
<template #option="{ data: { raw: { value, isTag} } }">
<!-- tag -->
<a-tag v-if="isTag" :color="dataColor(value, tagColor)">
{{ value }}
</a-tag>
<!-- 文本 -->
<template v-else>
{{ value }}
</template>
</template>
</a-auto-complete>
</div>
<!-- 授权主机 -->
<div class="terminal-setting-block" style="margin: 0;">
@@ -46,15 +60,20 @@
<div v-else class="host-view-container">
<!-- 分组视图列表 -->
<host-group-view v-if="NewConnectionType.GROUP === newConnectionType"
:hosts="hosts" />
:hosts="hosts"
:filter-value="filterValue" />
<!-- 列表视图 -->
<host-list-view v-if="NewConnectionType.LIST === newConnectionType"
:hosts="hosts" />
:host-list="hostList"
empty-value="无授权主机!" />
<!-- 我的收藏 -->
<host-list-view v-if="NewConnectionType.FAVORITE === newConnectionType"
:host-list="[]"
empty-value="无收藏记录, 快去点击主机右侧的进行收藏吧!" />
<!-- 最近连接 -->
<host-list-view v-if="NewConnectionType.LATEST === newConnectionType"
:host-list="[]"
empty-value="暂无连接记录, 快去体验吧!" />
</div>
</div>
</div>
@@ -69,37 +88,108 @@
</script>
<script lang="ts" setup>
import type { SelectOptionData } from '@arco-design/web-vue';
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
import type { HostQueryResponse } from '@/api/asset/host';
import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data';
import { onBeforeMount, ref } from 'vue';
import { onBeforeMount, ref, watch } from 'vue';
import { NewConnectionType, NewConnectionTypeKey } from '../../types/terminal.const';
import useLoading from '@/hooks/loading';
import { useDictStore } from '@/store';
import { dataColor } from '@/utils';
import { tagColor } from '@/views/asset/host-list/types/const';
import HostGroupView from './host-group-view.vue';
import HostListView from './host-list-view.vue';
const { loading, setLoading } = useLoading();
const { toOptions } = useDictStore();
const newConnectionType = ref(NewConnectionType.GROUP);
const newConnectionType = ref(NewConnectionType.LIST);
const filterValue = ref();
const filterOptions = ref<Array<SelectOptionData>>([]);
const hosts = ref<AuthorizedHostQueryResponse>({} as AuthorizedHostQueryResponse);
const hostList = ref<Array<HostQueryResponse>>([]);
// 修改连接类型
const changeConnectionType = () => {
// FIXME 持久化
};
// 加载主机信息
onBeforeMount(async () => {
// 过滤输入
const searchFilter = (searchValue: string, option: SelectOptionData) => {
if (searchValue.startsWith('@')) {
// tag 过滤
return option.isTag && (option.value as string).toLowerCase().startsWith(searchValue.substring(1, searchValue.length).toLowerCase());
} else {
// 文本过滤
return !option.isTag && (option.value as string).toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
}
};
// 数据处理
const shuffleHosts = () => {
let list = [...hosts.value?.hostList];
// 过滤
if (filterValue.value) {
console.log(filterValue.value);
}
// 排序
hostList.value = list?.sort((o1, o2) => {
if (o1.favorite || o2.favorite) {
if (o1.favorite && o2.favorite) {
return o2.id < o1.id ? 1 : -1;
}
return o2.favorite ? 1 : -1;
} else {
return o2.id < o1.id ? 1 : -1;
}
});
};
// 初始化过滤器项
const initFilterOptions = () => {
// 添加 tags
const tagNames = hosts.value.hostList?.map(s => s.tags)
.filter(s => s?.length)
.flat(1)
.sort((o1, o2) => o1.id - o2.id)
.map(s => s.name);
[...new Set(tagNames)].map(value => {
return { value: value, isTag: true };
}).forEach(s => filterOptions.value.push(s));
// 添加主机信息
const hostMeta = hosts.value.hostList?.map(s => {
return [s.name, s.code, s.address];
}).flat(1);
[...new Set(hostMeta)].map(value => {
return { value };
}).forEach(s => filterOptions.value.push(s));
// // 添加主机信息
// hosts.value.hostList?.map(s => {
// return `${s.name} (${s.code}) - ${s.address}`;
// }).map(value => {
// return { value };
// }).forEach(s => filterOptions.value.push(s));
};
// 初始化
const init = async () => {
try {
setLoading(true);
const { data } = await getCurrentAuthorizedHost();
hosts.value = data;
// 初始化过滤项
initFilterOptions();
// 处理数据
shuffleHosts();
} finally {
setLoading(false);
}
});
};
// 加载主机信息
onBeforeMount(init);
</script>
<style lang="less" scoped>
@@ -108,7 +198,7 @@
align-items: center;
justify-content: space-between;
.host-filter {
:deep(.host-filter) {
width: 36%;
}
}

View File

@@ -106,6 +106,8 @@
width: 100%;
height: 44px;
background: var(--color-bg-header);
position: relative;
z-index: 9999;
}
&-main {