Massive frontend overhaul
This commit is contained in:
parent
1fa74c9ccf
commit
d1028908bd
|
@ -1,5 +1,5 @@
|
||||||
before_script:
|
before_script:
|
||||||
- export VERSION=1.0.1
|
- export VERSION=1.1.0
|
||||||
- chmod +x packaging/build-package.sh packaging/package-upload.sh
|
- chmod +x packaging/build-package.sh packaging/package-upload.sh
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
|
|
19
package.json
19
package.json
|
@ -10,10 +10,15 @@
|
||||||
"build": "node build/build.js"
|
"build": "node build/build.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "^4.0.0-beta.2",
|
"@fortawesome/fontawesome": "^1.1.5",
|
||||||
"bootstrap-vue": "^1.3.0",
|
"@fortawesome/fontawesome-free-regular": "^5.0.9",
|
||||||
|
"@fortawesome/fontawesome-free-solid": "^5.0.9",
|
||||||
|
"@fortawesome/vue-fontawesome": "0.0.22",
|
||||||
|
"bootstrap": "^4.0.0",
|
||||||
|
"bootstrap-vue": "^1.5.1",
|
||||||
"event-emitter": "^0.3.5",
|
"event-emitter": "^0.3.5",
|
||||||
"jquery": "^3.2.1",
|
"jquery": "^3.2.1",
|
||||||
|
"moment": "^2.22.0",
|
||||||
"tiny-emitter": "^2.0.2",
|
"tiny-emitter": "^2.0.2",
|
||||||
"util.inherits": "^1.0.3",
|
"util.inherits": "^1.0.3",
|
||||||
"vue": "^2.4.2",
|
"vue": "^2.4.2",
|
||||||
|
@ -39,18 +44,20 @@
|
||||||
"friendly-errors-webpack-plugin": "^1.1.3",
|
"friendly-errors-webpack-plugin": "^1.1.3",
|
||||||
"html-webpack-plugin": "^2.28.0",
|
"html-webpack-plugin": "^2.28.0",
|
||||||
"http-proxy-middleware": "^0.17.3",
|
"http-proxy-middleware": "^0.17.3",
|
||||||
"webpack-bundle-analyzer": "^2.2.1",
|
"node-sass": "^4.8.3",
|
||||||
"semver": "^5.3.0",
|
|
||||||
"shelljs": "^0.7.6",
|
|
||||||
"opn": "^5.1.0",
|
"opn": "^5.1.0",
|
||||||
"optimize-css-assets-webpack-plugin": "^2.0.0",
|
"optimize-css-assets-webpack-plugin": "^2.0.0",
|
||||||
"ora": "^1.2.0",
|
"ora": "^1.2.0",
|
||||||
"rimraf": "^2.6.0",
|
"rimraf": "^2.6.0",
|
||||||
|
"sass-loader": "^6.0.7",
|
||||||
|
"semver": "^5.3.0",
|
||||||
|
"shelljs": "^0.7.6",
|
||||||
"url-loader": "^0.5.8",
|
"url-loader": "^0.5.8",
|
||||||
"vue-loader": "^13.0.4",
|
"vue-loader": "^13.0.4",
|
||||||
"vue-style-loader": "^3.0.1",
|
"vue-style-loader": "^3.0.1",
|
||||||
"vue-template-compiler": "^2.4.2",
|
"vue-template-compiler": "^2.4.2",
|
||||||
"webpack": "^2.6.1",
|
"webpack": "^2.7.0",
|
||||||
|
"webpack-bundle-analyzer": "^2.2.1",
|
||||||
"webpack-dev-middleware": "^1.10.0",
|
"webpack-dev-middleware": "^1.10.0",
|
||||||
"webpack-hot-middleware": "^2.18.0",
|
"webpack-hot-middleware": "^2.18.0",
|
||||||
"webpack-merge": "^4.1.0"
|
"webpack-merge": "^4.1.0"
|
||||||
|
|
23
src/App.vue
23
src/App.vue
|
@ -1,21 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="app" class="container">
|
<div id="app">
|
||||||
<div class="row" style="padding-bottom: 10px;">
|
<n :sensors="sensors" />
|
||||||
<div class="col-sm">
|
<div class="container">
|
||||||
<h1>Ripper</h1>
|
<div class="row">
|
||||||
|
<main role="main" class="col-md-12 ml-sm-auto col-lg-12 pt-3 px-4">
|
||||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
|
||||||
|
<h1 class="h2">Jobs</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<status :sensors="sensors"></status>
|
|
||||||
<temperatures :temperatures="sensors.temperatures"></temperatures>
|
|
||||||
<job v-for="(job, id) in jobs" :job="job" :key="job.id"></job>
|
<job v-for="(job, id) in jobs" :job="job" :key="job.id"></job>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import job from './components/Job';
|
import job from './components/Job';
|
||||||
import status from './components/Status';
|
import navbar from './components/Nav';
|
||||||
import temperatures from './components/TempStatus';
|
|
||||||
import EventWebSocket from './websocket';
|
import EventWebSocket from './websocket';
|
||||||
|
|
||||||
let d = {
|
let d = {
|
||||||
|
@ -70,8 +72,7 @@ export default {
|
||||||
name: 'app',
|
name: 'app',
|
||||||
components: {
|
components: {
|
||||||
job: job,
|
job: job,
|
||||||
status: status,
|
n: navbar,
|
||||||
temperatures: temperatures,
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return d;
|
return d;
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
body {
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feather {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sidebar
|
||||||
|
*/
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 100; /* Behind the navbar */
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-sticky {
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
top: 48px; /* Height of navbar */
|
||||||
|
height: calc(100vh - 48px);
|
||||||
|
padding-top: .5rem;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-link {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-link .feather {
|
||||||
|
margin-right: 4px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-link.active {
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-link:hover .feather,
|
||||||
|
.sidebar .nav-link.active .feather {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-heading {
|
||||||
|
font-size: .75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Navbar
|
||||||
|
*/
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
padding-top: .75rem;
|
||||||
|
padding-bottom: .75rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
background-color: rgba(0, 0, 0, .25);
|
||||||
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar .form-control {
|
||||||
|
padding: .75rem 1rem;
|
||||||
|
border-width: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control-dark {
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgba(255, 255, 255, .1);
|
||||||
|
border-color: rgba(255, 255, 255, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control-dark:focus {
|
||||||
|
border-color: transparent;
|
||||||
|
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
.border-top { border-top: 1px solid #e5e5e5; }
|
||||||
|
.border-bottom { border-bottom: 1px solid #e5e5e5; }
|
|
@ -0,0 +1,23 @@
|
||||||
|
$cold: #6CA6CD;
|
||||||
|
$cool: #79CDCD;
|
||||||
|
$info: #4F94CD;
|
||||||
|
$acceptable: #66CDAA;
|
||||||
|
$success: #A2CD5A;
|
||||||
|
$high: #EEE685;
|
||||||
|
$warning: #E3A869;
|
||||||
|
$danger: #EE8262;
|
||||||
|
|
||||||
|
$badge-color: #545454;
|
||||||
|
|
||||||
|
$theme-colors: (
|
||||||
|
"cold": $cold,
|
||||||
|
"cool": $cool,
|
||||||
|
"acceptable": $acceptable,
|
||||||
|
"high": $high
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $color, $value in $theme-colors {
|
||||||
|
.badge-#{$color} {
|
||||||
|
color: $badge-color !important;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
@import '_variables.scss';
|
||||||
|
@import '~bootstrap/scss/bootstrap';
|
||||||
|
@import '~bootstrap-vue/dist/bootstrap-vue.css';
|
||||||
|
@import '_sidebar.scss';
|
||||||
|
|
||||||
|
.margin-top-10 {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-bottom-10 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-bottom-20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-success {
|
||||||
|
color: #454545;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item .progress {
|
||||||
|
width: 70px;
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="row">
|
|
||||||
<job v-for="(job, index) in jobs" :job="job" :key="job_id"></job>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import job from '@/components/Job';
|
|
||||||
export default {
|
|
||||||
name: 'hello',
|
|
||||||
components: {
|
|
||||||
job: job
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
msg: 'Welcome to Your Vue.js App'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,11 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<h3>{{ job.title }} - {{ stageValue }}</h3>
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md">{{ job.title }}</div>
|
||||||
|
<div class="col-md text-center badge">{{ stageValue }}</div>
|
||||||
|
<div class="col-md text-right">Remaining: {{ eta }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
<template v-if="job.progress">
|
<template v-if="job.progress">
|
||||||
<span class="text-center">{{ job.progress.name }} ({{ job.progress.percentage }}%)</span>
|
<span class="text-center">{{ job.progress.name }} ({{ job.progress.percentage }}%)</span>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar bg-success" role="progressbar" :style="'width: ' + job.progress.percentage + '%'" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">{{ job.progress.percentage }}%</div>
|
<div class="progress-bar bg-success" role="progressbar" :style="'width: ' + job.progress.percentage + '%'" :aria-valuenow=job.progress.percentage aria-valuemin="0" aria-valuemax="100">{{ job.progress.percentage }}%</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="job.progress.totalPercentage">
|
<template v-if="job.progress.totalPercentage">
|
||||||
<span class="text-center">Total Progress ({{ job.progress.totalPercentage }}%)</span>
|
<span class="text-center">Total Progress ({{ job.progress.totalPercentage }}%)</span>
|
||||||
|
@ -16,6 +24,8 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -28,8 +38,20 @@
|
||||||
return 'Ripping';
|
return 'Ripping';
|
||||||
} else if (this.job.stage === 'transcode') {
|
} else if (this.job.stage === 'transcode') {
|
||||||
return 'Transcoding';
|
return 'Transcoding';
|
||||||
|
} else if (this.job.stage === 'copy') {
|
||||||
|
return 'Copying';
|
||||||
}
|
}
|
||||||
return 'Unknown: ' + this.job.stage;
|
return 'Unknown: ' + this.job.stage;
|
||||||
|
},
|
||||||
|
eta: function() {
|
||||||
|
if (this.job.eta == 0) {
|
||||||
|
return 'Unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job is in nanoseconds because of Go's time.Duration
|
||||||
|
let eta = moment.duration(this.job.eta / 1000);
|
||||||
|
|
||||||
|
return eta.humanize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<div class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top p-0">
|
||||||
|
<div class="container d-flex flex-column flex-md-row">
|
||||||
|
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="#">ARM</a>
|
||||||
|
<div class="collapse navbar-collapse">
|
||||||
|
<status :sensors="sensors" v-if="sensors"></status>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import status from './Status';
|
||||||
|
export default {
|
||||||
|
name: 'navbar',
|
||||||
|
props: [ 'sensors' ],
|
||||||
|
components: {
|
||||||
|
status: status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="row" v-if="sensors.cpu || sensors.memory || sensors.storage">
|
<ul class="navbar-nav ml-auto" v-if="sensors.cpu || sensors.memory || sensors.disks">
|
||||||
<cpu_status :status="sensors.cpu"></cpu_status>
|
<cpu_status :status="sensors.cpu" :cpuName="sensors.cpuName" :temperatures="sensors.temperatures"></cpu_status>
|
||||||
<memory_status :status="sensors.memory"></memory_status>
|
<memory_status :status="sensors.memory"></memory_status>
|
||||||
<disk_status :status="sensors.disk"></disk_status>
|
<disk_status :status="sensors.disks"></disk_status>
|
||||||
</div>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div style="padding-top: 10px;">
|
<div class="margin-top-10 margin-bottom-20 text-center">
|
||||||
<template v-for="(temp, id) in temperatures">
|
|
||||||
<span class="badge badge-info">{{ temp.name }}: {{ temp.temperature }}C</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -10,6 +7,7 @@
|
||||||
export default {
|
export default {
|
||||||
name: 'temp_status',
|
name: 'temp_status',
|
||||||
props: [ 'temperatures' ],
|
props: [ 'temperatures' ],
|
||||||
components: {},
|
methods: {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,21 +1,38 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="col">
|
<b-nav-item-dropdown no-caret right>
|
||||||
<h4>CPU <span :class="'badge badge-' + percentageClass">{{ formattedPercentage }}%</span></h4>
|
<template slot="button-content">
|
||||||
<div class="progress">
|
<font-awesome-icon icon="tachometer-alt" /> CPU
|
||||||
<div :class="'progress-bar bg-' + percentageClass" role="progressbar" :style="{ width : status.UserPct + '%' }" :aria-valuenow="status.UserPct" aria-valuemin="0" aria-valuemax="100"></div>
|
<b-progress :value="status.UserPct" height=".5rem" :variant="percentageClass"></b-progress>
|
||||||
|
</template>
|
||||||
|
<b-dropdown-header class="text-center">CPU</b-dropdown-header>
|
||||||
|
<div class="text-center">
|
||||||
|
<small>{{ cpuName }}</small>
|
||||||
|
<br />
|
||||||
|
<span :class="'badge badge-' + percentageClass">{{ formattedPercentage }}%</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<template v-for="(temp, id) in temperatures">
|
||||||
|
<span :class="'badge badge-' + badgeClass(temp.temperature)" :title="temp.name">{{ temp.temperature }}C</span>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
</b-nav-item-dropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'cpu_status',
|
name: 'cpu_status',
|
||||||
props: [ 'status' ],
|
props: [ 'status', 'cpuName', 'temperatures' ],
|
||||||
computed: {
|
computed: {
|
||||||
formattedPercentage: function() {
|
formattedPercentage: function() {
|
||||||
|
if (!this.status) {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
return parseFloat(this.status.UserPct).toFixed(2);
|
return parseFloat(this.status.UserPct).toFixed(2);
|
||||||
},
|
},
|
||||||
percentageClass: function() {
|
percentageClass: function() {
|
||||||
|
if (!this.status) {
|
||||||
|
return 'info';
|
||||||
|
}
|
||||||
if (this.status.UserPct >= 90) {
|
if (this.status.UserPct >= 90) {
|
||||||
return 'danger';
|
return 'danger';
|
||||||
} else if (this.status.UserPct >= 80) {
|
} else if (this.status.UserPct >= 80) {
|
||||||
|
@ -23,6 +40,24 @@
|
||||||
}
|
}
|
||||||
return 'success'
|
return 'success'
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
badgeClass: function(temp) {
|
||||||
|
if (temp >= 90) {
|
||||||
|
return 'danger';
|
||||||
|
} else if (temp >= 70) {
|
||||||
|
return 'warning';
|
||||||
|
} else if (temp >= 60) {
|
||||||
|
return 'high';
|
||||||
|
} else if (temp >= 50) {
|
||||||
|
return 'success';
|
||||||
|
} else if (temp >= 40) {
|
||||||
|
return 'acceptable';
|
||||||
|
} else if (temp >= 30) {
|
||||||
|
return 'cool';
|
||||||
|
}
|
||||||
|
return 'cold';
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="col">
|
<b-nav-item-dropdown no-caret right>
|
||||||
<h4>Disk Space <span :class="'badge badge-' + percentageClass"><formatBytes :bytes="status.used"></formatBytes> of <formatBytes :bytes="status.total"></formatBytes></span></h4>
|
<template slot="button-content">
|
||||||
<div class="progress">
|
<font-awesome-icon icon="hdd" /> Disks
|
||||||
<div :class="'progress-bar bg-' + percentageClass" role="progressbar" :style="{ width : usedPercentage + '%' }" :aria-valuenow="usedPercentage" aria-valuemin="0" aria-valuemax="100"></div>
|
<b-progress :value="totalUsedPercentage" height=".5rem" :variant="totalPercentageClass"></b-progress>
|
||||||
|
</template>
|
||||||
|
<b-dropdown-header class="text-center">Disks</b-dropdown-header>
|
||||||
|
<div class="text-center">
|
||||||
|
Total
|
||||||
|
<br />
|
||||||
|
<span :class="'badge badge-' + totalPercentageClass"><formatBytes :bytes="totalUsed" /> / <formatBytes :bytes="total" /></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-for="(disk, index) in status" class="text-center">
|
||||||
|
{{ disk.path }}
|
||||||
|
<br />
|
||||||
|
<span :class="'badge badge-' + individualPercentages[index]"><formatBytes :bytes="disk.used" /> / <formatBytes :bytes="disk.total" /></span>
|
||||||
</div>
|
</div>
|
||||||
|
</b-nav-item-dropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -12,14 +24,40 @@
|
||||||
name: 'disk_status',
|
name: 'disk_status',
|
||||||
props: [ 'status' ],
|
props: [ 'status' ],
|
||||||
computed: {
|
computed: {
|
||||||
usedPercentage: function() {
|
totalUsed: function() {
|
||||||
if (!this.status) {
|
let used = 0;
|
||||||
return 0;
|
|
||||||
|
let i;
|
||||||
|
for (i = 0; i < this.status.length; i++) {
|
||||||
|
used += this.status[i].used;
|
||||||
}
|
}
|
||||||
return parseFloat((this.status.used / this.status.total) * 100).toFixed(1).toString();
|
|
||||||
|
return used;
|
||||||
},
|
},
|
||||||
percentageClass: function() {
|
total: function() {
|
||||||
let usedPercent = (this.status.used / this.status.total) * 100;
|
let total = 0;
|
||||||
|
|
||||||
|
let i;
|
||||||
|
for (i = 0; i < this.status.length; i++) {
|
||||||
|
total += this.status[i].total;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
},
|
||||||
|
totalUsedPercentage: function() {
|
||||||
|
let used = 0;
|
||||||
|
let total = 0;
|
||||||
|
|
||||||
|
let i;
|
||||||
|
for (i = 0; i < this.status.length; i++) {
|
||||||
|
used += this.status[i].used;
|
||||||
|
total += this.status[i].total;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (used / total) * 100;
|
||||||
|
},
|
||||||
|
totalPercentageClass: function() {
|
||||||
|
let usedPercent = this.totalUsedPercentage;
|
||||||
if (usedPercent >= 90) {
|
if (usedPercent >= 90) {
|
||||||
return 'danger';
|
return 'danger';
|
||||||
} else if (usedPercent >= 75) {
|
} else if (usedPercent >= 75) {
|
||||||
|
@ -27,6 +65,24 @@
|
||||||
}
|
}
|
||||||
return 'success'
|
return 'success'
|
||||||
},
|
},
|
||||||
|
individualPercentages: function() {
|
||||||
|
let percentages = [];
|
||||||
|
let i, usedPercent;
|
||||||
|
|
||||||
|
for (i = 0; i < this.status.length; i++) {
|
||||||
|
usedPercent = (this.status[i].used / this.status[i].total) * 100;
|
||||||
|
|
||||||
|
if (usedPercent >= 90) {
|
||||||
|
percentages[i] = 'danger';
|
||||||
|
} else if (usedPercent >= 75) {
|
||||||
|
percentages[i] = 'warning';
|
||||||
|
} else {
|
||||||
|
percentages[i] = 'success';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return percentages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="col">
|
<b-nav-item-dropdown no-caret right>
|
||||||
<h4>Memory <span :class="'badge badge-' + percentageClass"><formatBytes :bytes="(status.MemUsed + status.Buffers + status.Cached) * 1024" /> / <formatBytes :bytes="status.MemTotal * 1024" /></span></h4>
|
<template slot="button-content">
|
||||||
<div class="progress">
|
<font-awesome-icon icon="microchip" /> Memory
|
||||||
<div class="progress-bar bg-success" role="progressbar" :style="{ width: memoryPercentage + '%' }" :aria-valuenow="memoryPercentage" aria-valuemin="0" aria-valuemax="100"></div>
|
<b-progress :value="memoryPercentage" height=".5rem" :variant="percentageClass"></b-progress>
|
||||||
<div class="progress-bar bg-warning" role="progressbar" :style="{ width: memoryBufCachePercentage + '%' }" :aria-valuenow="memoryBufCachePercentage" aria-valuemin="0" aria-valuemax="100"></div>
|
</template>
|
||||||
</div>
|
<b-dropdown-header class="text-center">Memory</b-dropdown-header>
|
||||||
|
<div class="text-center">
|
||||||
|
<span :class="'badge badge-' + percentageClass"><formatBytes :bytes="status.MemUsed * 1024" /> / <formatBytes :bytes="status.MemTotal * 1024" /></span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</b-nav-item-dropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -14,16 +18,10 @@
|
||||||
props: [ 'status' ],
|
props: [ 'status' ],
|
||||||
computed: {
|
computed: {
|
||||||
memoryPercentage: function() {
|
memoryPercentage: function() {
|
||||||
if (!this.status) {
|
return (this.status.MemUsed / this.status.MemTotal) * 100;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return parseFloat((this.status.MemUsed / this.status.MemTotal) * 100).toFixed(1).toString();
|
|
||||||
},
|
},
|
||||||
memoryBufCachePercentage: function() {
|
memoryBufCachePercentage: function() {
|
||||||
if (!this.status) {
|
return ((this.status.Buffers + this.status.Cached) / this.status.MemTotal) * 100;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return parseFloat(((this.status.Buffers + this.status.Cached) / this.status.MemTotal) * 100).toFixed(1).toString();
|
|
||||||
},
|
},
|
||||||
percentageClass: function() {
|
percentageClass: function() {
|
||||||
let usedPercent = (this.status.MemUsed / this.status.MemTotal) * 100;
|
let usedPercent = (this.status.MemUsed / this.status.MemTotal) * 100;
|
||||||
|
|
10
src/main.js
10
src/main.js
|
@ -1,14 +1,20 @@
|
||||||
// The Vue build version to load with the `import` command
|
// The Vue build version to load with the `import` command
|
||||||
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
|
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import 'bootstrap/dist/css/bootstrap.css'
|
import './assets/scss/app.scss'
|
||||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
|
||||||
import App from './App'
|
import App from './App'
|
||||||
|
import fontawesome from '@fortawesome/fontawesome'
|
||||||
|
import FontAwesomeIcon from '@fortawesome/vue-fontawesome'
|
||||||
|
import solid from '@fortawesome/fontawesome-free-solid'
|
||||||
|
|
||||||
import BootstrapVue from 'bootstrap-vue'
|
import BootstrapVue from 'bootstrap-vue'
|
||||||
|
|
||||||
Vue.use(BootstrapVue);
|
Vue.use(BootstrapVue);
|
||||||
|
|
||||||
|
fontawesome.library.add(solid);
|
||||||
|
|
||||||
|
Vue.component('font-awesome-icon', FontAwesomeIcon);
|
||||||
|
|
||||||
Vue.component('formatBytes', {
|
Vue.component('formatBytes', {
|
||||||
render: function (createElement) {
|
render: function (createElement) {
|
||||||
return createElement(
|
return createElement(
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
window.arm_config = {
|
window.arm_config = {
|
||||||
url: 'ws://127.0.0.1:8080/ws'
|
url: 'ws://192.168.2.85:8080/ws'
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue