refactor: 数据库管理迁移至数据库实例-库管理、机器管理-文件支持用户和组信息查看

This commit is contained in:
meilin.huang
2024-05-21 12:34:26 +08:00
parent a7632fbf58
commit bb1522f4dc
29 changed files with 486 additions and 535 deletions

View File

@@ -34,20 +34,15 @@
<Pane>
<div class="machine-terminal-tabs card pd5">
<el-tabs
v-if="state.tabs.size > 0"
type="card"
@tab-remove="onRemoveTab"
@tab-change="onTabChange"
style="width: 100%"
v-model="state.activeTermName"
class="h100"
>
<el-tabs v-if="state.tabs.size > 0" type="card" @tab-remove="onRemoveTab" style="width: 100%" v-model="state.activeTermName" class="h100">
<el-tab-pane class="h100" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
<template #label>
<el-popconfirm @confirm="handleReconnect(dt, true)" title="确认重新连接?">
<template #reference>
<el-icon class="mr5" :color="dt.status == 1 ? '#67c23a' : '#f56c6c'" :title="dt.status == 1 ? '' : '点击重连'"
<el-icon
class="mr5"
:color="EnumValue.getEnumByValue(TerminalStatusEnum, dt.status)?.extra?.iconColor"
:title="dt.status == TerminalStatusEnum.Connected.value ? '' : '点击重连'"
><Connection />
</el-icon>
</template>
@@ -62,7 +57,7 @@
<el-descriptions :column="1" size="small">
<el-descriptions-item label="机器名"> {{ dt.params?.name }} </el-descriptions-item>
<el-descriptions-item label="host"> {{ dt.params?.ip }} : {{ dt.params?.port }} </el-descriptions-item>
<el-descriptions-item label="username"> {{ dt.params?.username }} </el-descriptions-item>
<el-descriptions-item label="username"> {{ dt.params?.selectAuthCert.username }} </el-descriptions-item>
<el-descriptions-item label="remark"> {{ dt.params?.remark }} </el-descriptions-item>
</el-descriptions>
</template>
@@ -165,13 +160,14 @@ import TagTree from '../component/TagTree.vue';
import { Pane, Splitpanes } from 'splitpanes';
import { ContextmenuItem } from '@/components/contextmenu/index';
import TerminalBody from '@/components/terminal/TerminalBody.vue';
import { TerminalStatus } from '@/components/terminal/common';
import { TerminalStatus, TerminalStatusEnum } from '@/components/terminal/common';
import MachineRdp from '@/components/terminal-rdp/MachineRdp.vue';
import MachineFile from '@/views/ops/machine/file/MachineFile.vue';
import ResourceTags from '../component/ResourceTags.vue';
import { MachineProtocolEnum } from './enums';
import { useAutoOpenResource } from '@/store/autoOpenResource';
import { storeToRefs } from 'pinia';
import EnumValue from '@/common/Enum';
// 组件
const ScriptManage = defineAsyncComponent(() => import('./ScriptManage.vue'));
@@ -340,8 +336,13 @@ watch(
watch(
() => state.activeTermName,
(newValue, oldValue) => {
fitTerminal();
oldValue && terminalRefs[oldValue]?.blur && terminalRefs[oldValue]?.blur();
terminalRefs[newValue]?.focus && terminalRefs[newValue]?.focus();
const nowTab = state.tabs.get(state.activeTermName);
tagTreeRef.value.setCurrentKey(nowTab?.authCert);
}
);
@@ -509,7 +510,7 @@ const onRemoveTab = (targetName: string) => {
state.tabs.delete(targetName);
state.activeTermName = activeTermName;
onTabChange();
// onTabChange();
}
};
@@ -535,21 +536,13 @@ const onResizeTagTree = () => {
fitTerminal();
};
const onTabChange = () => {
fitTerminal();
const nowTab = state.tabs.get(state.activeTermName);
tagTreeRef.value.setCurrentKey(nowTab?.authCert);
};
const fitTerminal = () => {
setTimeout(() => {
let info = state.tabs.get(state.activeTermName);
if (info) {
terminalRefs[info.key]?.fitTerminal && terminalRefs[info.key]?.fitTerminal();
terminalRefs[info.key]?.focus && terminalRefs[info.key]?.focus();
}
}, 100);
});
};
const handleReconnect = (tab: any, force = false) => {

View File

@@ -12,6 +12,8 @@ export const machineApi = {
process: Api.newGet('/machines/{id}/process'),
// 终止进程
killProcess: Api.newDelete('/machines/{id}/process'),
users: Api.newGet('/machines/{id}/users'),
groups: Api.newGet('/machines/{id}/groups'),
testConn: Api.newPost('/machines/test-conn'),
// 保存按钮
saveMachine: Api.newPost('/machines'),

View File

@@ -3,6 +3,7 @@
<el-dialog
:title="title"
v-model="dialogVisible"
@open="search()"
:close-on-click-modal="false"
:before-close="cancel"
:show-close="true"
@@ -27,7 +28,7 @@
</template>
<script lang="ts" setup>
import { watch, ref, toRefs, reactive, Ref } from 'vue';
import { ref, toRefs, reactive, Ref } from 'vue';
import { cronJobApi } from '../api';
import PageTable from '@/components/pagetable/PageTable.vue';
import { TableColumn } from '@/components/pagetable';
@@ -47,8 +48,6 @@ const props = defineProps({
},
});
const emit = defineEmits(['update:visible', 'update:data', 'cancel']);
const searchItems = [SearchItem.input('machineCode', '机器编号'), SearchItem.select('status', '状态').withEnum(CronJobExecStatusEnum)];
const columns = ref([
@@ -65,7 +64,7 @@ const state = reactive({
tags: [] as any,
params: {
pageNum: 1,
pageSize: 10,
pageSize: 8,
cronJobId: 0,
status: null,
machineCode: '',
@@ -78,24 +77,17 @@ const state = reactive({
machines: [],
});
const { dialogVisible, params } = toRefs(state);
const { params } = toRefs(state);
watch(props, async (newValue: any) => {
state.dialogVisible = newValue.visible;
if (!newValue.visible) {
return;
}
state.params.cronJobId = props.data?.id;
setTimeout(() => search(), 300);
});
const dialogVisible = defineModel<boolean>('visible');
const search = async () => {
state.params.cronJobId = props.data?.id;
pageTableRef.value.search();
};
const cancel = () => {
emit('update:visible', false);
dialogVisible.value = false;
setTimeout(() => {
initData();
}, 500);

View File

@@ -18,12 +18,12 @@
</el-select>
</template>
</el-table-column>
<el-table-column prop="path" label="路径" min-width="150px" show-overflow-tooltip>
<el-table-column prop="path" label="路径" min-width="180" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.path" :disabled="scope.row.id != null" clearable> </el-input>
</template>
</el-table-column>
<el-table-column label="操作" min-wdith="120px">
<el-table-column label="操作" min-width="130">
<template #default="scope">
<el-button v-if="scope.row.id == null" @click="addFiles(scope.row)" type="success" icon="success-filled" plain></el-button>
<el-button v-if="scope.row.id != null" @click="getConf(scope.row)" type="primary" icon="tickets" plain></el-button>

View File

@@ -22,7 +22,7 @@
>
<el-table-column type="selection" width="30" />
<el-table-column prop="name" label="名称">
<el-table-column prop="name" label="名称" min-width="380">
<template #header>
<div class="machine-file-table-header">
<div>
@@ -171,7 +171,7 @@
</template>
</el-table-column>
<el-table-column prop="size" label="大小" width="100" sortable>
<el-table-column prop="size" label="大小" min-width="90" sortable>
<template #default="scope">
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == '-'"> {{ formatByteSize(scope.row.size) }} </span>
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == 'd' && scope.row.dirSize"> {{ scope.row.dirSize }} </span>
@@ -182,7 +182,11 @@
</el-table-column>
<el-table-column prop="mode" label="属性" width="110"> </el-table-column>
<el-table-column prop="modTime" label="修改时间" width="165" sortable> </el-table-column>
<el-table-column v-if="$props.protocol == MachineProtocolEnum.Ssh.value" prop="username" label="用户" min-width="55" show-overflow-tooltip>
</el-table-column>
<el-table-column v-if="$props.protocol == MachineProtocolEnum.Ssh.value" prop="groupname" label="组" min-width="55" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="modTime" label="修改时间" width="160" sortable> </el-table-column>
<el-table-column width="100">
<template #header>
@@ -288,6 +292,7 @@ import MachineFileContent from './MachineFileContent.vue';
import { getToken } from '@/common/utils/storage';
import { convertToBytes, formatByteSize } from '@/common/utils/format';
import { getMachineConfig } from '@/common/sysconfig';
import { MachineProtocolEnum } from '../enums';
const props = defineProps({
machineId: { type: Number },
@@ -303,6 +308,9 @@ const folderUploadRef: any = ref();
const folderType = 'd';
const userMap = new Map<number, any>();
const groupMap = new Map<number, any>();
// 路径分隔符
const pathSep = '/';
@@ -343,10 +351,50 @@ const { basePath, nowPath, loading, fileNameFilter, progressNum, uploadProgressS
onMounted(async () => {
state.basePath = props.path;
const machineId = props.machineId;
if (props.protocol == MachineProtocolEnum.Ssh.value) {
machineApi.users.request({ id: machineId }).then((res: any) => {
for (let user of res) {
userMap.set(user.uid, user);
}
});
machineApi.groups.request({ id: machineId }).then((res: any) => {
for (let group of res) {
groupMap.set(group.gid, group);
}
});
}
setFiles(props.path);
state.machineConfig = await getMachineConfig();
});
// watch(
// () => props.machineId,
// () => {
// if (props.protocol != MachineProtocolEnum.Ssh.value) {
// userMap.clear();
// groupMap.clear();
// return;
// }
// const machineId = props.machineId;
// machineApi.users.request({ machineId }).then((res: any) => {
// for (let user of res) {
// userMap.set(user.uid, user);
// }
// });
// machineApi.groups.request({ machineId }).then((res: any) => {
// for (let group of res) {
// groupMap.set(group.gid, group);
// }
// });
// }
// );
const filterFiles = computed(() =>
state.files.filter((data: any) => !state.fileNameFilter || data.name.toLowerCase().includes(state.fileNameFilter.toLowerCase()))
);
@@ -517,6 +565,11 @@ const lsFile = async (path: string) => {
path,
});
for (const file of res) {
if (props.protocol == MachineProtocolEnum.Ssh.value) {
file.username = userMap.get(file.uid)?.uname || file.uid;
file.groupname = groupMap.get(file.gid)?.gname || file.gid;
}
const type = file.type;
if (type == folderType) {
file.isFolder = true;

View File

@@ -11,7 +11,7 @@
</el-table-column>
<el-table-column prop="codePaths" label="关联机器" min-width="250px" show-overflow-tooltip>
<template #default="scope">
<TagCodePath :path="scope.row.tags.map((tag: any) => tag.codePath)" />
<TagCodePath :path="scope.row.tags?.map((tag: any) => tag.codePath)" />
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" show-overflow-tooltip width="120px"> </el-table-column>
@@ -173,7 +173,7 @@ const openFormDialog = (data: any) => {
state.form = { ...DefaultForm };
} else {
state.form = _.cloneDeep(data);
state.form.codePaths = data.tags.map((tag: any) => tag.codePath);
state.form.codePaths = data.tags?.map((tag: any) => tag.codePath);
}
state.dialogVisible = true;
};