<template> <div class="app-container"> <div style="margin-top: 10px"> <grid-layout :layout.sync="layout" :col-num="12" :row-height="30" :is-draggable="draggable" :is-resizable="resizable" :vertical-compact="true" :use-css-transforms="true" @layout-created="layoutCreatedEvent" @layout-before-mount="layoutBeforeMountEvent" @layout-mounted="layoutMountedEvent" @layout-ready="layoutReadyEvent" @layout-updated="layoutUpdatedEvent" > <grid-item :x="0" :y="0" :w="6" :h="5" i="1" @resize="resizeEvent" @move="moveEvent" @resized="resizedEvent" @container-resized="containerResizedEvent" @moved="movedEvent" > <p class="item-title">DEVICE</p> <div style="padding: 15px"> <el-select size="mini" v-model="device" @change="onSelectChange" placeholder="请选择设备" > <el-option v-for="(item, index) in deviceList" :key="index" :label="item" :value="item" ></el-option> </el-select> </div> </grid-item> <grid-item :x="6" :y="0" :w="6" :h="5" i="2" @resize="resizeEvent" @move="moveEvent" @resized="resizedEvent" @container-resized="containerResizedEvent" @moved="movedEvent" > <p class="item-title">SYSTEM</p> <el-table element-loading-text="Loading" :data="system" size="mini" border stripe fit highlight-current-row > <el-table-column prop="host" label="host" min-width="150" show-overflow-tooltip ></el-table-column> <el-table-column prop="path" label="path" min-width="150" show-overflow-tooltip ></el-table-column> <el-table-column prop="timestamp" label="timestamp" min-width="150" show-overflow-tooltip ></el-table-column> <el-table-column label="imei" min-width="150" show-overflow-tooltip> <template slot-scope="scope">{{ scope.row.imei }}</template> </el-table-column> <el-table-column label="free_size" min-width="100" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.free_size }}(KB)</template > </el-table-column> </el-table> </grid-item> <grid-item :x="0" :y="5" :w="6" :h="5" i="3" @resize="resizeEvent" @move="moveEvent" @resized="resizedEvent" @container-resized="containerResizedEvent" @moved="movedEvent" > <p class="item-title">EVM</p> <el-table element-loading-text="Loading" :data="evmList" size="mini" border stripe fit highlight-current-row > <el-table-column label="heap_map_size" min-width="150" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.heap_map_size }}(KB)</template > </el-table-column> <el-table-column label="heap_total_size" min-width="150" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.heap_total_size }}(KB)</template > </el-table-column> <el-table-column label="heap_used_size" min-width="150" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.heap_used_size }}(KB)</template > </el-table-column> <el-table-column label="stack_total_size" min-width="150" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.stack_total_size }}(KB)</template > </el-table-column> <el-table-column label="stack_used_size" min-width="150" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.stack_used_size }}(KB)</template > </el-table-column> </el-table> </grid-item> <grid-item :x="6" :y="5" :w="6" :h="5" i="4" @resize="resizeEvent" @move="moveEvent" @resized="resizedEvent" @container-resized="containerResizedEvent" @moved="movedEvent" > <p class="item-title">LVGL</p> <el-table element-loading-text="Loading" :data="lvglList" size="mini" border stripe fit highlight-current-row > <el-table-column label="total_size" min-width="100" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.total_size }}(KB)</template > </el-table-column> <el-table-column prop="free_cnt" label="free_cnt" min-width="100" show-overflow-tooltip ></el-table-column> <el-table-column label="free_size" min-width="120" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.free_size }}(KB)</template > </el-table-column> <el-table-column label="free_biggest_size" min-width="120"> <template slot-scope="scope" >{{ scope.row.free_biggest_size }}(KB)</template > </el-table-column> <el-table-column label="used_cnt" min-width="100"> <template slot-scope="scope">{{ scope.row.used_cnt }}</template> </el-table-column> <el-table-column label="used_pct" min-width="100"> <template slot-scope="scope" >{{ scope.row.used_pct }}(%)</template > </el-table-column> <el-table-column label="frag_pct" min-width="100"> <template slot-scope="scope" >{{ scope.row.frag_pct }}(%)</template > </el-table-column> </el-table> </grid-item> <grid-item :x="0" :y="10" :w="12" :h="10" i="5" @resize="resizeEvent" @move="moveEvent" @resized="resizedEvent" @container-resized="containerResizedEvent" @moved="movedEvent" > <div style="width: 100%; height: 100%; overflow-y: auto"> <p class="item-title">APP</p> <el-table element-loading-text="Loading" :data="imageList" size="mini" border stripe fit highlight-current-row > <el-table-column prop="uri" label="uri" min-width="150" show-overflow-tooltip ></el-table-column> <el-table-column label="length" min-width="150" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.length }}(KB)</template > </el-table-column> <el-table-column label="png_file_size" min-width="150" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.png_file_size }}(KB)</template > </el-table-column> <el-table-column prop="png_total_count" label="png_total_count" min-width="150" show-overflow-tooltip ></el-table-column> <el-table-column label="png_uncompressed_size" min-width="150" show-overflow-tooltip > <template slot-scope="scope" >{{ scope.row.png_uncompressed_size }}(KB)</template > </el-table-column> </el-table> </div> </grid-item> <grid-item :x="0" :y="20" :w="12" :h="7" i="6" @resize="resizeEvent" @move="moveEvent" @resized="resizedEvent" @container-resized="containerResizedEvent" @moved="movedEvent" > <EvmChart :chartData="evm"></EvmChart> </grid-item> <grid-item :x="0" :y="27" :w="12" :h="7" i="7" @resize="resizeEvent" @move="moveEvent" @resized="resizedEvent" @container-resized="containerResizedEvent" @moved="movedEvent" > <LvglChart :chartData="lvgl"></LvglChart> </grid-item> </grid-layout> </div> </div> </template> <script> import { getWatchList, getMonitorData } from "@/api/index"; import EvmChart from "./components/EvmChart"; import LvglChart from "./components/LvglChart"; import { GridLayout, GridItem } from "vue-grid-layout"; import { wsNotify } from "@/utils/eventBus.js"; export default { name: "Monitor", data() { return { watchs: [], device: null, devices: {}, deviceList: null, system: [], evm: {}, evmList: [], lvgl: {}, lvglList: [], image: {}, imageList: [], socket: null, form: { system: ["free_size"], lvgl: ["total_size", "free_size", "free_biggest_size"], evm: [ "total_size", "free_size", "heap_map_size", "heap_total_size", "heap_used_size", "stack_total_size", "stack_used_size", ], image: ["png_uncompressed_size", "png_file_size", "length"], }, layout: [ { x: 0, y: 0, w: 6, h: 5, i: "0", static: false }, { x: 6, y: 0, w: 6, h: 5, i: "1", static: true }, { x: 0, y: 5, w: 6, h: 5, i: "2", static: false }, { x: 6, y: 5, w: 6, h: 5, i: "3", static: false }, { x: 0, y: 10, w: 12, h: 10, i: "4", static: false }, { x: 0, y: 20, w: 12, h: 7, i: "5", static: false }, { x: 0, y: 27, w: 12, h: 7, i: "6", static: false }, ], draggable: true, resizable: true, }; }, components: { GridLayout, GridItem, EvmChart, LvglChart, }, methods: { moveEvent(i, newX, newY) { const msg = "MOVE i=" + i + ", X=" + newX + ", Y=" + newY; console.log(msg); }, movedEvent(i, newX, newY) { const msg = "MOVED i=" + i + ", X=" + newX + ", Y=" + newY; console.log(msg); }, resizeEvent(i, newH, newW, newHPx, newWPx) { const msg = "RESIZE i=" + i + ", H=" + newH + ", W=" + newW + ", H(px)=" + newHPx + ", W(px)=" + newWPx; console.log(msg); }, resizedEvent(i, newX, newY, newHPx, newWPx) { const msg = "RESIZED i=" + i + ", X=" + newX + ", Y=" + newY + ", H(px)=" + newHPx + ", W(px)=" + newWPx; console.log(msg); }, containerResizedEvent(i, newH, newW, newHPx, newWPx) { const msg = "CONTAINER RESIZED i=" + i + ", H=" + newH + ", W=" + newW + ", H(px)=" + newHPx + ", W(px)=" + newWPx; console.log(msg); }, layoutCreatedEvent(newLayout) { console.log("Created layout: ", newLayout); }, layoutBeforeMountEvent(newLayout) { console.log("beforeMount layout: ", newLayout); }, layoutMountedEvent(newLayout) { console.log("Mounted layout: ", newLayout); }, layoutReadyEvent(newLayout) { console.log("Ready layout: ", newLayout); }, layoutUpdatedEvent(newLayout) { console.log("Updated layout: ", newLayout); }, fetchData() { this.isLoading = true; getWatchList() .then((res) => { if (res.code == 200) this.watchs = res.data; }) .catch((err) => { this.$message.warning(err.msg); }) .finally(() => { this.isLoading = false; }); }, queryData() { getMonitorData({ watch: this.device, }) .then((res) => { if (res.type == "object") { this.evmList = res.data.evm; this.lvglList = res.data.lvgl; this.imageList = res.data.image; } }) .catch((err) => { this.$message.warning(err.msg); }); }, onChange(res) { if (!res) return null; var t = this.watchs.find((item) => { return item.id == res; }); if (t) this.device = t.imei; // 清空之前数据 this.resetData(); }, onSubmit() { this.queryData(); }, onReset(formName) { this.$refs[formName].resetFields(); this.fetchData(); }, sendMsg() { let message = JSON.stringify({ type: "auth", token: this.$store.getters.token, }); this.socket.send(message); }, handleMessage(msg) { if (msg.type !== "report" || !msg.imei) return false; if (!this.deviceList) { this.deviceList = []; } if (!this.deviceList.includes(msg.imei)) { this.deviceList.push(msg.imei); } if (!this.device) { if (this.deviceList && this.deviceList.length) this.device = this.deviceList[0]; else this.device = msg.imei; } this.devices[msg.imei] = msg; this.processData(msg); this.resetData(); }, processData(msg) { if (!msg) return null; Object.keys(msg).forEach((item) => { if (this.form[item]) { var keys = this.form[item]; for (var i = 0; i < keys.length; i++) { var k = keys[i]; if (item == "image") { for (var j = 0; j < msg[item].length; j++) { msg[item][j][k] = Math.ceil(msg[item][j][k] / 1024); } } else { msg[item][k] = Math.ceil(msg[item][k] / 1024); } } } }); }, onSelectChange(res) { this.device = res; this.processData(this.devices[this.device]); this.resetData(); console.log(res); }, resetData() { wsNotify.eventBus.$emit("resize"); this.evmList = [{ ...this.devices[this.device].evm }]; this.lvglList = [{ ...this.devices[this.device].lvgl }]; this.system = [{ imei: this.devices[this.device].imei, ...this.devices[this.device].system, ...this.devices[this.device].request }]; // 这里需要特殊处理下,先判断uri是否存在,不存在则添加,存在则更新 let uris = []; this.imageList.forEach((img) => { uris.push(img.uri); }); this.devices[this.device].image && this.devices[this.device].image.forEach((item) => { if (!uris.includes(item.uri)) { this.imageList.push(item); } }); // this.imageList = msg.image; if (this.devices[this.device]) { if (this.devices[this.device].evm) this.evm = this.devices[this.device].evm; if (this.devices[this.device].lvgl) this.lvgl = this.devices[this.device].lvgl; if (this.devices[this.device].image) this.image = this.devices[this.device].image; } }, }, mounted() {}, created() { this.socket = wsNotify; wsNotify.eventBus.$on("open", (message) => { this.sendMsg(); this.$nextTick(() => { console.log(message); }); // 这里启动一个定时器,10秒钟后,如果没有消息进来,说明当前没有在线设备 }); wsNotify.eventBus.$on("close", (message) => { this.$nextTick(() => { console.log(message); }); }); wsNotify.eventBus.$on("message", (message) => { this.$nextTick(() => { this.handleMessage(message); }); }); }, }; </script> <style lang="scss" scoped> .app-container { & > div.page-wrapper { margin: 10px 0px; } } .vue-grid-layout { background: none; } .vue-grid-item:not(.vue-grid-placeholder) { background: #fff; border: 0px solid #eee; } .vue-grid-item .resizing { opacity: 0.9; } .vue-grid-item .static { background: #cce; } .vue-grid-item .text { font-size: 24px; text-align: center; position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; height: 100%; width: 100%; } .vue-grid-item .item-title { margin-left: 15px; } .vue-grid-item .no-drag { height: 100%; width: 100%; } .vue-grid-item .minMax { font-size: 12px; } .vue-grid-item .add { cursor: pointer; } .vue-draggable-handle { position: absolute; width: 20px; height: 20px; top: 0; left: 0; background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>") no-repeat; background-position: bottom right; padding: 0 8px 8px 0; background-repeat: no-repeat; background-origin: content-box; box-sizing: border-box; cursor: pointer; } </style>