feat: terminal日志回放,节前提交一半关机没提成功
This commit is contained in:
@@ -43,6 +43,7 @@ $dropdown-hover-background-color: #fafafa; //下拉鼠标悬停背景色
|
||||
|
||||
$danger-color: #DE5D3F; //全局警告色红色
|
||||
$success-color: #23BF9A; //全局正常色绿色
|
||||
$warning-color: $btn-light-txt-color-hover-new; //全局警告橙色
|
||||
$suspended-color: #9e9c98; //全局停用色灰色
|
||||
|
||||
$left-menu-bgcolor:#FFF;
|
||||
|
||||
@@ -917,8 +917,8 @@ li{
|
||||
padding:2px 5px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.nz-table td.success .cell>span {
|
||||
background-color: $success-color;
|
||||
.nz-table td.warning .cell>span {
|
||||
background-color: $warning-color;
|
||||
color: white;
|
||||
padding:2px 5px;
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
<panel-tab :from="from" :obj="obj" ref="panelTab" v-if="(from == $CONSTANTS.fromRoute.model || from == $CONSTANTS.fromRoute.asset || from == $CONSTANTS.fromRoute.project || from == $CONSTANTS.fromRoute.rule || from == $CONSTANTS.fromRoute.endpoint) && targetTab == 'panel'" v-show="subResizeShow"
|
||||
@changeTab="changeTab" :targetTab.sync="targetTab" :detail="detail"></panel-tab>
|
||||
|
||||
<!--terminal-log的记录和回放-->
|
||||
<terminal-log-record-tab :from="from" :obj="obj" @changeTab="changeTab" ref="reminalLogRecordTab" v-if="from == $CONSTANTS.fromRoute.terminalLog && targetTab == 'record'"></terminal-log-record-tab>
|
||||
<terminal-log-replay-tab :from="from" :obj="obj" @changeTab="changeTab" ref="reminalLogReplayTab" v-if="from == $CONSTANTS.fromRoute.terminalLog && targetTab == 'replay'"></terminal-log-replay-tab>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -40,6 +44,8 @@
|
||||
import endpointQueryTab from "./tabs/endpointQueryTab";
|
||||
import endpointTab from "./tabs/endpointTab";
|
||||
import panelTab from "./tabs/panelTab";
|
||||
import terminalLogRecordTab from "./tabs/terminalLogRecordTab";
|
||||
import terminalLogReplayTab from "./tabs/terminalLogReplayTab";
|
||||
|
||||
export default {
|
||||
name: "bottomBox",
|
||||
@@ -49,6 +55,8 @@
|
||||
'endpoint-query-tab': endpointQueryTab,
|
||||
'endpoint-tab': endpointTab,
|
||||
'panel-tab': panelTab,
|
||||
terminalLogRecordTab,
|
||||
terminalLogReplayTab
|
||||
},
|
||||
props: {
|
||||
isFullScreen: false, //是否全屏
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<div style="height: 100%;">
|
||||
<div class="sub-top-tools">
|
||||
<div class="sub-list-tabs">
|
||||
<div class="sub-list-tab-title">ID:{{obj.id}}</div><div
|
||||
@click="changeTab('replay')" class="sub-list-tab">{{$t("config.terminallog.replay")}}</div><div
|
||||
class="sub-list-tab sub-list-tab-active">{{$t("config.terminallog.record.record")}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="record-container">
|
||||
<div class="record-container--record">
|
||||
<div class="record--title">{{$t('config.terminallog.record.history')}}</div>
|
||||
<div class="record--list">
|
||||
<template v-for="(record, index) in records">
|
||||
<template v-for="item in record.list">
|
||||
<div class="detail--time"><span>{{calcTime(item.time)}}</span></div>
|
||||
<div class="detail--cmd"><span :class="matchBgColor(item.cmd)">{{item.cmd}}</span></div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<div class="record--more" v-if="hasNext">
|
||||
<span @click="loadMore" class="nz-btn nz-btn-size-small nz-btn-style-light"><i class="nz-icon nz-icon-arrow-down"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="record-container--record record-container--record__tip">
|
||||
<div class="record--title">{{$t('config.terminallog.record.legendTip')}}</div>
|
||||
<div class="record--list">
|
||||
<div class="detail--time"><span>yyyy-MM-dd HH:mm:ss</span></div>
|
||||
<div class="detail--cmd"><span class="detail--cmd__red">{{$t("config.terminallog.record.dangerTip")}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "terminalLogRecordTab",
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
obj: Object, //关联的实体对象
|
||||
},
|
||||
computed: {
|
||||
calcTime() {
|
||||
return function(time) {
|
||||
if (this.obj.startTime) {
|
||||
let startTime = new Date(this.obj.startTime).getTime();
|
||||
if (startTime) {
|
||||
let thisTime = startTime+time;
|
||||
return this.dateFormat(thisTime);
|
||||
}
|
||||
}
|
||||
return "-";
|
||||
}
|
||||
},
|
||||
matchBgColor() {
|
||||
return function(cmd) {
|
||||
let className = "";
|
||||
let dangerCmd = this.$CONSTANTS.terminalLog.dangerCmd;
|
||||
let infoCmd = this.$CONSTANTS.terminalLog.infoCmd;
|
||||
let cmdSplit = cmd.split(" ");
|
||||
if (this.intersection(infoCmd, cmdSplit).length > 0) {
|
||||
className = "detail--cmd__green";
|
||||
}
|
||||
if (this.intersection(dangerCmd, cmdSplit).length > 0) {
|
||||
className = "detail--cmd__red";
|
||||
}
|
||||
return className;
|
||||
}
|
||||
},
|
||||
hasNext() {
|
||||
if (this.records.length > 0) {
|
||||
return this.records[this.records.length-1].hasNext;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filter: {
|
||||
uuid: this.obj.uuid,
|
||||
cmd: "",
|
||||
time: "",
|
||||
size: 200
|
||||
},
|
||||
records: [ // 加载更多时有多个record,否则只有一个
|
||||
|
||||
],
|
||||
/*record: { // cmd记录,含hasNext、time(最后一条时间)、total和list
|
||||
total: 4,
|
||||
hasNext: true,
|
||||
time: 9889,
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
uuid: 1,
|
||||
time: 1024,
|
||||
cmd: "ls -ef|grep java"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
uuid: 2,
|
||||
time: 3824,
|
||||
cmd: "su root ls -ef|grep java|afaewrafaser aolewr | awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|ls -ef|grep java|afaewrafaser aolewr | awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|ls -ef|grep java|afaewrafaser aolewr | awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|ls -ef|grep java|afaewrafaser aolewr | awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|ls -ef|grep java|afaewrafaser aolewr | awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|ls -ef|grep java|afaewrafaser aolewr | awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|ls -ef|grep java|afaewrafaser aolewr | awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|awk fajpoerja;welkrjfa;wle|"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
uuid: 3,
|
||||
time: 7740,
|
||||
cmd: "rm -rf /"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
uuid: 4,
|
||||
time: 9889,
|
||||
cmd: "exit"
|
||||
},
|
||||
]
|
||||
}*/
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initData(filter) {
|
||||
this.$get("/terminal/cmd", filter).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.records.push(res.data);
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
loadMore() {
|
||||
let filter = Object.assign({}, this.filter);
|
||||
filter.time = this.records[this.records.length-1].time;
|
||||
this.initData(filter);
|
||||
},
|
||||
// 切换tab
|
||||
changeTab(tab) {
|
||||
this.$emit('changeTab', tab);
|
||||
},
|
||||
intersection(a, b) {
|
||||
let s = new Set(b);
|
||||
return a.filter(x => s.has(x));
|
||||
},
|
||||
dateFormat(time) {
|
||||
if (!time) {
|
||||
return '-';
|
||||
}
|
||||
let date = new Date(time);
|
||||
let year = date.getFullYear();
|
||||
let month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;
|
||||
let day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
|
||||
let hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
|
||||
let minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
|
||||
let seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
|
||||
|
||||
return year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds;
|
||||
},
|
||||
formatUpdateTime(date) {
|
||||
let time=new Date(date);
|
||||
let hours=time.getHours()>9?time.getHours():'0'+time.getHours();
|
||||
let minutes=time.getMinutes()>9?time.getMinutes():'0'+time.getMinutes();
|
||||
|
||||
return hours+':'+minutes;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
obj: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler(n) {
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initData(this.filter);
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.record-container {
|
||||
height: calc(100% - 50px);
|
||||
overflow: auto;
|
||||
}
|
||||
.record-container--record {
|
||||
background-color: white;
|
||||
width: calc(50% - 14px);
|
||||
padding: 16px 15px 14px 15px;
|
||||
border: 1px solid #d8dce1;
|
||||
box-sizing: border-box;
|
||||
min-height: 100px;
|
||||
|
||||
.record--title {
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.record--list {
|
||||
display: grid;
|
||||
gap: 6px 12px;
|
||||
grid-template-columns: 150px auto;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
padding-left: 16px;
|
||||
|
||||
.detail--time {
|
||||
line-height: 18px;
|
||||
grid-column: 1/span 1;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
span {
|
||||
background-color: #ECECEC;
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.detail--cmd {
|
||||
line-height: 18px;
|
||||
grid-column: 2/span 1;
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.detail--cmd__green {
|
||||
background-color: #B4FDB1;
|
||||
}
|
||||
.detail--cmd__red {
|
||||
background-color: #FFD2C2;
|
||||
}
|
||||
}
|
||||
|
||||
.record--more {
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
}
|
||||
.record-container--record.record-container--record__tip {
|
||||
margin-top: 10px;
|
||||
min-height: unset;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div style="height: 100%;">
|
||||
<div class="sub-top-tools">
|
||||
<div class="sub-list-tabs">
|
||||
<div class="sub-list-tab-title">ID:{{obj.id}}</div><div
|
||||
class="sub-list-tab sub-list-tab-active">{{$t("config.terminallog.replay")}}</div><div
|
||||
@click="changeTab('record')" class="sub-list-tab">{{$t("config.terminallog.record.record")}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="replay-container">
|
||||
<div class="replay-operate">
|
||||
<button @click="pause" class="nz-btn nz-btn-style-light nz-btn-size-large">暂停</button>
|
||||
<button @click="play" class="nz-btn nz-btn-style-light nz-btn-size-large">播放</button>
|
||||
<button @click="restart" class="nz-btn nz-btn-style-light nz-btn-size-large">重新播放</button>
|
||||
<el-tag>{{progress}}</el-tag>
|
||||
</div>
|
||||
<div class="replay-console">
|
||||
<div :id="obj.uuid" class="replay-terminal"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Terminal from '../../js/Xterm';
|
||||
|
||||
export default {
|
||||
name: "terminalLogReplayTab",
|
||||
components: {
|
||||
Terminal
|
||||
},
|
||||
props: {
|
||||
obj: Object, //关联的实体对象
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filter: {
|
||||
size: 1,
|
||||
uuid: ""
|
||||
},
|
||||
operate: {
|
||||
pause: false, // 是否暂停
|
||||
|
||||
},
|
||||
terminal: null,
|
||||
|
||||
recordData: [],
|
||||
isNeedStop: false, // 是否需要停止
|
||||
playedCount: 0, // 当前已经播放完的数量
|
||||
isPlaying: true, // 是否正在播放
|
||||
isFinish: false, //是否已结束
|
||||
recordTick: 50, // 输出间隔时间ms,默认50
|
||||
playerTimer: null, // 定时器
|
||||
playerCurrentTime: 0, // 当前时间进度
|
||||
speedTable: [ // 快进倍速选项
|
||||
{speed: 1, name: 'Normal'},
|
||||
{speed: 2, name: 'x2'},
|
||||
{speed: 4, name: 'x4'},
|
||||
{speed: 8, name: 'x8'},
|
||||
{speed: 16, name: 'x16'}
|
||||
],
|
||||
speedOffset: 0, // 快进倍数index
|
||||
progress: 0, // 进度条进度
|
||||
needSkip: false, // 需要时间刻度,即是否跳过无操作时间,为true时表示需要,即不跳过无操作时间
|
||||
timeUsed: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 切换tab
|
||||
changeTab(tab) {
|
||||
this.$emit('changeTab', tab);
|
||||
},
|
||||
|
||||
getReplayData() {
|
||||
return new Promise(resolve => {
|
||||
this.$get("/terminal/record", this.filter).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.recordData = res.data.list;
|
||||
this.timeUsed = res.data.endTime;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
initAndPlay() {
|
||||
// terminal初始化
|
||||
if (!this.terminal) {
|
||||
this.terminal = new Terminal(
|
||||
{
|
||||
cols: 70,
|
||||
rows: 28
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.terminal.destroy();
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.terminal.open(document.getElementById(this.obj.uuid));
|
||||
this.terminal.resize(70, 28);
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.recordData.length > 0) {
|
||||
this.isNeedStop = false;
|
||||
this.isPlaying = true;
|
||||
this.isFinished = false;
|
||||
this.playedCount = 0;
|
||||
this.doPlay();
|
||||
} else {
|
||||
let req = this.getReplayData();
|
||||
req.then(() => {
|
||||
if (this.recordData.length > 0) {
|
||||
// 请求得到数据后,开始播放
|
||||
this.isNeedStop = false;
|
||||
this.isPlaying = true;
|
||||
this.isFinished = false;
|
||||
this.playedCount = 0;
|
||||
this.doPlay();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 800);
|
||||
},
|
||||
|
||||
/*原理:利用setTimeout和数据的时间偏移量来模拟实现视频播放效果*/
|
||||
doPlay() {
|
||||
if (this.isNeedStop) {
|
||||
this.isPlaying = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.recordData.length <= this.playedCount) {
|
||||
this.playerTimer = setTimeout(this.doPlay, this.recordTick);
|
||||
return;
|
||||
}
|
||||
this.playerCurrentTime += this.recordTick * this.speedTable[this.speedOffset].speed;
|
||||
|
||||
let recordTick = this.recordTick;
|
||||
|
||||
let playData;
|
||||
for (let i = this.playedCount; i < this.recordData.length; i++) {
|
||||
if (this.isNeedStop) {
|
||||
break;
|
||||
}
|
||||
playData = this.recordData[i];
|
||||
if (playData.t < this.playerCurrentTime) {
|
||||
this.terminal.write(playData.c);
|
||||
|
||||
if ((this.playedCount + 1) === this.recordData.length) {
|
||||
//播放完成
|
||||
this.progress = 100;
|
||||
this.isFinished = true;
|
||||
this.isPlaying = false;
|
||||
return;
|
||||
} else {
|
||||
this.playedCount++;
|
||||
}
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this.isNeedStop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.needSkip) {
|
||||
if (playData.t - this.playerCurrentTime > 800) {
|
||||
this.playerCurrentTime = playData.t; // - this.record_tick * this.speed_table[this.speed_offset].speed;
|
||||
recordTick = 800;
|
||||
}
|
||||
}
|
||||
// 同步进度条
|
||||
this.progress = parseInt(this.playerCurrentTime * 100 / this.timeUsed);
|
||||
|
||||
if (this.playedCount >= this.recordData.length) {
|
||||
this.progress = 100;
|
||||
//播放完成
|
||||
this.isFinished = true;
|
||||
this.isPlaying = false;
|
||||
} else {
|
||||
if (!this.isNeedStop)
|
||||
this.playerTimer = setTimeout(this.doPlay, recordTick);
|
||||
}
|
||||
|
||||
},
|
||||
play() {
|
||||
if (this.isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isFinished) {
|
||||
this.restart();
|
||||
return;
|
||||
}
|
||||
|
||||
this.isNeedStop = false;
|
||||
this.isPlaying = true;
|
||||
this.playerTimer = setTimeout(this.doPlay, this.recordTick);
|
||||
},
|
||||
|
||||
pause() {
|
||||
if (this.playerTimer) {
|
||||
clearTimeout(this.playerTimer);
|
||||
}
|
||||
this.isNeedStop = true;
|
||||
this.isPlaying = false;
|
||||
},
|
||||
|
||||
restart() {
|
||||
if (this.playerTimer) {
|
||||
clearTimeout(this.playerTimer);
|
||||
}
|
||||
this.playerCurrentTime = 0;
|
||||
this.initAndPlay();
|
||||
},
|
||||
|
||||
dateFormat(time) {
|
||||
if (!time) {
|
||||
return '-';
|
||||
}
|
||||
let date = new Date(time * 1000);
|
||||
let year = date.getFullYear();
|
||||
let month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;
|
||||
let day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
|
||||
let hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
|
||||
let minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
|
||||
let seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
|
||||
|
||||
return year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds;
|
||||
},
|
||||
formatUpdateTime(date) {
|
||||
let time=new Date(date);
|
||||
let hours=time.getHours()>9?time.getHours():'0'+time.getHours();
|
||||
let minutes=time.getMinutes()>9?time.getMinutes():'0'+time.getMinutes();
|
||||
|
||||
return hours+':'+minutes;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 入口,监听terminal session的变化,开始功能
|
||||
obj: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler(n) {
|
||||
if (n.uuid) {
|
||||
this.filter.uuid = n.uuid;
|
||||
this.initAndPlay();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.replay-console {
|
||||
width: 660px;
|
||||
height: 480px;
|
||||
padding: 10px 4px 10px 10px;
|
||||
background-color: black;
|
||||
}
|
||||
</style>
|
||||
@@ -149,6 +149,18 @@ export const setting = {
|
||||
]
|
||||
};
|
||||
|
||||
export const terminalLog = {
|
||||
status: {
|
||||
0: i18n.t('config.terminallog.statusItem.connecting'),
|
||||
1: i18n.t('config.terminallog.statusItem.connectionFailed'),
|
||||
2: i18n.t('config.terminallog.statusItem.over'),
|
||||
3: i18n.t('config.terminallog.statusItem.kickedOut'),
|
||||
4: i18n.t('config.terminallog.statusItem.unknownError'),
|
||||
},
|
||||
dangerCmd: ['chmod', 'chown', 'kill', 'rm', 'su', 'sudo'],
|
||||
infoCmd: ['exit']
|
||||
};
|
||||
|
||||
//公共组件的跳转来源
|
||||
export const fromRoute = {
|
||||
panel: "panel",
|
||||
@@ -160,5 +172,6 @@ export const fromRoute = {
|
||||
dc: "dc",
|
||||
endpoint: "endpoint",
|
||||
project: "project",
|
||||
endpointQuery: "endpointQuery"
|
||||
endpointQuery: "endpointQuery",
|
||||
terminalLog: "terminal"
|
||||
};
|
||||
|
||||
@@ -284,7 +284,17 @@ export function stringTimeParseToUnix(stringTime){
|
||||
let time=new Date(stringTime).getTime();
|
||||
return time/1000;
|
||||
}
|
||||
|
||||
export function calcDurationByStringTime(startTime, endTime) {
|
||||
let durationSecond = stringTimeParseToUnix(endTime)-stringTimeParseToUnix(startTime);
|
||||
let result = `${durationSecond%60}s`;
|
||||
if (durationSecond > 60) {
|
||||
result = `${(Math.floor(durationSecond/60))%60}m ${result}`;
|
||||
}
|
||||
if (durationSecond > 60*60) {
|
||||
result = `${Math.floor(durationSecond/(60*60))}h ${result}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function unixTimeParseToString(unixTime,fmt='yyyy-MM-dd hh:mm:ss'){
|
||||
let date=new Date(unixTime * 1000);
|
||||
var o = {
|
||||
@@ -592,13 +602,8 @@ export const tableSet = {
|
||||
}
|
||||
case 'temrminallog':
|
||||
switch(prop){
|
||||
case 'id': return 'id';
|
||||
case 'host': return 'host';
|
||||
case 'port': return 'port';
|
||||
case 'protocol': return 'protocol';
|
||||
case 'user': return 'user';
|
||||
case 'cmd': return 'cmd';
|
||||
case 'time': return 'time';
|
||||
case 'startTime': return 'startTime';
|
||||
default : return prop;
|
||||
}
|
||||
case 'alertRules':
|
||||
|
||||
@@ -631,6 +631,13 @@ const cn = {
|
||||
terminallog: {
|
||||
terminallog: "终端日志",
|
||||
status: "状态",
|
||||
statusItem: {
|
||||
connecting: "连接中",
|
||||
connectionFailed: "连接失败",
|
||||
over: "已结束",
|
||||
kickedOut: "被踢出",
|
||||
unknownError: "未知错误"
|
||||
},
|
||||
option: "操作",
|
||||
host: "主机",
|
||||
cmd: "命令",
|
||||
@@ -655,7 +662,18 @@ const cn = {
|
||||
path: "路径",
|
||||
file: "文件",
|
||||
success: "成功",
|
||||
fail: "失败"
|
||||
fail: "失败",
|
||||
startTime: "开始时间",
|
||||
duration: "持续",
|
||||
remote: "远程连接",
|
||||
replay: "回放",
|
||||
log: "日志",
|
||||
record: {
|
||||
record: "记录",
|
||||
history: "历史记录",
|
||||
dangerTip: "这条命令可能是危险的",
|
||||
legendTip: "图例说明",
|
||||
}
|
||||
},
|
||||
dc: {
|
||||
dc: "数据中心",
|
||||
|
||||
@@ -669,6 +669,13 @@ const en = {
|
||||
terminallog: {
|
||||
terminallog: 'Terminal log',
|
||||
status: 'Status',//"状态"
|
||||
statusItem: {
|
||||
connecting: "Connecting",
|
||||
connectionFailed: "Connection failed",
|
||||
over: "Over",
|
||||
kickedOut: "Kicked out",
|
||||
unknownError: "Unknown error"
|
||||
},
|
||||
option: 'Operation',//"操作",
|
||||
host: 'Host',
|
||||
cmd: 'CMD',
|
||||
@@ -693,7 +700,18 @@ const en = {
|
||||
path: 'Path',
|
||||
file: 'File',
|
||||
success: 'Success',
|
||||
fail: 'Fail'
|
||||
fail: 'Fail',
|
||||
startTime: "Start time",
|
||||
duration: "Duration",
|
||||
remote: "Remote",
|
||||
replay: "Replay",
|
||||
log: "Log",
|
||||
record: {
|
||||
record: "Record",
|
||||
history: "History record",
|
||||
dangerTip: "This CMD may be dangerous",
|
||||
legendTip: "Legend description",
|
||||
}
|
||||
},
|
||||
operationlog: {
|
||||
operationlog: 'Operation log',
|
||||
|
||||
@@ -96,14 +96,6 @@
|
||||
<span>-</span>
|
||||
</template>
|
||||
</template>
|
||||
<span v-else-if="item.prop == 'lang'">
|
||||
{{scope.row[item.prop] == 'en' ? 'English' : ''}}
|
||||
{{scope.row[item.prop] == 'zh' ? '中文' : ''}}
|
||||
{{scope.row[item.prop] == 'ru' ? 'русский' : ''}}
|
||||
</span>
|
||||
<span v-else-if="item.prop == 'receiver'">
|
||||
<template v-for="rec in scope.row[item.prop]">{{rec.name}} </template>
|
||||
</span>
|
||||
<span v-else-if="item.prop == 'status'">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
|
||||
@@ -5,55 +5,63 @@
|
||||
</style>
|
||||
<template>
|
||||
<div class="terminallog">
|
||||
<div class="top-tools">
|
||||
<div></div>
|
||||
<div>
|
||||
<div :class="{'main-list-with-sub': bottomBox.showSubList}" class="main-list">
|
||||
<!-- 顶部工具栏 -->
|
||||
<div class="main-modal"></div>
|
||||
<div class="top-tools" v-show="bottomBox.mainResizeShow">
|
||||
<div :class="{'top-tool-main-right-to-left': bottomBox.showSubList}" class="top-tool-main-right">
|
||||
<div class="top-tool-search margin-r-5">
|
||||
<search-input :searchMsg="searchMsg" @search="search"></search-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pagination-top pagination-top-hide display-none"></div>
|
||||
</div>
|
||||
<!-- 自定义table列 -->
|
||||
<transition name="el-zoom-in-top">
|
||||
<element-set
|
||||
id="terminal-log"
|
||||
v-if="tools.showCustomTableTitle"
|
||||
@close="tools.showCustomTableTitle = false"
|
||||
:custom-table-title.sync="tools.customTableTitle"
|
||||
:original-table-title="tableTitle"
|
||||
@close="tools.showCustomTableTitle = false"
|
||||
ref="customTableTitle"
|
||||
v-if="tools.showCustomTableTitle"
|
||||
></element-set>
|
||||
</transition>
|
||||
<el-table
|
||||
id="terminal-log-table"
|
||||
class="nz-table"
|
||||
:data="tableData"
|
||||
border
|
||||
ref="terminalLogTable"
|
||||
:height="$tableHeight.normal"
|
||||
v-loading="tools.loading"
|
||||
:cell-class-name="messageStyle"
|
||||
:data="tableData"
|
||||
:height="$tableHeight.normal"
|
||||
@sort-change="tableDataSort"
|
||||
border
|
||||
class="nz-table"
|
||||
ref="terminalLogTable"
|
||||
style="width: 100%;"
|
||||
@sort-change="tableDataSort">
|
||||
v-loading="tools.loading">
|
||||
<el-table-column
|
||||
:resizable="true"
|
||||
v-for="(item, index) in tools.customTableTitle"
|
||||
v-if="item.show"
|
||||
:key="`col-${index}`"
|
||||
:label="item.label"
|
||||
:sortable="$tableSet.sortableShow(item.prop,'temrminallog')"
|
||||
:prop="$tableSet.propTitle(item.prop,'temrminallog')"
|
||||
:resizable="true"
|
||||
:sort-orders="['ascending', 'descending']"
|
||||
:sortable="$tableSet.sortableShow(item.prop,'temrminallog')"
|
||||
v-for="(item, index) in tools.customTableTitle"
|
||||
v-if="item.show"
|
||||
>
|
||||
<template slot-scope="scope" :column="item">
|
||||
<span v-if="item.prop == 'lang'">
|
||||
{{scope.row[item.prop] == 'en' ? 'English' : ''}}
|
||||
{{scope.row[item.prop] == 'zh' ? '中文' : ''}}
|
||||
{{scope.row[item.prop] == 'ru' ? 'русский' : ''}}
|
||||
</span>
|
||||
<span v-else-if="item.prop == 'time'">{{utcTimeToTimezoneStr(scope.row[item.prop])}}</span>
|
||||
<template :column="item" slot-scope="scope">
|
||||
<span v-if="item.prop == 'time'">{{utcTimeToTimezoneStr(scope.row[item.prop])}}</span>
|
||||
<template v-else-if="item.prop == 'status'">
|
||||
<span>{{scope.row.status==='1' ? $t("config.terminallog.success") : $t("config.terminallog.fail")}}</span>
|
||||
<span>{{getStatusText(scope.row.status)}}</span>
|
||||
</template>
|
||||
<template v-else-if="item.prop == 'remote'">
|
||||
<span>{{getRemoteText(scope.row)}}</span>
|
||||
</template>
|
||||
<template v-else-if="item.prop == 'duration'">
|
||||
<span>{{getDuration(scope.row)}}</span>
|
||||
</template>
|
||||
<template v-else-if="item.prop == 'option'">
|
||||
<span :id="'terminalLog-replay-'+scope.row.id" :title="$t('config.terminallog.replay')" @click="showReplay(scope.row)" class="content-right-option"><i class="el-icon-video-play"></i></span>
|
||||
|
||||
<span :id="'terminalLog-log-'+scope.row.id" :title="$t('config.terminallog.log')" @click="showRecord(scope.row)" class="content-right-option"><i class="el-icon-tickets"></i></span>
|
||||
|
||||
</template>
|
||||
<span v-else>{{scope.row[item.prop]}}</span>
|
||||
</template>
|
||||
@@ -66,18 +74,41 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<button :class="{'to-top-is-hover': tools.tableHover}" :style="{top: tools.toTopBtnTop}" @click="toTop(scrollbarWrap)" class="to-top" v-show="tools.showTopBtn" id="terminal-log-totop"><i class="nz-icon nz-icon-top"></i></button>
|
||||
<Pagination :tableId="tableId" :pageObj="pageObj" @pageNo='pageNo' @pageSize='pageSize' ref="Pagination"></Pagination>
|
||||
<button :class="{'to-top-is-hover': tools.tableHover}" :style="{top: tools.toTopBtnTop}" @click="toTop(scrollbarWrap)" class="to-top" v-show="tools.showTopBtn"><i class="nz-icon nz-icon-top"></i></button>
|
||||
<div class="pagination-bottom" v-show="!bottomBox.showSubList">
|
||||
<Pagination :pageObj="pageObj" :tableId="tableId" @pageNo='pageNo' @pageSize='pageSize' ref="Pagination"></Pagination>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部上滑框 -->
|
||||
<transition name="el-zoom-in-bottom">
|
||||
<bottom-box :detail="bottomBox.terminalLog" :is-full-screen="bottomBox.isFullScreen" :obj="bottomBox.terminalLog" :sub-resize-show="bottomBox.subResizeShow" :target-tab.sync="bottomBox.targetTab" @closeSubList="bottomBox.showSubList = false" @exitFullScreen="exitFullScreen"
|
||||
@fullScreen="fullScreen" @listResize="listResize" from="terminal" v-if="bottomBox.showSubList" ></bottom-box>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import bus from "../../../libs/bus";
|
||||
import {terminalLog} from "../../common/js/constants";
|
||||
import {calcDurationByStringTime} from "../../common/js/tools";
|
||||
|
||||
export default {
|
||||
name: "terminallog",
|
||||
data() {
|
||||
return {
|
||||
tableId: 'terminalLogTable', //需要分页的table的id,用于记录每页数量
|
||||
|
||||
/*二级页面相关*/
|
||||
bottomBox: {
|
||||
terminalLog: {},
|
||||
mainResizeShow: true, //dom高度改变时是否展示|隐藏
|
||||
subResizeShow: true,
|
||||
isFullScreen: false, //全屏状态
|
||||
showSubList: false, //是否显示二级列表
|
||||
targetTab: '', //显示二级列表中的哪个页签
|
||||
inTransform: false, //搜索框相关,搜索条件下拉框是否在transform里
|
||||
},
|
||||
|
||||
/*工具参数*/
|
||||
tools: {
|
||||
loading: false, //是否显示table加载动画
|
||||
@@ -108,18 +139,6 @@
|
||||
isAdd: false, //false,true:resize
|
||||
title: ''
|
||||
},
|
||||
terminallog: {
|
||||
id: '',
|
||||
host: '',
|
||||
status: '1',
|
||||
time: '',
|
||||
protocol: '',
|
||||
port: '',
|
||||
user: '',
|
||||
cmd: '',
|
||||
authType: '',
|
||||
userName: ''
|
||||
},
|
||||
pageObj: {
|
||||
pageNo: 1,
|
||||
pageSize: this.$CONSTANTS.defaultPageSize,
|
||||
@@ -132,12 +151,17 @@
|
||||
show: true,
|
||||
width: 80
|
||||
}, {
|
||||
label: this.$t('config.terminallog.host'),
|
||||
prop: 'host',
|
||||
label: "Session ID",
|
||||
prop: 'uuid',
|
||||
show: true,
|
||||
}, {
|
||||
label: this.$t('config.terminallog.port'),
|
||||
prop: 'port',
|
||||
label: "Username",
|
||||
prop: 'username',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
label: this.$t('config.terminallog.remote'),
|
||||
prop: 'remote',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
@@ -146,8 +170,13 @@
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
label: this.$t('config.terminallog.user'),
|
||||
prop: 'user',
|
||||
label: this.$t('config.terminallog.startTime'),
|
||||
prop: 'startTime',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
label: this.$t('config.terminallog.duration'),
|
||||
prop: 'duration',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
@@ -156,25 +185,17 @@
|
||||
show: false
|
||||
},
|
||||
{
|
||||
label: this.$t('config.terminallog.cmd'),
|
||||
prop: 'cmd',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
label: 'UserName',
|
||||
prop: 'userName',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: this.$t('config.terminallog.time'),
|
||||
prop: 'time',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
label: this.$t('config.terminallog.status'),
|
||||
label: this.$t('config.terminallog.status'), // killusername鼠标悬停形式
|
||||
prop: 'status',
|
||||
show: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: this.$t('config.account.option'),
|
||||
prop: 'option',
|
||||
show: true,
|
||||
width: 120,
|
||||
fixed: "right"
|
||||
}
|
||||
],
|
||||
tableData: [],
|
||||
@@ -200,12 +221,6 @@
|
||||
type: 'input',
|
||||
label: 'user',
|
||||
disabled: false
|
||||
},{
|
||||
id: 13,
|
||||
name: this.$t('config.terminallog.cmd'),
|
||||
type: 'input',
|
||||
label: 'cmd',
|
||||
disabled: false
|
||||
},{
|
||||
id: 14,
|
||||
name: this.$t('config.terminallog.userId'),
|
||||
@@ -217,19 +232,72 @@
|
||||
},
|
||||
searchLabel: {}, //搜索参数
|
||||
scrollbarWrap: null,
|
||||
|
||||
testData: {
|
||||
code: 200,
|
||||
data: {
|
||||
pageNo: 1,
|
||||
pageSize: 20,
|
||||
total: 2,
|
||||
pages: 1,
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
uuid: "abcdde",
|
||||
host: "192.168.40.42",
|
||||
port: 22,
|
||||
protocol: "SSH",
|
||||
authType: 1,
|
||||
username: "admin",
|
||||
loginUser: "root",
|
||||
startTime: "2021-02-01 14:20:08",
|
||||
endTime: "2021-02-01 14:30:08",
|
||||
status: 3,
|
||||
killUserName: "leader",
|
||||
}, {
|
||||
id: 2,
|
||||
uuid: "zpppoe",
|
||||
host: "192.168.40.42",
|
||||
port: 22,
|
||||
protocol: "SSH",
|
||||
authType: 1,
|
||||
username: "admin",
|
||||
loginUser: "root",
|
||||
startTime: "2021-02-01 14:41:08",
|
||||
endTime: "2021-02-01 14:51:08",
|
||||
status: 2,
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getTableData: function () {
|
||||
this.$set(this.searchLabel, "pageNo", this.pageObj.pageNo);
|
||||
this.$set(this.searchLabel, "pageSize", this.pageObj.pageSize);
|
||||
this.tools.loading = true;
|
||||
this.$get('terminal/log', this.searchLabel).then(response => {
|
||||
this.tools.loading = false;
|
||||
if (response.code === 200) {
|
||||
for (let i = 0; i < response.data.list.length; i++) {
|
||||
response.data.list[i].status = response.data.list[i].status + "";
|
||||
computed: {
|
||||
getStatusText() {
|
||||
return function(status) {
|
||||
return terminalLog.status[status];
|
||||
}
|
||||
},
|
||||
getRemoteText() {
|
||||
return function(record) {
|
||||
return `${record.loginUser}@${record.host}:${record.port}`;
|
||||
}
|
||||
},
|
||||
getDuration() {
|
||||
return function(record) {
|
||||
if (record.endTime) {
|
||||
return calcDurationByStringTime(record.startTime, record.endTime);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getTableData() {
|
||||
this.$get('terminal/session', this.searchLabel).then(response => {
|
||||
this.tools.loading = false;
|
||||
//response = this.testData;
|
||||
if (response.code === 200) {
|
||||
this.tableData = response.data.list;
|
||||
this.pageObj.total = response.data.total;
|
||||
if (!this.scrollbarWrap) {
|
||||
@@ -241,11 +309,62 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/*getTableData: function () {
|
||||
this.$set(this.searchLabel, "pageNo", this.pageObj.pageNo);
|
||||
this.$set(this.searchLabel, "pageSize", this.pageObj.pageSize);
|
||||
this.tools.loading = true;
|
||||
this.$get('terminal/log', this.searchLabel).then(response => {
|
||||
this.tools.loading = false;
|
||||
response = this.testData;
|
||||
if (response.code === 200) {
|
||||
this.tableData = response.data.list;
|
||||
this.pageObj.total = response.data.total;
|
||||
if (!this.scrollbarWrap) {
|
||||
this.$nextTick(() => {
|
||||
this.scrollbarWrap = this.$refs.terminalLogTable.bodyWrapper;
|
||||
this.toTopBtnHandler(this.scrollbarWrap);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
},*/
|
||||
showReplay(record) {
|
||||
this.bottomBox.targetTab = 'replay';
|
||||
this.bottomBox.terminalLog = JSON.parse(JSON.stringify(record));
|
||||
this.bottomBox.showSubList = true;
|
||||
},
|
||||
showRecord(record) {
|
||||
this.bottomBox.targetTab = 'record';
|
||||
this.bottomBox.terminalLog = JSON.parse(JSON.stringify(record));
|
||||
this.bottomBox.showSubList = true;
|
||||
},
|
||||
// 全屏
|
||||
fullScreen() {
|
||||
let vm = this;
|
||||
this.$bottomBoxWindow.fullScreen(vm);
|
||||
},
|
||||
// 退出全屏
|
||||
exitFullScreen() {
|
||||
let vm = this;
|
||||
this.$bottomBoxWindow.exitFullScreen(vm);
|
||||
},
|
||||
// 鼠标拖动二级列表
|
||||
listResize(e) {
|
||||
let vm = this;
|
||||
this.$bottomBoxWindow.listResize(vm, e);
|
||||
},
|
||||
messageStyle(e) {
|
||||
if (e.column.label == this.$t('config.terminallog.status')) {
|
||||
if (e.row.status == '1') {
|
||||
if (e.row.status == '0') {
|
||||
return 'success';
|
||||
} else {
|
||||
} else if (e.row.status == '1') {
|
||||
return 'warning';
|
||||
} else if (e.row.status == '2') {
|
||||
return 'suspended';
|
||||
} else if (e.row.status == '3') {
|
||||
return 'danger';
|
||||
} else if (e.row.status == '4') {
|
||||
return 'danger';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user