add project console page feature

This commit is contained in:
idiotalex@163.com 2020-12-17 16:43:20 +08:00
parent 582be4a257
commit 703289f12e
6 changed files with 271 additions and 6 deletions

View File

@ -250,4 +250,38 @@ export function getRecoverData(params) {
method: 'post',
data: params
})
}
/**
* 获取项目日志文件大小
* @param {
* nodeId: 节点 ID
* id: 项目 ID
* copyId: copyId
* } params
*/
export function getProjectLogSize(params) {
return axios({
url: '/node/manage/log/logSize',
method: 'post',
data: params
})
}
/**
* 下载项目日志文件
* @param {
* nodeId: 节点 ID
* id: 项目 ID
* copyId: copyId
* } params
*/
export function downloadProjectLogFile(params) {
return axios({
url: '/node/manage/log/export.html',
method: 'get',
responseType: 'blob',
timeout: 0,
params
})
}

View File

@ -0,0 +1,185 @@
<template>
<div>
<div ref="filter" class="filter">
<a-button :disabled="project.status" type="primary" @click="start">启动</a-button>
<a-button :disabled="!project.status" type="danger" @click="restart">重启</a-button>
<a-button :disabled="!project.status" type="danger" @click="stop">停止</a-button>
<a-button type="primary" @click="handleDownload">导出日志</a-button>
<a-button type="primary" >备份列表</a-button>
<a-tag color="#87d068">文件大小: {{project.logSize}}</a-tag>
</div>
<div>
<a-input class="console" v-model="logContext" readOnly type="textarea" style="resize: none;"/>
</div>
</div>
</template>
<script>
import { getProjectLogSize, downloadProjectLogFile } from '../../../../api/node-project';
import { mapGetters } from 'vuex';
export default {
props: {
node: {
type: Object
},
project: {
type: Object
},
copyId: {
type: String
}
},
data() {
return {
socket: null,
//
logContext: '',
}
},
computed: {
...mapGetters([
'getToken'
]),
socketUrl() {
const protocol = location.protocol === 'https' ? 'wss://' : 'ws://';
return `${protocol}${location.host}/console?userId=${this.getToken}&projectId=${this.project.id}&nodeId=${this.node.id}&type=console&copyId=${this.copyId}`;
}
},
mounted() {
this.initWebSocket();
this.loadFileSize();
},
beforeDestroy() {
this.socket.close();
},
methods: {
//
initWebSocket() {
this.logContext = '';
if (!this.socket || this.socket.readyState !== this.socket.OPEN || this.socket.readyState !== this.socket.CONNECTING) {
this.socket = new WebSocket(this.socketUrl);
}
//
this.socket.onopen = () => {
this.sendMsg('status');
this.sendMsg('showlog');
}
this.socket.onmessage = (msg) => {
if (msg.data.indexOf('code') > -1 && msg.data.indexOf('msg') > -1) {
const res = JSON.parse(msg.data);
if (res.code === 200) {
this.$notification.success({
message: res.msg,
duration: 3
});
//
if (res.op === 'stop') {
this.project.status = false;
}
if (res.op === 'start') {
this.project.status = true;
}
} else {
this.$notification.error({
message: res.msg,
duration: 5
});
}
}
this.logContext += `${msg.data}\r\n`;
}
},
//
sendMsg(op) {
const data = {
op: op,
projectId: this.project.id,
copyId: this.copyId
}
this.socket.send(JSON.stringify(data));
},
//
loadFileSize() {
const params = {
nodeId: this.node.id,
id: this.project.id,
copyId: this.copyId
}
getProjectLogSize(params).then(res => {
if (res.code === 200) {
this.project.logSize = res.data.logSize;
}
})
},
//
start() {
this.sendMsg('start');
},
//
restart() {
this.$confirm({
title: '系统提示',
content: '真的要重启项目么?',
okText: '确认',
cancelText: '取消',
onOk: () => {
this.sendMsg('restart');
}
});
},
//
stop() {
this.$confirm({
title: '系统提示',
content: '真的要重启项目么?',
okText: '确认',
cancelText: '取消',
onOk: () => {
this.sendMsg('stop');
}
});
},
//
handleDownload() {
this.$notification.info({
message: '正在下载,请稍等...',
duration: 5
});
//
const params = {
nodeId: this.node.id,
id: this.project.id,
copyId: this.copyId
}
// blob
downloadProjectLogFile(params).then(blob => {
const url = window.URL.createObjectURL(blob);
let link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', this.project.log);
document.body.appendChild(link);
link.click();
})
}
}
}
</script>
<style scoped>
.filter {
margin: 0 0 10px;
}
.ant-btn {
margin-right: 10px;
}
.console {
padding: 5px;
color: #fff;
font-size: 14px;
background-color: black;
width: 100%;
height: calc(100vh - 120px);
overflow-y: auto;
border: 1px solid #e2e2e2;
border-radius: 5px 5px;
}
</style>

