mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-12-01 19:38:09 +08:00
fix 日志组件采用虚拟滚动渲染,避免日志过多浏览器卡死
This commit is contained in:
parent
cb6503511a
commit
693f184426
@ -9,6 +9,7 @@
|
||||
1. 【server】修复 页面关闭 docker 终端未主动关闭后台终端进程问题
|
||||
2. 【server】优化 docker 终端页面缓冲区大小自动适应
|
||||
3. 【server】优化 项目列表可以查看项目日志(避免控制台卡顿无法操作下载日志)(感谢@阿超)
|
||||
4. 【server】优化 日志组件采用虚拟滚动渲染,避免日志过多浏览器卡死
|
||||
|
||||
------
|
||||
|
||||
|
@ -26,11 +26,12 @@
|
||||
"vue-codemirror": "^4.0.6",
|
||||
"vue-json-viewer": "^2.2.20",
|
||||
"vue-router": "^3.5.2",
|
||||
"vue-virtual-scroller": "^1.1.2",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuex": "^3.5.1",
|
||||
"xterm": "^4.13.0",
|
||||
"xterm-addon-attach": "^0.6.0",
|
||||
"xterm-addon-fit": "^0.5.0",
|
||||
"vuedraggable": "^2.24.3"
|
||||
"xterm-addon-fit": "^0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.13",
|
||||
@ -43,9 +44,9 @@
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-vue": "^7.15.1",
|
||||
"prettier": "^2.3.2",
|
||||
"less": "^3.0.4",
|
||||
"less-loader": "^5.0.0",
|
||||
"prettier": "^2.3.2",
|
||||
"vue-template-compiler": "^2.6.14"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
@ -10,11 +10,11 @@
|
||||
<a-col v-if="this.extendBar" style="padding-left: 10px">
|
||||
<a-space>
|
||||
<a-tooltip title="清空当前缓冲区内容">
|
||||
<a-button type="link" style="padding: 0" @click="clearLogCache" icon="delete"><span style="margin-left: 2px">清空</span></a-button>
|
||||
<a-button type="primary" size="small" @click="clearLogCache" icon="delete">清空</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="内容超过边界自动换行">
|
||||
<!-- <a-tooltip title="内容超过边界自动换行">
|
||||
<a-switch v-model="temp.wordBreak" checked-children="自动换行" un-checked-children="不换行" @change="onChange" />
|
||||
</a-tooltip>
|
||||
</a-tooltip> -->
|
||||
<a-tooltip title="有新内容后是否自动滚动到底部">
|
||||
<a-switch v-model="temp.logScroll" checked-children="自动滚动" un-checked-children="不滚动" @change="onChange" />
|
||||
</a-tooltip>
|
||||
|
@ -1,21 +1,43 @@
|
||||
<template>
|
||||
<div>
|
||||
<code-editor
|
||||
ref="codemirror"
|
||||
:style="`height:${this.height}`"
|
||||
v-model="showContext"
|
||||
:options="{ theme: 'panda-syntax', mode: 'verilog', cursorBlinkRate: -1, tabSize: 2, readOnly: true, styleActiveLine: true, lineWrapping: this.config.wordBreak }"
|
||||
></code-editor>
|
||||
<div class="wrapper">
|
||||
<RecycleScroller class="scroller" :id="uniqueId" :style="`min-height:${height};height:${height}`" key-field="id" :items="showList" :item-size="itemHeight" :emitUpdate="false">
|
||||
<template v-slot="{ index, item }">
|
||||
<div class="item">
|
||||
<template v-if="!item.warp">
|
||||
<span class="linenumber">{{ index + 1 }}</span> {{ item.text }}
|
||||
</template>
|
||||
</div>
|
||||
<!-- <code-editor
|
||||
ref="codemirror"
|
||||
v-model="item.text"
|
||||
:options="{
|
||||
theme: 'panda-syntax',
|
||||
mode: 'verilog',
|
||||
// maxHighlightLength: 5,
|
||||
viewportMargin: 1,
|
||||
cursorBlinkRate: -1,
|
||||
tabSize: 2,
|
||||
readOnly: true,
|
||||
styleActiveLine: true,
|
||||
lineNumbers: true,
|
||||
firstLineNumber: index + 1,
|
||||
lineWrapping: config.wordBreak,
|
||||
}"
|
||||
></code-editor> -->
|
||||
</template>
|
||||
</RecycleScroller>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ansiparse from "@/utils/parse-ansi";
|
||||
import codeEditor from "@/components/codeEditor";
|
||||
|
||||
// import codeEditor from "@/components/codeEditor";
|
||||
import { RecycleScroller } from "vue-virtual-scroller";
|
||||
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
|
||||
export default {
|
||||
components: {
|
||||
codeEditor,
|
||||
// codeEditor,
|
||||
RecycleScroller,
|
||||
},
|
||||
props: {
|
||||
height: {
|
||||
@ -33,29 +55,92 @@ export default {
|
||||
return {
|
||||
defText: "loading context...",
|
||||
logContext: "",
|
||||
dataArray: [],
|
||||
idInc: 0,
|
||||
visibleStartIndex: -1,
|
||||
itemHeight: 24,
|
||||
uniqueId: `component_${Math.random().toString(36).substring(2, 15)}`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
wordBreak() {
|
||||
this.changeBuffer();
|
||||
// this.changeBuffer();
|
||||
return this.config.wordBreak || false;
|
||||
},
|
||||
showContext: {
|
||||
get() {
|
||||
return this.logContext || this.defText;
|
||||
},
|
||||
set() {},
|
||||
showList() {
|
||||
const element = document.querySelector(`#${this.uniqueId}`);
|
||||
|
||||
let result = [...this.dataArray];
|
||||
let warp = false;
|
||||
if (element) {
|
||||
const min = Math.ceil(element.clientHeight / this.itemHeight);
|
||||
const le = min - result.length;
|
||||
for (let i = 0; i < le; i++) {
|
||||
result.push({
|
||||
id: "system-warp-empty:" + i,
|
||||
warp: true,
|
||||
});
|
||||
warp = true;
|
||||
}
|
||||
}
|
||||
if (!warp) {
|
||||
result = result.concat([
|
||||
{
|
||||
id: "system-warp-end:1",
|
||||
warp: true,
|
||||
},
|
||||
{
|
||||
id: "system-warp-end:2",
|
||||
text: "",
|
||||
warp: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
// showContext: {
|
||||
// get() {
|
||||
// return this.logContext || this.defText;
|
||||
// },
|
||||
// set() {},
|
||||
// },
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
//
|
||||
appendLine(data) {
|
||||
if (!data) {
|
||||
return;
|
||||
scrollToBottom() {
|
||||
const element = document.querySelector(`#${this.uniqueId}`);
|
||||
if (element) {
|
||||
// console.log(element, element.scrollHeight);
|
||||
element.scrollTop = element.scrollHeight - element.clientHeight;
|
||||
// this.scrollTo(element, element.scrollHeight - element.clientHeight, 500);
|
||||
// element.scrollIntoView(false);
|
||||
}
|
||||
const dataArray = Array.isArray(data) ? data : [data];
|
||||
this.logContext += dataArray
|
||||
},
|
||||
scrollTo(element, position) {
|
||||
if (!window.requestAnimationFrame) {
|
||||
window.requestAnimationFrame = function (cb) {
|
||||
return setTimeout(cb, 10);
|
||||
};
|
||||
}
|
||||
var scrollTop = element.scrollTop;
|
||||
var step = function () {
|
||||
var distance = position - scrollTop;
|
||||
scrollTop = scrollTop + distance / 5;
|
||||
if (Math.abs(distance) < 1) {
|
||||
element.scrollTop = position;
|
||||
} else {
|
||||
element.scrollTop = scrollTop;
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
};
|
||||
step();
|
||||
},
|
||||
onUpdate(viewStartIndex, viewEndIndex, visibleStartIndex, visibleEndIndex) {
|
||||
const tempArray = this.dataArray.slice(visibleStartIndex, visibleEndIndex);
|
||||
this.logContext = tempArray
|
||||
.map((item) => {
|
||||
return item.text;
|
||||
})
|
||||
.map((item) => {
|
||||
return (
|
||||
// gitee isuess I657JR
|
||||
@ -67,23 +152,79 @@ export default {
|
||||
);
|
||||
})
|
||||
.join("");
|
||||
this.visibleStartIndex = visibleStartIndex;
|
||||
|
||||
// console.log(this.dataArray.length, tempArray.length, visibleStartIndex, visibleEndIndex);
|
||||
// console.log(this.logContext);
|
||||
},
|
||||
//
|
||||
appendLine(data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
const tempArray = (Array.isArray(data) ? data : [data]).map((item) => {
|
||||
return {
|
||||
text: ansiparse(item)
|
||||
.map((ansiItem) => {
|
||||
return ansiItem.text;
|
||||
})
|
||||
.join(""),
|
||||
id: this.idInc++,
|
||||
};
|
||||
});
|
||||
this.dataArray = [...this.dataArray, ...tempArray];
|
||||
// console.log(this.dataArray);
|
||||
if (this.config.logScroll) {
|
||||
setTimeout(() => {
|
||||
// 延迟触发滚动
|
||||
this.$nextTick(() => {
|
||||
this.$refs?.codemirror?.scrollToBottom();
|
||||
this.scrollToBottom(".scroller");
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
clearLogCache() {
|
||||
this.logContext = "";
|
||||
this.dataArray = [];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.wrapper {
|
||||
/* overflow: hidden; */
|
||||
/* position: absolute; */
|
||||
/* top: 0; */
|
||||
/* bottom: 0; */
|
||||
/* left: 0; */
|
||||
/* right: 0; */
|
||||
}
|
||||
.scroller {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
font-family: Operator Mono, Source Code Pro, Menlo, Monaco, Consolas, Courier New, monospace;
|
||||
position: relative;
|
||||
|
||||
<style></style>
|
||||
overflow-y: scroll;
|
||||
}
|
||||
/deep/ .vue-recycle-scroller__item-wrapper {
|
||||
background: #292a2b;
|
||||
color: #ffb86c;
|
||||
white-space: nowrap;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.item {
|
||||
padding: 0px 6px;
|
||||
}
|
||||
|
||||
.linenumber {
|
||||
color: #e6e6e6;
|
||||
padding: 0px 4px;
|
||||
opacity: 0.6;
|
||||
/* overflow: auto; */
|
||||
/* white-space: nowrap; */
|
||||
}
|
||||
</style>
|
||||
|
@ -25,8 +25,8 @@
|
||||
|
||||
<a-dropdown>
|
||||
<!-- <a type="link" class="ant-dropdown-link"> 更多<a-icon type="down" /> </a> -->
|
||||
<a
|
||||
class="ant-dropdown-link"
|
||||
<a-button
|
||||
size="small"
|
||||
@click="
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
@ -34,12 +34,12 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<a-tag>
|
||||
文件大小: {{ project.logSize || "-" }}
|
||||
<!-- 更多 -->
|
||||
<a-icon type="fullscreen" />
|
||||
</a-tag>
|
||||
</a>
|
||||
<!-- <a-tag> -->
|
||||
日志大小: {{ project.logSize || "-" }}
|
||||
<!-- 更多 -->
|
||||
<a-icon type="fullscreen" />
|
||||
<!-- </a-tag> -->
|
||||
</a-button>
|
||||
<!-- <a-menu slot="overlay">
|
||||
<a-menu-item>
|
||||
<a-button type="primary" size="small" :disabled="!project.logSize" @click="handleDownload">导出日志</a-button>
|
||||
|
Loading…
Reference in New Issue
Block a user