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
|
// src/assets/fonts.scss
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'DingTalk JinBuTi'; // 自定义字体名称
|
font-family: "DingTalk JinBuTi"; // 自定义字体名称
|
||||||
src: url('@/assets/fonts/DingTalk JinBuTi.ttf') format('truetype');
|
src: url("@/assets/fonts/DingTalk JinBuTi.ttf") format("truetype");
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: 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>
|