View File

@ -17,7 +17,7 @@
<template slot="operation" slot-scope="text, record">
<a-button type="primary" @click="handleEdit(record)">编辑</a-button>
<a-button type="primary" @click="handleFile(record)">文件</a-button>
<a-button type="primary" @click="handleEdit(record)">控制台</a-button>
<a-button type="primary" @click="handleConsole(record)">控制台</a-button>
<a-button type="primary" @click="handleEdit(record)">监控</a-button>
<a-button type="danger" @click="handleDelete(record)">删除</a-button>
</template>
@ -74,16 +74,21 @@
</a-form-model-item>
</a-form-model>
</a-modal>
<!-- 项目文件 -->
<!-- 项目文件组件 -->
<a-drawer :title="drawerTitle" placement="right" width="85vw"
:visible="drawerFileVisible" @close="onFileClose">
<!-- 项目文件组件 -->
<file v-if="drawerFileVisible" :node="node" :project="temp" />
</a-drawer>
<!-- 项目控制台组件 -->
<a-drawer :title="drawerTitle" placement="right" width="85vw"
:visible="drawerConsoleVisible" @close="onConsoleClose">
<console v-if="drawerConsoleVisible" :node="node" :project="temp" />
</a-drawer>
</div>
</template>
<script>
import File from './project-file';
import Console from './project-console';
import { getJdkList, getRuningProjectInfo, getProjectById, deleteProject, getProjectList, getPorjectGroupList, getProjectAccessList, editProject } from '../../../../api/node-project';
export default {
props: {
@ -92,7 +97,8 @@ export default {
}
},
components: {
File
File,
Console
},
data() {
return {
@ -113,6 +119,7 @@ export default {
editProjectVisible: false,
drawerTitle: '',
drawerFileVisible: false,
drawerConsoleVisible: false,
columns: [
{title: '项目名称', dataIndex: 'name', width: 150, ellipsis: true, scopedSlots: {customRender: 'name'}},
{title: '创建时间', dataIndex: 'createTime', width: 170, ellipsis: true, scopedSlots: {customRender: 'createTime'}},
@ -272,6 +279,17 @@ export default {
onFileClose() {
this.drawerFileVisible = false;
},
//
handleConsole(record) {
this.temp = Object.assign(record);
this.drawerTitle = `控制台(${this.temp.name})`;
this.drawerConsoleVisible = true;
},
//
onConsoleClose() {
this.drawerConsoleVisible = false;
this.handleFilter();
},
//
handleDelete(record) {
this.$confirm({

View File

@ -13,7 +13,7 @@
<a-button type="primary" @click="loadData">刷新</a-button>
</div>
<div>
<a-input v-model="logContext" readOnly type="textarea" style="resize: none;height: calc(100vh - 185px);"/>
<a-input class="console" v-model="logContext" readOnly type="textarea" style="resize: none;"/>
</div>
</a-layout-content>
<!-- 对话框 -->
@ -191,4 +191,15 @@ export default {
.ant-btn {
margin: 10px;
}
.console {
padding: 5px;
color: #fff;
font-size: 14px;
background-color: black;
width: 100%;
height: calc(100vh - 185px);
overflow-y: auto;
border: 1px solid #e2e2e2;
border-radius: 5px 5px;
}
</style>

View File

@ -13,7 +13,7 @@
<a-button type="primary" @click="loadData">刷新</a-button>
</div>
<div>
<a-input v-model="logContext" readOnly type="textarea" style="resize: none;height: calc(100vh - 165px);"/>
<a-input class="console" v-model="logContext" readOnly type="textarea" style="resize: none;"/>
</div>
</a-layout-content>
<!-- 对话框 -->
@ -190,4 +190,15 @@ export default {
.ant-btn {
margin: 10px;
}
.console {
padding: 5px;
color: #fff;
font-size: 14px;
background-color: black;
width: 100%;
height: calc(100vh - 165px);
overflow-y: auto;
border: 1px solid #e2e2e2;
border-radius: 5px 5px;
}
</style>

View File

@ -18,6 +18,12 @@ module.exports = {
ws: false,
logLevel: 'debug'
},
'/console': {
target: 'ws://localhost:2122',
// true/false: if you want to proxy websockets
ws: false,
logLevel: 'debug'
},
// http
'/*': {
target: 'http://localhost:2122'