测试过程问题修改
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,3 +27,4 @@ hs_err_pid*
|
|||||||
**/target/
|
**/target/
|
||||||
|
|
||||||
/log/
|
/log/
|
||||||
|
/.trae/
|
||||||
|
|||||||
@@ -0,0 +1,214 @@
|
|||||||
|
package com.ruoyi.web.controller.schedule;
|
||||||
|
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.schedule.ScheduleThread;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程管理控制器
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/schedule/thread")
|
||||||
|
public class ThreadManageController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ScheduleThread scheduleThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取线程列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/list")
|
||||||
|
public AjaxResult list() {
|
||||||
|
if (scheduleThread.threadTypeMap.isEmpty()) {
|
||||||
|
scheduleThread.refreshThreadMap();
|
||||||
|
}
|
||||||
|
return AjaxResult.success(scheduleThread.getAllThreadStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动线程
|
||||||
|
*/
|
||||||
|
@PostMapping("/start/{key}")
|
||||||
|
public AjaxResult start(@PathVariable String key) {
|
||||||
|
try {
|
||||||
|
scheduleThread.startThread(key);
|
||||||
|
return AjaxResult.success("启动成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("启动失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止线程
|
||||||
|
*/
|
||||||
|
@PostMapping("/stop/{key}")
|
||||||
|
public AjaxResult stop(@PathVariable String key) {
|
||||||
|
try {
|
||||||
|
scheduleThread.stopThread(key);
|
||||||
|
return AjaxResult.success("停止指令已下发");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("停止失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动全部线程
|
||||||
|
*/
|
||||||
|
@PostMapping("/startAll")
|
||||||
|
public AjaxResult startAll() {
|
||||||
|
try {
|
||||||
|
for (String key : scheduleThread.threadMap.keySet()) {
|
||||||
|
try {
|
||||||
|
scheduleThread.startThread(key);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 忽略单个启动失败,继续启动下一个
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AjaxResult.success("全部启动指令已下发");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("操作失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止全部线程
|
||||||
|
*/
|
||||||
|
@PostMapping("/stopAll")
|
||||||
|
public AjaxResult stopAll() {
|
||||||
|
try {
|
||||||
|
for (String key : scheduleThread.threadMap.keySet()) {
|
||||||
|
scheduleThread.stopThread(key);
|
||||||
|
}
|
||||||
|
return AjaxResult.success("全部停止指令已下发");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("操作失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动分组线程
|
||||||
|
*/
|
||||||
|
@PostMapping("/startGroup/{type}")
|
||||||
|
public AjaxResult startGroup(@PathVariable String type) {
|
||||||
|
try {
|
||||||
|
for (Map.Entry<String, String> entry : scheduleThread.threadTypeMap.entrySet()) {
|
||||||
|
if (type.equals(entry.getValue())) {
|
||||||
|
try {
|
||||||
|
scheduleThread.startThread(entry.getKey());
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AjaxResult.success("分组启动指令已下发");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("操作失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止分组线程
|
||||||
|
*/
|
||||||
|
@PostMapping("/stopGroup/{type}")
|
||||||
|
public AjaxResult stopGroup(@PathVariable String type) {
|
||||||
|
try {
|
||||||
|
for (Map.Entry<String, String> entry : scheduleThread.threadTypeMap.entrySet()) {
|
||||||
|
if (type.equals(entry.getValue())) {
|
||||||
|
scheduleThread.stopThread(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AjaxResult.success("分组停止指令已下发");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("操作失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取线程日志
|
||||||
|
*/
|
||||||
|
@GetMapping("/log/{key}")
|
||||||
|
public AjaxResult log(@PathVariable String key) {
|
||||||
|
// 如果key是"ALL",则不进行关键词过滤,显示所有日志(除了堆栈合并逻辑外)
|
||||||
|
String target = "ALL".equals(key) ? null : key;
|
||||||
|
|
||||||
|
// 日志路径,根据logback配置及实际环境确认
|
||||||
|
File file = new File("D:/home/ruoyi/logs/sys-info.log");
|
||||||
|
if (!file.exists()) {
|
||||||
|
return AjaxResult.error("日志文件不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedList<String> lines = new LinkedList<>();
|
||||||
|
// 简单的日志行首匹配模式 (yyyy-MM-dd HH:mm:ss)
|
||||||
|
Pattern logStartPattern = Pattern.compile("^\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}");
|
||||||
|
boolean isMatchingStack = false;
|
||||||
|
// 如果是查看所有日志,默认所有行都匹配,除非遇到新的日志行且我们想做更复杂的过滤(目前全显)
|
||||||
|
// 但为了支持堆栈合并,我们还是需要判断行首
|
||||||
|
|
||||||
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "GBK"))) {
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
boolean isLogStart = logStartPattern.matcher(line).find();
|
||||||
|
|
||||||
|
if (target == null) {
|
||||||
|
// 查看所有日志:直接添加,或者如果是异常日志查看模式可以在这里过滤
|
||||||
|
// 暂时逻辑:全部添加
|
||||||
|
lines.add(line);
|
||||||
|
} else {
|
||||||
|
// 查看特定线程日志
|
||||||
|
if (line.contains(target)) {
|
||||||
|
isMatchingStack = true;
|
||||||
|
lines.add(line);
|
||||||
|
} else if (isLogStart) {
|
||||||
|
isMatchingStack = false;
|
||||||
|
} else if (isMatchingStack) {
|
||||||
|
lines.add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制行数,避免内存溢出,保留最后1000行
|
||||||
|
if (lines.size() > 1000) {
|
||||||
|
lines.removeFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("读取日志失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return AjaxResult.success(lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取异常日志
|
||||||
|
*/
|
||||||
|
@GetMapping("/errorLog")
|
||||||
|
public AjaxResult errorLog() {
|
||||||
|
File file = new File("D:/home/ruoyi/logs/sys-error.log");
|
||||||
|
if (!file.exists()) {
|
||||||
|
// 如果没有单独的error log,尝试从info log中过滤ERROR级别?
|
||||||
|
// 假设有 sys-error.log
|
||||||
|
return AjaxResult.error("异常日志文件不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedList<String> lines = new LinkedList<>();
|
||||||
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "GBK"))) {
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
lines.add(line);
|
||||||
|
if (lines.size() > 1000) {
|
||||||
|
lines.removeFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("读取日志失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return AjaxResult.success(lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
73
ruoyi-ui/src/api/schedule/thread.js
Normal file
73
ruoyi-ui/src/api/schedule/thread.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询线程列表
|
||||||
|
export function listThread() {
|
||||||
|
return request({
|
||||||
|
url: '/schedule/thread/list',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动线程
|
||||||
|
export function startThread(key) {
|
||||||
|
return request({
|
||||||
|
url: '/schedule/thread/start/' + key,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止线程
|
||||||
|
export function stopThread(key) {
|
||||||
|
return request({
|
||||||
|
url: '/schedule/thread/stop/' + key,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取日志
|
||||||
|
export function getThreadLog(key) {
|
||||||
|
return request({
|
||||||
|
url: '/schedule/thread/log/' + key,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动全部线程
|
||||||
|
export function startAllThread() {
|
||||||
|
return request({
|
||||||
|
url: '/schedule/thread/startAll',
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止全部线程
|
||||||
|
export function stopAllThread() {
|
||||||
|
return request({
|
||||||
|
url: '/schedule/thread/stopAll',
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动分组线程
|
||||||
|
export function startGroupThread(type) {
|
||||||
|
return request({
|
||||||
|
url: '/schedule/thread/startGroup/' + type,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止分组线程
|
||||||
|
export function stopGroupThread(type) {
|
||||||
|
return request({
|
||||||
|
url: '/schedule/thread/stopGroup/' + type,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取异常日志
|
||||||
|
export function getErrorLog() {
|
||||||
|
return request({
|
||||||
|
url: '/schedule/thread/errorLog',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
331
ruoyi-ui/src/views/schedule/thread/index.vue
Normal file
331
ruoyi-ui/src/views/schedule/thread/index.vue
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<!-- 全局控制卡片 -->
|
||||||
|
<el-card class="box-card" shadow="hover" style="margin-bottom: 20px;">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span style="font-weight: bold; font-size: 18px;"><i class="el-icon-setting"></i> 全局控制中心</span>
|
||||||
|
</div>
|
||||||
|
<el-row :gutter="20" type="flex" align="middle">
|
||||||
|
<el-col :span="7">
|
||||||
|
<div class="control-group">
|
||||||
|
<span class="control-label">系统总控:</span>
|
||||||
|
<el-button type="success" size="small" icon="el-icon-video-play" @click="handleStartAll">全部开启</el-button>
|
||||||
|
<el-button type="danger" size="small" icon="el-icon-video-pause" @click="handleStopAll">全部关闭</el-button>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="11" style="border-left: 1px solid #EBEEF5; border-right: 1px solid #EBEEF5;">
|
||||||
|
<div class="control-group" style="justify-content: space-evenly;">
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<span class="control-label"><i class="el-icon-cpu"></i> 通讯组:</span>
|
||||||
|
<el-button-group>
|
||||||
|
<el-button type="primary" size="mini" icon="el-icon-video-play" @click="handleStartGroup('read')">开启</el-button>
|
||||||
|
<el-button type="info" size="mini" icon="el-icon-video-pause" @click="handleStopGroup('read')">停止</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider direction="vertical" style="height: 20px;"></el-divider>
|
||||||
|
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<span class="control-label"><i class="el-icon-s-operation"></i> 调度组:</span>
|
||||||
|
<el-button-group>
|
||||||
|
<el-button type="primary" size="mini" icon="el-icon-video-play" @click="handleStartGroup('schedule')">开启</el-button>
|
||||||
|
<el-button type="info" size="mini" icon="el-icon-video-pause" @click="handleStopGroup('schedule')">停止</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<div class="control-group" style="justify-content: flex-end;">
|
||||||
|
<span class="control-label">日志:</span>
|
||||||
|
<el-button type="primary" plain size="small" icon="el-icon-document" @click="handleGlobalLog">系统日志</el-button>
|
||||||
|
<el-button type="danger" plain size="small" icon="el-icon-warning-outline" @click="handleErrorLog">异常日志</el-button>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<!-- 读取线程组 -->
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card class="box-card" shadow="hover">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span style="font-weight: bold; font-size: 16px;"><i class="el-icon-cpu"></i> PLC读取线程</span>
|
||||||
|
<el-tag size="small" type="info" style="float: right">硬件通信</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-table v-loading="loading" :data="readThreads" size="small" :show-header="true">
|
||||||
|
<el-table-column label="线程名称" prop="name" :show-overflow-tooltip="true" />
|
||||||
|
<el-table-column label="状态" align="center" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :type="scope.row.isRun ? 'success' : 'danger'" size="mini" effect="dark">
|
||||||
|
{{ scope.row.isRun ? '运行中' : '已停止' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-video-play" v-if="!scope.row.isRun" @click="handleStart(scope.row)">启动</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-video-pause" v-if="scope.row.isRun" @click="handleStop(scope.row)">停止</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<!-- 调度线程组 -->
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card class="box-card" shadow="hover">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span style="font-weight: bold; font-size: 16px;"><i class="el-icon-s-operation"></i> 调度控制线程</span>
|
||||||
|
<el-tag size="small" type="primary" style="float: right">业务逻辑</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-table v-loading="loading" :data="scheduleThreads" size="small" :show-header="true">
|
||||||
|
<el-table-column label="线程名称" prop="name" :show-overflow-tooltip="true" />
|
||||||
|
<el-table-column label="状态" align="center" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :type="scope.row.isRun ? 'success' : 'danger'" size="mini" effect="dark">
|
||||||
|
{{ scope.row.isRun ? '运行中' : '已停止' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-video-play" v-if="!scope.row.isRun" @click="handleStart(scope.row)">启动</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-video-pause" v-if="scope.row.isRun" @click="handleStop(scope.row)">停止</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20" style="margin-top: 20px;">
|
||||||
|
<!-- 监控线程组 -->
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-card class="box-card" shadow="hover">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span style="font-weight: bold; font-size: 16px;"><i class="el-icon-monitor"></i> 系统监控线程</span>
|
||||||
|
<el-tag size="small" type="warning" style="float: right">状态监控</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-table v-loading="loading" :data="monitorThreads" size="small" :show-header="true">
|
||||||
|
<el-table-column label="线程名称" prop="name" :show-overflow-tooltip="true" />
|
||||||
|
<el-table-column label="标识" prop="key" :show-overflow-tooltip="true" />
|
||||||
|
<el-table-column label="状态" align="center" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :type="scope.row.isRun ? 'success' : 'danger'" size="mini" effect="dark">
|
||||||
|
{{ scope.row.isRun ? '运行中' : '已停止' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-video-play" v-if="!scope.row.isRun" @click="handleStart(scope.row)">启动</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-video-pause" v-if="scope.row.isRun" @click="handleStop(scope.row)">停止</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 日志弹窗 -->
|
||||||
|
<el-dialog :title="logTitle" :visible.sync="openLog" width="800px" append-to-body>
|
||||||
|
<div class="log-container" v-loading="logLoading">
|
||||||
|
<pre v-if="logContent.length > 0">{{ logContent.join('\n') }}</pre>
|
||||||
|
<div v-else style="text-align: center; color: #909399; padding-top: 200px;">暂无日志</div>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="refreshLog">刷 新</el-button>
|
||||||
|
<el-button @click="openLog = false">关 闭</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listThread, startThread, stopThread, getThreadLog, startAllThread, stopAllThread, startGroupThread, stopGroupThread, getErrorLog } from "@/api/schedule/thread";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Thread",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 遮罩层
|
||||||
|
loading: true,
|
||||||
|
// 线程表格数据
|
||||||
|
threadList: [],
|
||||||
|
// 日志弹窗
|
||||||
|
openLog: false,
|
||||||
|
logTitle: "",
|
||||||
|
logLoading: false,
|
||||||
|
logContent: [],
|
||||||
|
logType: 'system', // system, error
|
||||||
|
currentLogKey: null // for refresh
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
readThreads() {
|
||||||
|
return this.threadList.filter(item => item.type === 'read' || !item.type);
|
||||||
|
},
|
||||||
|
scheduleThreads() {
|
||||||
|
return this.threadList.filter(item => item.type === 'schedule');
|
||||||
|
},
|
||||||
|
monitorThreads() {
|
||||||
|
return this.threadList.filter(item => item.type === 'monitor');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 查询线程列表 */
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listThread().then(response => {
|
||||||
|
this.threadList = response.data;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 启动按钮操作 */
|
||||||
|
handleStart(row) {
|
||||||
|
this.$confirm('确认要启动 "' + row.name + '" 吗?', "警告", {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning"
|
||||||
|
}).then(() => {
|
||||||
|
return startThread(row.key);
|
||||||
|
}).then(response => {
|
||||||
|
this.$modal.msgSuccess("启动指令已下发");
|
||||||
|
setTimeout(() => {
|
||||||
|
this.getList();
|
||||||
|
}, 1000);
|
||||||
|
}).catch(error => {
|
||||||
|
console.error("启动失败:", error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 停止按钮操作 */
|
||||||
|
handleStop(row) {
|
||||||
|
this.$confirm('确认要停止 "' + row.name + '" 吗?', "警告", {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning"
|
||||||
|
}).then(() => {
|
||||||
|
return stopThread(row.key);
|
||||||
|
}).then(response => {
|
||||||
|
this.$modal.msgSuccess("停止指令已下发");
|
||||||
|
// 稍微延迟一下刷新,等待线程退出
|
||||||
|
setTimeout(() => {
|
||||||
|
this.getList();
|
||||||
|
}, 1000);
|
||||||
|
}).catch(error => {
|
||||||
|
console.error("停止失败:", error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 一键开启所有 */
|
||||||
|
handleStartAll() {
|
||||||
|
this.$confirm('确认要启动所有线程吗?', "警告", {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning"
|
||||||
|
}).then(() => {
|
||||||
|
return startAllThread();
|
||||||
|
}).then(() => {
|
||||||
|
this.$modal.msgSuccess("操作成功");
|
||||||
|
setTimeout(() => { this.getList(); }, 2000);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 一键关闭所有 */
|
||||||
|
handleStopAll() {
|
||||||
|
this.$confirm('确认要停止所有线程吗?', "警告", {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "error"
|
||||||
|
}).then(() => {
|
||||||
|
return stopAllThread();
|
||||||
|
}).then(() => {
|
||||||
|
this.$modal.msgSuccess("操作成功");
|
||||||
|
setTimeout(() => { this.getList(); }, 2000);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 分组开启 */
|
||||||
|
handleStartGroup(type) {
|
||||||
|
startGroupThread(type).then(() => {
|
||||||
|
this.$modal.msgSuccess("操作成功");
|
||||||
|
setTimeout(() => { this.getList(); }, 1500);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 分组停止 */
|
||||||
|
handleStopGroup(type) {
|
||||||
|
stopGroupThread(type).then(() => {
|
||||||
|
this.$modal.msgSuccess("操作成功");
|
||||||
|
setTimeout(() => { this.getList(); }, 1500);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 系统日志 */
|
||||||
|
handleGlobalLog() {
|
||||||
|
this.logTitle = "系统运行日志";
|
||||||
|
this.logType = 'system';
|
||||||
|
this.currentLogKey = 'ALL';
|
||||||
|
this.openLog = true;
|
||||||
|
this.refreshLog();
|
||||||
|
},
|
||||||
|
/** 异常日志 */
|
||||||
|
handleErrorLog() {
|
||||||
|
this.logTitle = "系统异常日志";
|
||||||
|
this.logType = 'error';
|
||||||
|
this.openLog = true;
|
||||||
|
this.refreshLog();
|
||||||
|
},
|
||||||
|
refreshLog() {
|
||||||
|
this.logLoading = true;
|
||||||
|
if (this.logType === 'system') {
|
||||||
|
getThreadLog(this.currentLogKey).then(response => {
|
||||||
|
this.logContent = response.data;
|
||||||
|
this.logLoading = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const container = this.$el.querySelector('.log-container');
|
||||||
|
if (container) container.scrollTop = container.scrollHeight;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
getErrorLog().then(response => {
|
||||||
|
this.logContent = response.data;
|
||||||
|
this.logLoading = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const container = this.$el.querySelector('.log-container');
|
||||||
|
if (container) container.scrollTop = container.scrollHeight;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.log-container {
|
||||||
|
height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
font-family: Consolas, Monaco, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.box-card {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.control-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
.control-label {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 10px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user