<div>
<el-dialog
v-dialogDrag
:title="title + `${rowData.qname ? `-${rowData.qname}` : ''}`"
:visible.sync="logVisible"
width="60%"
:close-on-click-modal="false"
@close="handleClose"
>
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane
v-for="item in tabList"
:key="item.key"
:label="item.label"
:name="item.key"
>
<div v-loading="loading">
<div class="log-box">
<ul
v-if="
logSocketDataList.length > 0 &&
logSocketDataList[0].status &&
logSocketDataList[0].status === 'error'
"
>
<li v-for="item in logSocketDataList" :key="item.id">
{{ item.message }}
</li>
</ul>
<template v-else>
<virtual-list
ref="testRef"
:containerHeight="containerHeight"
:listData="logSocketDataList"
>
<template slot-scope="row">
<div style="" v-html="renderHtml(row.data.content)">
</div>
</template>
</virtual-list>
</template>
</div>
</div>
</el-tab-pane>
</el-tabs>
<el-row style="margin: 20px 0; text-align: right">
<el-button size="mini" @click="handleDownLodLog">{{$t("dsync_operate.downloadLog")}}</el-button>
<el-button size="mini" @click="handleClose">{{$t("dsync_operate.cancel")}}</el-button>
</el-row>
</el-dialog>
</div>
</template>
<script>
import logSocket from "./logSocket.js";
import { getConnectToken } from "@/utils/auth";
import { downloadLog } from "@/message/uniteApi/15-dsync/dsyncQueueApis.js";
import virtualList from "./virtualList.vue";
import i18n from "../../../../lang/index.js";
export default {
name: "logDetail",
mixins: [logSocket],
components: { virtualList },
data() {
return {
title: i18n.t("dsync_operate.logDetail"),
logVisible: false,
rowData: {},
activeName: "sourceDsync",
tabList: [
{ label: i18n.t("dsync_queuesPage.sourceDsync"), key: "sourceDsync" },
{ label: i18n.t("dsync_queuesPage.targentDsync"), key: "targentDsync" },
{ label: i18n.t("dsync_queuesPage.manageDbpsd"), key: "manageDbpsd" },
{ label: i18n.t("dsync_queuesPage.sourceDagent"), key: "sourceDagent" },
{ label: i18n.t("dsync_queuesPage.tragetDagent"), key: "tragetDagent" },
],
logParams: {},
logSocketDataList: [], // logsocket数据
timer1: null,
timer2: null,
loading: false,
containerHeight: 450 + "px",
once: 0, //每次插入的数量
countRender: 0, //已经渲染次数
loopCount: 0, //需要插入的次数
};
},
methods: {
openLog(rowData) {
this.rowData = rowData;
this.logParams = this.buildSocketParams(rowData, "sourceDsync");
this.connectLogSocket(this.logParams);
this.logVisible = true;
this.loading = true;
// this.handleInit();
if (this.timer1) {
clearInterval(this.timer1);
}
this.timer1 = setInterval(() => {
this.logSocket.send(JSON.stringify(this.logParams));
}, 5000);
},
handleClose() {
if (this.timer1) {
clearInterval(this.timer1);
}
if (this.timer2) {
clearInterval(this.timer2);
}
this.logVisible = false;
this.loading = false;
this.activeName = "sourceDsync";
},
handleTabClick(tab, event) {
this.loading = true;
this.activeName = tab.name;
const tabChangeParams = this.buildSocketParams(
this.rowData,
this.activeName
);
if (this.timer1) {
clearInterval(this.timer1);
}
if (this.timer2) {
clearInterval(this.timer2);
}
this.timer2 = setInterval(() => {
this.logSocket.send(JSON.stringify(tabChangeParams));
}, 5000);
},
// logws消息
onmessageWsLog(event) {
this.logSocketReconnect = true;
const eventData = JSON.parse(event.data);
if (eventData[0].status && eventData[0].status === "error") {
this.logSocketDataList = JSON.parse(event.data);
} else {
const resData = JSON.parse(event.data).map((item, index) => {
return {
id: index,
content: item,
};
});
this.logSocketDataList = resData;
}
this.loading = false;
},
// 构建websocket消息参数
buildSocketParams(rowData, activeKey) {
// 0:源端 1:目标端
const {
source_ip,
target_ip,
source_agent_port,
target_agent_port,
qname,
} = rowData;
const socketParams = {
QNAME: qname,
TOKEN: getConnectToken(),
};
let changeParams = {};
if (activeKey === "sourceDsync") {
// 源端dsync
changeParams = {
VIEW_IP: source_ip,
VIEW_PORT: source_agent_port,
MD: "dsync",
SOURCE_TARGET: 0,
};
} else if (activeKey === "targentDsync") {
// 目标端dsync
changeParams = {
VIEW_IP: target_ip,
VIEW_PORT: target_agent_port,
MD: "dsync",
SOURCE_TARGET: 1,
};
} else if (activeKey === "manageDbpsd") {
// 管理端dbpsd
changeParams = {
VIEW_IP: source_ip,
VIEW_PORT: source_agent_port,
MD: "dbpsd",
SOURCE_TARGET: 0,
};
} else if (activeKey === "sourceDagent") {
// 源端dagent
changeParams = {
VIEW_IP: source_ip,
VIEW_PORT: source_agent_port,
MD: "dagent",
SOURCE_TARGET: 0,
};
} else if (activeKey === "tragetDagent") {
// 目标端dagent
changeParams = {
VIEW_IP: target_ip,
VIEW_PORT: target_agent_port,
MD: "dagent",
SOURCE_TARGET: 1,
};
}
Object.assign(socketParams, changeParams);
return socketParams;
},
// 下载日志
async handleDownLodLog() {
const {
QNAME,
MD,
VIEW_PORT,
VIEW_IP,
SOURCE_TARGET,
} = this.buildSocketParams(this.rowData, this.activeName);
const logParams = {
qname: QNAME,
md: MD,
viewPort: VIEW_PORT,
viewIp: VIEW_IP,
sourceOrTarget: SOURCE_TARGET,
};
const res = await downloadLog(logParams);
const blob = new Blob([res], { type: "text/plain;charset=UTF-8" });
const filename = `log${new Date().toLocaleString()}.txt`;
if ("download" in document.createElement("a")) {
// 非IE下载
const downlink = document.createElement("a");
downlink.download = filename;
downlink.style.display = "none";
downlink.href = URL.createObjectURL(blob);
document.body.appendChild(downlink);
downlink.click();
URL.revokeObjectURL(downlink.href); //释放URL对象
document.body.removeChild(downlink);
} else {
// IE10+下载
window.navigator.msSaveBlob(blob, filename);
}
},
handleInit() {
setTimeout(() => {
console.log("handleInit --- settimeout");
// 单次插入 可自定义
this.once = 20;
// 需要插入的次数 向上取整
this.loopCount = Math.ceil(this.logSocketDataList.length / this.once);
// 当前渲染次数
this.countRender = 0;
this.handleRender();
}, 500);
},
//百万数据分段插入
handleRender() {
console.log("handleRender");
for (let i = 0; i < this.once; i++) {
this.logSocketDataList.push({ id: this.countRender + "-" + i });
}
console.log(this.logSocketDataList, "this.logSocketDataList");
// 渲染次数加1,控制递归的次数
this.countRender++;
if (this.countRender < this.loopCount) {
window.requestAnimationFrame(this.handleRender);
}
},
renderHtml(html) {
return `<pre>${html}<pre>`
}
},
beforeDestroy() {
if (this.timer1) {
clearInterval(this.timer1);
}
if (this.timer2) {
clearInterval(this.timer2);
}
this.activeName = "sourceDsync";
},
};
</script>
<style lang="scss" scoped>
ul {
list-style: none;
padding: 6px;
margin: 0 0 0 8px;
}
li {
list-style: none;
padding: 0;
margin: 0;
height: 36px;
line-height: 36px;
}
.el-tabs__content {
height: 100px;
}
.log-box {
background-color: black;
color: #fff;
max-height: 450px;
overflow: auto;
::-webkit-scrollbar-thumb {
background-color: #ccc !important;
}
::-webkit-scrollbar-corner {
background: black !important;
}
}
</style>
data() {
return {
logSocket: null,
logSocketMsg: {},
logSocketReconnect: false
};
},
computed: {
logSocketUrl() {
return `ws://${
process.env.NODE_ENV === 'production'
? window.location.host
: '192.168.6.163:9091'
}/getLoggingWebsocket`; // 服务端连接的url
}
},
methods: {
connectLogSocket(logParams) {
if (!('WebSocket' in window)) {
alert('当前浏览器不支持WebSocket');
return;
}
console.log(logParams, 'logParams----24');
this.logSocket = null;
this.logSocket = new WebSocket(this.logSocketUrl);
this.logSocket.onopen = () => {
this.logSocket.send(JSON.stringify(logParams));
console.log('logSocket开启');
};
this.logSocket.onmessage = this.onmessageWsLog;
this.logSocket.onclose = function(e) {
console.log('logSocket关闭');
console.log(
'websocket 断开: ' + e.code + ' ' + e.reason + ' ' + e.wasClean
);
};
this.logSocket.onerror = function() {
console.log('logSocket出错');
this.reConnectSocket(this.logSocketUrl);
};
},
reConnectSocket() {
if (this.logSocketReconnect) return;
// 没连接上会一直重连,设置延迟避免请求过多
setTimeout(function() {
this.connectLogSocket(this.logParams);
this.logSocketReconnect = false;
}, 4000);
}
},
beforeDestroy() {
if (this.logSocket) {
console.log('??????????');
this.logSocket.close();
}
}
};
<div
ref="listRef"
:style="{ height: containerHeight }"
class="listContainer"
@scroll="scrollEvent($event)"
>
<div
class="listPhantom"
:style="{ height: computedListHeight + 'px' }"
></div>
<div
class="list"
ref="infiniteListRef"
:style="{ transform: computedGetTransform }"
>
<div
ref="items"
class="listItem"
v-for="(item, key) in computedVisibleData"
:key="key"
:style="{ height: '100%' }"
>
<slot :data="item" />
</div>
</div>
</div>
</template>
<script>
export default {
name: "virtualList",
props: {
//所有列表数据
listData: {
type: Array,
default: () => [],
},
//容器高度
containerHeight: {
type: String,
default: "100%",
},
},
watch: {
//监听列表数据
listData: {
handler() {
//修改每一个列的高度
this.$nextTick(() => {
this.screenHeight = this.$el.clientHeight; //客户端高度
this.$refs.listRef.scrollTop =
(this.listData.length - 1) * this.itemHeight;
// console.log(this.$refs.listRef.scrollTop, "00");
this.handleInit();
//获取每个列表高度
});
},
deep: true,
immediate: true,
},
},
computed: {
//获取真实显示列表数据
computedVisibleData() {
return this.listData.slice(
this.start,
Math.min(this.end, this.listData.length)
);
},
//列表总高度
computedListHeight() {
return BigInt(this.listData.length * this.itemHeight);
},
//可显示的列表项数
computedVisibleCount() {
return Math.ceil(this.screenHeight / this.itemHeight);
},
//偏移量对应的style
computedGetTransform() {
return `translate3d(0,${this.startOffset}px,0)`;
},
},
data() {
return {
//每列高度
itemHeight: 36,
//可视区域高度
screenHeight: 0,
//偏移量
startOffset: 0,
//起始索引
start: 0,
//结束索引
end: null,
};
},
methods: {
scrollEvent() {
//当前滚动位置
let scrollTop = this.$refs.listRef.scrollTop;
//此时的开始索引
this.start = Math.floor(scrollTop / this.itemHeight);
//此时的结束索引
this.end = this.start + this.computedVisibleCount;
//此时的偏移量
this.startOffset = scrollTop - (scrollTop % this.itemHeight);
},
//页面初始化
handleInit() {
this.screenHeight = this.$el.clientHeight; //客户端高度
this.start = 0; //列表开始索引
this.end = this.start + this.computedVisibleCount; //列表结束索引
},
},
mounted() {
// console.log("mounted");
this.handleInit();
},
};
</script>
<style scoped lang="scss">
.listContainer {
overflow: auto;
position: relative;
// -webkit-overflow-scrolling: touch;
}
.listPhantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list {
left: 0;
right: 0;
top: 0;
position: absolute;
z-index: 10;
}
.listItem {
height: 36px;
line-height: 36px;
// padding: 8px 0 8px 8px;
padding-left: 8px;
color: #fff;
box-sizing: border-box;
}
</style>
此文章为10月Day027学习笔记,内容来源于极客时间《重学前端》,强烈推荐该课程