优化储存进度条样式

This commit is contained in:
远野千束 2024-10-05 09:14:16 +08:00
parent 3bff292a6d
commit cbd742a85b
7 changed files with 77 additions and 47 deletions

View File

@ -8,7 +8,7 @@ const year = new Date().getFullYear()
<h1 style="text-align: center">Server Status</h1> <h1 style="text-align: center">Server Status</h1>
<router-view/> <router-view/>
<footer> <footer>
<a href="https://github.com/snowykami/server-status-server">Server Status Dashboard</a><br> <a href="https://github.com/snowykami/server-status-server">Server status dashboard</a><br>
© Copyright 2024-{{year}} <a href="https://sfkm.me" target="_blank">Snowykami</a> All Rights Reserved © Copyright 2024-{{year}} <a href="https://sfkm.me" target="_blank">Snowykami</a> All Rights Reserved
</footer> </footer>
</template> </template>

View File

@ -72,7 +72,6 @@ export function formatDate(timestamp: number, timeOnly: boolean = false) {
} }
export function getBaseColor(percent: number, disable: boolean = false) { export function getBaseColor(percent: number, disable: boolean = false) {
// 获取基础颜色
// 0~60: green, 60~80: yellow, 80~90: orange, 90~100: red // 0~60: green, 60~80: yellow, 80~90: orange, 90~100: red
if (disable) { if (disable) {
return '#9ca3af' return '#9ca3af'
@ -93,8 +92,6 @@ export function getBlankColor(percent: number, disable: boolean = false) {
if (disable) { if (disable) {
return '#e5e7eb' return '#e5e7eb'
} }
//相比base更浅的颜色
if (percent < 60) { if (percent < 60) {
return '#bbf7d0' return '#bbf7d0'
} else if (percent < 80) { } else if (percent < 80) {
@ -115,4 +112,12 @@ export function formatUptime(uptime: number): string {
const m = Math.floor((seconds % 3600) / 60).toString().padStart(2, '0'); const m = Math.floor((seconds % 3600) / 60).toString().padStart(2, '0');
const s = Math.floor(seconds % 60).toString().padStart(2, '0'); const s = Math.floor(seconds % 60).toString().padStart(2, '0');
return `${d}:${h}:${m}:${s}`; return `${d}:${h}:${m}:${s}`;
}
export function formatDuration(duration: number): string {
const d = Math.floor(duration / 86400);
const h = Math.floor((duration % 86400) / 3600);
const m = Math.floor((duration % 3600) / 60);
const s = duration % 60;
return d > 0 ? `${d}d` : h > 0 ? `${h}h` : m > 0 ? `${m}m` : `${s}s`;
} }

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { format2Size, getBaseColor, getBlankColor } from '../api/utils.ts'; import {format2Size, getBaseColor, getBlankColor} from '../api/utils.ts';
const props = defineProps<{ const props = defineProps<{
mountpoint: string; mountpoint: string;
@ -14,8 +14,8 @@ const colorBlank = getBlankColor(props.used / props.total * 100);
</script> </script>
<template> <template>
<div class="disk" :style="{ backgroundColor: colorBlank }">
<div class="disk-used" :style="{ width: props.used / props.total * 100 + '%', backgroundColor: colorUsed }"></div> <div class="disk">
<div class="hover-text"> <div class="hover-text">
<div class="left-text"> <div class="left-text">
<span class="disk-text">{{ props.mountpoint }}</span> <span class="disk-text">{{ props.mountpoint }}</span>
@ -24,59 +24,67 @@ const colorBlank = getBlankColor(props.used / props.total * 100);
<span class="disk-text">{{ format2Size(props.used, props.total) }} [{{ props.fstype }}]</span> <span class="disk-text">{{ format2Size(props.used, props.total) }} [{{ props.fstype }}]</span>
</div> </div>
</div> </div>
<div class="progress-bar" style="display: flex; align-items: center">
<div class="disk-total" :style="{ backgroundColor: colorBlank }" style="margin-right: 0.5rem;">
<div class="disk-used"
:style="{ width: props.used / props.total * 100 + '%', backgroundColor: colorUsed }"></div>
</div>
<div style="color: var(--text-color-2)">
<div class="percentage" style="text-align: right">{{ (props.used / props.total * 100).toFixed(1) }}%</div>
</div>
</div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
:host {
--text-size: 2px;
}
.disk { .disk {
margin-top: 0.5rem; margin-top: 0.75rem;
height: 2rem; }
width: 100%;
.disk-total {
height: 0.618rem;
width: 90%;
border-radius: 1rem; border-radius: 1rem;
position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: flex-start;
overflow: hidden; /* Ensure the used part doesn't overflow */ overflow: hidden; /* Ensure the used part doesn't overflow */
} }
.disk-used { .disk-used {
height: 100%; height: 100%;
position: absolute;
left: 0; left: 0;
top: 0; top: 0;
clip-path: inset(0 0 0 0 round var(--border-radius)); /* Apply border-radius using clip-path */ clip-path: inset(0 0 0 0 round 1rem);
} }
.hover-text { .hover-text {
position: absolute;
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
text-align: center; text-align: center;
padding: 0 1rem;
} }
.left-text { .left-text {
margin-left: 1rem;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
} }
.right-text { .right-text {
margin-right: 1rem;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
} }
.disk-text { .disk-text {
font-size: var(--text-size); font-size: 16px;
color: var(--text-color-2);
}
.percentage{
font-size: 14px;
color: var(--text-color-2); color: var(--text-color-2);
} }
</style> </style>

View File

@ -4,7 +4,7 @@ import {computed, onMounted, ref, watch} from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { import {
format2Size, format2Size,
formatDate, formatDate, formatDuration,
formatSizeByUnit, formatSizeByUnit,
formatUptime, formatUptime,
getBaseColor, getBaseColor,
@ -63,6 +63,7 @@ const hoverBorderColor = computed(() => {
return statusColor.value return statusColor.value
}) })
const fontFam = 'Josefin Sans'
function onMountedFunc() { function onMountedFunc() {
const cpuChart = echarts.init(cpuChartRef.value); const cpuChart = echarts.init(cpuChartRef.value);
@ -73,15 +74,16 @@ function onMountedFunc() {
// style // style
const titleStyle = { const titleStyle = {
color: 'rgba(0, 0, 0, 0.8)', color: 'rgba(0, 0, 0, 0.8)',
fontSize: 15, fontSize: 18,
} }
const radius = ['65%', '90%'] const radius = ['65%', '80%']
const netColor = ['#a2d8f4', '#0194e3'] // Tx Rx const netColor = ['#a2d8f4', '#0194e3'] // Tx Rx
const pieLabelPosition = 'center' const pieLabelPosition = 'center'
const emphasis = { const emphasis = {
label: { label: {
show: true, show: true,
fontSize: 15, fontSize: 15,
fontFamily: fontFam,
position: ['50%', '20%'] // position: ['50%', '20%'] //
}, },
} }
@ -96,7 +98,7 @@ function onMountedFunc() {
function update() { function update() {
const timeDiff = (Date.now()) / 1000 - status.value.meta.observed_at const timeDiff = (Date.now()) / 1000 - status.value.meta.observed_at
deltaTime.value = timeDiff.toFixed(1) deltaTime.value = formatDuration(timeDiff)
// push // push
if (netStats.length === 0 || netStats[netStats.length - 1][0] !== status.value.meta.observed_at) { if (netStats.length === 0 || netStats[netStats.length - 1][0] !== status.value.meta.observed_at) {
netStats.push([status.value.meta.observed_at, status.value.hardware.net.up, status.value.hardware.net.down]) // netStats.push([status.value.meta.observed_at, status.value.hardware.net.up, status.value.hardware.net.down]) //
@ -119,6 +121,9 @@ function onMountedFunc() {
top: 'center', top: 'center',
textStyle: titleStyle, textStyle: titleStyle,
}, },
textStyle: {
fontFamily: fontFam
},
series: [ series: [
{ {
type: 'pie', type: 'pie',
@ -154,6 +159,9 @@ function onMountedFunc() {
top: 'center', top: 'center',
textStyle: titleStyle textStyle: titleStyle
}, },
textStyle: {
fontFamily: fontFam
},
series: [ series: [
{ {
type: 'pie', type: 'pie',
@ -187,6 +195,9 @@ function onMountedFunc() {
top: 'center', top: 'center',
textStyle: titleStyle, textStyle: titleStyle,
}, },
textStyle: {
fontFamily: fontFam
},
series: [ series: [
{ {
type: 'pie', type: 'pie',
@ -215,6 +226,12 @@ function onMountedFunc() {
netChart.setOption( netChart.setOption(
{ {
color: netColor, color: netColor,
title: {
textStyle: titleStyle
},
textStyle: {
fontFamily: fontFam
},
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
@ -228,7 +245,7 @@ function onMountedFunc() {
} else { } else {
return formatDate(params.value, true); return formatDate(params.value, true);
} }
} },
} }
}, },
formatter: function (params: any) { formatter: function (params: any) {
@ -237,12 +254,10 @@ function onMountedFunc() {
result += item.marker + (item.seriesName == 'Tx' ? '↑' : '↓') + ': ' + formatSizeByUnit(item.value * 8, null, 'bps') + '<br/>'; result += item.marker + (item.seriesName == 'Tx' ? '↑' : '↓') + ': ' + formatSizeByUnit(item.value * 8, null, 'bps') + '<br/>';
}); });
return result; return result;
} },
}, },
toolbox: { toolbox: {
feature: { feature: {}
saveAsImage: {}
}
}, },
grid: { grid: {
top: '25%', top: '25%',
@ -259,7 +274,7 @@ function onMountedFunc() {
axisLabel: { axisLabel: {
formatter: function (value: number) { formatter: function (value: number) {
return formatDate(value, true) return formatDate(value, true)
} },
} }
} }
], ],
@ -269,7 +284,7 @@ function onMountedFunc() {
axisLabel: { axisLabel: {
formatter: function (value: number) { formatter: function (value: number) {
return formatSizeByUnit(value * 8, null, 'b') return formatSizeByUnit(value * 8, null, 'b')
} },
} }
} }
], ],
@ -280,7 +295,7 @@ function onMountedFunc() {
stack: 'Total', stack: 'Total',
areaStyle: {}, areaStyle: {},
emphasis: { emphasis: {
focus: 'series' focus: 'series',
}, },
data: netStats.map(item => item[1]), data: netStats.map(item => item[1]),
showSymbol: false, showSymbol: false,
@ -318,9 +333,9 @@ function onMountedFunc() {
// link.download = `screenshot-${status.value.meta.id}-${formatDate(Date.now(), false)}.svg`; // link.download = `screenshot-${status.value.meta.id}-${formatDate(Date.now(), false)}.svg`;
function downloadScreenshot() { function downloadScreenshot() {
const hostElement = document.querySelector(".host#"+status.value.meta.id); const hostElement = document.querySelector(".host#" + status.value.meta.id);
if (hostElement) { if (hostElement) {
html2canvas(<HTMLElement>hostElement, { scale: 2 }).then((canvas) => { html2canvas(<HTMLElement>hostElement, {scale: 2}).then((canvas) => {
const dataURL = canvas.toDataURL("image/png"); const dataURL = canvas.toDataURL("image/png");
const link = document.createElement("a"); const link = document.createElement("a");
link.href = dataURL; link.href = dataURL;
@ -335,19 +350,21 @@ onMounted(
onMountedFunc() onMountedFunc()
} }
) )
</script> </script>
<template> <template>
<div class="host" :style="[gradientStyle, { '--hover-border-color': hoverBorderColor }]" :id="status.meta.id"> <div class="host" :style="[gradientStyle, { '--hover-border-color': hoverBorderColor }]" :id="status.meta.id">
<!-- 主机名--> <!-- 主机名-->
<div class="host-name">{{ status.meta.name }}</div> <div class="host-name">{{ status.meta.name }}</div>
<div class="meta-1" style="display: flex; justify-content: space-between"> <div class="meta-1" style="display: flex; justify-content: space-between">
<div class="meta1-left" style="display: flex; justify-content: flex-start; align-items: center"> <div class="meta1-left" style="display: flex; justify-content: flex-start; align-items: center">
<OutlineAnime class="outline-anime" :color="statusColor" :spreadColor="statusColor2" :is-online="isOnline"/>&nbsp; <OutlineAnime class="outline-anime" :color="statusColor" :spreadColor="statusColor2" :is-online="isOnline"/>&nbsp;
<div class="uptime" style="margin-right: 5px" <div class="uptime time-tag" style="margin-right: 5px"
:style="{backgroundColor: statusColor2, borderColor: statusColor}">{{ uptime }} :style="{backgroundColor: statusColor2, borderColor: statusColor}">{{ uptime }}
</div> </div>
<div class="offline-time time-tag" v-if="!isOnline">
Offline for {{ deltaTime }}
</div>
</div> </div>
<div class="meta1-right" style="display: flex; justify-content: flex-end; align-items: center"> <div class="meta1-right" style="display: flex; justify-content: flex-end; align-items: center">
<img @click="downloadScreenshot" src="/svg/screenshots.svg" alt="download" style="width: 20px; height: 20px"> <img @click="downloadScreenshot" src="/svg/screenshots.svg" alt="download" style="width: 20px; height: 20px">
@ -356,7 +373,7 @@ onMounted(
<div class="meta-2"> <div class="meta-2">
<div class="section"> <div class="section">
<img class="icon" :src="os.icon" alt="system"> <img class="icon" :src="os.icon" alt="system">
<span class="meta2-text">{{ os.name }} {{status.meta.os.release}} · {{status.meta.os.machine}}</span> <span class="meta2-text">{{ os.name }} {{ status.meta.os.release }} · {{ status.meta.os.machine }}</span>
</div> </div>
<div class="section"> <div class="section">
<img class="icon" src="/svg/timezone.svg" alt="location"> <img class="icon" src="/svg/timezone.svg" alt="location">
@ -366,9 +383,9 @@ onMounted(
<img class="icon" src="/svg/label.svg" alt="labels"> <img class="icon" src="/svg/label.svg" alt="labels">
<span><span class="label meta2-text" v-for="label in status.meta.labels" :key="label">{{ label }}</span></span> <span><span class="label meta2-text" v-for="label in status.meta.labels" :key="label">{{ label }}</span></span>
</div> </div>
</div> </div>
<hr> <hr>
<div class="section-name"> <div class="section-name">
Hardware Hardware
</div> </div>
@ -390,7 +407,7 @@ onMounted(
</div> </div>
</div> </div>
<hr> <hr>
<!-- --> <!-- -->
<div class="net"> <div class="net">
<div class="section-name"> <div class="section-name">
Network Network
@ -398,7 +415,7 @@ onMounted(
<div class="net-chart" ref="netChartRef"></div> <div class="net-chart" ref="netChartRef"></div>
</div> </div>
<hr> <hr>
<!-- --> <!-- -->
<div class="disks"> <div class="disks">
<div class="section-name"> <div class="section-name">
Storage Storage
@ -484,7 +501,7 @@ onMounted(
align-items: center; align-items: center;
} }
.uptime { .time-tag {
padding: 0 0.5rem; padding: 0 0.5rem;
font-size: 0.8rem; font-size: 0.8rem;
border-radius: 50px; border-radius: 50px;

View File

@ -12,7 +12,7 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
--text-color-1: #000; --text-color-1: #000;
--text-color-2: #383838; --text-color-2: #5f5f5f;
} }

View File

@ -49,7 +49,7 @@ onUnmounted(() => {
<style scoped> <style scoped>
.overview { .overview {
display: flex; display: flex;
justify-content: space-between; justify-content: center;
padding: 10px; padding: 10px;
} }

View File

@ -1 +1 @@
{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/index.ts","./src/api/node.ts","./src/api/utils.ts","./src/router/index.ts","./src/app.vue","./src/components/disk.vue","./src/components/helloworld.vue","./src/components/host.vue","./src/components/hostdisks.vue","./src/components/nav.vue","./src/components/outlineanime.vue","./src/views/home.vue","./src/views/test.vue"],"version":"5.6.2"} {"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/index.ts","./src/api/node.ts","./src/api/utils.ts","./src/router/index.ts","./src/app.vue","./src/components/disk.vue","./src/components/helloworld.vue","./src/components/host.vue","./src/components/nav.vue","./src/components/outlineanime.vue","./src/views/home.vue","./src/views/test.vue"],"version":"5.6.2"}