💄 修改主机配置模态框样式.

This commit is contained in:
lijiahangmax
2024-03-01 00:35:55 +08:00
parent 1c6a38d5d9
commit c48e48ab72
10 changed files with 233 additions and 169 deletions

View File

@@ -44,8 +44,8 @@ demo: http://101.43.254.243:1081/#/
* 资产管理: 支持灵活配置主机分组, 统一管理主机、秘钥和身份。
* 资产授权: 可将资产数据授权给指定角色和用户。
* 权限控制: 全面管理用户角色, 支持动态菜单配置和强制下线等功能。
* 在线终端: 提供便捷的在线 Web 终端服务, 支持自定义快捷键和主题风格。
* 文件管理: 实现远程主机文件的批量上传、下载和在线编辑等操作。
* 在线终端: 提供便捷的在线 Web 终端服务, 支持快捷命令、自定义快捷键和主题风格。
* 文件管理: 实现远程主机文件的批量上传、下载和在线编辑等操作。
* 可扩展性: 前后端代码规范统一, 代码质量高、健壮且易于阅读和扩展。
[comment]: <> ( FIXME * 批量操作: 支持远程主机批量执行命令 以及 批量执行上传文件)

View File

@@ -44,8 +44,8 @@ demo: http://101.43.254.243:1081/#/
* 资产管理: 支持灵活配置主机分组, 统一管理主机、秘钥和身份。
* 资产授权: 可将资产数据授权给指定角色和用户。
* 权限控制: 全面管理用户角色, 支持动态菜单配置和强制下线等功能。
* 在线终端: 提供便捷的在线 Web 终端服务, 支持自定义快捷键和主题风格。
* 文件管理: 实现远程主机文件的批量上传、下载和在线编辑等操作。
* 在线终端: 提供便捷的在线 Web 终端服务, 支持快捷命令、自定义快捷键和主题风格。
* 文件管理: 实现远程主机文件的批量上传、下载和在线编辑等操作。
* 可扩展性: 前后端代码规范统一, 代码质量高、健壮且易于阅读和扩展。
[comment]: <> ( FIXME * 批量操作: 支持远程主机批量执行命令 以及 批量执行上传文件)

View File

@@ -147,7 +147,6 @@ public class DataExtraServiceImpl implements DataExtraService {
@Override
public Map<Long, String> getExtraItemValuesByCache(Long userId, String type, String item) {
// todo TEST
// 查询缓存
String key = DataExtraCacheKeyDefine.DATA_EXTRA.format(userId, type, item);
Map<String, String> entities = RedisMaps.entities(key);
@@ -187,7 +186,6 @@ public class DataExtraServiceImpl implements DataExtraService {
@Override
public Integer deleteByUserId(Long userId) {
// 查询数据 TODO TEST
List<DataExtraDO> list = this.getCacheSelectWrapper()
.eq(DataExtraDO::getUserId, userId)
.then()
@@ -204,7 +202,6 @@ public class DataExtraServiceImpl implements DataExtraService {
@Override
public Integer deleteByRelId(String type, Long relId) {
// 查询数据 TODO TEST
List<DataExtraDO> list = this.getCacheSelectWrapper()
.eq(DataExtraDO::getType, type)
.eq(DataExtraDO::getRelId, relId)

View File

@@ -160,7 +160,7 @@
</li>
<!-- 用户信息 -->
<li>
<a-dropdown trigger="click">
<a-dropdown trigger="click" position="br">
<!-- 头像 -->
<a-avatar draggable="false"
:size="32"
@@ -256,10 +256,10 @@
const localeRef = ref();
// 打开应用设置
const openAppSetting = inject<() => void>(openAppSettingKey);
const openAppSetting = inject(openAppSettingKey) as () => void;
// 注入收缩菜单
const toggleDrawerMenu = inject<() => void>(toggleDrawerMenuKey);
const toggleDrawerMenu = inject(toggleDrawerMenuKey) as () => void;
// 切换主题
const handleToggleTheme = () => {

View File

@@ -137,13 +137,13 @@
</div>
</div>
</a-tooltip>
<!-- 连接设置 -->
<!-- 主机设置 -->
<a-tooltip position="top"
:mini="true"
:auto-fix-position="false"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
content="连接设置">
content="主机设置">
<div class="terminal-sidebar-icon-wrapper">
<div class="terminal-sidebar-icon" @click="openSetting(item)">
<icon-settings />
@@ -185,7 +185,7 @@
import { dataColor } from '@/utils';
import { tagColor } from '@/views/asset/host-list/types/const';
import { updateHostAlias } from '@/api/asset/host-extra';
import { openSshSettingModalKey, PanelSessionType } from '../../types/terminal.const';
import { openSettingModalKey, PanelSessionType } from '../../types/terminal.const';
import { useTerminalStore } from '@/store';
const props = defineProps<{
@@ -231,7 +231,7 @@
};
// 打开配置
const openSetting = inject(openSshSettingModalKey) as (record: HostQueryResponse) => void;
const openSetting = inject(openSettingModalKey) as (record: HostQueryResponse) => void;
// 设置收藏
const setFavorite = async (item: HostQueryResponse) => {

View File

@@ -21,8 +21,8 @@
class="list-view-container"
:hostList="hostList"
empty-value="暂无连接记录, 快去体验吧!" />
<!-- 修改主机设置模态框 -->
<ssh-extra-modal ref="sshModal" />
<!-- 主机设置模态框 -->
<host-setting-modal ref="settingModal" />
</div>
</template>
@@ -34,12 +34,12 @@
<script lang="ts" setup>
import { onMounted, provide, ref, watch } from 'vue';
import { NewConnectionType, openSshSettingModalKey } from '../../types/terminal.const';
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 SshExtraModal from '../setting/ssh-extra-modal.vue';
import HostSettingModal from '../setting/extra/host-setting-modal.vue';
const props = defineProps<{
hosts: AuthorizedHostQueryResponse,
@@ -54,11 +54,11 @@
? props.hosts.groupTree[0].key
: 0
);
const sshModal = ref();
const settingModal = ref();
// 暴露打开 ssh 配置模态框
provide(openSshSettingModalKey, (record: any) => {
sshModal.value?.open(record);
provide(openSettingModalKey, (record: any) => {
settingModal.value?.open(record);
});
// 主机数据处理

View File

@@ -0,0 +1,118 @@
<template>
<a-modal v-model:visible="visible"
title-align="start"
:title="title"
:top="180"
:width="600"
:align-center="false"
:draggable="true"
:mask-closable="false"
:unmount-on-close="true"
ok-text="保存当前页"
cancel-text="关闭"
:ok-button-props="{ disabled: loading }"
:cancel-button-props="{ disabled: loading }"
:on-before-ok="handlerOk"
@close="handleClose">
<a-spin class="full" :loading="loading">
<a-tabs v-model="item"
position="left"
type="rounded"
:lazy-load="true">
<!-- SSH 配置 -->
<a-tab-pane :key="ExtraSettingItems.SSH" title="SSH 配置">
<ssh-setting-form ref="sshForm"
:host-id="hostId as number"
:item="ExtraSettingItems.SSH" />
</a-tab-pane>
<!-- 标签颜色 -->
<a-tab-pane :key="ExtraSettingItems.COLOR" title="标签颜色">
<!-- TODO -->
<span>颜色</span>
</a-tab-pane>
</a-tabs>
</a-spin>
</a-modal>
</template>
<script lang="ts">
export default {
name: 'hostSettingModal'
};
</script>
<script lang="ts" setup>
import type { HostQueryResponse } from '@/api/asset/host';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible';
import { ExtraSettingItems } from '../../../types/terminal.const';
import SshSettingForm from './ssh-setting-form.vue';
import { updateHostExtra } from '@/api/asset/host-extra';
import { Message } from '@arco-design/web-vue';
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
const item = ref<string>(ExtraSettingItems.SSH);
const title = ref<string>();
const hostId = ref<number>();
const sshForm = ref();
const colorForm = ref();
// 打开配置
const open = (record: HostQueryResponse) => {
hostId.value = record.id;
title.value = record.alias || `${record.name} (${record.code})`;
setVisible(true);
};
defineExpose({ open });
// 确定
const handlerOk = async () => {
setLoading(true);
try {
let value;
if (item.value === ExtraSettingItems.SSH) {
// SSH 配置
value = await sshForm.value.getValue();
} else if (item.value === ExtraSettingItems.COLOR) {
// 颜色配置
value = await colorForm.value.getValue();
}
if (!value) {
return false;
}
// 保存
await updateHostExtra({
hostId: hostId.value,
item: item.value,
extra: value as string
});
Message.success('保存成功');
} catch (e) {
} finally {
setLoading(false);
}
return false;
};
// 关闭
const handleClose = () => {
handlerClear();
};
// 清空
const handlerClear = () => {
setLoading(false);
};
</script>
<style lang="less" scoped>
:deep(.arco-tabs-pane) {
border-left: 1px var(--color-neutral-3) solid;
padding-left: 16px;
}
</style>

View File

@@ -0,0 +1,89 @@
<template>
<a-form :model="formModel"
ref="formRef"
label-align="right"
:style="{ width: '460px' }"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }"
:rules="{}">
<!-- 验证方式 -->
<a-form-item field="authType" label="验证方式">
<a-radio-group type="button"
v-model="formModel.authType"
:options="toRadioOptions(extraSshAuthTypeKey)" />
</a-form-item>
<!-- 用户名 -->
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_KEY"
field="username"
label="用户名">
<a-input v-model="formModel.username" placeholder="请输入用户名" />
</a-form-item>
<!-- 主机秘钥 -->
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_KEY"
field="keyId"
label="主机秘钥"
:rules="{ required: true, message: '请选择主机秘钥' }">
<host-key-selector v-model="formModel.keyId"
:authorized="true" />
</a-form-item>
<!-- 主机身份 -->
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_IDENTITY"
field="identityId"
label="主机身份"
:rules="{ required: true, message: '请选择主机身份' }">
<host-identity-selector v-model="formModel.identityId"
:authorized="true" />
</a-form-item>
</a-form>
</template>
<script lang="ts">
export default {
name: 'sshSettingForm'
};
</script>
<script lang="ts" setup>
import type { SshExtraModel } from '../../../types/terminal.type';
import { onMounted, ref } from 'vue';
import { getHostExtraItem } from '@/api/asset/host-extra';
import { ExtraSshAuthType, extraSshAuthTypeKey } from '../../../types/terminal.const';
import { useDictStore } from '@/store';
import HostKeySelector from '@/components/asset/host-key/host-key-selector.vue';
import HostIdentitySelector from '@/components/asset/host-identity/host-identity-selector.vue';
const props = defineProps<{
hostId: number,
item: string
}>();
const { toRadioOptions } = useDictStore();
const formRef = ref();
const formModel = ref<SshExtraModel>({});
// 渲染表单
const renderForm = async () => {
const { data } = await getHostExtraItem<SshExtraModel>({ hostId: props.hostId, item: props.item });
formModel.value = data;
};
// 获取值
const getValue = async () => {
// 验证参数
const error = await formRef.value.validate();
if (error) {
return false;
}
return JSON.stringify(formModel.value);
};
defineExpose({ getValue });
onMounted(renderForm);
</script>
<style lang="less" scoped>
</style>

View File

@@ -1,146 +0,0 @@
<template>
<a-modal v-model:visible="visible"
body-class="modal-form"
title-align="start"
:title="title"
:top="80"
:align-center="false"
:draggable="true"
:mask-closable="false"
:unmount-on-close="true"
ok-text="保存"
:ok-button-props="{ disabled: loading }"
:cancel-button-props="{ disabled: loading }"
:on-before-ok="handlerOk"
@close="handleClose">
<a-spin class="full" :loading="loading">
<a-form :model="formModel"
ref="formRef"
label-align="right"
:style="{ width: '460px' }"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }"
:rules="{}">
<!-- 验证方式 -->
<a-form-item field="authType" label="验证方式">
<a-radio-group type="button"
v-model="formModel.authType"
:options="toRadioOptions(extraSshAuthTypeKey)" />
</a-form-item>
<!-- 用户名 -->
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_KEY"
field="username"
label="用户名">
<a-input v-model="formModel.username" placeholder="请输入用户名" />
</a-form-item>
<!-- 主机秘钥 -->
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_KEY"
field="keyId"
label="主机秘钥"
:rules="{ required: true, message: '请选择主机秘钥' }">
<host-key-selector v-model="formModel.keyId"
:authorized="true" />
</a-form-item>
<!-- 主机身份 -->
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_IDENTITY"
field="identityId"
label="主机身份"
:rules="{ required: true, message: '请选择主机身份' }">
<host-identity-selector v-model="formModel.identityId"
:authorized="true" />
</a-form-item>
</a-form>
</a-spin>
</a-modal>
</template>
<script lang="ts">
export default {
name: 'sshExtraModal'
};
</script>
<script lang="ts" setup>
import type { HostQueryResponse } from '@/api/asset/host';
import type { SshExtraModel } from '../../types/terminal.type';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible';
import { Message } from '@arco-design/web-vue';
import { getHostExtraItem, updateHostExtra } from '@/api/asset/host-extra';
import { ExtraSshAuthType, extraSshAuthTypeKey } from '../../types/terminal.const';
import { useDictStore } from '@/store';
import HostIdentitySelector from '@/components/asset/host-identity/host-identity-selector.vue';
import HostKeySelector from '@/components/asset/host-key/host-key-selector.vue';
const { toRadioOptions } = useDictStore();
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
const title = ref<string>();
const hostId = ref<number>();
const formRef = ref();
const formModel = ref<SshExtraModel>({});
// 打开配置
const open = (record: HostQueryResponse) => {
hostId.value = record.id;
title.value = record.alias || `${record.name} (${record.code})`;
renderForm();
setVisible(true);
};
defineExpose({ open });
// 渲染表单
const renderForm = async () => {
try {
setLoading(true);
const { data } = await getHostExtraItem<SshExtraModel>({ hostId: hostId.value, item: 'ssh' });
formModel.value = data;
} catch (e) {
} finally {
setLoading(false);
}
};
// 确定
const handlerOk = async () => {
setLoading(true);
try {
// 验证参数
const error = await formRef.value.validate();
if (error) {
return false;
}
// 修改
await updateHostExtra({
hostId: hostId.value,
item: 'ssh',
extra: JSON.stringify(formModel.value)
});
Message.success('保存成功');
// 清空
handlerClear();
} catch (e) {
return false;
} finally {
setLoading(false);
}
};
// 关闭
const handleClose = () => {
handlerClear();
};
// 清空
const handlerClear = () => {
setLoading(false);
};
</script>
<style lang="less" scoped>
</style>

View File

@@ -42,6 +42,12 @@ export const NewConnectionType = {
LATEST: 'latest'
};
// 主机额外配置项
export const ExtraSettingItems = {
SSH: 'ssh',
COLOR: 'color'
};
// 主机额外配置 ssh 认证方式
export const ExtraSshAuthType = {
// 使用默认认证方式
@@ -318,8 +324,8 @@ export const TransferReceiverType = {
DOWNLOAD_ERROR: 'downloadError',
};
// 打开 sshSettingModal key
export const openSshSettingModalKey = Symbol();
// 打开 settingModal key
export const openSettingModalKey = Symbol();
// 打开 sftpCreateModal key
export const openSftpCreateModalKey = Symbol();