🚧 批量上传.
This commit is contained in:
@@ -9,7 +9,8 @@
|
|||||||
* 操作手册
|
* 操作手册
|
||||||
* [资产管理](operator/asset.md)
|
* [资产管理](operator/asset.md)
|
||||||
* [主机运维](operator/host-ops.md)
|
* [主机运维](operator/host-ops.md)
|
||||||
* [命令执行](operator/exec.md)
|
|
||||||
* [运维审计](operator/asset-audit.md)
|
* [运维审计](operator/asset-audit.md)
|
||||||
|
* [批量执行](operator/exec.md)
|
||||||
|
* [计划任务](operator/job.md)
|
||||||
* [用户管理](operator/user.md)
|
* [用户管理](operator/user.md)
|
||||||
* [系统管理](operator/system.md)
|
* [系统管理](operator/system.md)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
### 批量执行
|
### 命令执行
|
||||||
|
|
||||||
批量执行 ssh 主机 shell 脚本。
|
批量执行 ssh 主机 shell 脚本。
|
||||||
|
|
||||||
@@ -22,29 +22,6 @@
|
|||||||
* 日志: 查看执行日志, ctrl + 左键点击会用新页面打开
|
* 日志: 查看执行日志, ctrl + 左键点击会用新页面打开
|
||||||
* 下载: 下载执行日志
|
* 下载: 下载执行日志
|
||||||
|
|
||||||
### 计划任务
|
|
||||||
|
|
||||||
维护计划任务, 定时执行命令。
|
|
||||||
|
|
||||||
* 新增: 新增计划任务
|
|
||||||
* 详情: 查看计划任务详情
|
|
||||||
* 修改: 修改计划任务
|
|
||||||
* 状态: 修改计划任务状态
|
|
||||||
* 手动触发: 手动触发计划任务
|
|
||||||
* 删除: 删除计划任务
|
|
||||||
|
|
||||||
### 计划任务日志
|
|
||||||
|
|
||||||
查看计划任务执行日志
|
|
||||||
|
|
||||||
* 清空: 清空执行日志
|
|
||||||
* 删除: 删除执行日志
|
|
||||||
* 命令: 查看执行时的命令
|
|
||||||
* 参数: 查看执行时的参数
|
|
||||||
* 中断: 中断命令执行
|
|
||||||
* 日志: 查看执行日志, ctrl + 左键点击会用新页面打开
|
|
||||||
* 下载: 下载执行日志
|
|
||||||
|
|
||||||
### 执行模板
|
### 执行模板
|
||||||
|
|
||||||
用来维护批量执行的命令模板, 支持动态参数, 使用 `@{{ xxx }}` 来替换命令参数。
|
用来维护批量执行的命令模板, 支持动态参数, 使用 `@{{ xxx }}` 来替换命令参数。
|
||||||
@@ -54,7 +31,7 @@
|
|||||||
* 修改: 修改执行模板
|
* 修改: 修改执行模板
|
||||||
* 删除: 删除执行模板
|
* 删除: 删除执行模板
|
||||||
|
|
||||||
> 日志面板快捷键
|
### 日志面板快捷键
|
||||||
|
|
||||||
* 回车: `Enter`
|
* 回车: `Enter`
|
||||||
* 向上滚动一行: `↑`
|
* 向上滚动一行: `↑`
|
||||||
@@ -66,7 +43,7 @@
|
|||||||
* 搜索: `ctrl` `F`
|
* 搜索: `ctrl` `F`
|
||||||
* 清空: `ctrl` `L`
|
* 清空: `ctrl` `L`
|
||||||
|
|
||||||
> 命令内置参数
|
### 命令内置参数
|
||||||
|
|
||||||
⚡ 使用 `@{{ xxx }}` 来替换命令参数
|
⚡ 使用 `@{{ xxx }}` 来替换命令参数
|
||||||
|
|
||||||
|
|||||||
24
docs/operator/job.md
Normal file
24
docs/operator/job.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
### 任务列表
|
||||||
|
|
||||||
|
⚡ 内置参数同 `批量执行 > 命令执行` [查看](/operator/exec.md?id=命令内置参数)
|
||||||
|
|
||||||
|
维护计划任务, 定时执行命令。
|
||||||
|
|
||||||
|
* 新增: 新增计划任务
|
||||||
|
* 详情: 查看计划任务详情
|
||||||
|
* 修改: 修改计划任务
|
||||||
|
* 状态: 修改计划任务状态
|
||||||
|
* 手动触发: 手动触发计划任务
|
||||||
|
* 删除: 删除计划任务
|
||||||
|
|
||||||
|
### 任务日志
|
||||||
|
|
||||||
|
查看计划任务执行日志
|
||||||
|
|
||||||
|
* 清空: 清空执行日志
|
||||||
|
* 删除: 删除执行日志
|
||||||
|
* 命令: 查看执行时的命令
|
||||||
|
* 参数: 查看执行时的参数
|
||||||
|
* 中断: 中断命令执行
|
||||||
|
* 日志: 查看执行日志, ctrl + 左键点击会用新页面打开
|
||||||
|
* 下载: 下载执行日志
|
||||||
@@ -51,7 +51,7 @@ public class CodeGenerators {
|
|||||||
// .build(),
|
// .build(),
|
||||||
Template.create("upload_task", "上传任务", "upload")
|
Template.create("upload_task", "上传任务", "upload")
|
||||||
.disableUnitTest()
|
.disableUnitTest()
|
||||||
.vue("exec", "batch-upload")
|
.vue("exec", "upload-task")
|
||||||
.enableRowSelection()
|
.enableRowSelection()
|
||||||
.dict("uploadTaskStatus", "status")
|
.dict("uploadTaskStatus", "status")
|
||||||
.comment("上传任务状态")
|
.comment("上传任务状态")
|
||||||
@@ -61,7 +61,7 @@ public class CodeGenerators {
|
|||||||
.build(),
|
.build(),
|
||||||
Template.create("upload_task_file", "上传任务文件", "upload")
|
Template.create("upload_task_file", "上传任务文件", "upload")
|
||||||
.disableUnitTest()
|
.disableUnitTest()
|
||||||
.vue("exec", "batch-upload-file")
|
.vue("exec", "upload-task-file")
|
||||||
.enableRowSelection()
|
.enableRowSelection()
|
||||||
.dict("uploadTaskFileStatus", "status")
|
.dict("uploadTaskFileStatus", "status")
|
||||||
.comment("上传任务文件状态")
|
.comment("上传任务文件状态")
|
||||||
|
|||||||
@@ -43,8 +43,6 @@ public class UploadTaskController {
|
|||||||
@Resource
|
@Resource
|
||||||
private UploadTaskService uploadTaskService;
|
private UploadTaskService uploadTaskService;
|
||||||
|
|
||||||
// TODO 字典颜色 菜单 操作日志
|
|
||||||
|
|
||||||
@OperatorLog(UploadTaskOperatorType.UPLOAD)
|
@OperatorLog(UploadTaskOperatorType.UPLOAD)
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
@Operation(summary = "创建上传任务")
|
@Operation(summary = "创建上传任务")
|
||||||
@@ -106,7 +104,7 @@ public class UploadTaskController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/query-count")
|
@PostMapping("/query-count")
|
||||||
@Operation(summary = "查询主机连接日志数量")
|
@Operation(summary = "查询上传任务数量")
|
||||||
@PreAuthorize("@ss.hasPermission('asset:upload-task:management:clear')")
|
@PreAuthorize("@ss.hasPermission('asset:upload-task:management:clear')")
|
||||||
public Long getUploadTaskCount(@RequestBody UploadTaskQueryRequest request) {
|
public Long getUploadTaskCount(@RequestBody UploadTaskQueryRequest request) {
|
||||||
return uploadTaskService.getUploadTaskCount(request);
|
return uploadTaskService.getUploadTaskCount(request);
|
||||||
@@ -114,7 +112,7 @@ public class UploadTaskController {
|
|||||||
|
|
||||||
@OperatorLog(UploadTaskOperatorType.CLEAR)
|
@OperatorLog(UploadTaskOperatorType.CLEAR)
|
||||||
@PostMapping("/clear")
|
@PostMapping("/clear")
|
||||||
@Operation(summary = "清空主机连接日志")
|
@Operation(summary = "清空上传任务")
|
||||||
@PreAuthorize("@ss.hasPermission('asset:upload-task:management:clear')")
|
@PreAuthorize("@ss.hasPermission('asset:upload-task:management:clear')")
|
||||||
public Integer clearUploadTask(@RequestBody UploadTaskQueryRequest request) {
|
public Integer clearUploadTask(@RequestBody UploadTaskQueryRequest request) {
|
||||||
return uploadTaskService.clearUploadTask(request);
|
return uploadTaskService.clearUploadTask(request);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class UploadTaskOperatorType extends InitializingOperatorTypes {
|
|||||||
@Override
|
@Override
|
||||||
public OperatorType[] types() {
|
public OperatorType[] types() {
|
||||||
return new OperatorType[]{
|
return new OperatorType[]{
|
||||||
new OperatorType(M, UPLOAD, "批量上传文件 <sb>${count}</sb>个 (<sb>${name}</sb>)"),
|
new OperatorType(M, UPLOAD, "批量上传文件 <sb>${count}</sb>个 (${name})"),
|
||||||
new OperatorType(M, CANCEL, "取消上传文件 <sb>${name}</sb>"),
|
new OperatorType(M, CANCEL, "取消上传文件 <sb>${name}</sb>"),
|
||||||
new OperatorType(H, DELETE, "删除上传记录 <sb>${count}</sb>条"),
|
new OperatorType(H, DELETE, "删除上传记录 <sb>${count}</sb>条"),
|
||||||
new OperatorType(H, CLEAR, "清理上传记录 <sb>${count}</sb>条"),
|
new OperatorType(H, CLEAR, "清理上传记录 <sb>${count}</sb>条"),
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ public class FileUploadTask implements IFileUploadTask {
|
|||||||
List<FileUploadFileItemDTO> files = v.stream()
|
List<FileUploadFileItemDTO> files = v.stream()
|
||||||
.map(s -> FileUploadFileItemDTO.builder()
|
.map(s -> FileUploadFileItemDTO.builder()
|
||||||
.id(s.getId())
|
.id(s.getId())
|
||||||
|
.fileId(s.getFileId())
|
||||||
.remotePath(record.getRemotePath() + Const.SLASH + s.getFilePath())
|
.remotePath(record.getRemotePath() + Const.SLASH + s.getFilePath())
|
||||||
.status(UploadTaskFileStatusEnum.WAITING.name())
|
.status(UploadTaskFileStatusEnum.WAITING.name())
|
||||||
.current(0L)
|
.current(0L)
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public class FileUploader implements IFileUploader {
|
|||||||
private boolean initSession() {
|
private boolean initSession() {
|
||||||
log.info("HostFileUploader.initSession start taskId: {}, hostId: {}", taskId, hostId);
|
log.info("HostFileUploader.initSession start taskId: {}, hostId: {}", taskId, hostId);
|
||||||
try {
|
try {
|
||||||
// TODO 测试 打开 executor 后 是否会connect, 不需要的话就关闭 executor 然后重新打开
|
// TODO 测试看看有没有问题, 则修改为 打开 executor 后 是否会connect, 不需要的话就关闭 executor 然后重新打开
|
||||||
// 打开会话
|
// 打开会话
|
||||||
HostTerminalConnectDTO connectInfo = hostTerminalService.getTerminalConnectInfo(hostId);
|
HostTerminalConnectDTO connectInfo = hostTerminalService.getTerminalConnectInfo(hostId);
|
||||||
this.sessionStore = hostTerminalService.openSessionStore(connectInfo);
|
this.sessionStore = hostTerminalService.openSessionStore(connectInfo);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { createAppWebSocket } from '@/utils/http';
|
||||||
|
|
||||||
// 终端主题
|
// 终端主题
|
||||||
export interface TerminalTheme {
|
export interface TerminalTheme {
|
||||||
@@ -49,3 +50,17 @@ export function getTerminalThemes() {
|
|||||||
export function getTerminalAccessToken() {
|
export function getTerminalAccessToken() {
|
||||||
return axios.get<string>('/asset/host-terminal/access');
|
return axios.get<string>('/asset/host-terminal/access');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开主机终端 websocket
|
||||||
|
*/
|
||||||
|
export const openHostTerminalChannel = (accessToken: string) => {
|
||||||
|
return createAppWebSocket(`/host/terminal/${accessToken}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开主机传输 websocket
|
||||||
|
*/
|
||||||
|
export const openHostTransferChannel = (accessToken: string) => {
|
||||||
|
return createAppWebSocket(`/host/transfer/${accessToken}`);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Pagination } from '@/types/global';
|
import type { Pagination } from '@/types/global';
|
||||||
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
||||||
|
import { createAppWebSocket } from '@/utils/http';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行日志查询请求
|
* 执行日志查询请求
|
||||||
@@ -82,3 +83,10 @@ export interface ExecLogInterruptRequest {
|
|||||||
logId?: number;
|
logId?: number;
|
||||||
hostLogId?: number;
|
hostLogId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开执行日志 websocket
|
||||||
|
*/
|
||||||
|
export const openExecLogChannel = (token: string) => {
|
||||||
|
return createAppWebSocket(`/exec/log/${token}`);
|
||||||
|
};
|
||||||
|
|||||||
146
orion-ops-ui/src/api/exec/upload-task.ts
Normal file
146
orion-ops-ui/src/api/exec/upload-task.ts
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import type { DataGrid, Pagination } from '@/types/global';
|
||||||
|
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
||||||
|
import axios from 'axios';
|
||||||
|
import qs from 'query-string';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务创建请求
|
||||||
|
*/
|
||||||
|
export interface UploadTaskCreateRequest {
|
||||||
|
userId?: number;
|
||||||
|
username?: string;
|
||||||
|
remotePath?: string;
|
||||||
|
description?: string;
|
||||||
|
status?: string;
|
||||||
|
extraInfo?: string;
|
||||||
|
startTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务创建响应
|
||||||
|
*/
|
||||||
|
export interface UploadTaskCreateResponse {
|
||||||
|
id: number;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务请求
|
||||||
|
*/
|
||||||
|
export interface UploadTaskRequest {
|
||||||
|
id?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务查询请求
|
||||||
|
*/
|
||||||
|
export interface UploadTaskQueryRequest extends Pagination {
|
||||||
|
id?: number;
|
||||||
|
userId?: number;
|
||||||
|
remotePath?: string;
|
||||||
|
description?: string;
|
||||||
|
status?: string;
|
||||||
|
createTimeRange?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务查询响应
|
||||||
|
*/
|
||||||
|
export interface UploadTaskQueryResponse extends TableData {
|
||||||
|
id: number;
|
||||||
|
userId: number;
|
||||||
|
username: string;
|
||||||
|
remotePath: string;
|
||||||
|
description: string;
|
||||||
|
status: string;
|
||||||
|
extraInfo: string;
|
||||||
|
startTime: number;
|
||||||
|
endTime: number;
|
||||||
|
createTime: number;
|
||||||
|
files: Array<UploadTaskFileQueryResponse>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务文件查询响应
|
||||||
|
*/
|
||||||
|
export interface UploadTaskFileQueryResponse {
|
||||||
|
id: number;
|
||||||
|
taskId: number;
|
||||||
|
hostId: number;
|
||||||
|
fileId: string;
|
||||||
|
filePath: string;
|
||||||
|
fileSize: number;
|
||||||
|
status: string;
|
||||||
|
startTime: number;
|
||||||
|
endTime: number;
|
||||||
|
current: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建上传任务
|
||||||
|
*/
|
||||||
|
export function createUploadTask(request: UploadTaskCreateRequest) {
|
||||||
|
return axios.post<UploadTaskCreateResponse>('/asset/upload-task/create', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建上传任务
|
||||||
|
*/
|
||||||
|
export function startUploadTask(request: UploadTaskRequest) {
|
||||||
|
return axios.post('/asset/upload-task/start', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建上传任务
|
||||||
|
*/
|
||||||
|
export function cancelUploadTask(request: UploadTaskRequest) {
|
||||||
|
return axios.post('/asset/upload-task/cancel', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询上传任务
|
||||||
|
*/
|
||||||
|
export function getUploadTask(id: number) {
|
||||||
|
return axios.get<UploadTaskQueryResponse>('/asset/upload-task/get', { params: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询上传任务
|
||||||
|
*/
|
||||||
|
export function getUploadTaskPage(request: UploadTaskQueryRequest) {
|
||||||
|
return axios.post<DataGrid<UploadTaskQueryResponse>>('/asset/upload-task/query', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除上传任务
|
||||||
|
*/
|
||||||
|
export function deleteUploadTask(id: number) {
|
||||||
|
return axios.delete('/asset/upload-task/delete', { params: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除上传任务
|
||||||
|
*/
|
||||||
|
export function batchDeleteUploadTask(idList: Array<number>) {
|
||||||
|
return axios.delete('/asset/upload-task/batch-delete', {
|
||||||
|
params: { idList },
|
||||||
|
paramsSerializer: params => {
|
||||||
|
return qs.stringify(params, { arrayFormat: 'comma' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询主机连接日志数量
|
||||||
|
*/
|
||||||
|
export function getUploadTaskCount(request: UploadTaskQueryRequest) {
|
||||||
|
return axios.post<number>('/asset/upload-task/query-count', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空主机连接日志
|
||||||
|
*/
|
||||||
|
export function clearUploadTask(request: UploadTaskQueryRequest) {
|
||||||
|
return axios.post<number>('/asset/upload-task/clear', request);
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
ExecLogStatusResponse,
|
ExecLogStatusResponse,
|
||||||
ExecLogTailRequest,
|
ExecLogTailRequest,
|
||||||
ExecLogInterruptRequest
|
ExecLogInterruptRequest
|
||||||
} from './exec-log';
|
} from '../exec/exec-log';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import qs from 'query-string';
|
import qs from 'query-string';
|
||||||
|
|
||||||
8
orion-ops-ui/src/api/system/upload.ts
Normal file
8
orion-ops-ui/src/api/system/upload.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { createAppWebSocket } from '@/utils/http';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开文件上传 websocket
|
||||||
|
*/
|
||||||
|
export const openFileUploadChannel = (uploadToken: string) => {
|
||||||
|
return createAppWebSocket(`"/file/upload/${uploadToken}`);
|
||||||
|
};
|
||||||
@@ -11,8 +11,8 @@
|
|||||||
<a-link target="_blank" :href="`https://github.com/lijiahangmax/orion-ops-pro/releases/tag/v${version}`">v{{ version }} 开源版</a-link>
|
<a-link target="_blank" :href="`https://github.com/lijiahangmax/orion-ops-pro/releases/tag/v${version}`">v{{ version }} 开源版</a-link>
|
||||||
</a-space>
|
</a-space>
|
||||||
<span class="copyright">
|
<span class="copyright">
|
||||||
Copyright<icon-copyright /> 2024 Li Jiahang All rights reserved.
|
Copyright<icon-copyright /> {{ new Date().getFullYear() }} Li Jiahang All rights reserved.
|
||||||
</span>
|
</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-layout-footer>
|
</a-layout-footer>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
import { nextTick, ref } from 'vue';
|
import { nextTick, ref } from 'vue';
|
||||||
import { getExecCommandLog } from '@/api/exec/exec-command-log';
|
import { getExecCommandLog } from '@/api/exec/exec-command-log';
|
||||||
import { getExecJobLog } from '@/api/exec/exec-job-log';
|
import { getExecJobLog } from '@/api/job/exec-job-log';
|
||||||
import ExecLogPanel from '../panel/index.vue';
|
import ExecLogPanel from '../panel/index.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
import type { ExecType } from '../const';
|
import type { ExecType } from '../const';
|
||||||
import { onUnmounted, ref, nextTick, onMounted } from 'vue';
|
import { onUnmounted, ref, nextTick, onMounted } from 'vue';
|
||||||
import { getExecCommandLogStatus } from '@/api/exec/exec-command-log';
|
import { getExecCommandLogStatus } from '@/api/exec/exec-command-log';
|
||||||
import { getExecJobLogStatus } from '@/api/exec/exec-job-log';
|
import { getExecJobLogStatus } from '@/api/job/exec-job-log';
|
||||||
import { dictKeys, execHostStatus, execStatus } from '../const';
|
import { dictKeys, execHostStatus, execStatus } from '../const';
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore } from '@/store';
|
||||||
import ExecHost from './exec-host.vue';
|
import ExecHost from './exec-host.vue';
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ import type { ILogAppender, LogAddons, LogAppenderConf, LogDomRef } from './appe
|
|||||||
import { LogAppenderOptions } from './appender-const';
|
import { LogAppenderOptions } from './appender-const';
|
||||||
import type { ExecType } from '../const';
|
import type { ExecType } from '../const';
|
||||||
import type { ExecLogTailRequest } from '@/api/exec/exec-log';
|
import type { ExecLogTailRequest } from '@/api/exec/exec-log';
|
||||||
|
import { openExecLogChannel } from '@/api/exec/exec-log';
|
||||||
import { getExecCommandLogTailToken } from '@/api/exec/exec-command-log';
|
import { getExecCommandLogTailToken } from '@/api/exec/exec-command-log';
|
||||||
import { getExecJobLogTailToken } from '@/api/exec/exec-job-log';
|
import { getExecJobLogTailToken } from '@/api/job/exec-job-log';
|
||||||
import { webSocketBaseUrl } from '@/utils/env';
|
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { createWebSocket } from '@/utils';
|
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
import { addEventListen, removeEventListen } from '@/utils/event';
|
import { addEventListen, removeEventListen } from '@/utils/event';
|
||||||
import { copy as copyText } from '@/hooks/copy';
|
import { copy as copyText } from '@/hooks/copy';
|
||||||
@@ -158,7 +157,7 @@ export default class LogAppender implements ILogAppender {
|
|||||||
const { data } = await tokenMaker;
|
const { data } = await tokenMaker;
|
||||||
// 打开会话
|
// 打开会话
|
||||||
try {
|
try {
|
||||||
this.client = await createWebSocket(`${webSocketBaseUrl}/exec/log/${data}`);
|
this.client = await openExecLogChannel(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Message.error('连接失败');
|
Message.error('连接失败');
|
||||||
console.error('log error', e);
|
console.error('log error', e);
|
||||||
|
|||||||
@@ -174,7 +174,7 @@
|
|||||||
import { formatDuration } from '@/utils';
|
import { formatDuration } from '@/utils';
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore } from '@/store';
|
||||||
import { downloadExecCommandLogFile } from '@/api/exec/exec-command-log';
|
import { downloadExecCommandLogFile } from '@/api/exec/exec-command-log';
|
||||||
import { downloadExecJobLogFile } from '@/api/exec/exec-job-log';
|
import { downloadExecJobLogFile } from '@/api/job/exec-job-log';
|
||||||
import { downloadFile } from '@/utils/file';
|
import { downloadFile } from '@/utils/file';
|
||||||
import XtermSearchModal from '@/components/xtrem/search-modal/index.vue';
|
import XtermSearchModal from '@/components/xtrem/search-modal/index.vue';
|
||||||
import 'xterm/css/xterm.css';
|
import 'xterm/css/xterm.css';
|
||||||
|
|||||||
@@ -17,6 +17,16 @@ const EXEC: AppRouteRecordRaw[] = [
|
|||||||
path: '/exec-log',
|
path: '/exec-log',
|
||||||
component: () => import('@/views/exec/exec-command-log/index.vue'),
|
component: () => import('@/views/exec/exec-command-log/index.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'batchUpload',
|
||||||
|
path: '/batch-upload',
|
||||||
|
component: () => import('@/views/exec/batch-upload/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'uploadTaskLog',
|
||||||
|
path: '/upload-log',
|
||||||
|
component: () => import('@/views/exec/upload-task-log/index.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'execTemplate',
|
name: 'execTemplate',
|
||||||
path: '/exec-template',
|
path: '/exec-template',
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { getHostGroupTree } from '@/api/asset/host-group';
|
|||||||
import { getMenuList } from '@/api/system/menu';
|
import { getMenuList } from '@/api/system/menu';
|
||||||
import { getCurrentAuthorizedHostIdentity, getCurrentAuthorizedHostKey } from '@/api/asset/asset-authorized-data';
|
import { getCurrentAuthorizedHostIdentity, getCurrentAuthorizedHostKey } from '@/api/asset/asset-authorized-data';
|
||||||
import { getCommandSnippetGroupList } from '@/api/asset/command-snippet-group';
|
import { getCommandSnippetGroupList } from '@/api/asset/command-snippet-group';
|
||||||
import { getExecJobList } from '@/api/exec/exec-job';
|
import { getExecJobList } from '@/api/job/exec-job';
|
||||||
import { getPathBookmarkGroupList } from '@/api/asset/path-bookmark-group';
|
import { getPathBookmarkGroupList } from '@/api/asset/path-bookmark-group';
|
||||||
|
|
||||||
export type CacheType = 'users' | 'menus' | 'roles'
|
export type CacheType = 'users' | 'menus' | 'roles'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
export const AdminRoleCode = 'admin';
|
export const AdminRoleCode = 'admin';
|
||||||
|
|
||||||
// 表格视图分页数配置
|
// 表格视图分页数配置
|
||||||
export const TablePageSizeOptions = [10, 20, 30, 50, 100];
|
export const TablePageSizeOptions = [10, 15, 20, 30, 50, 100];
|
||||||
|
|
||||||
// 卡片视图分页数配置
|
// 卡片视图分页数配置
|
||||||
export const CardPageSizeOptions = [12, 18, 36, 48, 96];
|
export const CardPageSizeOptions = [12, 18, 36, 48, 96];
|
||||||
|
|||||||
25
orion-ops-ui/src/utils/http.ts
Normal file
25
orion-ops-ui/src/utils/http.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { webSocketBaseUrl } from '@/utils/env';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建应用 websocket
|
||||||
|
*/
|
||||||
|
export const createAppWebSocket = (url: string): Promise<WebSocket> => {
|
||||||
|
return createWebSocket(webSocketBaseUrl + url);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 websocket
|
||||||
|
*/
|
||||||
|
export const createWebSocket = (url: string): Promise<WebSocket> => {
|
||||||
|
return new Promise<WebSocket>((resolve, reject) => {
|
||||||
|
const socket = new WebSocket(url);
|
||||||
|
|
||||||
|
socket.onopen = () => {
|
||||||
|
resolve(socket);
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onerror = e => {
|
||||||
|
reject(e);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -197,23 +197,6 @@ export const objectTruthKeyCount = (obj: any, ignore: string[] = []) => {
|
|||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建 websocket
|
|
||||||
*/
|
|
||||||
export const createWebSocket = async (url: string) => {
|
|
||||||
return new Promise<WebSocket>((resolve, reject) => {
|
|
||||||
const socket = new WebSocket(url);
|
|
||||||
|
|
||||||
socket.onopen = () => {
|
|
||||||
resolve(socket);
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.onerror = e => {
|
|
||||||
reject(e);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 休眠
|
* 休眠
|
||||||
*/
|
*/
|
||||||
|
|||||||
17
orion-ops-ui/src/views/exec/batch-upload/index.vue
Normal file
17
orion-ops-ui/src/views/exec/batch-upload/index.vue
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<div>批量上传</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'index'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
17
orion-ops-ui/src/views/exec/upload-task-log/index.vue
Normal file
17
orion-ops-ui/src/views/exec/upload-task-log/index.vue
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<div>upload-task-log</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'index'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -2,9 +2,8 @@ import type { ISftpTransferManager, ISftpTransferUploader, SftpTransferItem } fr
|
|||||||
import { ISftpTransferDownloader, SftpFile, TransferOperatorResponse } from '../types/terminal.type';
|
import { ISftpTransferDownloader, SftpFile, TransferOperatorResponse } from '../types/terminal.type';
|
||||||
import { TransferReceiverType, TransferStatus, TransferType } from '../types/terminal.const';
|
import { TransferReceiverType, TransferStatus, TransferType } from '../types/terminal.const';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { getTerminalAccessToken } from '@/api/asset/host-terminal';
|
import { getTerminalAccessToken, openHostTransferChannel } from '@/api/asset/host-terminal';
|
||||||
import { createWebSocket, nextId } from '@/utils';
|
import { nextId } from '@/utils';
|
||||||
import { webSocketBaseUrl } from '@/utils/env';
|
|
||||||
import SftpTransferUploader from './sftp-transfer-uploader';
|
import SftpTransferUploader from './sftp-transfer-uploader';
|
||||||
import SftpTransferDownloader from './sftp-transfer-downloader';
|
import SftpTransferDownloader from './sftp-transfer-downloader';
|
||||||
|
|
||||||
@@ -113,7 +112,7 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
|||||||
const { data: accessToken } = await getTerminalAccessToken();
|
const { data: accessToken } = await getTerminalAccessToken();
|
||||||
// 打开会话
|
// 打开会话
|
||||||
try {
|
try {
|
||||||
this.client = await createWebSocket(`${webSocketBaseUrl}/host/transfer/${accessToken}`);
|
this.client = await openHostTransferChannel(accessToken);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 打开失败将传输列表置为失效
|
// 打开失败将传输列表置为失效
|
||||||
Message.error('会话打开失败');
|
Message.error('会话打开失败');
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import type { InputPayload, ITerminalChannel, ITerminalOutputProcessor, ITerminalSessionManager, OutputPayload, Protocol, } from '../types/terminal.type';
|
import type { InputPayload, ITerminalChannel, ITerminalOutputProcessor, ITerminalSessionManager, OutputPayload, Protocol, } from '../types/terminal.type';
|
||||||
import { OutputProtocol } from '../types/terminal.protocol';
|
import { OutputProtocol } from '../types/terminal.protocol';
|
||||||
import { getTerminalAccessToken } from '@/api/asset/host-terminal';
|
import { getTerminalAccessToken, openHostTerminalChannel } from '@/api/asset/host-terminal';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { createWebSocket } from '@/utils';
|
|
||||||
import { webSocketBaseUrl } from '@/utils/env';
|
|
||||||
import TerminalOutputProcessor from './terminal-output-processor';
|
import TerminalOutputProcessor from './terminal-output-processor';
|
||||||
|
|
||||||
// 终端通信处理器 实现
|
// 终端通信处理器 实现
|
||||||
@@ -23,7 +21,7 @@ export default class TerminalChannel implements ITerminalChannel {
|
|||||||
const { data: accessToken } = await getTerminalAccessToken();
|
const { data: accessToken } = await getTerminalAccessToken();
|
||||||
// 打开会话
|
// 打开会话
|
||||||
try {
|
try {
|
||||||
this.client = await createWebSocket(`${webSocketBaseUrl}/host/terminal/${accessToken}`);
|
this.client = await openHostTerminalChannel(accessToken);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Message.error('无法连接至服务器');
|
Message.error('无法连接至服务器');
|
||||||
console.error('terminal error', e);
|
console.error('terminal error', e);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, nextTick } from 'vue';
|
import { onMounted, ref, nextTick } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { getExecJobLog } from '@/api/exec/exec-job-log';
|
import { getExecJobLog } from '@/api/job/exec-job-log';
|
||||||
import ExecLogPanel from '@/components/exec/log/panel/index.vue';
|
import ExecLogPanel from '@/components/exec/log/panel/index.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|||||||
@@ -102,15 +102,14 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ExecLogQueryResponse, ExecHostLogQueryResponse } from '@/api/exec/exec-log';
|
import type { ExecLogQueryResponse, ExecHostLogQueryResponse } from '@/api/exec/exec-log';
|
||||||
import { deleteExecJobHostLog } from '@/api/exec/exec-job-log';
|
import { deleteExecJobHostLog, interruptHostExecJob } from '@/api/job/exec-job-log';
|
||||||
import { interruptHostExecJob } from '@/api/exec/exec-job-log';
|
|
||||||
import { execHostStatusKey, execHostStatus } from '@/components/exec/log/const';
|
import { execHostStatusKey, execHostStatus } from '@/components/exec/log/const';
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore } from '@/store';
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
import columns from '@/views/exec/exec-command-log/types/host-table.columns';
|
import columns from '@/views/exec/exec-command-log/types/host-table.columns';
|
||||||
import { useExpandable } from '@/types/table';
|
import { useExpandable } from '@/types/table';
|
||||||
import { dateFormat, formatDuration } from '@/utils';
|
import { dateFormat, formatDuration } from '@/utils';
|
||||||
import { downloadExecJobLogFile } from '@/api/exec/exec-job-log';
|
import { downloadExecJobLogFile } from '@/api/job/exec-job-log';
|
||||||
import { copy } from '@/hooks/copy';
|
import { copy } from '@/hooks/copy';
|
||||||
import { downloadFile } from '@/utils/file';
|
import { downloadFile } from '@/utils/file';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
import useVisible from '@/hooks/visible';
|
import useVisible from '@/hooks/visible';
|
||||||
import { execStatusKey } from '@/components/exec/log/const';
|
import { execStatusKey } from '@/components/exec/log/const';
|
||||||
import { getExecJobLogCount, clearExecJobLog } from '@/api/exec/exec-job-log';
|
import { getExecJobLogCount, clearExecJobLog } from '@/api/job/exec-job-log';
|
||||||
import { Message, Modal } from '@arco-design/web-vue';
|
import { Message, Modal } from '@arco-design/web-vue';
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore } from '@/store';
|
||||||
import ExecJobSelector from '@/components/exec/job/selector/index.vue';
|
import ExecJobSelector from '@/components/exec/job/selector/index.vue';
|
||||||
|
|||||||
@@ -198,7 +198,7 @@
|
|||||||
getExecJobHostLogList,
|
getExecJobHostLogList,
|
||||||
getExecJobLogPage,
|
getExecJobLogPage,
|
||||||
getExecJobLogStatus
|
getExecJobLogStatus
|
||||||
} from '@/api/exec/exec-job-log';
|
} from '@/api/job/exec-job-log';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
import columns from '../types/table.columns';
|
import columns from '../types/table.columns';
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
import { useExpandable, usePagination, useRowSelection } from '@/types/table';
|
import { useExpandable, usePagination, useRowSelection } from '@/types/table';
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore } from '@/store';
|
||||||
import { dateFormat, formatDuration } from '@/utils';
|
import { dateFormat, formatDuration } from '@/utils';
|
||||||
import { interruptExecJob } from '@/api/exec/exec-job-log';
|
import { interruptExecJob } from '@/api/job/exec-job-log';
|
||||||
import ExecJobHostLogTable from './exec-job-host-log-table.vue';
|
import ExecJobHostLogTable from './exec-job-host-log-table.vue';
|
||||||
|
|
||||||
const emits = defineEmits(['viewCommand', 'viewParams', 'viewLog', 'openClear']);
|
const emits = defineEmits(['viewCommand', 'viewParams', 'viewLog', 'openClear']);
|
||||||
|
|||||||
@@ -86,14 +86,14 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ExecJobQueryResponse } from '@/api/exec/exec-job';
|
import type { ExecJobQueryResponse } from '@/api/job/exec-job';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
import useVisible from '@/hooks/visible';
|
import useVisible from '@/hooks/visible';
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore } from '@/store';
|
||||||
import { dateFormat } from '@/utils';
|
import { dateFormat } from '@/utils';
|
||||||
import { copy } from '@/hooks/copy';
|
import { copy } from '@/hooks/copy';
|
||||||
import { getExecJob } from '@/api/exec/exec-job';
|
import { getExecJob } from '@/api/job/exec-job';
|
||||||
import { EnabledStatus } from '@/types/const';
|
import { EnabledStatus } from '@/types/const';
|
||||||
import { execJobStatusKey } from '../types/const';
|
import { execJobStatusKey } from '../types/const';
|
||||||
|
|
||||||
|
|||||||
@@ -128,14 +128,14 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ExecJobUpdateRequest } from '@/api/exec/exec-job';
|
import type { ExecJobUpdateRequest } from '@/api/job/exec-job';
|
||||||
import type { ExecTemplateQueryResponse } from '@/api/exec/exec-template';
|
import type { ExecTemplateQueryResponse } from '@/api/exec/exec-template';
|
||||||
import { onUnmounted, ref } from 'vue';
|
import { onUnmounted, ref } from 'vue';
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
import useVisible from '@/hooks/visible';
|
import useVisible from '@/hooks/visible';
|
||||||
import formRules from '../types/form.rules';
|
import formRules from '../types/form.rules';
|
||||||
import { jobBuiltinsParams } from '../types/const';
|
import { jobBuiltinsParams } from '../types/const';
|
||||||
import { createExecJob, getExecJob, updateExecJob } from '@/api/exec/exec-job';
|
import { createExecJob, getExecJob, updateExecJob } from '@/api/job/exec-job';
|
||||||
import { getExecTemplateWithAuthorized } from '@/api/exec/exec-template';
|
import { getExecTemplateWithAuthorized } from '@/api/exec/exec-template';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { EnabledStatus } from '@/types/const';
|
import { EnabledStatus } from '@/types/const';
|
||||||
|
|||||||
@@ -172,9 +172,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ExecJobQueryRequest, ExecJobQueryResponse } from '@/api/exec/exec-job';
|
import type { ExecJobQueryRequest, ExecJobQueryResponse } from '@/api/job/exec-job';
|
||||||
import { reactive, ref, onMounted } from 'vue';
|
import { reactive, ref, onMounted } from 'vue';
|
||||||
import { deleteExecJob, getExecJobPage, triggerExecJob, updateExecJobStatus } from '@/api/exec/exec-job';
|
import { deleteExecJob, getExecJobPage, triggerExecJob, updateExecJobStatus } from '@/api/job/exec-job';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import usePermission from '@/hooks/permission';
|
import usePermission from '@/hooks/permission';
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
|
|||||||
@@ -53,15 +53,6 @@
|
|||||||
placeholder="菜单权限 infra:system-menu:query"
|
placeholder="菜单权限 infra:system-menu:query"
|
||||||
allow-clear />
|
allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- 外链地址 -->
|
|
||||||
<a-form-item v-if="formModel.type !== MenuType.FUNCTION"
|
|
||||||
field="path"
|
|
||||||
label="外链地址"
|
|
||||||
tooltip="输入组件名称后则不会生效">
|
|
||||||
<a-input v-model="formModel.path"
|
|
||||||
placeholder="外链地址与组件名称二选一"
|
|
||||||
allow-clear />
|
|
||||||
</a-form-item>
|
|
||||||
<!-- 组件名称 -->
|
<!-- 组件名称 -->
|
||||||
<a-form-item v-if="formModel.type !== MenuType.FUNCTION"
|
<a-form-item v-if="formModel.type !== MenuType.FUNCTION"
|
||||||
field="component"
|
field="component"
|
||||||
@@ -70,6 +61,15 @@
|
|||||||
placeholder="路由组件名称"
|
placeholder="路由组件名称"
|
||||||
allow-clear />
|
allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<!-- 外链地址 -->
|
||||||
|
<a-form-item v-if="formModel.type !== MenuType.FUNCTION"
|
||||||
|
field="path"
|
||||||
|
label="外链地址"
|
||||||
|
tooltip="输入组件名称后则不会生效">
|
||||||
|
<a-input v-model="formModel.path"
|
||||||
|
placeholder="组件名称与外链地址二选一"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
<!-- 菜单排序 -->
|
<!-- 菜单排序 -->
|
||||||
<a-form-item field="sort" label="菜单排序">
|
<a-form-item field="sort" label="菜单排序">
|
||||||
<a-input-number v-model="formModel.sort"
|
<a-input-number v-model="formModel.sort"
|
||||||
@@ -212,6 +212,7 @@
|
|||||||
if (error) {
|
if (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// 验证父菜单
|
||||||
if (formModel.value.parentId === 0
|
if (formModel.value.parentId === 0
|
||||||
&& (formModel.value.type === MenuType.SUB_MENU || formModel.value.type === MenuType.FUNCTION)) {
|
&& (formModel.value.type === MenuType.SUB_MENU || formModel.value.type === MenuType.FUNCTION)) {
|
||||||
formRef.value.setFields({
|
formRef.value.setFields({
|
||||||
@@ -222,6 +223,19 @@
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// 验证组件名称
|
||||||
|
if ((formModel.value.type === MenuType.PARENT_MENU || formModel.value.type === MenuType.SUB_MENU)
|
||||||
|
&& !formModel.value.component
|
||||||
|
&& !formModel.value.path) {
|
||||||
|
formRef.value.setFields({
|
||||||
|
component: {
|
||||||
|
status: 'error',
|
||||||
|
message: '组件名称与外链地址二选一'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (isAddHandle.value) {
|
if (isAddHandle.value) {
|
||||||
// 新增
|
// 新增
|
||||||
await createMenu(formModel.value);
|
await createMenu(formModel.value);
|
||||||
|
|||||||
Reference in New Issue
Block a user