LoongPanel-Asp/web/components/Cards/MiniCard.vue

179 lines
4.2 KiB
Vue
Executable File

<script lang="ts" setup>
import {useDataStore} from "~/strores/DataStore";
import {useMainLayoutStore} from "~/strores/UseMainLayoutStore";
import { MoveDown,CircleAlert,CircleX } from 'lucide-vue-next';
const props = defineProps({
title: {
type: String,
default: "CPU使用率",
},
info:{
type:Array,
default:()=>[],
},
unit: {
type: String,
default: "%"
},
watcher: {
type: [String, Array],
default: "cpuTotalUsage",
validator: (value) => {
return typeof value === 'string' || Array.isArray(value);
}
},
reverse: {
type: Boolean,
default: false
}
})
const values=ref<string[]>(Array( Array.isArray(props.watcher)?props.watcher.length:1).fill('0'))
const dataStore = useDataStore()
const mainStore = useMainLayoutStore()
const status=ref<string>("success")
//监听dataStore变更
const value=ref<number>(Number(values.value[0]))
dataStore.$subscribe((_, state) => {
//获得d3YT#cpuUserUsage的key的value
const watcher = Array.isArray(props.watcher) ? [...props.watcher] : [props.watcher];
// 使用map函数从state.data中取出每个watcherKey对应的值
values.value = watcher.map(watcherKey => state.data[watcherKey as string]);
//检测第一个值超过50则warning 超过80则error
value.value=Number(values.value[0])
status.value = value.value > 80 ? "error" : value.value > 50 ? "warning" : "success";
})
</script>
<template>
<n-popover trigger="hover" placement="bottom">
<template #trigger>
<div class="mini-card-box">
<n-progress type="circle" processing :status="status" :percentage="
values[0]
" style="width: 68px;">
<MoveDown class="arrow" :style="{transform:`rotate(${(100-value)/100*360*-1}deg)`}" v-if="status==='success'"/>
<div class="warning" v-if="status==='warning'">
<CircleAlert />
</div>
<div class="error" v-if="status==='error'">
<CircleX />
</div>
</n-progress>
<div class="text">
<h3>
{{ title }}
</h3>
<h2>
{{ Number(values[0]??0).toFixed(2)}}{{ unit }}
</h2>
</div>
</div>
</template>
<n-tag type="info" v-for="i in info">
{{ i }}
</n-tag>
</n-popover>
</template>
<style lang="scss" scoped>
@import "../../base";
.mini-card-box {
display: flex;
align-items: center;
background: $light-bg-color;
padding: 20px 24px ;
border-radius: $radius;
box-shadow: 0 10px 30px 0 rgba(17, 38, 146, 0.05);
gap: 25px;
border: $border;
*{
@include SC_Font()
}
> svg {
width: 68px;
height: 68px;
}
.dark-mode & {
background: $dark-bg-color;
}
}
.arrow{
width: 30px;
height: 30px;
stroke: $light-unfocused-color;
//旋转中心为几何中心
transform-origin: center;
//过度
transition: transform 0.3s ease-in-out;
}
.text {
display: flex;
flex-direction: column;
justify-content: center;
h2, h3 {
color: $light-unfocused-color;
line-height: 175%; /* 28px */
font-size: 16px;
font-weight: 400;
}
h2 {
font-size: 19px;
font-weight: 600;
color: $light-text-color;
.dark-mode & {
color: $dark-text-color;
}
}
}
.warning,.error{
display: flex;
place-items: center;
place-content: center;
width: 70px;
height: 70px;
border-radius: 50%;
animation: background-fade-error 0.5s infinite ease-in-out;
svg{
width: 40px;
height: 40px;
stroke: #fff;
}
}
.warning{
animation: background-fade 1s infinite ease-in-out;
}
@keyframes background-fade {
0% {
background-color: rgba(255, 255, 0, 0.3); /* 黄色,完全透明 */
}
50% {
background-color: rgba(255, 255, 0, 1); /* 黄色,完全不透明 */
}
100% {
background-color: rgba(255, 255, 0, 0.3); /* 黄色,完全透明 */
}
}
@keyframes background-fade-error {
0% {
background-color: rgba(255, 0, 0, 0.3); /* 红色,完全透明 */
}
50% {
background-color: rgba(255, 0, 0, 1); /* 红色,完全不透明 */
box-shadow: 0 0 10px rgba(255, 0, 0, 0.5); /* 红色阴影 */
}
100% {
background-color: rgba(255, 0, 0, 0.3); /* 红色,完全透明 */
}
}
</style>