<template>
|
<div class="tree-container">
|
<el-input placeholder="输入关键字进行过滤" v-model="filterText" class="filter-input">
|
</el-input>
|
<div class="custom-tree-wrapper">
|
<el-tree ref="tree" class="filter-tree" :filter-node-method="filterNode" :data="data" node-key="id" accordion>
|
<template #default="{ node, data }">
|
<div class="custom-tree-node">
|
<span class="node-label">{{ node.label }}</span>
|
<span v-if="data.hidden" class="node-actions">
|
<el-button type="text" size="mini" @click="() => view(data)" class="action-btn">
|
查看
|
</el-button>
|
<el-button type="text" size="mini" @click="() => dowmload(node, data)" class="action-btn">
|
下载
|
</el-button>
|
</span>
|
</div>
|
</template>
|
</el-tree>
|
</div>
|
</div>
|
<div class="log-container">
|
<el-card shadow="always" v-if="logName" class="log-card">
|
<template #header>
|
<div class="card-header">
|
<el-tag type="info" size="small">日志文件</el-tag>
|
<span class="log-title">{{ logName }}</span>
|
</div>
|
</template>
|
<div class="log-content">
|
<div v-for="(item, index) in log" :key="index" class="log-line">
|
<span class="line-number">{{ index + 1 }}</span>
|
<span class="line-content">{{ item }}</span>
|
</div>
|
</div>
|
</el-card>
|
<div v-else class="empty-log">
|
<el-empty description="请选择日志文件进行查看" />
|
</div>
|
</div>
|
</template>
|
|
<script>
|
export default {
|
data() {
|
return {
|
data: [],
|
defaultProps: {
|
children: "children",
|
label: "label",
|
},
|
filterText: "",
|
logName: "",
|
log: [],
|
};
|
},
|
watch: {
|
filterText(val) {
|
this.$refs.tree.filter(val);
|
},
|
},
|
created() {
|
this.getLogName();
|
},
|
|
methods: {
|
filterNode(value, data) {
|
if (!value) return true;
|
return data.label.indexOf(value) !== -1;
|
},
|
getLogName() {
|
this.http
|
.post("/api/Sys_Log/GetLogName", null, "正在执行中...")
|
.then((x) => {
|
if (x.status) {
|
this.data = x.data;
|
}
|
});
|
},
|
|
view(data) {
|
// var params = {
|
// MainData: { fileName: data.label },
|
// };
|
this.http
|
.post("/api/Sys_Log/GetLog?fileName=" + data.label, "正在查询中...")
|
.then((x) => {
|
if (x.status) {
|
this.logName = data.label;
|
this.log = x.data;
|
}
|
});
|
},
|
|
dowmload(node, data) {
|
let ipAddress = this.http.ipAddress;
|
let url =
|
"api/Sys_Log/DownLoadLog?fileName=" +
|
data.fatherNode +
|
"\\" +
|
data.label;
|
let fileName = data.label;
|
let xmlResquest = new XMLHttpRequest();
|
xmlResquest.open("GET", ipAddress + url, true);
|
xmlResquest.setRequestHeader("Content-type", "application/json");
|
xmlResquest.setRequestHeader(
|
"Authorization",
|
this.$store.getters.getToken()
|
);
|
let elink = this.$refs.template;
|
xmlResquest.responseType = "blob";
|
let $_vue = this;
|
this.loadingStatus = true;
|
xmlResquest.onload = function (e) {
|
// 请求成功
|
if (this.status == 200) {
|
let blob = this.response;
|
let a = document.createElement("a");
|
//window.URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。
|
let url = window.URL.createObjectURL(blob);
|
a.href = url;
|
a.download = fileName;
|
a.click();
|
//URL.revokeObjectURL() 静态方法用来释放一个之前已经存在的、通过调用 URL.createObjectURL() 创建的 URL 对象。当你结束使用某个 URL 对象之后,应该通过调用这个方法来让浏览器知道不用在内存中继续保留对这个文件的引用了。
|
window.URL.revokeObjectURL(url);
|
|
} else {
|
//下载失败处理
|
|
}
|
};
|
xmlResquest.send();
|
},
|
},
|
};
|
</script>
|
|
<style scoped>
|
.tree-container {
|
width: 30%;
|
float: left;
|
padding: 16px;
|
background: #f8f9fa;
|
border-radius: 8px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
}
|
|
.filter-input {
|
margin-bottom: 16px;
|
}
|
|
.filter-input :deep(.el-input__inner) {
|
border-radius: 20px;
|
border-color: #dcdfe6;
|
transition: all 0.3s;
|
}
|
|
.filter-input :deep(.el-input__inner):focus {
|
border-color: #409eff;
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
}
|
|
.custom-tree-wrapper {
|
height: 760px;
|
overflow-y: auto;
|
padding: 8px;
|
background: white;
|
border-radius: 6px;
|
border: 1px solid #ebeef5;
|
}
|
|
.filter-tree :deep(.el-tree-node__content) {
|
height: 40px;
|
margin: 2px 0;
|
border-radius: 4px;
|
transition: all 0.2s;
|
}
|
|
.filter-tree :deep(.el-tree-node__content:hover) {
|
background-color: #f0f9ff;
|
}
|
|
.filter-tree :deep(.el-tree-node.is-current > .el-tree-node__content) {
|
background-color: #ecf5ff;
|
font-weight: 600;
|
}
|
|
.custom-tree-node {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
width: 100%;
|
padding: 0 8px;
|
}
|
|
.node-label {
|
font-size: 14px;
|
color: #606266;
|
flex: 1;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
}
|
|
.node-actions {
|
display: flex;
|
gap: 8px;
|
margin-left: 12px;
|
}
|
|
.action-btn {
|
padding: 4px 8px;
|
font-size: 12px;
|
color: #409eff;
|
border-radius: 3px;
|
}
|
|
.action-btn:hover {
|
background-color: rgba(64, 158, 255, 0.1);
|
}
|
|
.log-container {
|
width: 68%;
|
float: right;
|
padding: 16px;
|
}
|
|
.log-card {
|
height: 800px;
|
border-radius: 8px;
|
border: 1px solid #ebeef5;
|
}
|
|
.log-card :deep(.el-card__header) {
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
border-bottom: 1px solid #ebeef5;
|
padding: 16px 20px;
|
}
|
|
.card-header {
|
display: flex;
|
align-items: center;
|
gap: 12px;
|
}
|
|
.log-title {
|
font-size: 16px;
|
font-weight: 600;
|
color: white;
|
flex: 1;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
}
|
|
.log-content {
|
height: 700px;
|
overflow-y: auto;
|
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
background: #f8f9fa;
|
padding: 12px;
|
border-radius: 4px;
|
}
|
|
.log-line {
|
display: flex;
|
align-items: flex-start;
|
margin-bottom: 4px;
|
line-height: 1.5;
|
background: white;
|
padding: 8px 12px;
|
border-radius: 4px;
|
border-left: 3px solid #409eff;
|
transition: all 0.2s;
|
}
|
|
.log-line:hover {
|
background: #f0f9ff;
|
transform: translateX(2px);
|
}
|
|
.line-number {
|
display: inline-block;
|
min-width: 40px;
|
padding-right: 12px;
|
text-align: right;
|
color: #909399;
|
font-size: 12px;
|
user-select: none;
|
}
|
|
.line-content {
|
flex: 1;
|
color: #303133;
|
font-size: 13px;
|
word-break: break-all;
|
white-space: pre-wrap;
|
}
|
|
.empty-log {
|
height: 800px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
background: white;
|
border-radius: 8px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
}
|
|
.empty-log :deep(.el-empty__description) {
|
margin-top: 8px;
|
}
|
|
/* 滚动条样式 */
|
.custom-tree-wrapper::-webkit-scrollbar,
|
.log-content::-webkit-scrollbar {
|
width: 6px;
|
height: 6px;
|
}
|
|
.custom-tree-wrapper::-webkit-scrollbar-track,
|
.log-content::-webkit-scrollbar-track {
|
background: #f1f1f1;
|
border-radius: 3px;
|
}
|
|
.custom-tree-wrapper::-webkit-scrollbar-thumb,
|
.log-content::-webkit-scrollbar-thumb {
|
background: #c1c1c1;
|
border-radius: 3px;
|
}
|
|
.custom-tree-wrapper::-webkit-scrollbar-thumb:hover,
|
.log-content::-webkit-scrollbar-thumb:hover {
|
background: #a8a8a8;
|
}
|
|
/* 清除浮动 */
|
.tree-container::after,
|
.log-container::after {
|
content: "";
|
display: table;
|
clear: both;
|
}
|
</style>
|