⚡ 优化资产授权交互逻辑.
This commit is contained in:
@@ -27,15 +27,30 @@
|
||||
</template>
|
||||
</span>
|
||||
</a-alert>
|
||||
<!-- 授权 -->
|
||||
<a-button class="grant-button"
|
||||
type="primary"
|
||||
@click="doGrant">
|
||||
授权
|
||||
<template #icon>
|
||||
<icon-safe />
|
||||
</template>
|
||||
</a-button>
|
||||
<!-- 操作按钮 -->
|
||||
<a-space>
|
||||
<!-- 全选 -->
|
||||
<a-button @click="emits('selectAll')">
|
||||
全选
|
||||
<template #icon>
|
||||
<icon-select-all />
|
||||
</template>
|
||||
</a-button>
|
||||
<!-- 反选 -->
|
||||
<a-button @click="emits('reverse')">
|
||||
反选
|
||||
<template #icon>
|
||||
<icon-list />
|
||||
</template>
|
||||
</a-button>
|
||||
<!-- 授权 -->
|
||||
<a-button type="primary" @click="doGrant">
|
||||
授权
|
||||
<template #icon>
|
||||
<icon-safe />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<!-- 主体部分 -->
|
||||
<div class="data-main">
|
||||
@@ -63,7 +78,7 @@
|
||||
type: string;
|
||||
loading: boolean;
|
||||
}>();
|
||||
const emits = defineEmits(['fetch', 'grant']);
|
||||
const emits = defineEmits(['fetch', 'grant', 'selectAll', 'reverse']);
|
||||
|
||||
const subjectId = ref();
|
||||
const currentSubject = ref();
|
||||
@@ -115,16 +130,13 @@
|
||||
|
||||
.alert-wrapper {
|
||||
padding: 4px 16px;
|
||||
margin-right: 12px;
|
||||
|
||||
.alert-message {
|
||||
display: block;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.grant-button {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.data-main {
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
<grant-layout :type="type"
|
||||
:loading="loading"
|
||||
@fetch="fetchAuthorizedData"
|
||||
@grant="doGrant">
|
||||
@grant="doGrant"
|
||||
@select-all="selectAll"
|
||||
@reverse="reverseSelect">
|
||||
<!-- 分组 -->
|
||||
<host-group-tree v-model:checked-keys="checkedGroups"
|
||||
ref="tree"
|
||||
outer-class="group-main-tree"
|
||||
:checkable="true"
|
||||
:editable="false"
|
||||
:loading="loading"
|
||||
@set-loading="setLoading"
|
||||
@selected-node="(e) => selectedGroup = e" />
|
||||
@selected-node="(e) => selectedGroup = e"
|
||||
@on-selected="clickGroup" />
|
||||
<!-- 主机列表 -->
|
||||
<host-list class="group-main-hosts sticky-list" :group="selectedGroup" />
|
||||
</grant-layout>
|
||||
@@ -28,6 +32,8 @@
|
||||
import { ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { getAuthorizedHostGroup, grantHostGroup } from '@/api/asset/asset-data-grant';
|
||||
import { useCacheStore } from '@/store';
|
||||
import { flatNodeKeys } from '@/utils/tree';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import HostGroupTree from '@/components/asset/host-group/tree/index.vue';
|
||||
import HostList from './host-list.vue';
|
||||
@@ -37,12 +43,27 @@
|
||||
type: string;
|
||||
}>();
|
||||
|
||||
const cacheStore = useCacheStore();
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
const tree = ref();
|
||||
const authorizedGroups = ref<Array<number>>([]);
|
||||
const checkedGroups = ref<Array<number>>([]);
|
||||
const selectedGroup = ref<TreeNodeData>({});
|
||||
|
||||
// 点击分组
|
||||
const clickGroup = (groups: Array<number>) => {
|
||||
if (groups && groups.length) {
|
||||
const group = groups[0];
|
||||
const index = checkedGroups.value.findIndex((s) => s === group);
|
||||
if (index === -1) {
|
||||
checkedGroups.value.push(group);
|
||||
} else {
|
||||
checkedGroups.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 获取授权列表
|
||||
const fetchAuthorizedData = async (request: AssetAuthorizedDataQueryRequest) => {
|
||||
setLoading(true);
|
||||
@@ -74,6 +95,24 @@
|
||||
await fetchAuthorizedData(request);
|
||||
};
|
||||
|
||||
// 全选
|
||||
const selectAll = async () => {
|
||||
// 从缓存中查询全部分组
|
||||
const groups = await cacheStore.loadHostGroups();
|
||||
const groupKeys: number[] = [];
|
||||
flatNodeKeys(groups, groupKeys);
|
||||
checkedGroups.value = groupKeys;
|
||||
};
|
||||
|
||||
// 反选
|
||||
const reverseSelect = async () => {
|
||||
// 从缓存中查询全部分组
|
||||
const groups = await cacheStore.loadHostGroups();
|
||||
const groupKeys: number[] = [];
|
||||
flatNodeKeys(groups, groupKeys);
|
||||
checkedGroups.value = groupKeys.filter(s => !checkedGroups.value.includes(s));
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<grant-layout :type="type"
|
||||
:loading="loading"
|
||||
@fetch="fetchAuthorizedData"
|
||||
@grant="doGrant">
|
||||
@grant="doGrant"
|
||||
@select-all="selectAll"
|
||||
@reverse="reverseSelect">
|
||||
<!-- 主机身份表格 -->
|
||||
<a-table row-key="id"
|
||||
class="host-identity-main-table"
|
||||
@@ -102,6 +104,17 @@
|
||||
await fetchAuthorizedData(request);
|
||||
};
|
||||
|
||||
// 全选
|
||||
const selectAll = () => {
|
||||
selectedKeys.value = hostIdentities.value.map(s => s.id);
|
||||
};
|
||||
|
||||
// 反选
|
||||
const reverseSelect = () => {
|
||||
selectedKeys.value = hostIdentities.value.map(s => s.id)
|
||||
.filter(s => !selectedKeys.value.includes(s));
|
||||
};
|
||||
|
||||
// 点击行
|
||||
const clickRow = ({ id }: TableData) => {
|
||||
const index = selectedKeys.value.indexOf(id);
|
||||
@@ -112,7 +125,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化数据
|
||||
// 初始化身份数据
|
||||
onMounted(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
@@ -124,7 +137,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化数据
|
||||
// 初始化秘钥数据
|
||||
onMounted(async () => {
|
||||
// 加载主机秘钥
|
||||
hostKeys.value = await cacheStore.loadHostKeys();
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<grant-layout :type="type"
|
||||
:loading="loading"
|
||||
@fetch="fetchAuthorizedData"
|
||||
@grant="doGrant">
|
||||
@grant="doGrant"
|
||||
@select-all="selectAll"
|
||||
@reverse="reverseSelect">
|
||||
<!-- 主机秘钥表格 -->
|
||||
<a-table row-key="id"
|
||||
class="host-key-main-table"
|
||||
@@ -78,6 +80,17 @@
|
||||
await fetchAuthorizedData(request);
|
||||
};
|
||||
|
||||
// 全选
|
||||
const selectAll = () => {
|
||||
selectedKeys.value = hostKeys.value.map(s => s.id);
|
||||
};
|
||||
|
||||
// 反选
|
||||
const reverseSelect = () => {
|
||||
selectedKeys.value = hostKeys.value.map(s => s.id)
|
||||
.filter(s => !selectedKeys.value.includes(s));
|
||||
};
|
||||
|
||||
// 点击行
|
||||
const clickRow = ({ id }: TableData) => {
|
||||
const index = selectedKeys.value.indexOf(id);
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
<host-group-tree outer-class="tree-card-main"
|
||||
ref="tree"
|
||||
:loading="loading"
|
||||
:editable="true"
|
||||
@set-loading="setLoading"
|
||||
@selected-node="selectGroup" />
|
||||
</div>
|
||||
|
||||
@@ -74,7 +74,6 @@
|
||||
<!-- 主机标签 -->
|
||||
<a-form-item field="tags" label="主机标签">
|
||||
<tag-multi-selector v-model="formModel.tags"
|
||||
:allowCreate="false"
|
||||
:limit="0"
|
||||
type="HOST"
|
||||
:tagColor="tagColor"
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
<a-form-item field="tags" label="主机标签">
|
||||
<tag-multi-selector v-model="formModel.tags"
|
||||
ref="tagSelector"
|
||||
:allowCreate="false"
|
||||
:limit="0"
|
||||
type="HOST"
|
||||
:tagColor="tagColor"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
import CommandSnippetListItem from './command-snippet-list-item.vue';
|
||||
|
||||
defineProps<{
|
||||
snippet: CommandSnippetWrapperResponse
|
||||
snippet: CommandSnippetWrapperResponse;
|
||||
}>();
|
||||
|
||||
// 计算总量
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
import { openUpdateSnippetKey, removeSnippetKey } from '../types/const';
|
||||
|
||||
const props = defineProps<{
|
||||
item: CommandSnippetQueryResponse
|
||||
item: CommandSnippetQueryResponse;
|
||||
}>();
|
||||
|
||||
const { getAndCheckCurrentSshSession } = useTerminalStore();
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
:content="action.content">
|
||||
<div v-if="action.visible !== false"
|
||||
class="terminal-sidebar-icon-wrapper"
|
||||
:class="[wrapperClass]">
|
||||
:class="[ wrapperClass ]">
|
||||
<div class="terminal-sidebar-icon"
|
||||
:class="[
|
||||
iconClass,
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { HostQueryResponse } from '@/api/asset/host';
|
||||
import type { HostGroupQueryResponse } from '@/api/asset/host-group';
|
||||
import { computed } from 'vue';
|
||||
import { HostQueryResponse } from '@/api/asset/host';
|
||||
import { HostGroupQueryResponse } from '@/api/asset/host-group';
|
||||
import HostListView from './host-list-view.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -23,18 +23,18 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||
import type { HostQueryResponse } from '@/api/asset/host';
|
||||
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';
|
||||
import HostGroupView from './host-group-view.vue';
|
||||
import HostListView from './host-list-view.vue';
|
||||
import HostSettingModal from '../setting/extra/host-setting-modal.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
hosts: AuthorizedHostQueryResponse,
|
||||
filterValue: string,
|
||||
newConnectionType: string
|
||||
hosts: AuthorizedHostQueryResponse;
|
||||
filterValue: string;
|
||||
newConnectionType: string;
|
||||
}>();
|
||||
|
||||
const hostList = ref<Array<HostQueryResponse>>([]);
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
ref="formRef"
|
||||
label-align="right"
|
||||
:label-col-props="{ span: 6 }"
|
||||
:wrapper-col-props="{ span: 18 }"
|
||||
:rules="{}">
|
||||
:wrapper-col-props="{ span: 18 }">
|
||||
<!-- 验证方式 -->
|
||||
<a-form-item field="authType" label="验证方式">
|
||||
<a-radio-group type="button"
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
defineProps<{
|
||||
title: string;
|
||||
type: number;
|
||||
items: Array<TerminalShortcutKeyEditable>
|
||||
items: Array<TerminalShortcutKeyEditable>;
|
||||
}>();
|
||||
|
||||
const emits = defineEmits(['setEditable', 'clearEditable', 'updateEnabled']);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="terminal-example" ref="terminal"></div>
|
||||
<div class="terminal-example" ref="terminal"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -14,7 +14,7 @@
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
schema: TerminalThemeSchema | Record<string, any>
|
||||
schema: TerminalThemeSchema | Record<string, any>;
|
||||
}>();
|
||||
|
||||
const terminal = ref();
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
import SftpUploadModal from './sftp-upload-modal.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
tab: TerminalTabItem
|
||||
tab: TerminalTabItem;
|
||||
}>();
|
||||
|
||||
const { preference, sessionManager, transferManager } = useTerminalStore();
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
import XtermSearchModal from '@/components/xtrem/search-modal/index.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
tab: TerminalTabItem
|
||||
tab: TerminalTabItem;
|
||||
}>();
|
||||
|
||||
const { getDictValue } = useDictStore();
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { UserQueryResponse, LoginHistoryQueryResponse } from '@/api/user/user';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { ref, onBeforeMount } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { ResultStatus } from '../types/const';
|
||||
import { getCurrentLoginHistory } from '@/api/user/mine';
|
||||
import { getLoginHistory } from '@/api/user/user';
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { UserUpdateRequest, UserQueryResponse } from '@/api/user/user';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import formRules from '../../user/types/form.rules';
|
||||
import { useUserStore } from '@/store';
|
||||
import { getCurrentUser, updateCurrentUser } from '@/api/user/mine';
|
||||
|
||||
@@ -68,14 +68,14 @@
|
||||
<script lang="ts" setup>
|
||||
import type { UserQueryResponse } from '@/api/user/user';
|
||||
import type { UserSessionQueryResponse } from '@/api/user/user';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { ref, onBeforeMount } from 'vue';
|
||||
import { getUserSessionList, offlineUserSession } from '@/api/user/user';
|
||||
import { getCurrentUserSessionList, offlineCurrentUserSession } from '@/api/user/mine';
|
||||
import { dateFormat } from '@/utils';
|
||||
import { isMobile } from '@/utils/is';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import usePermission from '@/hooks/permission';
|
||||
import { getUserSessionList, offlineUserSession } from '@/api/user/user';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
user?: UserQueryResponse;
|
||||
|
||||
Reference in New Issue
Block a user