This commit is contained in:
ljx
2025-11-03 18:56:57 +08:00
parent fd4e05a802
commit 7b4cdd2b3c
3 changed files with 197 additions and 43 deletions

View File

@ -5,10 +5,6 @@
"ComputedRef": true, "ComputedRef": true,
"DirectiveBinding": true, "DirectiveBinding": true,
"EffectScope": true, "EffectScope": true,
"ElLoading": true,
"ElMessage": true,
"ElMessageBox": true,
"ElNotification": true,
"ExtractDefaultPropTypes": true, "ExtractDefaultPropTypes": true,
"ExtractPropTypes": true, "ExtractPropTypes": true,
"ExtractPublicPropTypes": true, "ExtractPublicPropTypes": true,
@ -318,6 +314,10 @@
"watchThrottled": true, "watchThrottled": true,
"watchTriggerable": true, "watchTriggerable": true,
"watchWithFilter": true, "watchWithFilter": true,
"whenever": true "whenever": true,
"ElMessage": true,
"ElLoading": true,
"ElMessageBox": true,
"ElNotification": true
} }
} }

View File

@ -56,7 +56,7 @@ export const getOption = (xData: any, yData: any) => {
{ {
// show: true, // show: true,
start: 0, start: 0,
end: 30, end: 100,
bottom: 2, // 下滑块距离x轴底部的距离 bottom: 2, // 下滑块距离x轴底部的距离
height: 23 height: 23
}, },
@ -177,7 +177,7 @@ export const getOption2 = (data: any) => {
// show: true, // show: true,
start: 0, start: 0,
end: 30, end: 100,
bottom: 2, // 下滑块距离x轴底部的距离 bottom: 2, // 下滑块距离x轴底部的距离
height: 23 height: 23
}, },
@ -322,7 +322,7 @@ export const getLineOption = (lineData: any) => {
{ {
// show: true, // show: true,
start: 0, start: 0,
end: 30, end: 100,
bottom: 2, // 下滑块距离x轴底部的距离 bottom: 2, // 下滑块距离x轴底部的距离
height: 23 height: 23
}, },
@ -674,7 +674,7 @@ export const getBarOptions = (data: any) => {
{ {
// show: true, // show: true,
start: 0, start: 0,
end: 30, end: 100,
bottom: 2, // 下滑块距离x轴底部的距离 bottom: 2, // 下滑块距离x轴底部的距离
height: 23 height: 23
}, },

View File

@ -5,28 +5,38 @@
<div class="header"> <div class="header">
<img src="@/assets/large/right1.png" style="width: 17px; height: 18px" alt="" /> <img src="@/assets/large/right1.png" style="width: 17px; height: 18px" alt="" />
<span class="title">告警信息中心</span> <span class="title">告警信息中心</span>
<!-- <el-badge :value="unhandledCount" class="unhandled-badge" type="danger"> {{ unhandledCount }}条未处理 </el-badge> -->
<span class="jgao">{{ alarmData.length }}条信息未处理</span> <span class="jgao">{{ alarmData.length }}条信息未处理</span>
</div> </div>
<!-- 告警卡片列表可循环渲染这里演示单条 --> <!-- 告警卡片列表 -->
<div class="alarm_list"> <div class="alarm_list">
<el-card class="alarm-card" shadow="hover" v-for="(item, index) in alarmData" :key="index"> <div class="alarm-card" v-for="(item, index) in alarmData" :key="index">
<div class="card-header"> <div class="card-header">
<img src="@/assets/large/right2.png" style="width: 15px; height: 15px" alt="" /> <img src="@/assets/large/right2.png" style="width: 15px; height: 15px" alt="" />
<span class="card-title">{{ item.alarmMsg }}</span> <span class="card-title">{{ item.alarmMsg }}</span>
<span class="time">{{ formatDate(item.alarmBeginTime) }}</span> <span class="time">{{ formatDate(item.alarmBeginTime) }}</span>
</div> </div>
<div class="card-content">
{{ item.advice }}
</div>
<div class="card-footer"> <div class="card-footer">
<el-tag type="danger" size="small">紧急</el-tag> <el-tag type="danger" size="small">紧急</el-tag>
<el-tag type="danger" size="small">处理</el-tag> <el-tag type="danger" size="small">处理</el-tag>
</div> </div>
</el-card> <div class="card-content">
<!-- 文本容器控制3行溢出 -->
<div class="text-container">
{{ item.advice }}
</div>
<!-- 查看更多按钮右侧显示 -->
<div class="read-more" @click="open(item)">查看更多</div>
</div>
</div>
</div>
</div>
<div class="detailBox" :class="{ 'show': newDetail }">
<div class="boxContent" v-html="newDetail?.advice"></div>
<!-- 假设 item content 字段根据实际调整 -->
<div class="close" @click="newDetail = null">
<CircleClose style="width: 1.2em; height: 1.2em" />
</div> </div>
</div> </div>
<div class="overview"> <div class="overview">
<div class="left_title"> <div class="left_title">
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
@ -73,7 +83,7 @@
<span <span
class="progress-percent" class="progress-percent"
:class="{ :class="{
green1: item.rate >= 99, // 可根据需求调整颜色规则 green1: item.rate >= 99,
orange1: item.rate < 99 && item.rate >= 90 orange1: item.rate < 99 && item.rate >= 90
}" }"
>{{ item.rate }}%</span >{{ item.rate }}%</span
@ -84,7 +94,7 @@
class="progress-fg" class="progress-fg"
:style="{ width: item.rate + '%' }" :style="{ width: item.rate + '%' }"
:class="{ :class="{
green: item.rate >= 99, // 可根据需求调整颜色规则 green: item.rate >= 99,
orange: item.rate < 99 && item.rate >= 90 orange: item.rate < 99 && item.rate >= 90
}" }"
></div> ></div>
@ -93,7 +103,6 @@
</div> </div>
<div class="container_item_two"> <div class="container_item_two">
<div>正常{{ item.normal }}</div> <div>正常{{ item.normal }}</div>
<div>异常{{ item.abnormal }}</div> <div>异常{{ item.abnormal }}</div>
</div> </div>
</div> </div>
@ -103,15 +112,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref, nextTick, onUnmounted } from 'vue';
import { getAlarmListOverview } from '@/api/large'; import { getAlarmListOverview } from '@/api/large';
import { formatDate } from '@/utils/index'; import { formatDate } from '@/utils/index';
const alarmData: any = ref({}); const alarmData = ref<any[]>([]);
const deviceStats = ref([ const deviceStats = ref([
{ {
name: '光伏组件', name: '光伏组件',
icon: '../../../assets/large/right5.png', // 示例图标 icon: '../../../assets/large/right5.png',
total: '25,680', total: '25,680',
unit: '块', unit: '块',
rate: 99.2, rate: 99.2,
@ -155,13 +164,85 @@ const deviceStats = ref([
abnormal: 12 abnormal: 12
} }
]); ]);
// 存储所有绑定的事件处理函数,用于卸载时解绑
const eventHandlers: Array<() => void> = [];
// 处理“查看更多/收起”逻辑
const handleReadMore = async () => {
await nextTick();
const textContainers: any = document.querySelectorAll('.alarm-card .text-container');
const readMoreBtns: any = document.querySelectorAll('.alarm-card .read-more');
// 清空之前的事件绑定(避免重复绑定)
eventHandlers.forEach((handler) => handler());
eventHandlers.length = 0;
textContainers.forEach((textContainer, index) => {
const btn = readMoreBtns[index] as HTMLDivElement;
if (!btn) return;
// 关键修改判断文本是否超过3行scrollHeight > clientHeight 表示溢出)
const isOverflow = textContainer.scrollHeight > textContainer.clientHeight;
btn.style.display = isOverflow ? 'inline-block' : 'none';
// 定义点击事件处理函数
// const toggleExpand = () => {
// const isExpanded = textContainer.style.webkitLineClamp === 'none';
// if (isExpanded) {
// // 收起恢复3行限制
// textContainer.style.webkitLineClamp = '3';
// btn.textContent = '查看更多';
// } else {
// // 展开:取消行数限制
// textContainer.style.webkitLineClamp = 'none';
// btn.textContent = '收起';
// }
// };
// 绑定点击事件并存储,用于后续解绑
// btn.addEventListener('click', toggleExpand);
// eventHandlers.push(() => {
// btn.removeEventListener('click', toggleExpand);
// });
});
};
// 接口请求:获取告警数据后执行处理逻辑
const getAlarm = () => { const getAlarm = () => {
getAlarmListOverview().then((res) => { getAlarmListOverview().then((res) => {
console.log(res); console.log(res);
alarmData.value = res.data; alarmData.value = res.data;
handleReadMore(); // 数据渲染后执行文本溢出处理
}); });
}; };
// 初始化调用接口
getAlarm(); getAlarm();
// 定义 item 类型(替换 any提升类型安全性
interface ItemType {
id: string | number;
advice: string; // 存储富文本内容的字段,根据实际数据结构调整
// 其他字段...
}
const newDetail = ref(null);
// 假设 item 是当前列表项(实际可能来自 v-for 循环)
const item = ref<ItemType>({
id: 1,
advice: '<p>这是要显示的详情内容...</p>' // 示例富文本内容
});
// 切换展开/隐藏逻辑
const open = (targetItem: ItemType) => {
// 如果当前展开的就是目标项 → 隐藏;否则 → 展开目标项
newDetail.value = newDetail.value === targetItem ? null : targetItem;
console.log(newDetail.value);
};
// 组件卸载时解绑所有事件(避免内存泄漏)
onUnmounted(() => {
eventHandlers.forEach((handler) => handler());
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -171,10 +252,9 @@ getAlarm();
} }
.alarm-container { .alarm-container {
border: 1px solid #1e2b3d; /* 深色背景模拟,可替换成项目背景 */ border: 1px solid #1e2b3d;
border-radius: 8px; border-radius: 8px;
color: #fff; color: #fff;
// box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
padding: 10px; padding: 10px;
} }
@ -203,8 +283,8 @@ getAlarm();
.alarm_list { .alarm_list {
width: 100%; width: 100%;
padding: 5px 0; padding: 5px 0;
height: 14vh; height: 17vh;
overflow-y: auto; /* 垂直方向超出时显示滚动条 */ // overflow-y: auto; /* 垂直方向超出时显示滚动条 */
} }
// 滚动条优化 // 滚动条优化
.alarm_list::-webkit-scrollbar { .alarm_list::-webkit-scrollbar {
@ -220,18 +300,19 @@ getAlarm();
} }
/* 告警卡片 */ /* 告警卡片 */
.alarm-card { .alarm-card {
height: 100%;
background: rgba(12, 30, 53, 0.3); background: rgba(12, 30, 53, 0.3);
color: #fff; color: #fff;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
border: 1px solid #f56c6c; border: 1px solid #f56c6c;
margin-top: 10px; padding: 5px;
margin-bottom: 8px; /* 增加卡片间距,避免重叠 */
} }
.card-header { .card-header {
display: flex; display: flex;
align-items: center; align-items: center;
// justify-content: space-between; margin-bottom: 8px;
margin-bottom: 12px;
} }
.card-title { .card-title {
font-size: 16px; font-size: 16px;
@ -244,16 +325,49 @@ getAlarm();
color: #909399; color: #909399;
margin-left: auto; /* 右对齐 */ margin-left: auto; /* 右对齐 */
} }
/* 关键修改:卡片内容容器(承载文本和按钮) */
.card-content { .card-content {
font-size: 13px; font-size: 13px;
color: #dcdfe6; color: #dcdfe6;
margin-bottom: 12px; // margin-bottom: 12px;
line-height: 1.6; line-height: 1.6;
position: relative;
width: 100%;
line-height: 1.5;
// padding-right: 70px; /* 给右侧按钮预留空间,避免超出卡片 */
min-height: 4.5em; /* 3行文本高度1.5line-height * 3确保按钮位置稳定 */
}
/* 关键修改文本容器限制3行 */
.text-container {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3; /* 限制显示3行 */
overflow: hidden;
word-break: break-all;
padding-bottom: 2px; /* 避免文本底部被按钮遮挡 */
}
/* 关键修改:查看更多按钮(右侧显示+深色适配) */
.read-more {
display: none;
position: absolute;
right: 0;
bottom: -20px;
/* 渐变遮罩改为卡片背景色,避免白色块 */
background: linear-gradient(to right, transparent, rgba(12, 30, 53, 0.3) 60%);
padding-left: 15px; /* 增加遮罩宽度,避免文字与按钮重叠 */
color: #1677ff;
cursor: pointer;
z-index: 1; /* 确保按钮在文本上方 */
white-space: nowrap; /* 按钮文字不换行 */
font-size: 13px; /* 与文本字号一致 */
} }
.card-footer { .card-footer {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding-bottom: 5px;
} }
.left_title { .left_title {
width: 100%; width: 100%;
@ -290,7 +404,7 @@ img {
} }
.overview { .overview {
width: 100%; width: 100%;
height: 28vh; height: 25vh;
padding: 10px; padding: 10px;
border-radius: 10px; border-radius: 10px;
border: 1px solid #1e2b3d; border: 1px solid #1e2b3d;
@ -301,7 +415,7 @@ img {
width: 100%; width: 100%;
font-size: 14px; font-size: 14px;
line-height: 30px; line-height: 30px;
overflow-y: auto; /* 垂直方向超出时显示滚动条 */ overflow-y: auto;
} }
// 滚动条优化 // 滚动条优化
.overview_content::-webkit-scrollbar { .overview_content::-webkit-scrollbar {
@ -325,15 +439,14 @@ img {
border-radius: 10px; border-radius: 10px;
.stats-container { .stats-container {
width: 100%; /* 可根据实际场景调整宽度 */ width: 100%;
height: 87%; height: 87%;
padding: 10px; padding: 10px;
border-radius: 8px; border-radius: 8px;
box-sizing: border-box; box-sizing: border-box;
overflow-y: auto; /* 垂直方向超出时显示滚动条 */ overflow-y: auto;
.container_item { .container_item {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
@ -359,13 +472,10 @@ img {
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
padding-left: 10px; padding-left: 10px;
// align-items: center;
} }
} }
/* 右侧区域:进度条 + 数据 */
.card-right { .card-right {
display: flex; display: flex;
margin-left: 10px; margin-left: 10px;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
@ -381,7 +491,7 @@ img {
font-weight: bold; font-weight: bold;
} }
.abnormal { .abnormal {
color: #ff9900; /* 异常数据颜色 */ color: #ff9900;
} }
.progress-bg { .progress-bg {
height: 6px; height: 6px;
@ -397,7 +507,6 @@ img {
width: 100px; width: 100px;
transition: width 0.3s; transition: width 0.3s;
} }
/* 进度条颜色区分(可扩展更多规则) */
.green { .green {
background-color: #28a745; background-color: #28a745;
} }
@ -429,14 +538,59 @@ img {
width: 5px; width: 5px;
height: 5px; height: 5px;
} }
.stats-container::-webkit-scrollbar-thumb { .stats-container::-webkit-scrollbar-thumb {
background-color: #0ff; background-color: #0ff;
border-radius: 5px; border-radius: 5px;
} }
.stats-container::-webkit-scrollbar-track { .stats-container::-webkit-scrollbar-track {
background-color: rgba(0, 255, 255, 0.2); background-color: rgba(0, 255, 255, 0.2);
} }
} }
.detailBox {
display: flex;
position: absolute;
right: 0;
top: 10vh;
width: 300px;
padding: 20px 15px;
box-sizing: border-box;
/* background: rgba(138, 157, 161, 0.5); */
background: #040c1c;
// border: 2px dashed rgba(29, 214, 255, 0.3);
border-right: none;
border-radius: 4px;
transition: all 0.3s ease;
opacity: 0;
z-index: -1;
& > .boxContent {
flex: 1;
height: 300px;
max-height: 500px;
overflow-y: auto;
&::-webkit-scrollbar-track {
background: rgba(204, 204, 204, 0.1);
border-radius: 10px;
}
&::-webkit-scrollbar-thumb {
background: rgba(29, 214, 255, 0.78);
border-radius: 10px;
}
}
&.show {
right: 25vw;
opacity: 1;
z-index: 1;
}
.close {
position: absolute;
top: 3px;
right: 7px;
cursor: pointer;
}
}
</style> </style>