mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-12-30 19:36:34 +08:00
refactor: 数据库管理迁移至数据库实例-库管理、机器管理-文件支持用户和组信息查看
This commit is contained in:
@@ -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) => {
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user