vue实现日志消息带格式展示

76 阅读1分钟
  <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学习笔记,内容来源于极客时间《重学前端》,强烈推荐该课程