feat: terminal日志回放,节前提交一半关机没提成功

This commit is contained in:
chenjinsong
2021-02-18 10:13:52 +08:00
parent cb9e0fcd60
commit aee43ea920
11 changed files with 827 additions and 133 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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, //是否全屏

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"
};

View File

@@ -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':

View File

@@ -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: "数据中心",

View File

@@ -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',

View File

@@ -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}}&nbsp;&nbsp;</template>
</span>
<span v-else-if="item.prop == 'status'">
<el-switch
v-model="scope.row.status"

View File

@@ -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>
&nbsp;
<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, //falsetrueresize
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';
}
}