✨ 授权主机选择模态框.
This commit is contained in:
@@ -9,10 +9,12 @@
|
||||
:columns="hostIdentityColumns"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
:row-selection="rowSelection"
|
||||
row-class="pointer"
|
||||
:sticky-header="true"
|
||||
:data="hostIdentities"
|
||||
:pagination="false"
|
||||
:bordered="false">
|
||||
:bordered="false"
|
||||
@row-click="clickRow">
|
||||
<!-- 秘钥名称 -->
|
||||
<template #keyId="{ record }">
|
||||
<a-tag color="arcoblue" v-if="record.keyId">
|
||||
@@ -30,6 +32,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
||||
import type { AssetAuthorizedDataQueryRequest, AssetDataGrantRequest } from '@/api/asset/asset-data-grant';
|
||||
import type { HostIdentityQueryResponse } from '@/api/asset/host-identity';
|
||||
import type { HostKeyQueryResponse } from '@/api/asset/host-key';
|
||||
@@ -81,6 +84,16 @@
|
||||
}
|
||||
};
|
||||
|
||||
// 点击行
|
||||
const clickRow = ({ id }: TableData) => {
|
||||
const index = selectedKeys.value.indexOf(id);
|
||||
if (index === -1) {
|
||||
selectedKeys.value.push(id);
|
||||
} else {
|
||||
selectedKeys.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化数据
|
||||
onMounted(async () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
:columns="hostKeyColumns"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
:row-selection="rowSelection"
|
||||
row-class="pointer"
|
||||
:sticky-header="true"
|
||||
:data="hostKeys"
|
||||
:pagination="false"
|
||||
:bordered="false" />
|
||||
:bordered="false"
|
||||
@row-click="clickRow" />
|
||||
</grant-layout>
|
||||
</template>
|
||||
|
||||
@@ -23,6 +25,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
||||
import type { AssetAuthorizedDataQueryRequest, AssetDataGrantRequest } from '@/api/asset/asset-data-grant';
|
||||
import type { HostKeyQueryResponse } from '@/api/asset/host-key';
|
||||
import { ref, onMounted } from 'vue';
|
||||
@@ -71,6 +74,16 @@
|
||||
}
|
||||
};
|
||||
|
||||
// 点击行
|
||||
const clickRow = ({ id }: TableData) => {
|
||||
const index = selectedKeys.value.indexOf(id);
|
||||
if (index === -1) {
|
||||
selectedKeys.value.push(id);
|
||||
} else {
|
||||
selectedKeys.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化数据
|
||||
onMounted(async () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -176,13 +176,13 @@
|
||||
import { computed, reactive, ref, onMounted } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { dataColor, objectTruthKeyCount, resetObject } from '@/utils';
|
||||
import fieldConfig from '../types/host.card.fields';
|
||||
import fieldConfig from '../types/card.fields';
|
||||
import { deleteHost, getHostPage } from '@/api/asset/host';
|
||||
import { Message, Modal } from '@arco-design/web-vue';
|
||||
import { tagColor } from '../types/const';
|
||||
import TagMultiSelector from '@/components/meta/tag/tag-multi-selector.vue';
|
||||
import useCopy from '@/hooks/copy';
|
||||
import { GrantKey, GrantRouteName } from '@/views/asset/grant/types/const';
|
||||
import TagMultiSelector from '@/components/meta/tag/tag-multi-selector.vue';
|
||||
|
||||
const emits = defineEmits(['openAdd', 'openUpdate', 'openUpdateConfig', 'openHostGroup']);
|
||||
|
||||
|
||||
@@ -63,13 +63,13 @@
|
||||
import { ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import formRules from '../types/host.form.rules';
|
||||
import formRules from '../types/form.rules';
|
||||
import { createHost, getHost, updateHost } from '@/api/asset/host';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { pick } from 'lodash';
|
||||
import { tagColor } from '@/views/asset/host-list/types/const';
|
||||
import TagMultiSelector from '@/components/meta/tag/tag-multi-selector.vue';
|
||||
import HostGroupTreeSelector from '@/components/asset/host-group/host-group-tree-selector.vue';
|
||||
import { tagColor } from '@/views/asset/host-list/types/const';
|
||||
|
||||
const { visible, setVisible } = useVisible();
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
@@ -112,7 +112,9 @@
|
||||
</template>
|
||||
<!-- 标签 -->
|
||||
<template #tags="{ record }">
|
||||
<a-space v-if="record.tags">
|
||||
<a-space v-if="record.tags"
|
||||
style="margin-bottom: -8px;"
|
||||
:wrap="true">
|
||||
<a-tag v-for="tag in record.tags"
|
||||
:key="tag.id"
|
||||
:color="dataColor(tag.name, tagColor)">
|
||||
@@ -166,14 +168,14 @@
|
||||
import { reactive, ref, onMounted } from 'vue';
|
||||
import { deleteHost, getHostPage } from '@/api/asset/host';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import columns from '../types/host.table.columns';
|
||||
import { tagColor } from '../types/const';
|
||||
import { usePagination } from '@/types/table';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useCopy from '@/hooks/copy';
|
||||
import columns from '../types/table.columns';
|
||||
import { dataColor } from '@/utils';
|
||||
import TagMultiSelector from '@/components/meta/tag/tag-multi-selector.vue';
|
||||
import { GrantKey, GrantRouteName } from '@/views/asset/grant/types/const';
|
||||
import TagMultiSelector from '@/components/meta/tag/tag-multi-selector.vue';
|
||||
|
||||
const tagSelector = ref();
|
||||
const tableRenderData = ref<HostQueryResponse[]>([]);
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
</a-grid-item>
|
||||
</a-grid>
|
||||
</div>
|
||||
<authorized-host-modal />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -40,7 +39,6 @@
|
||||
import QuickOperation from './components/quick-operation.vue';
|
||||
import Docs from './components/docs.vue';
|
||||
import OperatorLogSimpleTable from '@/views/user/operator-log/components/operator-log-simple-table.vue';
|
||||
import AuthorizedHostModal from '@/components/asset/authorized-host-modal/index.vue';
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
@@ -27,55 +27,55 @@
|
||||
<span class="host-item-left-name">
|
||||
<!-- 名称文本 -->
|
||||
<template v-if="!item.editable">
|
||||
<!-- 文本 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
:content="item.alias || `${item.name} (${item.code})`">
|
||||
<span class="host-item-text host-item-left-name-text">
|
||||
<template v-if="item.alias">
|
||||
{{ item.alias }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ `${item.name} (${item.code})` }}
|
||||
</template>
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<!-- 文本 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
:content="item.alias || `${item.name} (${item.code})`">
|
||||
<span class="host-item-text host-item-left-name-text">
|
||||
<template v-if="item.alias">
|
||||
{{ item.alias }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ `${item.name} (${item.code})` }}
|
||||
</template>
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<!-- 修改别名 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
content="修改别名">
|
||||
<icon-edit class="host-item-left-name-edit"
|
||||
@click="clickEditAlias(item)" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
content="修改别名">
|
||||
<icon-edit class="host-item-left-name-edit"
|
||||
@click="clickEditAlias(item)" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<!-- 名称输入框 -->
|
||||
<template v-else>
|
||||
<a-input v-model="item.alias"
|
||||
ref="aliasNameInput"
|
||||
class="host-item-left-name-input"
|
||||
:max-length="32"
|
||||
:disabled="item.loading"
|
||||
size="mini"
|
||||
:placeholder="item.name"
|
||||
@blur="saveAlias(item)"
|
||||
@pressEnter="saveAlias(item)"
|
||||
@change="saveAlias(item)">
|
||||
<template #suffix>
|
||||
<!-- 加载中 -->
|
||||
<icon-loading v-if="item.loading" />
|
||||
<!-- 保存 -->
|
||||
<icon-check v-else
|
||||
class="pointer"
|
||||
title="保存"
|
||||
@click="saveAlias(item)" />
|
||||
</template>
|
||||
</a-input>
|
||||
</template>
|
||||
<a-input v-model="item.alias"
|
||||
ref="aliasNameInput"
|
||||
class="host-item-left-name-input"
|
||||
:max-length="32"
|
||||
:disabled="item.loading"
|
||||
size="mini"
|
||||
:placeholder="item.name"
|
||||
@blur="saveAlias(item)"
|
||||
@pressEnter="saveAlias(item)"
|
||||
@change="saveAlias(item)">
|
||||
<template #suffix>
|
||||
<!-- 加载中 -->
|
||||
<icon-loading v-if="item.loading" />
|
||||
<!-- 保存 -->
|
||||
<icon-check v-else
|
||||
class="pointer"
|
||||
title="保存"
|
||||
@click="saveAlias(item)" />
|
||||
</template>
|
||||
</a-input>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
<!-- 中间ip -->
|
||||
|
||||
@@ -8,19 +8,9 @@
|
||||
:host-list="hostList"
|
||||
:filter-value="filterValue" />
|
||||
<!-- 列表视图 -->
|
||||
<host-list-view v-if="NewConnectionType.LIST === newConnectionType"
|
||||
:hostList="hostList"
|
||||
empty-value="无授权主机/主机未启用 SSH 配置!" />
|
||||
<!-- 我的收藏 -->
|
||||
<host-list-view v-if="NewConnectionType.FAVORITE === newConnectionType"
|
||||
class="list-view-container"
|
||||
:hostList="hostList"
|
||||
empty-value="无收藏记录, 快去点击主机右侧的⭐进行收藏吧!" />
|
||||
<!-- 最近连接 -->
|
||||
<host-list-view v-if="NewConnectionType.LATEST === newConnectionType"
|
||||
class="list-view-container"
|
||||
:hostList="hostList"
|
||||
empty-value="暂无连接记录, 快去体验吧!" />
|
||||
<host-list-view v-else
|
||||
:host-list="hostList"
|
||||
:empty-value="emptyMessage" />
|
||||
<!-- 主机设置模态框 -->
|
||||
<host-setting-modal ref="settingModal" />
|
||||
</div>
|
||||
@@ -33,7 +23,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, provide, ref, watch } from 'vue';
|
||||
import { computed, onMounted, provide, ref, watch } from 'vue';
|
||||
import { NewConnectionType, openSettingModalKey } from '../../types/terminal.const';
|
||||
import { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||
import { HostQueryResponse } from '@/api/asset/host';
|
||||
@@ -56,6 +46,20 @@
|
||||
);
|
||||
const settingModal = ref();
|
||||
|
||||
const emptyMessage = computed(() => {
|
||||
if (props.newConnectionType === NewConnectionType.LIST) {
|
||||
// 列表
|
||||
return '无授权主机/主机未启用 SSH 配置!';
|
||||
} else if (props.newConnectionType === NewConnectionType.FAVORITE) {
|
||||
// 收藏
|
||||
return '无收藏记录, 快去点击主机右侧的⭐进行收藏吧!';
|
||||
} else if (props.newConnectionType === NewConnectionType.LATEST) {
|
||||
// 最近连接
|
||||
return '暂无连接记录, 快去体验吧!';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// 暴露打开 ssh 配置模态框
|
||||
provide(openSettingModalKey, (record: any) => {
|
||||
settingModal.value?.open(record);
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
placeholder="别名/名称/编码/IP @标签"
|
||||
:allow-clear="true"
|
||||
:data="filterOptions"
|
||||
:filter-option="searchFilter">
|
||||
<template #option="{ data: { raw: { label, isTag} } }">
|
||||
:filter-option="tagLabelFilter">
|
||||
<template #option="{ data: { raw: { label, isTag } } }">
|
||||
<!-- tag -->
|
||||
<a-tag v-if="isTag" :color="dataColor(label, tagColor)">
|
||||
{{ label }}
|
||||
@@ -72,7 +72,9 @@
|
||||
import { useDictStore, useTerminalStore } from '@/store';
|
||||
import { TerminalPreferenceItem } from '@/store/modules/terminal';
|
||||
import { dataColor } from '@/utils';
|
||||
import { tagLabelFilter } from '@/types/form';
|
||||
import { tagColor } from '@/views/asset/host-list/types/const';
|
||||
import { getAuthorizedHostOptions } from '@/types/options';
|
||||
import HostsView from './hosts-view.vue';
|
||||
|
||||
const { toRadioOptions } = useDictStore();
|
||||
@@ -82,39 +84,10 @@
|
||||
const filterValue = ref('');
|
||||
const filterOptions = ref<Array<SelectOptionData>>([]);
|
||||
|
||||
// 过滤输入
|
||||
const searchFilter = (searchValue: string, option: SelectOptionData) => {
|
||||
if (searchValue.startsWith('@')) {
|
||||
// tag 过滤
|
||||
return option.isTag && (option.label as string).toLowerCase().startsWith(searchValue.substring(1, searchValue.length).toLowerCase());
|
||||
} else {
|
||||
// 文本过滤
|
||||
return !option.isTag && (option.label as string).toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化过滤器项
|
||||
const initFilterOptions = () => {
|
||||
// 添加 tags
|
||||
const tagNames = hosts.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 { label: value, value: `@${value}`, isTag: true };
|
||||
}).forEach(s => filterOptions.value.push(s));
|
||||
// 添加主机信息
|
||||
const hostMeta = hosts.hostList?.map(s => {
|
||||
return [s.name, s.code, s.address, s.alias];
|
||||
}).filter(Boolean).flat(1);
|
||||
[...new Set(hostMeta)].map(value => {
|
||||
return { label: value, value };
|
||||
}).forEach(s => filterOptions.value.push(s));
|
||||
};
|
||||
|
||||
// 初始化过滤器项
|
||||
onBeforeMount(initFilterOptions);
|
||||
onBeforeMount(() => {
|
||||
filterOptions.value = getAuthorizedHostOptions(hosts.hostList);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user