Authored by 鲁尚清

日志视图列表页及数据调用 #1

  1 +<title>日志视图</title>
  2 +<iframe src="/vue3/index.html#/esData" frameborder="0" class="layadmin-iframe" style="height: 99.5%!important;"/>
@@ -5,6 +5,8 @@ @@ -5,6 +5,8 @@
5 @import "../css/cacheData.css"; 5 @import "../css/cacheData.css";
6 /*告警消除处理*/ 6 /*告警消除处理*/
7 @import "../css/alarmsClear.css"; 7 @import "../css/alarmsClear.css";
  8 +/*日志详情*/
  9 +@import "../css/esData.css";
8 10
9 .d-flex { 11 .d-flex {
10 display: flex; 12 display: flex;
  1 +.esData-detail-container{
  2 + padding:0 15px;
  3 + min-height: 500px;
  4 + max-height:800px;
  5 + overflow: auto;
  6 +}
  7 +.esData-detail-container .info-title{
  8 + display: flex;
  9 + justify-content: space-between;
  10 + margin-bottom: 10px;
  11 + padding-top:10px;
  12 + font-size: 16px;
  13 + margin-top:10px;
  14 +}
  15 +.esData-detail-container .info-content{
  16 + margin-bottom: 5px;
  17 +}
  18 +.esData-detail-container .info-content-body{
  19 + background: #ffffff;
  20 + padding:10px 30px;
  21 +}
  22 +.esData-detail-container .content-body-info{
  23 + display: flex;
  24 + align-items: center;
  25 + justify-content: flex-start;
  26 + flex-wrap: wrap;
  27 +}
  28 +.esData-detail-container .info-item{
  29 + width:33%;
  30 + margin:8px 0;
  31 + text-align: left;
  32 +}
  33 +.esData-detail-info-content{
  34 + text-align: left;
  35 +}
@@ -103,6 +103,12 @@ const routes = [{ @@ -103,6 +103,12 @@ const routes = [{
103 name: 'busDoc', 103 name: 'busDoc',
104 component: () => myImport('views/busDoc/index') 104 component: () => myImport('views/busDoc/index')
105 }, 105 },
  106 + //日志
  107 + {
  108 + path: '/esData',
  109 + name: 'esDataIndex',
  110 + component: () => myImport('views/esData/index'),
  111 + },
106 ]; 112 ];
107 113
108 // hash模式: createWebHashHistory 114 // hash模式: createWebHashHistory
@@ -271,6 +271,7 @@ global.openDetail = (resId, resType, proxy) => { @@ -271,6 +271,7 @@ global.openDetail = (resId, resType, proxy) => {
271 271
272 // 获取资源详情 272 // 获取资源详情
273 proxy.$http.get(`/api-web/v32/res/detail/${resId}`, {}, function (res) { 273 proxy.$http.get(`/api-web/v32/res/detail/${resId}`, {}, function (res) {
  274 + if(res && res.success){
274 if (res && res.map) { 275 if (res && res.map) {
275 let data = res.map; 276 let data = res.map;
276 277
@@ -287,6 +288,11 @@ global.openDetail = (resId, resType, proxy) => { @@ -287,6 +288,11 @@ global.openDetail = (resId, resType, proxy) => {
287 288
288 lyaui.commonCols.detailPage(resId, resType, editFlag, provider, name, ip, resTypeName, adminName, manageIp, collProtocol); 289 lyaui.commonCols.detailPage(resId, resType, editFlag, provider, name, ip, resTypeName, adminName, manageIp, collProtocol);
289 } 290 }
  291 + }else{
  292 + proxy.$global.showMsg('该资源暂未监控', "error");
  293 +
  294 + }
  295 +
290 }); 296 });
291 } 297 }
292 298
  1 +<div class="esData-detail-container">
  2 + <el-descriptions
  3 + class=""
  4 + title="日志信息"
  5 + :column="2"
  6 + :size="size"
  7 + border
  8 + >
  9 + <el-descriptions-item>
  10 + <template #label>
  11 + <div class="cell-item">
  12 + 资源名称
  13 + </div>
  14 + </template>
  15 + {{detail.resName}}
  16 + </el-descriptions-item>
  17 + <el-descriptions-item>
  18 + <template #label>
  19 + <div class="cell-item">
  20 + IP地址
  21 + </div>
  22 + </template>
  23 + {{detail.host}}
  24 + </el-descriptions-item>
  25 + <el-descriptions-item>
  26 + <template #label>
  27 + <div class="cell-item">
  28 + 资源类型
  29 + </div>
  30 + </template>
  31 + {{detail.resTypeName}}
  32 + </el-descriptions-item>
  33 + <el-descriptions-item>
  34 + <template #label>
  35 + <div class="cell-item">
  36 + 日志来源
  37 + </div>
  38 + </template>
  39 + {{detail.type}}
  40 + </el-descriptions-item>
  41 + <el-descriptions-item>
  42 + <template #label>
  43 + <div class="cell-item">
  44 + 日志类型
  45 + </div>
  46 + </template>
  47 + {{detail.program}}
  48 + </el-descriptions-item>
  49 + <el-descriptions-item>
  50 + <template #label>
  51 + <div class="cell-item">
  52 + 采集时间
  53 + </div>
  54 + </template>
  55 + {{detail.dbTimeStr}}
  56 + </el-descriptions-item>
  57 + <el-descriptions-item>
  58 + <template #label>
  59 + <div class="cell-item">
  60 + 日志时间
  61 + </div>
  62 + </template>
  63 + {{detail.timestamp}}
  64 + </el-descriptions-item>
  65 + </el-descriptions>
  66 + <div class="info-title">
  67 + <span style="font-weight:bold">日志内容</span>
  68 + </div>
  69 + <div class="esData-detail-info-content" v-html="detail.message">
  70 + </div>
  71 +</div>
  1 +export default {
  2 + name: 'esDataDetail',
  3 + template: '',
  4 + components: {
  5 +
  6 + },
  7 + props: {
  8 + detail:{
  9 + type:Object,
  10 + default: {}
  11 + }
  12 + },
  13 + setup(props, {attrs, slots, emit}) {
  14 + const {proxy} = Vue.getCurrentInstance();
  15 +
  16 + // 挂载完
  17 + Vue.onMounted(() => {
  18 + })
  19 +
  20 +
  21 + return {
  22 +
  23 + }
  24 + }
  25 +
  26 +}
  1 +<div class="container" :style="{'height':height+'px','max-height':height+'px'}">
  2 + <div class="cm-card" :style="{'min-height':height+'px','max-height':height+'px','height':'100%','padding-top':'3px'}">
  3 + <div class="search">
  4 + <div class="condition">
  5 + <el-form-item >
  6 + <el-input v-model="search.keyword" placeholder="=资源名称、日志内容="></el-input>
  7 + </el-form-item>
  8 + <el-form-item >
  9 + <el-dropdown>
  10 + <cm-res-type-tree-input multiple clearable collapseTags @callback="getResType"/>
  11 + </el-dropdown>
  12 + </el-form-item>
  13 + <el-form-item >
  14 + <el-select v-model="search.program" class="m-2" placeholder="日志类型" clearable>
  15 + <el-option
  16 + v-for="item in logTypeData"
  17 + :key="item.ddicCode"
  18 + :label="item.ddicName"
  19 + :value="item.ddicCode"
  20 + />
  21 + </el-select>
  22 + </el-form-item>
  23 +
  24 + <el-form-item>
  25 + <el-date-picker
  26 + v-model="search.dateTime"
  27 + type="datetimerange"
  28 + range-separator="-"
  29 + start-placeholder="开始时间"
  30 + end-placeholder="结束时间"
  31 + format="YYYY-MM-DD HH:mm:ss"
  32 + value-format="YYYY-MM-DD HH:mm:ss"
  33 + />
  34 + </el-form-item>
  35 + <el-form-item >
  36 + <el-button @click="getDataList">查询</el-button>
  37 + </el-form-item>
  38 + </div>
  39 + </div>
  40 +
  41 + <div class="search-table">
  42 + <cm-table-page :columns="tableData.columns" :dataList="tableData.dataList"
  43 + :showIndex="true"
  44 + :total="tableData.count"
  45 + @loaddata = "loaddata"
  46 + :showSelection="true"
  47 + :showBorder="true"
  48 + :loading="false"
  49 + :pageSize="pageSize"
  50 + :showPage="true"
  51 + :showTools="true"
  52 + :height="height - 110">
  53 + <template #default="{row,prop,column}">
  54 + <div v-if="prop == 'resName'">
  55 + <span class="resName-span" style="cursor: pointer;color:#1e9fff" @click="goResDetail(row.resId,row.resName,row.resType)">{{row.resName}}</span>
  56 + </div>
  57 + <div v-if="prop == 'message'" >
  58 + <div v-html="row.message" style="overflow: hidden; text-overflow: ellipsis;display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical;"></div>
  59 + </div>
  60 + </template>
  61 + <template #tools="{scope}">
  62 + <div class="list-handle">
  63 + <span class="icon-bg">
  64 + <i class="el-icon-view" title="查看" @click="handleView(scope.row)"></i>
  65 + </span>
  66 + </div>
  67 + </template>
  68 + </cm-table-page>
  69 + </div>
  70 + </div>
  71 +</div>
  72 +
  73 +<!--弹框-->
  74 +<cm-dialog :title="dialog.title" width="60%" :showDialogVisible="dialog.show" @hidedialog="hideDialog" :showFooter="false">
  75 + <template v-slot>
  76 + <esDataDetail :detail="dialog.detail" />
  77 + </template>
  78 +</cm-dialog>
  79 +
  1 +export default {
  2 + name: 'esDataIndex',
  3 + template: '',
  4 + components: {
  5 + 'esDataDetail': Vue.defineAsyncComponent(
  6 + () => myImport('views/esData/esDataDetail/index')
  7 + )
  8 + },
  9 + props: [],
  10 + setup(props, {attrs, slots, emit}) {
  11 + const {proxy} = Vue.getCurrentInstance();
  12 + let height = Vue.ref(window.innerHeight);
  13 + let dateTime=Vue.ref([])
  14 + let search = Vue.ref({
  15 + program:'',
  16 + sortBy:'dbTime',
  17 + scopeBy:'dbTime',
  18 + keyword: '',
  19 + type:'syslog',
  20 + pageNum: 1,
  21 + pageSize: 20,
  22 + dateTime:[],
  23 + resType:'',
  24 + });
  25 + let dialog = Vue.ref({
  26 + title : "日志详情",
  27 + show:false,
  28 + esId : ''
  29 + });
  30 + //表格字段
  31 + let tableData = Vue.ref({
  32 + count:0,
  33 + dataList: [],
  34 + columns: [
  35 + {
  36 + prop: 'timestamp',
  37 + label: '日志时间',
  38 + sortable: true,
  39 + align: 'center',
  40 + width: '250',
  41 + },
  42 + {
  43 + prop: 'resName',
  44 + label: '资源名称',
  45 + sortable: true,
  46 + align: 'center',
  47 + width: '250'
  48 + },
  49 + {
  50 + prop: 'resTypeName',
  51 + label: '资源类型',
  52 + sortable: true,
  53 + align: 'center',
  54 + width: '200'
  55 + }, {
  56 + prop: 'host',
  57 + label: 'IP地址',
  58 + sortable: true,
  59 + align: 'center',
  60 + width: '200'
  61 + }, {
  62 + prop: 'type',
  63 + label: '日志来源',
  64 + sortable: true,
  65 + align: 'center',
  66 + width: '200'
  67 + },
  68 + {
  69 + prop: 'program',
  70 + label: '日志类型',
  71 + sortable: true,
  72 + align: 'center',
  73 + width: '200'
  74 + },
  75 + {
  76 + prop: 'message',
  77 + label: '日志内容',
  78 + sortable: true,
  79 + align: 'center',
  80 + },
  81 + ]
  82 + })
  83 + let resTypeArr = Vue.ref([]);
  84 + let getResType = (arr) => {
  85 + var types = arr.map(function (v) {
  86 + return v.id;
  87 + });
  88 + resTypeArr.value = types;
  89 + search.value.resType=resTypeArr.value.join(',');
  90 + // getDataList();
  91 + }
  92 + //获取时间点 转年月日的方法
  93 + const getDateTime=(newDate)=>{
  94 + let dateTime='';
  95 + let year=newDate.getFullYear();//获取当前年
  96 + let month1=(newDate.getMonth()+1)+'';
  97 + let month=timeFormat(month1);//获取当前月
  98 + let day=timeFormat(newDate.getDate());//获取当前日
  99 + let hours=timeFormat(newDate.getHours()+'');//获取当前时
  100 + let minutes=timeFormat(newDate.getMinutes()+'');//获取当前分
  101 + let seconds=timeFormat(newDate.getSeconds()+'');//获取当前秒
  102 + dateTime= year+'-'+month+'-'+day;//' '+hours+':'+minutes+':'+seconds;
  103 + return dateTime;
  104 + }
  105 + //转换个位数为 00
  106 + let timeFormat =(number)=> {
  107 + return number.length == 1 ? ('0' + number) : number
  108 + }
  109 + // 获取列表
  110 + let getDataList = () => {
  111 + let nowDate=getDateTime(new Date());
  112 + let startTime=search.value.dateTime[0]?search.value.dateTime[0]:'';
  113 + let endTime=search.value.dateTime[1]?search.value.dateTime[1]:'';
  114 + let startDateStr=startTime?startTime.split(' ')[0]:'';
  115 + let endDateStr=endTime?endTime.split(' ')[0]:'';
  116 + let dateStr='';
  117 + if(search.value.dateTime.length>0){
  118 + if(startDateStr!=endDateStr){
  119 + dateStr='log-syslog-search';
  120 + }else{
  121 + dateStr='log-syslog_'+startDateStr;
  122 +
  123 + }
  124 + }else {
  125 + dateStr='log-syslog_'+nowDate;
  126 + }
  127 +
  128 + let params={
  129 + indexName:dateStr,
  130 + pageNum: search.value.pageNum,
  131 + pageSize: search.value.pageSize,
  132 + type:search.value.type,
  133 + sortBy:search.value.sortBy,
  134 + scopeBy:search.value.scopeBy,
  135 + startTime:startTime,
  136 + endTime: endTime,
  137 + resType:search.value.resType,
  138 + program:search.value.program,
  139 + param:{
  140 + 'resName.keyword': search.value.keyword,
  141 + 'host.keyword':search.value.keyword,
  142 + 'message.keyword':search.value.keyword,
  143 + }
  144 + }
  145 + proxy.$http.post(`/api-web/esData/list`, params, function (res) {
  146 + if (res && res.object) {
  147 + let dataList=res.object.content;
  148 + let arr=[];
  149 + dataList.map(item=>{
  150 + /* for(let key in item[0]){
  151 + if(key=='timestamp'){
  152 + console.log("item[0]['timestamp']",item[0]['timestamp'])
  153 + item[0]['timestampStr']=getDateTime(item[0]['timestamp'])
  154 + }
  155 + }*/
  156 + arr.push(item[0])
  157 + })
  158 + tableData.value.dataList = arr;
  159 + tableData.value.count = parseInt(res.object.total);
  160 + } else {
  161 + tableData.value.dataList = [];
  162 + tableData.value.count = 0;
  163 + }
  164 + });
  165 + }
  166 +
  167 + let loaddata = ({page, limit}) => {
  168 + search.value.pageNum = page;
  169 + search.value.pageSize = limit;
  170 + }
  171 +
  172 + let hideDialog = (flg) => {
  173 + dialog.value.show = flg;
  174 + }
  175 +
  176 + // 处理弹框
  177 + let handle = (row) =>{
  178 + hideDialog(true);
  179 + dialog.value.detail = row;
  180 + }
  181 +
  182 + //查看详情
  183 + let handleView = (row) =>{
  184 + handle(row);
  185 + }
  186 + //查看资源详情
  187 + let goResDetail=(resId,resName,resType)=>{
  188 + proxy.$global.openDetail(resId, resType, proxy);
  189 +
  190 + }
  191 + //日志类型数据
  192 + let logTypeData=Vue.ref([]);
  193 + let getLogType=()=>{
  194 + proxy.$http.post(`/api-web/manage/ddic/findSucDdics/LOG_SOURCE_TYPE`, {}, function (res) {
  195 + if (res && res.data) {
  196 + logTypeData.value = res.data;
  197 + }
  198 + });
  199 + }
  200 + // 挂载完
  201 + Vue.onMounted(() => {
  202 + getDataList();
  203 + getLogType();
  204 + })
  205 +
  206 +
  207 + return {
  208 + logTypeData,
  209 + getLogType,
  210 + dateTime,
  211 + height,
  212 + search,
  213 + dialog,
  214 + hideDialog,
  215 + handle,
  216 + loaddata,
  217 + tableData,
  218 + getDataList,
  219 + resTypeArr,
  220 + getResType,
  221 + handleView,
  222 + goResDetail,
  223 + getDateTime
  224 + }
  225 + }
  226 +
  227 +}
@@ -359,6 +359,26 @@ layui.define(['view'], function(exports){ @@ -359,6 +359,26 @@ layui.define(['view'], function(exports){
359 //纠正首尾 359 //纠正首尾
360 return href.replace(/^(\/+)/, '/') 360 return href.replace(/^(\/+)/, '/')
361 .replace(new RegExp('\/' + setter.entry + '$'), '/'); //过滤路由最后的默认视图文件名(如:index) 361 .replace(new RegExp('\/' + setter.entry + '$'), '/'); //过滤路由最后的默认视图文件名(如:index)
  362 + },
  363 + //封装获取cookie
  364 + getCookie:function(name) {
  365 + var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
  366 + if (arr = document.cookie.match(reg))
  367 + return (arr[2]);
  368 + else
  369 + return null;
  370 + },
  371 + setCookie:function (c_name, value, expiredays) {
  372 + var exdate = new Date();
  373 + exdate.setDate(exdate.getDate() + expiredays);
  374 + document.cookie = c_name + "=" + escape(value) + ((expiredays == null) ? "" : ";expires=" + exdate.toGMTString());
  375 + },
  376 + delCookie:function (name) {
  377 + var exp = new Date();
  378 + exp.setTime(exp.getTime() - 1);
  379 + var cval = getCookie(name);
  380 + if (cval != null)
  381 + document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
362 } 382 }
363 383
364 //…… 384 //……
@@ -169,7 +169,13 @@ const routes = [{ @@ -169,7 +169,13 @@ const routes = [{
169 path: '/alarmsubscribe/excludeKpi', 169 path: '/alarmsubscribe/excludeKpi',
170 name: 'alarmSubscribeExcludeKpi', 170 name: 'alarmSubscribeExcludeKpi',
171 component: () => myImport('views/alarmsubscribe/index') 171 component: () => myImport('views/alarmsubscribe/index')
172 - } 172 + },
  173 + //日志
  174 + {
  175 + path: '/esData',
  176 + name: 'esDataIndex',
  177 + component: () => myImport('views/esData/index'),
  178 + },
173 ]; 179 ];
174 180
175 // hash模式: createWebHashHistory 181 // hash模式: createWebHashHistory