You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
5.1 KiB
Vue
240 lines
5.1 KiB
Vue
<template>
|
|
<div class="card">
|
|
<div class="panel-title">
|
|
<span class="title-dot"></span>
|
|
<span>事件提醒</span>
|
|
</div>
|
|
<div class="panel-body body">
|
|
<div class="event-list">
|
|
<div v-for="item in eventItems" :key="item.key" class="event-row">
|
|
<div class="event-name">
|
|
<span class="event-bullet" :style="{ borderColor: item.color }"></span>
|
|
<span>{{ item.name }}</span>
|
|
</div>
|
|
<div class="event-count" :style="{ color: item.color }">{{ item.count }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="event-chart">
|
|
<div class="chart-container">
|
|
<div ref="chartRef" class="chart"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, onUnmounted, ref } from 'vue'
|
|
import * as echarts from 'echarts'
|
|
import { colors } from '../utils'
|
|
|
|
const chartRef = ref<HTMLElement | null>(null)
|
|
let chart: echarts.ECharts | null = null
|
|
|
|
const eventItems = [
|
|
{ key: 'check', name: '点检', count: 1, percent: 30, color: colors.cyan },
|
|
{ key: 'maintain', name: '保养', count: 1, percent: 42, color: colors.warn },
|
|
{ key: 'repair', name: '维修', count: 1, percent: 20, color: colors.danger }
|
|
]
|
|
|
|
const render = () => {
|
|
if (!chart) return
|
|
const percentMap = Object.fromEntries(eventItems.map((i) => [i.name, i.percent])) as Record<string, number>
|
|
chart.setOption({
|
|
backgroundColor: 'transparent',
|
|
tooltip: { trigger: 'item' },
|
|
legend: {
|
|
orient: 'vertical',
|
|
right: 10,
|
|
top: 'center',
|
|
icon: 'circle',
|
|
itemWidth: 10,
|
|
itemHeight: 10,
|
|
itemGap: 14,
|
|
selectedMode: false,
|
|
textStyle: {
|
|
rich: {
|
|
percent: { fontSize: 18, fontWeight: 900, color: '#e5f0ff', lineHeight: 20 },
|
|
name: { fontSize: 12, fontWeight: 700, color: 'rgba(148,163,184,0.95)', lineHeight: 16 }
|
|
}
|
|
},
|
|
formatter: (name: string) => `{percent|${percentMap[name] ?? 0}%}\n{name|${name}}`
|
|
},
|
|
series: [
|
|
{
|
|
type: 'pie',
|
|
radius: ['48%', '70%'],
|
|
center: ['35%', '50%'],
|
|
avoidLabelOverlap: true,
|
|
label: { show: false },
|
|
labelLine: { show: false },
|
|
padAngle: 2,
|
|
itemStyle: { borderRadius: 8, borderWidth: 6, borderColor: 'rgba(2,6,23,0.9)' },
|
|
emphasis: { scale: false },
|
|
data: [
|
|
{ value: 30, name: '点检', itemStyle: { color: colors.cyan } },
|
|
{ value: 42, name: '保养', itemStyle: { color: colors.warn } },
|
|
{ value: 20, name: '维修', itemStyle: { color: colors.danger } }
|
|
]
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
const resize = () => {
|
|
chart?.resize()
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (!chartRef.value) return
|
|
chart = echarts.init(chartRef.value, 'dark', { renderer: 'canvas' })
|
|
render()
|
|
window.addEventListener('resize', resize)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('resize', resize)
|
|
chart?.dispose()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.card {
|
|
height: 100%;
|
|
background: linear-gradient(135deg, rgba(15,23,42,0.96), rgba(15,23,42,0.88));
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(30,64,175,0.85);
|
|
box-shadow:
|
|
0 18px 45px rgba(15,23,42,0.95),
|
|
0 0 0 1px rgba(15,23,42,1),
|
|
inset 0 0 0 1px rgba(56,189,248,0.05);
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
min-height: 0;
|
|
}
|
|
|
|
.card::before,
|
|
.card::after {
|
|
content: "";
|
|
position: absolute;
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 2px;
|
|
border: 1px solid rgba(56,189,248,0.75);
|
|
opacity: 0.6;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.card::before {
|
|
top: -1px;
|
|
left: -1px;
|
|
border-right: none;
|
|
border-bottom: none;
|
|
}
|
|
|
|
.card::after {
|
|
right: -1px;
|
|
bottom: -1px;
|
|
border-left: none;
|
|
border-top: none;
|
|
}
|
|
|
|
.panel-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 12px;
|
|
border-bottom: 1px solid rgba(41, 54, 95, 0.9);
|
|
font-size: 16px;
|
|
font-weight: 900;
|
|
color: #e5f0ff;
|
|
}
|
|
|
|
.title-dot {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
border: 1px solid rgba(56,189,248,0.95);
|
|
box-shadow: 0 0 12px rgba(56,189,248,0.45);
|
|
}
|
|
|
|
.panel-body {
|
|
flex: 1;
|
|
min-height: 0;
|
|
padding: 10px 12px;
|
|
}
|
|
|
|
.body {
|
|
display: grid;
|
|
grid-template-columns: 0.7fr 1.3fr;
|
|
gap: 10px;
|
|
align-items: center;
|
|
}
|
|
|
|
.event-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.event-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 8px 10px;
|
|
border-radius: 6px;
|
|
border: 1px solid rgba(30,64,175,0.7);
|
|
background: rgba(15,23,42,0.7);
|
|
}
|
|
|
|
.event-name {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 12px;
|
|
color: #e5f0ff;
|
|
}
|
|
|
|
.event-bullet {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
border: 3px solid;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.event-count {
|
|
font-size: 14px;
|
|
font-weight: 900;
|
|
}
|
|
|
|
.event-chart {
|
|
height: 100%;
|
|
min-height: 170px;
|
|
display: flex;
|
|
min-height: 0;
|
|
}
|
|
|
|
.chart-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 0;
|
|
}
|
|
|
|
.chart {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
@media (max-width: 1366px) {
|
|
.body {
|
|
grid-template-columns: 0.8fr 1.2fr;
|
|
}
|
|
}
|
|
</style>
|