优化资产授权交互逻辑.

This commit is contained in:
lijiahang
2024-04-18 11:31:05 +08:00
parent 1034ba4896
commit fe4b87927e
26 changed files with 166 additions and 84 deletions

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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();

View File

@@ -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);

View File

@@ -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>

View File

@@ -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"

View File

@@ -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"

View File

@@ -31,7 +31,7 @@
import CommandSnippetListItem from './command-snippet-list-item.vue';
defineProps<{
snippet: CommandSnippetWrapperResponse
snippet: CommandSnippetWrapperResponse;
}>();
// 计算总量

View File

@@ -121,7 +121,7 @@
import { openUpdateSnippetKey, removeSnippetKey } from '../types/const';
const props = defineProps<{
item: CommandSnippetQueryResponse
item: CommandSnippetQueryResponse;
}>();
const { getAndCheckCurrentSshSession } = useTerminalStore();

View File

@@ -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,

View File

@@ -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<{

View File

@@ -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>>([]);

View File

@@ -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"

View File

@@ -80,7 +80,7 @@
defineProps<{
title: string;
type: number;
items: Array<TerminalShortcutKeyEditable>
items: Array<TerminalShortcutKeyEditable>;
}>();
const emits = defineEmits(['setEditable', 'clearEditable', 'updateEnabled']);

View File

@@ -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();

View File

@@ -79,7 +79,7 @@
import SftpUploadModal from './sftp-upload-modal.vue';
const props = defineProps<{
tab: TerminalTabItem
tab: TerminalTabItem;
}>();
const { preference, sessionManager, transferManager } = useTerminalStore();

View File

@@ -79,7 +79,7 @@
import XtermSearchModal from '@/components/xtrem/search-modal/index.vue';
const props = defineProps<{
tab: TerminalTabItem
tab: TerminalTabItem;
}>();
const { getDictValue } = useDictStore();

View File

@@ -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';

View File

@@ -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';

View File

@@ -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;