mirror of
https://github.com/snowykami/server-status-web.git
synced 2025-06-04 14:15:23 +00:00
✨ 优化储存进度条样式
This commit is contained in:
parent
3bff292a6d
commit
cbd742a85b
@ -8,7 +8,7 @@ const year = new Date().getFullYear()
|
||||
<h1 style="text-align: center">Server Status</h1>
|
||||
<router-view/>
|
||||
<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
|
||||
</footer>
|
||||
</template>
|
||||
|
@ -72,7 +72,6 @@ export function formatDate(timestamp: number, timeOnly: boolean = false) {
|
||||
}
|
||||
|
||||
export function getBaseColor(percent: number, disable: boolean = false) {
|
||||
// 获取基础颜色
|
||||
// 0~60: green, 60~80: yellow, 80~90: orange, 90~100: red
|
||||
if (disable) {
|
||||
return '#9ca3af'
|
||||
@ -93,8 +92,6 @@ export function getBlankColor(percent: number, disable: boolean = false) {
|
||||
if (disable) {
|
||||
return '#e5e7eb'
|
||||
}
|
||||
|
||||
//相比base更浅的颜色
|
||||
if (percent < 60) {
|
||||
return '#bbf7d0'
|
||||
} 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 s = Math.floor(seconds % 60).toString().padStart(2, '0');
|
||||
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`;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { format2Size, getBaseColor, getBlankColor } from '../api/utils.ts';
|
||||
import {format2Size, getBaseColor, getBlankColor} from '../api/utils.ts';
|
||||
|
||||
const props = defineProps<{
|
||||
mountpoint: string;
|
||||
@ -14,8 +14,8 @@ const colorBlank = getBlankColor(props.used / props.total * 100);
|
||||
</script>
|
||||
|
||||
<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="left-text">
|
||||
<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>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:host {
|
||||
--text-size: 2px;
|
||||
}
|
||||
|
||||
.disk {
|
||||
margin-top: 0.5rem;
|
||||
height: 2rem;
|
||||
width: 100%;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.disk-total {
|
||||
height: 0.618rem;
|
||||
width: 90%;
|
||||
border-radius: 1rem;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: flex-start;
|
||||
overflow: hidden; /* Ensure the used part doesn't overflow */
|
||||
}
|
||||
|
||||
.disk-used {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 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 {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
text-align: center;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.left-text {
|
||||
margin-left: 1rem;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.right-text {
|
||||
margin-right: 1rem;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.disk-text {
|
||||
font-size: var(--text-size);
|
||||
font-size: 16px;
|
||||
color: var(--text-color-2);
|
||||
}
|
||||
|
||||
.percentage{
|
||||
font-size: 14px;
|
||||
color: var(--text-color-2);
|
||||
}
|
||||
</style>
|
@ -4,7 +4,7 @@ import {computed, onMounted, ref, watch} from "vue";
|
||||
import * as echarts from "echarts";
|
||||
import {
|
||||
format2Size,
|
||||
formatDate,
|
||||
formatDate, formatDuration,
|
||||
formatSizeByUnit,
|
||||
formatUptime,
|
||||
getBaseColor,
|
||||
@ -63,6 +63,7 @@ const hoverBorderColor = computed(() => {
|
||||
return statusColor.value
|
||||
})
|
||||
|
||||
const fontFam = 'Josefin Sans'
|
||||
|
||||
function onMountedFunc() {
|
||||
const cpuChart = echarts.init(cpuChartRef.value);
|
||||
@ -73,15 +74,16 @@ function onMountedFunc() {
|
||||
// style
|
||||
const titleStyle = {
|
||||
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 pieLabelPosition = 'center'
|
||||
const emphasis = {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 15,
|
||||
fontFamily: fontFam,
|
||||
position: ['50%', '20%'] // 设置标签位置为圆环外部
|
||||
},
|
||||
}
|
||||
@ -96,7 +98,7 @@ function onMountedFunc() {
|
||||
|
||||
function update() {
|
||||
const timeDiff = (Date.now()) / 1000 - status.value.meta.observed_at
|
||||
deltaTime.value = timeDiff.toFixed(1)
|
||||
deltaTime.value = formatDuration(timeDiff)
|
||||
// 判断该时间与上一个时间不同才push
|
||||
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]) // 时间 上行 下行
|
||||
@ -119,6 +121,9 @@ function onMountedFunc() {
|
||||
top: 'center',
|
||||
textStyle: titleStyle,
|
||||
},
|
||||
textStyle: {
|
||||
fontFamily: fontFam
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
@ -154,6 +159,9 @@ function onMountedFunc() {
|
||||
top: 'center',
|
||||
textStyle: titleStyle
|
||||
},
|
||||
textStyle: {
|
||||
fontFamily: fontFam
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
@ -187,6 +195,9 @@ function onMountedFunc() {
|
||||
top: 'center',
|
||||
textStyle: titleStyle,
|
||||
},
|
||||
textStyle: {
|
||||
fontFamily: fontFam
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
@ -215,6 +226,12 @@ function onMountedFunc() {
|
||||
netChart.setOption(
|
||||
{
|
||||
color: netColor,
|
||||
title: {
|
||||
textStyle: titleStyle
|
||||
},
|
||||
textStyle: {
|
||||
fontFamily: fontFam
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
@ -228,7 +245,7 @@ function onMountedFunc() {
|
||||
} else {
|
||||
return formatDate(params.value, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
formatter: function (params: any) {
|
||||
@ -237,12 +254,10 @@ function onMountedFunc() {
|
||||
result += item.marker + (item.seriesName == 'Tx' ? '↑' : '↓') + ': ' + formatSizeByUnit(item.value * 8, null, 'bps') + '<br/>';
|
||||
});
|
||||
return result;
|
||||
}
|
||||
},
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
saveAsImage: {}
|
||||
}
|
||||
feature: {}
|
||||
},
|
||||
grid: {
|
||||
top: '25%',
|
||||
@ -259,7 +274,7 @@ function onMountedFunc() {
|
||||
axisLabel: {
|
||||
formatter: function (value: number) {
|
||||
return formatDate(value, true)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -269,7 +284,7 @@ function onMountedFunc() {
|
||||
axisLabel: {
|
||||
formatter: function (value: number) {
|
||||
return formatSizeByUnit(value * 8, null, 'b')
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -280,7 +295,7 @@ function onMountedFunc() {
|
||||
stack: 'Total',
|
||||
areaStyle: {},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
focus: 'series',
|
||||
},
|
||||
data: netStats.map(item => item[1]),
|
||||
showSymbol: false,
|
||||
@ -318,9 +333,9 @@ function onMountedFunc() {
|
||||
// link.download = `screenshot-${status.value.meta.id}-${formatDate(Date.now(), false)}.svg`;
|
||||
|
||||
function downloadScreenshot() {
|
||||
const hostElement = document.querySelector(".host#"+status.value.meta.id);
|
||||
const hostElement = document.querySelector(".host#" + status.value.meta.id);
|
||||
if (hostElement) {
|
||||
html2canvas(<HTMLElement>hostElement, { scale: 2 }).then((canvas) => {
|
||||
html2canvas(<HTMLElement>hostElement, {scale: 2}).then((canvas) => {
|
||||
const dataURL = canvas.toDataURL("image/png");
|
||||
const link = document.createElement("a");
|
||||
link.href = dataURL;
|
||||
@ -335,19 +350,21 @@ onMounted(
|
||||
onMountedFunc()
|
||||
}
|
||||
)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="host" :style="[gradientStyle, { '--hover-border-color': hoverBorderColor }]" :id="status.meta.id">
|
||||
<!-- 主机名-->
|
||||
<!-- 主机名-->
|
||||
<div class="host-name">{{ status.meta.name }}</div>
|
||||
<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">
|
||||
<OutlineAnime class="outline-anime" :color="statusColor" :spreadColor="statusColor2" :is-online="isOnline"/>
|
||||
<div class="uptime" style="margin-right: 5px"
|
||||
<div class="uptime time-tag" style="margin-right: 5px"
|
||||
:style="{backgroundColor: statusColor2, borderColor: statusColor}">{{ uptime }}
|
||||
</div>
|
||||
<div class="offline-time time-tag" v-if="!isOnline">
|
||||
Offline for {{ deltaTime }}
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
@ -356,7 +373,7 @@ onMounted(
|
||||
<div class="meta-2">
|
||||
<div class="section">
|
||||
<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 class="section">
|
||||
<img class="icon" src="/svg/timezone.svg" alt="location">
|
||||
@ -366,9 +383,9 @@ onMounted(
|
||||
<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>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<div class="section-name">
|
||||
Hardware
|
||||
</div>
|
||||
@ -390,7 +407,7 @@ onMounted(
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<!-- -->
|
||||
<!-- -->
|
||||
<div class="net">
|
||||
<div class="section-name">
|
||||
Network
|
||||
@ -398,7 +415,7 @@ onMounted(
|
||||
<div class="net-chart" ref="netChartRef"></div>
|
||||
</div>
|
||||
<hr>
|
||||
<!-- -->
|
||||
<!-- -->
|
||||
<div class="disks">
|
||||
<div class="section-name">
|
||||
Storage
|
||||
@ -484,7 +501,7 @@ onMounted(
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uptime {
|
||||
.time-tag {
|
||||
padding: 0 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 50px;
|
||||
|
@ -12,7 +12,7 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
--text-color-1: #000;
|
||||
--text-color-2: #383838;
|
||||
--text-color-2: #5f5f5f;
|
||||
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ onUnmounted(() => {
|
||||
<style scoped>
|
||||
.overview {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
@ -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"}
|
Loading…
x
Reference in New Issue
Block a user