From b49183529cd350329c2e0d652863c316a8f4da10 Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Sat, 18 Aug 2018 04:52:06 +0900 Subject: [PATCH] Improve control panel --- .../views/pages/admin/admin.cpu-memory.vue | 143 ++++++++++++++++++ .../views/pages/admin/admin.dashboard.vue | 28 +++- .../app/desktop/views/pages/admin/admin.vue | 2 +- 3 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 src/client/app/desktop/views/pages/admin/admin.cpu-memory.vue diff --git a/src/client/app/desktop/views/pages/admin/admin.cpu-memory.vue b/src/client/app/desktop/views/pages/admin/admin.cpu-memory.vue new file mode 100644 index 000000000..168d07892 --- /dev/null +++ b/src/client/app/desktop/views/pages/admin/admin.cpu-memory.vue @@ -0,0 +1,143 @@ +<template> +<div class="zyknedwtlthezamcjlolyusmipqmjgxz"> + <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`"> + <defs> + <linearGradient :id="cpuGradientId" x1="0" x2="0" y1="1" y2="0"> + <stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop> + <stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop> + </linearGradient> + <mask :id="cpuMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY"> + <polygon + :points="cpuPolygonPoints" + fill="#fff" + fill-opacity="0.5"/> + <polyline + :points="cpuPolylinePoints" + fill="none" + stroke="#fff" + stroke-width="0.3"/> + </mask> + </defs> + <rect + x="0" y="0" + :width="viewBoxX" :height="viewBoxY" + :style="`stroke: none; fill: url(#${ cpuGradientId }); mask: url(#${ cpuMaskId })`"/> + <text x="1" y="5">CPU <tspan>{{ cpuP }}%</tspan></text> + </svg> + <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`"> + <defs> + <linearGradient :id="memGradientId" x1="0" x2="0" y1="1" y2="0"> + <stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop> + <stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop> + </linearGradient> + <mask :id="memMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY"> + <polygon + :points="memPolygonPoints" + fill="#fff" + fill-opacity="0.5"/> + <polyline + :points="memPolylinePoints" + fill="none" + stroke="#fff" + stroke-width="0.3"/> + </mask> + </defs> + <rect + x="0" y="0" + :width="viewBoxX" :height="viewBoxY" + :style="`stroke: none; fill: url(#${ memGradientId }); mask: url(#${ memMaskId })`"/> + <text x="1" y="5">MEM <tspan>{{ memP }}%</tspan></text> + </svg> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import * as uuid from 'uuid'; + +export default Vue.extend({ + props: ['connection'], + data() { + return { + viewBoxX: 50, + viewBoxY: 20, + stats: [], + cpuGradientId: uuid(), + cpuMaskId: uuid(), + memGradientId: uuid(), + memMaskId: uuid(), + cpuPolylinePoints: '', + memPolylinePoints: '', + cpuPolygonPoints: '', + memPolygonPoints: '', + cpuP: '', + memP: '' + }; + }, + mounted() { + this.connection.on('stats', this.onStats); + this.connection.on('statsLog', this.onStatsLog); + this.connection.send({ + type: 'requestLog', + id: Math.random().toString() + }); + }, + beforeDestroy() { + this.connection.off('stats', this.onStats); + this.connection.off('statsLog', this.onStatsLog); + }, + methods: { + onStats(stats) { + this.stats.push(stats); + if (this.stats.length > 50) this.stats.shift(); + + const cpuPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - s.cpu_usage) * this.viewBoxY]); + const memPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.mem.used / s.mem.total)) * this.viewBoxY]); + this.cpuPolylinePoints = cpuPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' '); + this.memPolylinePoints = memPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' '); + + this.cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.cpuPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`; + this.memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.memPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`; + + this.cpuP = (stats.cpu_usage * 100).toFixed(0); + this.memP = (stats.mem.used / stats.mem.total * 100).toFixed(0); + }, + onStatsLog(statsLog) { + statsLog.forEach(stats => this.onStats(stats)); + } + } +}); +</script> + +<style lang="stylus" scoped> +root(isDark) + > svg + display block + width 50% + float left + + &:first-child + padding-right 5px + + &:last-child + padding-left 5px + + > text + font-size 2px + fill isDark ? rgba(#fff, 0.55) : rgba(#000, 0.55) + + > tspan + opacity 0.5 + + &:after + content "" + display block + clear both + +.zyknedwtlthezamcjlolyusmipqmjgxz[data-darkmode] + root(true) + +.zyknedwtlthezamcjlolyusmipqmjgxz:not([data-darkmode]) + root(false) + +</style> diff --git a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue index 182d97460..efe7ea0ca 100644 --- a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue +++ b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue @@ -3,9 +3,12 @@ <header>%i18n:@dashboard%</header> <div v-if="stats" class="stats"> <div><b>%fa:user% {{ stats.originalUsersCount | number }}</b><span>%i18n:@original-users%</span></div> - <div><b>%fa:user% {{ stats.usersCount | number }}</b><span>%i18n:@all-users%</span></div> + <div><span>%fa:user% {{ stats.usersCount | number }}</span><span>%i18n:@all-users%</span></div> <div><b>%fa:pen% {{ stats.originalNotesCount | number }}</b><span>%i18n:@original-notes%</span></div> - <div><b>%fa:pen% {{ stats.notesCount | number }}</b><span>%i18n:@all-notes%</span></div> + <div><span>%fa:pen% {{ stats.notesCount | number }}</span><span>%i18n:@all-notes%</span></div> + </div> + <div class="cpu-memory"> + <x-cpu-memory :connection="connection"/> </div> <div> <button class="ui" @click="invite">%i18n:@invite%</button> @@ -16,19 +19,31 @@ <script lang="ts"> import Vue from "vue"; +import XCpuMemory from "./admin.cpu-memory.vue"; export default Vue.extend({ + components: { + XCpuMemory + }, data() { return { stats: null, - inviteCode: null + inviteCode: null, + connection: null, + connectionId: null }; }, created() { + this.connection = (this as any).os.streams.serverStatsStream.getConnection(); + this.connectionId = (this as any).os.streams.serverStatsStream.use(); + (this as any).api('stats').then(stats => { this.stats = stats; }); }, + beforeDestroy() { + (this as any).os.streams.serverStatsStream.dispose(this.connectionId); + }, methods: { invite() { (this as any).api('admin/invite').then(x => { @@ -47,16 +62,19 @@ export default Vue.extend({ display flex justify-content center margin-bottom 16px + padding 16px + border solid 1px #eee + border-radius 8px > div flex 1 text-align center - > b + > *:first-child display block color $theme-color - > span + > *:last-child font-size 70% </style> diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue index f719ccfda..e32cd1321 100644 --- a/src/client/app/desktop/views/pages/admin/admin.vue +++ b/src/client/app/desktop/views/pages/admin/admin.vue @@ -9,7 +9,7 @@ </ul> </nav> <main> - <div v-if="page == 'dashboard'"> + <div v-show="page == 'dashboard'"> <x-dashboard/> <x-users-chart/> <x-notes-chart/>