feat: 新增dev分支作为0812版本改版
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 405 KiB |
After Width: | Height: | Size: 567 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 563 B |
After Width: | Height: | Size: 450 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 334 KiB |
@ -0,0 +1,320 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref, watch, nextTick } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
|
||||
// 定义组件接收的props
|
||||
const props = defineProps({
|
||||
xData: {
|
||||
type: Array as PropType<Array<string>>,
|
||||
default: () => [],
|
||||
},
|
||||
legendArr: {
|
||||
type: Array as PropType<Array<string>>,
|
||||
default: () => [],
|
||||
},
|
||||
datas: {
|
||||
type: Array as PropType<Array<Array<number>>>,
|
||||
default: () => [],
|
||||
},
|
||||
colorArr: {
|
||||
type: Array as PropType<Array<Array<string>>>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const chartContainer = ref<HTMLDivElement | null>(null);
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
|
||||
// 自定义3D形状
|
||||
const CubeLeft = echarts.graphic.extendShape({
|
||||
shape: { x: 0, y: 0 },
|
||||
buildPath: (ctx, shape) => {
|
||||
const { xAxisPoint } = shape;
|
||||
const [c0, c1, c2, c3] = [
|
||||
[shape.x, shape.y],
|
||||
[shape.x - 12, shape.y - 6],
|
||||
[xAxisPoint[0] - 12, xAxisPoint[1] - 6],
|
||||
[xAxisPoint[0], xAxisPoint[1]],
|
||||
];
|
||||
ctx
|
||||
.moveTo(c0[0], c0[1])
|
||||
.lineTo(c1[0], c1[1])
|
||||
.lineTo(c2[0], c2[1])
|
||||
.lineTo(c3[0], c3[1])
|
||||
.closePath();
|
||||
},
|
||||
});
|
||||
|
||||
const CubeRight = echarts.graphic.extendShape({
|
||||
shape: { x: 0, y: 0 },
|
||||
buildPath: (ctx, shape) => {
|
||||
const { xAxisPoint } = shape;
|
||||
const [c1, c2, c3, c4] = [
|
||||
[shape.x, shape.y],
|
||||
[xAxisPoint[0], xAxisPoint[1]],
|
||||
[xAxisPoint[0] + 12, xAxisPoint[1] - 6],
|
||||
[shape.x + 12, shape.y - 6],
|
||||
];
|
||||
ctx
|
||||
.moveTo(c1[0], c1[1])
|
||||
.lineTo(c2[0], c2[1])
|
||||
.lineTo(c3[0], c3[1])
|
||||
.lineTo(c4[0], c4[1])
|
||||
.closePath();
|
||||
},
|
||||
});
|
||||
|
||||
const CubeTop = echarts.graphic.extendShape({
|
||||
shape: { x: 0, y: 0 },
|
||||
buildPath: (ctx, shape) => {
|
||||
const [c1, c2, c3, c4] = [
|
||||
[shape.x, shape.y],
|
||||
[shape.x + 12, shape.y - 6],
|
||||
[shape.x, shape.y - 12],
|
||||
[shape.x - 12, shape.y - 6],
|
||||
];
|
||||
ctx
|
||||
.moveTo(c1[0], c1[1])
|
||||
.lineTo(c2[0], c2[1])
|
||||
.lineTo(c3[0], c3[1])
|
||||
.lineTo(c4[0], c4[1])
|
||||
.closePath();
|
||||
},
|
||||
});
|
||||
// 注册三个面图形
|
||||
echarts.graphic.registerShape("CubeLeft", CubeLeft);
|
||||
echarts.graphic.registerShape("CubeRight", CubeRight);
|
||||
echarts.graphic.registerShape("CubeTop", CubeTop);
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (!chartContainer.value) return;
|
||||
|
||||
chartInstance = echarts.init(chartContainer.value);
|
||||
setTimeout(() => {
|
||||
chartInstance?.resize();
|
||||
updateChart();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// 更新图表配置
|
||||
const updateChart = () => {
|
||||
if (!chartInstance) return;
|
||||
const series = props.datas
|
||||
.map((item, index) => [
|
||||
{
|
||||
type: "custom",
|
||||
name: props.legendArr[index],
|
||||
renderItem: (params, api) => ({
|
||||
type: "group",
|
||||
x: (index - props.datas.length / 2) * 30 + 15,
|
||||
children: [
|
||||
{
|
||||
type: "CubeLeft",
|
||||
shape: {
|
||||
api,
|
||||
xValue: api.value(0),
|
||||
yValue: api.value(1),
|
||||
x: api.coord([api.value(0), api.value(1)])[0],
|
||||
y: api.coord([api.value(0), api.value(1)])[1],
|
||||
xAxisPoint: api.coord([api.value(0), 0]),
|
||||
},
|
||||
style: {
|
||||
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: props.colorArr[index % props.colorArr.length][1],
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: props.colorArr[index % props.colorArr.length][0],
|
||||
},
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "CubeRight",
|
||||
shape: {
|
||||
api,
|
||||
xValue: api.value(0),
|
||||
yValue: api.value(1),
|
||||
x: api.coord([api.value(0), api.value(1)])[0],
|
||||
y: api.coord([api.value(0), api.value(1)])[1],
|
||||
xAxisPoint: api.coord([api.value(0), 0]),
|
||||
},
|
||||
style: {
|
||||
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: props.colorArr[index % props.colorArr.length][1],
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: props.colorArr[index % props.colorArr.length][0],
|
||||
},
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "CubeTop",
|
||||
shape: {
|
||||
api,
|
||||
xValue: api.value(0),
|
||||
yValue: api.value(1),
|
||||
x: api.coord([api.value(0), api.value(1)])[0],
|
||||
y: api.coord([api.value(0), api.value(1)])[1],
|
||||
xAxisPoint: api.coord([api.value(0), 0]),
|
||||
},
|
||||
style: {
|
||||
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: props.colorArr[index % props.colorArr.length][1],
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: props.colorArr[index % props.colorArr.length][1],
|
||||
},
|
||||
]),
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
data: item,
|
||||
},
|
||||
{
|
||||
type: "bar",
|
||||
name: props.legendArr[index],
|
||||
barWidth: 25,
|
||||
// label: {
|
||||
// normal: {
|
||||
// show: true,
|
||||
// position: "top",
|
||||
// fontSize: 16,
|
||||
// color: "#fff",
|
||||
// offset: [0, -10],
|
||||
// },
|
||||
// },
|
||||
itemStyle: { color: "transparent" },
|
||||
data: item,
|
||||
yAxisIndex: 0, // 使用左y轴
|
||||
},
|
||||
{
|
||||
type: "bar",
|
||||
name: props.legendArr[index],
|
||||
barWidth: 25,
|
||||
itemStyle: { color: "transparent" },
|
||||
data: item,
|
||||
yAxisIndex: 1, // 使用左y轴
|
||||
},
|
||||
])
|
||||
.flat();
|
||||
|
||||
chartInstance.setOption({
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
borderWidth: 0,
|
||||
backgroundColor: "rgba(8,36,68,.9)",
|
||||
color: "#fff",
|
||||
textStyle: { color: "#fff" },
|
||||
formatter: (params) => {
|
||||
let str = params[0].name + "</br>";
|
||||
params.forEach((item, index) => {
|
||||
if (item.seriesType === "custom") {
|
||||
str += `
|
||||
<div style='display:flex;justify-content:space-between;align-items:center'>
|
||||
<div style='margin-right:20px;'>
|
||||
<span style="display:inline-block;width:10px;height:10px;border-radius:5px;background-color:${
|
||||
props.colorArr[index % props.colorArr.length][0]
|
||||
}"></span>
|
||||
${item.seriesName}
|
||||
</div>
|
||||
<span> ${item.value ? item.value : "-"}</span>
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: "5%",
|
||||
right: "5%",
|
||||
top: "5%",
|
||||
bottom: "10%",
|
||||
containLabel: true,
|
||||
},
|
||||
legend: {
|
||||
left: "center",
|
||||
top: "90%",
|
||||
itemWidth: 12, // 图例项宽度
|
||||
itemHeight: 8,
|
||||
textStyle: { color: "#fff", fontSize: 12 },
|
||||
data: props.legendArr.map((name, index) => ({
|
||||
name,
|
||||
textStyle: { color: "#fff", fontSize: 12 },
|
||||
itemStyle: { color: props.colorArr[index % props.colorArr.length][1] },
|
||||
})),
|
||||
},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: props.xData,
|
||||
axisLine: { lineStyle: { color: "rgba(239, 247, 253, .1)" } },
|
||||
axisLabel: { fontSize: 12, color: "#fff", margin: 12 },
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
// name: "kWh",
|
||||
// nameTextStyle: { color: "#fff", fontSize: 12 },
|
||||
splitLine: {
|
||||
lineStyle: { type: "dashed", color: "rgba(80,112,242,0.3)" },
|
||||
},
|
||||
axisLabel: { textStyle: { color: "#8C8C8C" }, fontSize: 12 },
|
||||
// axisLine: { lineStyle: { color: "#8C8C8C" } },
|
||||
// scale: true, // 同步刻度
|
||||
},
|
||||
{
|
||||
// name: "kWh",
|
||||
// nameTextStyle: { color: "#fff", fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: "transparent" } },
|
||||
axisLabel: { textStyle: { color: "#8C8C8C" }, fontSize: 12 },
|
||||
// axisLine: { lineStyle: { color: "#8C8C8C" } },
|
||||
position: "right",
|
||||
// scale: true, // 同步刻度
|
||||
},
|
||||
],
|
||||
series,
|
||||
});
|
||||
};
|
||||
const handleResize = () => {
|
||||
chartInstance?.resize();
|
||||
};
|
||||
// 生命周期管理
|
||||
onMounted(async () => {
|
||||
await nextTick();
|
||||
initChart();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
chartInstance?.resize();
|
||||
}
|
||||
});
|
||||
|
||||
// 响应式更新
|
||||
watch(
|
||||
() => props,
|
||||
async () => {
|
||||
await nextTick();
|
||||
updateChart();
|
||||
// delay(600).then(() => resize());
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div ref="chartContainer" style="width: 100%; height: 100%" />
|
||||
</template>
|
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div id="pieChart" style="width: 100%; height: 100%"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import * as echarts from "echarts";
|
||||
function initChart() {
|
||||
const chartDom = document.getElementById("pieChart");
|
||||
const myChart = echarts.init(chartDom);
|
||||
|
||||
const option = {
|
||||
backgroundColor: "",
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
formatter: "{b}: {c} ({d}%)",
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "pie",
|
||||
radius: ["10%", "50%"],
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: true,
|
||||
position: "outer",
|
||||
formatter: "{b}: {d}%",
|
||||
color: "#fff", // Set label text color to white
|
||||
},
|
||||
labelLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: "#fff", // Set label line color to white
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{ value: 45, name: "小门搭扣丢失", itemStyle: { color: "#29ABE2" } },
|
||||
{ value: 21, name: "门折页座脱落", itemStyle: { color: "#1BBC9B" } },
|
||||
{ value: 8, name: "小窗裂纹", itemStyle: { color: "#F64747" } },
|
||||
{ value: 6, name: "搭扣未搭", itemStyle: { color: "#9B59B6" } },
|
||||
{ value: 5, name: "搭扣未搭", itemStyle: { color: "#F9690E" } },
|
||||
{ value: 5, name: "下侧门板缺失", itemStyle: { color: "#F7CA18" } },
|
||||
{
|
||||
value: 10,
|
||||
name: "Other",
|
||||
itemStyle: { color: "rgba(255,255,255,0.1)" },
|
||||
label: { show: false },
|
||||
labelLine: { show: false },
|
||||
}, // Remaining percentage
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: "rgba(0, 0, 0, 0.5)",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
myChart.setOption(option);
|
||||
}
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
});
|
||||
</script>
|
@ -0,0 +1,54 @@
|
||||
<!--
|
||||
* @Author: donghao donghao@supervision.ltd
|
||||
* @Date: 2025-03-06 15:52:40
|
||||
* @LastEditors: donghao donghao@supervision.ltd
|
||||
* @LastEditTime: 2025-08-12 16:29:23
|
||||
* @FilePath: \vite-ai\data-dashboard\src\components\contentHeader.vue
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
-->
|
||||
<template>
|
||||
<div class="flex items-center justify-between content_header_wrap"
|
||||
:style="{ 'background-image': `url(${sub_title_bg})`, 'background-size': 'cover', 'background-repeat': 'no-repeat', 'background-position': 'left bottom' }">
|
||||
<!-- 左侧标题区域 -->
|
||||
<div class="flex items-center left-section pl-[16px]">
|
||||
<img src="@/assets/common/alarm_title.png" class="w-[16px] h-[20px]">
|
||||
<div class="fg-title ml-[12px] text-[18px]">
|
||||
{{ title }}
|
||||
<slot name="title"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧内容 -->
|
||||
<div>
|
||||
<slot name="extra"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import sub_title_bg from '@/assets/home/sub_title_bg.png';
|
||||
// 定义组件 props
|
||||
const props = withDefaults(defineProps<{
|
||||
// bgLayout: number | string; // 背景样式 1855 918 800 450
|
||||
title: string; // 标题
|
||||
}>(), {
|
||||
// bgLayout: 1855,
|
||||
title: ""
|
||||
});
|
||||
|
||||
// 计算是否显示右侧内容
|
||||
// const bgImageUrl = computed(() => {
|
||||
// switch (Number(props.bgLayout)) {
|
||||
// default:
|
||||
// return sub_title_bg;
|
||||
|
||||
// }
|
||||
// });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.content_header_wrap {
|
||||
width: 431px;
|
||||
height: 38px;
|
||||
// border-image: linear-gradient(90deg, rgba(25, 61, 72, 0), rgba(67, 215, 226, 1), rgba(29, 65, 76, 0)) 1 1;
|
||||
}
|
||||
</style>
|
@ -1,7 +1,9 @@
|
||||
// src/assets/fonts.scss
|
||||
@font-face {
|
||||
font-family: 'DingTalk JinBuTi'; // 自定义字体名称
|
||||
src: url('@/assets/fonts/DingTalk JinBuTi.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-family: "DingTalk JinBuTi"; // 自定义字体名称
|
||||
src: url("@/assets/fonts/DingTalk JinBuTi.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,422 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>环形图组件设计</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Microsoft+YaHei:wght@400;700&display=swap">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #1a253a, #0d1425);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 40px;
|
||||
text-align: center;
|
||||
text-shadow: 0 0 10px rgba(52, 176, 255, 0.5);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
background: linear-gradient(145deg, #0C1D45, #07122E);
|
||||
padding: 25px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
border: 1px solid rgba(52, 176, 255, 0.2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 科技感装饰 */
|
||||
.container::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, rgba(52, 176, 255, 0.5), transparent);
|
||||
}
|
||||
|
||||
.container::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, rgba(242, 214, 75, 0.5), transparent);
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
width: 280px;
|
||||
height: 280px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chart-center {
|
||||
position: absolute;
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
border-radius: 50%;
|
||||
background: rgba(12, 29, 69, 0.7);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
border: 3px solid rgba(52, 176, 255, 0.1);
|
||||
}
|
||||
|
||||
.total-label {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.total-value {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-top: 5px;
|
||||
text-shadow: 0 0 8px rgba(52, 176, 255, 0.6);
|
||||
}
|
||||
|
||||
.legend-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-left: 30px;
|
||||
margin-left: 30px;
|
||||
border-left: 1px solid rgba(52, 176, 255, 0.2);
|
||||
width: calc(100% - 310px);
|
||||
}
|
||||
|
||||
.legend-title {
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.legend-title::before {
|
||||
content: "";
|
||||
width: 4px;
|
||||
height: 20px;
|
||||
background: linear-gradient(to bottom, #34B0FF, #0E8DE0);
|
||||
border-radius: 2px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 12px 0;
|
||||
padding: 15px 20px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.legend-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(52, 176, 255, 0.3);
|
||||
}
|
||||
|
||||
.color-indicator {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
margin-right: 15px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.color-indicator::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: conic-gradient(var(--color-start) 0%, var(--color-end) var(--percentage), rgba(255,255,255,0.1) var(--percentage), rgba(255,255,255,0.1) 100%);
|
||||
border-radius: 50%;
|
||||
animation: rotate 1.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(-90deg); }
|
||||
to { transform: rotate(0); }
|
||||
}
|
||||
|
||||
.legend-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 16px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.percentage {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 5px rgba(52, 176, 255, 0.5);
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
height: 6px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
transition: width 1.5s ease-out;
|
||||
}
|
||||
|
||||
.footer {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding: 15px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
/* 动画装饰元素 */
|
||||
.decoration {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
opacity: 0.3;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.dec-1 {
|
||||
width: 350px;
|
||||
height: 350px;
|
||||
top: -50px;
|
||||
left: -50px;
|
||||
background: radial-gradient(circle, #34B0FF, transparent 70%);
|
||||
animation: pulse 4s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.dec-2 {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
bottom: -70px;
|
||||
right: -20px;
|
||||
background: radial-gradient(circle, #F2D64B, transparent 70%);
|
||||
animation: pulse 3s infinite ease-in-out 0.5s;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 0.2; }
|
||||
50% { transform: scale(1.1); opacity: 0.3; }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.legend-container {
|
||||
padding-left: 0;
|
||||
margin-left: 0;
|
||||
border-left: none;
|
||||
border-top: 1px solid rgba(52, 176, 255, 0.2);
|
||||
padding-top: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="dashboard">
|
||||
<h1 class="title">撑杆状态监测分析</h1>
|
||||
|
||||
<div class="container">
|
||||
<div class="decoration dec-1"></div>
|
||||
<div class="decoration dec-2"></div>
|
||||
|
||||
<div class="chart-container">
|
||||
<div id="chart" style="width: 100%; height: 100%; z-index: 5;"></div>
|
||||
<div class="chart-center">
|
||||
<div class="total-label">故障总量</div>
|
||||
<div class="total-value">128</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="legend-container">
|
||||
<div class="legend-title">撑杆故障分类</div>
|
||||
|
||||
<div class="legend-item">
|
||||
<div class="color-indicator" style="--color-start: #34B0FF; --color-end: #0E8DE0; --percentage: 65%">
|
||||
<div class="inner-circle"></div>
|
||||
</div>
|
||||
<div class="legend-text">
|
||||
<div class="item-title">
|
||||
<div class="label">撑杆折断</div>
|
||||
<div class="percentage">65%</div>
|
||||
</div>
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar" style="width: 65%; background: linear-gradient(90deg, #34B0FF, #0E8DE0);"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="legend-item">
|
||||
<div class="color-indicator" style="--color-start: #F2D64B; --color-end: #E6B400; --percentage: 35%">
|
||||
<div class="inner-circle"></div>
|
||||
</div>
|
||||
<div class="legend-text">
|
||||
<div class="item-title">
|
||||
<div class="label">撑杆弯曲</div>
|
||||
<div class="percentage">35%</div>
|
||||
</div>
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar" style="width: 35%; background: linear-gradient(90deg, #F2D64B, #E6B400);"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
数据更新时间: 2023年11月15日 | 监测系统版本 v2.4.1
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 初始化图表
|
||||
const chartDom = document.getElementById('chart');
|
||||
const myChart = echarts.init(chartDom);
|
||||
|
||||
// 创建渐变效果
|
||||
const createGradient = (start, end) => {
|
||||
return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: start },
|
||||
{ offset: 1, color: end }
|
||||
]);
|
||||
};
|
||||
|
||||
// 图表配置
|
||||
const option = {
|
||||
backgroundColor: 'transparent',
|
||||
series: [{
|
||||
name: '撑杆状态',
|
||||
type: 'pie',
|
||||
radius: ['65%', '85%'],
|
||||
center: ['50%', '50%'],
|
||||
startAngle: 90,
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(12, 29, 69, 0.7)',
|
||||
borderWidth: 3
|
||||
},
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: 65,
|
||||
name: '撑杆折断',
|
||||
itemStyle: {
|
||||
color: createGradient('#34B0FF', '#0E8DE0')
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 35,
|
||||
name: '撑杆弯曲',
|
||||
itemStyle: {
|
||||
color: createGradient('#F2D64B', '#E6B400')
|
||||
}
|
||||
}
|
||||
],
|
||||
animationType: 'scale',
|
||||
animationEasing: 'elasticOut',
|
||||
animationDelay: function (idx) {
|
||||
return Math.random() * 200;
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
// 应用配置
|
||||
myChart.setOption(option);
|
||||
|
||||
// 响应式调整
|
||||
window.addEventListener('resize', function() {
|
||||
myChart.resize();
|
||||
});
|
||||
|
||||
// 进度条动画
|
||||
document.querySelectorAll('.progress-bar').forEach(bar => {
|
||||
const width = bar.style.width;
|
||||
bar.style.width = '0';
|
||||
setTimeout(() => {
|
||||
bar.style.width = width;
|
||||
}, 300);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|