feat: 主机列表视图.
This commit is contained in:
@@ -33,7 +33,4 @@ public class AuthorizedHostWrapperVO {
|
|||||||
@Schema(description = "分组树节点映射 'groupId':hostIdList")
|
@Schema(description = "分组树节点映射 'groupId':hostIdList")
|
||||||
private Map<String, Set<Long>> treeNodes;
|
private Map<String, Set<Long>> treeNodes;
|
||||||
|
|
||||||
// TODO 我的收藏
|
|
||||||
// TODO 最近连接
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ public class HostVO implements Serializable {
|
|||||||
@Schema(description = "修改人")
|
@Schema(description = "修改人")
|
||||||
private String updater;
|
private String updater;
|
||||||
|
|
||||||
|
@Schema(description = "是否收藏")
|
||||||
|
private Boolean favorite;
|
||||||
|
|
||||||
@Schema(description = "tags")
|
@Schema(description = "tags")
|
||||||
private List<TagDTO> tags;
|
private List<TagDTO> tags;
|
||||||
|
|
||||||
|
|||||||
@@ -12,18 +12,20 @@ import com.orion.ops.module.asset.service.AssetAuthorizedDataService;
|
|||||||
import com.orion.ops.module.asset.service.HostIdentityService;
|
import com.orion.ops.module.asset.service.HostIdentityService;
|
||||||
import com.orion.ops.module.asset.service.HostKeyService;
|
import com.orion.ops.module.asset.service.HostKeyService;
|
||||||
import com.orion.ops.module.asset.service.HostService;
|
import com.orion.ops.module.asset.service.HostService;
|
||||||
import com.orion.ops.module.infra.api.DataGroupApi;
|
import com.orion.ops.module.infra.api.*;
|
||||||
import com.orion.ops.module.infra.api.DataGroupRelApi;
|
|
||||||
import com.orion.ops.module.infra.api.DataPermissionApi;
|
|
||||||
import com.orion.ops.module.infra.api.SystemUserApi;
|
|
||||||
import com.orion.ops.module.infra.entity.dto.data.DataGroupDTO;
|
import com.orion.ops.module.infra.entity.dto.data.DataGroupDTO;
|
||||||
|
import com.orion.ops.module.infra.entity.dto.tag.TagDTO;
|
||||||
import com.orion.ops.module.infra.enums.DataGroupTypeEnum;
|
import com.orion.ops.module.infra.enums.DataGroupTypeEnum;
|
||||||
import com.orion.ops.module.infra.enums.DataPermissionTypeEnum;
|
import com.orion.ops.module.infra.enums.DataPermissionTypeEnum;
|
||||||
|
import com.orion.ops.module.infra.enums.FavoriteTypeEnum;
|
||||||
|
import com.orion.ops.module.infra.enums.TagTypeEnum;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -59,6 +61,12 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
|||||||
@Resource
|
@Resource
|
||||||
private HostIdentityService hostIdentityService;
|
private HostIdentityService hostIdentityService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FavoriteApi favoriteApi;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TagRelApi tagRelApi;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Long> getAuthorizedDataRelId(DataPermissionTypeEnum type, AssetAuthorizedDataQueryRequest request) {
|
public List<Long> getAuthorizedDataRelId(DataPermissionTypeEnum type, AssetAuthorizedDataQueryRequest request) {
|
||||||
Long userId = request.getUserId();
|
Long userId = request.getUserId();
|
||||||
@@ -77,7 +85,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
|||||||
public AuthorizedHostWrapperVO getUserAuthorizedHostGroup(Long userId) {
|
public AuthorizedHostWrapperVO getUserAuthorizedHostGroup(Long userId) {
|
||||||
if (systemUserApi.isAdminUser(userId)) {
|
if (systemUserApi.isAdminUser(userId)) {
|
||||||
// 管理员查询所有
|
// 管理员查询所有
|
||||||
return this.buildUserAuthorizedHostGroup(null);
|
return this.buildUserAuthorizedHostGroup(userId, null);
|
||||||
} else {
|
} else {
|
||||||
// 其他用户 查询授权的数据
|
// 其他用户 查询授权的数据
|
||||||
List<Long> authorizedIdList = dataPermissionApi.getUserAuthorizedRelIdList(DataPermissionTypeEnum.HOST_GROUP, userId);
|
List<Long> authorizedIdList = dataPermissionApi.getUserAuthorizedRelIdList(DataPermissionTypeEnum.HOST_GROUP, userId);
|
||||||
@@ -88,7 +96,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
|||||||
.hostList(Lists.empty())
|
.hostList(Lists.empty())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
return this.buildUserAuthorizedHostGroup(authorizedIdList);
|
return this.buildUserAuthorizedHostGroup(userId, authorizedIdList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,14 +147,16 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
|||||||
/**
|
/**
|
||||||
* 构建授权的主机分组树
|
* 构建授权的主机分组树
|
||||||
*
|
*
|
||||||
|
* @param userId userId
|
||||||
* @param authorizedGroupIdList authorizedGroupIdList
|
* @param authorizedGroupIdList authorizedGroupIdList
|
||||||
* @return tree
|
* @return tree
|
||||||
*/
|
*/
|
||||||
private AuthorizedHostWrapperVO buildUserAuthorizedHostGroup(List<Long> authorizedGroupIdList) {
|
@SneakyThrows
|
||||||
|
private AuthorizedHostWrapperVO buildUserAuthorizedHostGroup(Long userId, List<Long> authorizedGroupIdList) {
|
||||||
final boolean allData = Lists.isEmpty(authorizedGroupIdList);
|
final boolean allData = Lists.isEmpty(authorizedGroupIdList);
|
||||||
AuthorizedHostWrapperVO wrapper = new AuthorizedHostWrapperVO();
|
AuthorizedHostWrapperVO wrapper = new AuthorizedHostWrapperVO();
|
||||||
// TODO async get 最近连接
|
// 查询我的收藏
|
||||||
// TODO async get 我的收藏
|
Future<List<Long>> favoriteResult = favoriteApi.getFavoriteRelIdListAsync(FavoriteTypeEnum.HOST, userId);
|
||||||
// 查询分组
|
// 查询分组
|
||||||
List<DataGroupDTO> dataGroup = dataGroupApi.getDataGroupList(DataGroupTypeEnum.HOST);
|
List<DataGroupDTO> dataGroup = dataGroupApi.getDataGroupList(DataGroupTypeEnum.HOST);
|
||||||
// 查询分组引用
|
// 查询分组引用
|
||||||
@@ -161,12 +171,17 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
|||||||
// 设置主机分组树
|
// 设置主机分组树
|
||||||
wrapper.setGroupTree(this.getAuthorizedHostGroupTree(dataGroup));
|
wrapper.setGroupTree(this.getAuthorizedHostGroupTree(dataGroup));
|
||||||
// 设置主机分组下的主机
|
// 设置主机分组下的主机
|
||||||
wrapper.setTreeNodes(this.getAuthorizedHostGroupNodes(allData, dataGroup, dataGroupRel, authorizedGroupIdList));
|
wrapper.setTreeNodes(this.getAuthorizedHostGroupNodes(allData,
|
||||||
|
dataGroup,
|
||||||
|
dataGroupRel,
|
||||||
|
authorizedGroupIdList));
|
||||||
// 设置已授权的所有主机
|
// 设置已授权的所有主机
|
||||||
wrapper.setHostList(this.getAuthorizedHostList(allData, dataGroup, dataGroupRel, authorizedGroupIdList));
|
wrapper.setHostList(this.getAuthorizedHostList(allData,
|
||||||
// TODO set 最近连接
|
dataGroup,
|
||||||
// TODO set 我的收藏
|
dataGroupRel,
|
||||||
|
authorizedGroupIdList));
|
||||||
|
// 设置主机拓展信息
|
||||||
|
this.getAuthorizedHostExtra(wrapper.getHostList(), favoriteResult.get());
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +238,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
|||||||
List<Long> authorizedGroupIdList) {
|
List<Long> authorizedGroupIdList) {
|
||||||
// 查询主机列表
|
// 查询主机列表
|
||||||
List<HostVO> hosts = hostService.getHostListByCache();
|
List<HostVO> hosts = hostService.getHostListByCache();
|
||||||
|
|
||||||
// 全部数据直接返回
|
// 全部数据直接返回
|
||||||
if (allData) {
|
if (allData) {
|
||||||
return hosts;
|
return hosts;
|
||||||
@@ -232,6 +248,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
|||||||
// 仅设置已授权的数据
|
// 仅设置已授权的数据
|
||||||
return dataGroup.stream()
|
return dataGroup.stream()
|
||||||
.map(DataGroupDTO::getId)
|
.map(DataGroupDTO::getId)
|
||||||
|
// 因为可能父菜单没有授权 这里需要判断分组权限
|
||||||
.filter(authorizedGroupIdList::contains)
|
.filter(authorizedGroupIdList::contains)
|
||||||
.map(dataGroupRel::get)
|
.map(dataGroupRel::get)
|
||||||
.filter(Lists::isNoneEmpty)
|
.filter(Lists::isNoneEmpty)
|
||||||
@@ -241,4 +258,29 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置授权主机的额外参数
|
||||||
|
*
|
||||||
|
* @param hosts hosts
|
||||||
|
* @param favorite favorite
|
||||||
|
*/
|
||||||
|
private void getAuthorizedHostExtra(List<HostVO> hosts,
|
||||||
|
List<Long> favorite) {
|
||||||
|
if (Lists.isEmpty(hosts)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置收藏结果
|
||||||
|
if (!Lists.isEmpty(favorite)) {
|
||||||
|
hosts.forEach(s -> s.setFavorite(favorite.contains(s.getId())));
|
||||||
|
}
|
||||||
|
List<Long> hostIdList = hosts.stream()
|
||||||
|
.map(HostVO::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
// 查询 tag 信息
|
||||||
|
List<List<TagDTO>> tags = tagRelApi.getRelTags(TagTypeEnum.HOST, hostIdList);
|
||||||
|
for (int i = 0; i < hosts.size(); i++) {
|
||||||
|
hosts.get(i).setTags(tags.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.orion.ops.module.infra.api;
|
|||||||
import com.orion.ops.module.infra.enums.FavoriteTypeEnum;
|
import com.orion.ops.module.infra.enums.FavoriteTypeEnum;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收藏 对外服务类
|
* 收藏 对外服务类
|
||||||
@@ -22,6 +23,15 @@ public interface FavoriteApi {
|
|||||||
*/
|
*/
|
||||||
List<Long> getFavoriteRelIdList(FavoriteTypeEnum type, Long userId);
|
List<Long> getFavoriteRelIdList(FavoriteTypeEnum type, Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步查询用户收藏
|
||||||
|
*
|
||||||
|
* @param type type
|
||||||
|
* @param userId userId
|
||||||
|
* @return relIdList
|
||||||
|
*/
|
||||||
|
Future<List<Long>> getFavoriteRelIdListAsync(FavoriteTypeEnum type, java.lang.Long userId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过 relId 删除收藏
|
* 通过 relId 删除收藏
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收藏 对外服务实现类
|
* 收藏 对外服务实现类
|
||||||
@@ -38,6 +40,14 @@ public class FavoriteApiImpl implements FavoriteApi {
|
|||||||
return favoriteService.getFavoriteRelIdList(request);
|
return favoriteService.getFavoriteRelIdList(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Async("asyncExecutor")
|
||||||
|
public Future<List<Long>> getFavoriteRelIdListAsync(FavoriteTypeEnum type, Long userId) {
|
||||||
|
Valid.allNotNull(type, userId);
|
||||||
|
// 查询
|
||||||
|
return CompletableFuture.completedFuture(this.getFavoriteRelIdList(type, userId));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteByRelId(FavoriteTypeEnum type, Long relId) {
|
public void deleteByRelId(FavoriteTypeEnum type, Long relId) {
|
||||||
Valid.allNotNull(type, relId);
|
Valid.allNotNull(type, relId);
|
||||||
|
|||||||
@@ -46,16 +46,24 @@ public class FavoriteServiceImpl implements FavoriteService {
|
|||||||
public Long addFavorite(FavoriteOperatorRequest request) {
|
public Long addFavorite(FavoriteOperatorRequest request) {
|
||||||
String type = Valid.valid(FavoriteTypeEnum::of, request.getType()).name();
|
String type = Valid.valid(FavoriteTypeEnum::of, request.getType()).name();
|
||||||
Long userId = SecurityUtils.getLoginUserId();
|
Long userId = SecurityUtils.getLoginUserId();
|
||||||
|
// 检查是否存在
|
||||||
|
FavoriteDO check = favoriteDAO.of()
|
||||||
|
.createWrapper()
|
||||||
|
.eq(FavoriteDO::getUserId, userId)
|
||||||
|
.eq(FavoriteDO::getType, request.getType())
|
||||||
|
.eq(FavoriteDO::getRelId, request.getRelId())
|
||||||
|
.then()
|
||||||
|
.getOne();
|
||||||
|
if (check != null) {
|
||||||
|
return check.getId();
|
||||||
|
}
|
||||||
// 转换
|
// 转换
|
||||||
FavoriteDO record = FavoriteConvert.MAPPER.to(request);
|
FavoriteDO record = FavoriteConvert.MAPPER.to(request);
|
||||||
record.setUserId(userId);
|
record.setUserId(userId);
|
||||||
// 插入
|
// 插入
|
||||||
int effect = favoriteDAO.insert(record);
|
int effect = favoriteDAO.insert(record);
|
||||||
// 设置缓存
|
// 删除缓存
|
||||||
String key = FavoriteCacheKeyDefine.FAVORITE.format(type, userId);
|
RedisLists.delete(FavoriteCacheKeyDefine.FAVORITE.format(type, userId));
|
||||||
RedisLists.push(key, request.getRelId(), String::valueOf);
|
|
||||||
// 设置过期时间
|
|
||||||
RedisLists.setExpire(key, FavoriteCacheKeyDefine.FAVORITE);
|
|
||||||
return record.getId();
|
return record.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +75,7 @@ public class FavoriteServiceImpl implements FavoriteService {
|
|||||||
// 删除库
|
// 删除库
|
||||||
int effect = favoriteDAO.deleteFavorite(type, userId, relId);
|
int effect = favoriteDAO.deleteFavorite(type, userId, relId);
|
||||||
// 删除缓存
|
// 删除缓存
|
||||||
String key = FavoriteCacheKeyDefine.FAVORITE.format(type, userId);
|
RedisLists.delete(FavoriteCacheKeyDefine.FAVORITE.format(type, userId));
|
||||||
redisTemplate.opsForList().remove(key, 1, relId.toString());
|
|
||||||
return effect;
|
return effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export interface HostQueryRequest extends Pagination {
|
|||||||
name?: string;
|
name?: string;
|
||||||
code?: string;
|
code?: string;
|
||||||
address?: string;
|
address?: string;
|
||||||
favorite?: boolean;
|
|
||||||
tags?: Array<number>;
|
tags?: Array<number>;
|
||||||
queryTag?: boolean;
|
queryTag?: boolean;
|
||||||
}
|
}
|
||||||
@@ -46,7 +45,6 @@ export interface HostQueryResponse extends TableData {
|
|||||||
updateTime: number;
|
updateTime: number;
|
||||||
creator: string;
|
creator: string;
|
||||||
updater: string;
|
updater: string;
|
||||||
// FIXME 删除
|
|
||||||
favorite: boolean;
|
favorite: boolean;
|
||||||
tags: Array<{ id: number, name: string }>;
|
tags: Array<{ id: number, name: string }>;
|
||||||
groupIdList: Array<number>;
|
groupIdList: Array<number>;
|
||||||
|
|||||||
@@ -94,6 +94,10 @@ body {
|
|||||||
border-color: rgb(var(--gray-2));
|
border-color: rgb(var(--gray-2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.usn {
|
.usn {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
@@ -106,16 +110,28 @@ body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m0 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.mx0 {
|
.mx0 {
|
||||||
margin: 0 0;
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my0 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx2 {
|
.mx2 {
|
||||||
margin: 0 2px;
|
margin-left: 2px;
|
||||||
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx4 {
|
.mx4 {
|
||||||
margin: 0 4px;
|
margin-left: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ml4 {
|
.ml4 {
|
||||||
|
|||||||
@@ -1,30 +1,29 @@
|
|||||||
import type { FavoriteType } from '@/api/meta/favorite';
|
import type { FavoriteType } from '@/api/meta/favorite';
|
||||||
import { Message } from '@arco-design/web-vue';
|
|
||||||
import { addFavorite, cancelFavorite } from '@/api/meta/favorite';
|
import { addFavorite, cancelFavorite } from '@/api/meta/favorite';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
export default function useFavorite(type: FavoriteType) {
|
export default function useFavorite(type: FavoriteType) {
|
||||||
|
const loading = ref(false);
|
||||||
const toggle = async (record: any, id: number, cancelField = 'favorite') => {
|
const toggle = async (record: any, id: number, cancelField = 'favorite') => {
|
||||||
const request = { relId: id, type };
|
const request = { relId: id, type };
|
||||||
Message.clear();
|
|
||||||
const loading = Message.loading(record[cancelField] ? '取消中' : '收藏中');
|
|
||||||
try {
|
try {
|
||||||
|
loading.value = true;
|
||||||
if (record[cancelField]) {
|
if (record[cancelField]) {
|
||||||
// 取消收藏
|
// 取消收藏
|
||||||
await cancelFavorite(request);
|
await cancelFavorite(request);
|
||||||
record[cancelField] = false;
|
record[cancelField] = false;
|
||||||
Message.success('已取消');
|
|
||||||
} else {
|
} else {
|
||||||
// 添加收藏
|
// 添加收藏
|
||||||
await addFavorite(request);
|
await addFavorite(request);
|
||||||
record[cancelField] = true;
|
record[cancelField] = true;
|
||||||
Message.success('已收藏');
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
} finally {
|
} finally {
|
||||||
loading.close();
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
|
loading,
|
||||||
toggle
|
toggle
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,7 +199,6 @@
|
|||||||
name: undefined,
|
name: undefined,
|
||||||
code: undefined,
|
code: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
favorite: undefined,
|
|
||||||
tags: undefined,
|
tags: undefined,
|
||||||
queryTag: true
|
queryTag: true
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,18 +49,6 @@
|
|||||||
<!-- 右侧操作 -->
|
<!-- 右侧操作 -->
|
||||||
<div class="table-right-bar-handle">
|
<div class="table-right-bar-handle">
|
||||||
<a-space>
|
<a-space>
|
||||||
<!-- 仅看收藏 fixme 去掉 -->
|
|
||||||
<a-checkbox v-if="false" v-model="formModel.favorite" @change="fetchTableData()">
|
|
||||||
<template #checkbox="{ checked }">
|
|
||||||
<a-tag :checked="checked"
|
|
||||||
class="only-favorite"
|
|
||||||
size="large"
|
|
||||||
color="arcoblue"
|
|
||||||
checkable>
|
|
||||||
仅看收藏
|
|
||||||
</a-tag>
|
|
||||||
</template>
|
|
||||||
</a-checkbox>
|
|
||||||
<!-- 主机分组 -->
|
<!-- 主机分组 -->
|
||||||
<a-button type="primary"
|
<a-button type="primary"
|
||||||
v-permission="['asset:host-group:update']"
|
v-permission="['asset:host-group:update']"
|
||||||
@@ -162,19 +150,6 @@
|
|||||||
删除
|
删除
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
<!-- 收藏 -->
|
|
||||||
<span v-if="record.favorite"
|
|
||||||
title="取消收藏"
|
|
||||||
class="host-favorite host-favorite-choice"
|
|
||||||
@click="() => toggleFavorite(record, record.id)">
|
|
||||||
<icon-star-fill />
|
|
||||||
</span>
|
|
||||||
<span v-else
|
|
||||||
title="收藏"
|
|
||||||
class="host-favorite host-favorite-un-choice"
|
|
||||||
@click="() => toggleFavorite(record, record.id)">
|
|
||||||
<icon-star />
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
@@ -197,7 +172,6 @@
|
|||||||
import { tagColor } from '../types/const';
|
import { tagColor } from '../types/const';
|
||||||
import { usePagination } from '@/types/table';
|
import { usePagination } from '@/types/table';
|
||||||
import useCopy from '@/hooks/copy';
|
import useCopy from '@/hooks/copy';
|
||||||
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';
|
import { GrantKey, GrantRouteName } from '@/views/asset/grant/types/const';
|
||||||
@@ -208,7 +182,6 @@
|
|||||||
const emits = defineEmits(['openAdd', 'openUpdate', 'openUpdateConfig', 'openHostGroup']);
|
const emits = defineEmits(['openAdd', 'openUpdate', 'openUpdateConfig', 'openHostGroup']);
|
||||||
|
|
||||||
const { copy } = useCopy();
|
const { copy } = useCopy();
|
||||||
const { toggle: toggleFavorite } = useFavorite('HOST');
|
|
||||||
|
|
||||||
const pagination = usePagination();
|
const pagination = usePagination();
|
||||||
|
|
||||||
@@ -217,7 +190,6 @@
|
|||||||
name: undefined,
|
name: undefined,
|
||||||
code: undefined,
|
code: undefined,
|
||||||
address: undefined,
|
address: undefined,
|
||||||
favorite: undefined,
|
|
||||||
tags: undefined,
|
tags: undefined,
|
||||||
queryTag: true
|
queryTag: true
|
||||||
});
|
});
|
||||||
@@ -284,37 +256,5 @@
|
|||||||
.row-handle-wrapper {
|
.row-handle-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.host-favorite {
|
|
||||||
cursor: pointer;
|
|
||||||
line-height: 19px;
|
|
||||||
font-size: 19px;
|
|
||||||
margin: 0 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.host-favorite-choice {
|
|
||||||
color: rgb(var(--yellow-6));
|
|
||||||
|
|
||||||
:hover {
|
|
||||||
transition: .2s;
|
|
||||||
color: rgba(232, 203, 12, .9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.host-favorite-un-choice {
|
|
||||||
color: rgb(var(--gray-6));
|
|
||||||
|
|
||||||
:hover {
|
|
||||||
transition: .2s;
|
|
||||||
color: rgb(var(--yellow-6));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.only-favorite {
|
|
||||||
user-select: none;
|
|
||||||
color: rgb(var(--arcoblue-5));
|
|
||||||
background: rgb(var(--gray-2));
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<template>
|
||||||
|
<div class="list-view-container">
|
||||||
|
<!-- 主机列表 -->
|
||||||
|
<host-list :hosts="hosts" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'hostListView'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||||
|
import HostList from './host-list.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
hosts: AuthorizedHostQueryResponse
|
||||||
|
}>();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.list-view-container {
|
||||||
|
max-height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-list size="large"
|
<a-list ref="listRel"
|
||||||
|
size="large"
|
||||||
max-height="100%"
|
max-height="100%"
|
||||||
:hoverable="true"
|
:hoverable="true"
|
||||||
:data="hosts.hostList">
|
:data="hosts.hostList">
|
||||||
@@ -14,7 +15,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- 数据 -->
|
<!-- 数据 -->
|
||||||
<template #item="{ item }">
|
<template #item="{ item }">
|
||||||
<a-list-item class="host-item-wrapper">
|
<a-list-item class="host-item-wrapper" @click="openTerminal(item)">
|
||||||
<div class="host-item">
|
<div class="host-item">
|
||||||
<!-- 左侧图标-名称 -->
|
<!-- 左侧图标-名称 -->
|
||||||
<div class="flex-center host-item-left">
|
<div class="flex-center host-item-left">
|
||||||
@@ -46,24 +47,65 @@
|
|||||||
</span>
|
</span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<!-- flex-center 右侧tag-操作 -->
|
<!-- 右侧tag-操作 -->
|
||||||
<div class="flex-center host-item-right">
|
<div class="flex-center host-item-right">
|
||||||
<div v-if="item.code === 'main'">
|
<!-- tags -->
|
||||||
<a-tag v-for="i in 5"
|
<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"
|
class="host-item-text"
|
||||||
|
:key="tag.id"
|
||||||
:style="{
|
:style="{
|
||||||
width: `calc(${100/5}% - ${i !== 5 ? '8px' : '0px'})`,
|
maxWidth: `calc(${100 / item.tags.length}% - ${i !== item.tags.length - 1 ? '8px' : '0px'})`,
|
||||||
maxWidth: `calc(${100/5}% - ${i !== 5 ? '8px' : '0px'})`,
|
marginRight: `${i !== item.tags.length - 1 ? '8px' : '0'}`,
|
||||||
marginRight: `${i !== 5 ? '8px' : '0'}`,
|
|
||||||
}"
|
}"
|
||||||
color="arcoblue">
|
:color="dataColor(tag.name, tagColor)">
|
||||||
<template v-for="j in i*5">
|
{{ tag.name }}
|
||||||
{{ j }}
|
|
||||||
</template>
|
|
||||||
</a-tag>
|
</a-tag>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<!-- 操作 -->
|
||||||
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,11 +122,37 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
import { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||||
|
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<{
|
const props = defineProps<{
|
||||||
hosts: AuthorizedHostQueryResponse
|
hosts: AuthorizedHostQueryResponse
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const { toggle: toggleFavorite, loading: favoriteLoading } = useFavorite('HOST');
|
||||||
|
|
||||||
|
const listRel = ref();
|
||||||
|
|
||||||
|
// 打开终端
|
||||||
|
const openTerminal = (item: any) => {
|
||||||
|
console.log('ter', item);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开配置
|
||||||
|
const openSetting = (item: any) => {
|
||||||
|
console.log('set', item);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置收藏
|
||||||
|
const setFavorite = async (item: any) => {
|
||||||
|
if (favoriteLoading.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await toggleFavorite(item, item.id);
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@@ -160,13 +228,44 @@
|
|||||||
width: 25%;
|
width: 25%;
|
||||||
|
|
||||||
&-address {
|
&-address {
|
||||||
max-width: calc(100% - 8px);
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.host-item-right {
|
.host-item-right {
|
||||||
width: 40%;
|
width: 40%;
|
||||||
|
height: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&-tags {
|
||||||
|
// 必须设置 最外层用的是 min-width
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-actions {
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.host-item-right-tags {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.host-item-right-actions {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.favorite {
|
||||||
|
color: rgb(var(--yellow-6));
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -48,6 +48,8 @@
|
|||||||
<host-group-view v-if="NewConnectionType.GROUP === newConnectionType"
|
<host-group-view v-if="NewConnectionType.GROUP === newConnectionType"
|
||||||
:hosts="hosts" />
|
:hosts="hosts" />
|
||||||
<!-- 列表视图 -->
|
<!-- 列表视图 -->
|
||||||
|
<host-list-view v-if="NewConnectionType.LIST === newConnectionType"
|
||||||
|
:hosts="hosts" />
|
||||||
|
|
||||||
<!-- 我的收藏 -->
|
<!-- 我的收藏 -->
|
||||||
|
|
||||||
@@ -72,8 +74,9 @@
|
|||||||
import { onBeforeMount, ref } from 'vue';
|
import { onBeforeMount, ref } from 'vue';
|
||||||
import { NewConnectionType, NewConnectionTypeKey } from '../../types/terminal.const';
|
import { NewConnectionType, NewConnectionTypeKey } from '../../types/terminal.const';
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
import HostGroupView from './host-group-view.vue';
|
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore } from '@/store';
|
||||||
|
import HostGroupView from './host-group-view.vue';
|
||||||
|
import HostListView from './host-list-view.vue';
|
||||||
|
|
||||||
const { loading, setLoading } = useLoading();
|
const { loading, setLoading } = useLoading();
|
||||||
const { toOptions } = useDictStore();
|
const { toOptions } = useDictStore();
|
||||||
|
|||||||
Reference in New Issue
Block a user