11 Commits

Author SHA1 Message Date
ljx
79d77d16c6 提交 2025-11-14 18:59:59 +08:00
ljx
20afedd3d1 提交 2025-11-14 16:46:34 +08:00
ljx
13a1b32d6d Merge branch 'master' of http://xny.yj-3d.com:3000/taoge_xiaodi/maintenance_system into ljx 2025-11-14 16:39:25 +08:00
ljx
d5a7397744 提交 2025-11-14 16:38:51 +08:00
ljx
07e43a1611 提交 2025-11-14 16:34:39 +08:00
cc3cf9dae5 优化 2025-11-03 19:36:51 +08:00
56f6be1998 优化 2025-11-03 19:35:45 +08:00
d34212f82a 1 2025-11-03 19:35:23 +08:00
ljx
7b4cdd2b3c 提交 2025-11-03 18:56:57 +08:00
Teo
2c3ed5612a Merge branch 'master' of http://192.168.110.2:3000/taoge_xiaodi/maintenance_system 2025-11-03 17:34:50 +08:00
Teo
57784ab74d 1 2025-11-03 17:34:47 +08:00
13 changed files with 381 additions and 54 deletions

View File

@ -14,7 +14,7 @@ VITE_APP_MONITOR_ADMIN = '/admin/applications'
VITE_APP_SNAILJOB_ADMIN = '/snail-job'
# 生产环境
VITE_APP_BASE_API = '/prod-api'
VITE_APP_BASE_API = 'http://xny.yj-3d.com:18899'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip

View File

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

View File

@ -62,7 +62,7 @@
.el-date-picker {
/* --el-datepicker-text-color: var(--el-text-color-regular); */
--el-datepicker-off-text-color: var(--el-text-color-placeholder);
--el-datepicker-off-text-color: #fff !important;
--el-datepicker-header-text-color: #fff !important;
--el-datepicker-icon-color: #fff !important;
/* --el-datepicker-border-color: var(--el-disabled-border-color); */
@ -71,6 +71,7 @@
/* --el-datepicker-inrange-hover-bg-color: var(--el-border-color-extra-light); */
/* --el-datepicker-active-color: var(--el-color-primary); */
--el-datepicker-hover-text-color: #fff !important;
--el-datepicker-placeholder-text-color: #fff !important;
}
.el-date-picker__header-label {

BIN
src/assets/ueimg/bj.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

BIN
src/assets/ueimg/xuyi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

View File

@ -67,6 +67,11 @@ export const constantRoutes: RouteRecordRaw[] = [
component: () => import('@/views/largeScreen/index.vue'),
hidden: true
},
{
path: '/ueScreen',
component: () => import('@/views/ueScreen/index.vue'),
hidden: true
},
{
path: '',
component: Layout,

View File

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

View File

@ -5,28 +5,38 @@
<div class="header">
<img src="@/assets/large/right1.png" style="width: 17px; height: 18px" alt="" />
<span class="title">告警信息中心</span>
<!-- <el-badge :value="unhandledCount" class="unhandled-badge" type="danger"> {{ unhandledCount }}条未处理 </el-badge> -->
<span class="jgao">{{ alarmData.length }}条信息未处理</span>
</div>
<!-- 告警卡片列表可循环渲染这里演示单条 -->
<!-- 告警卡片列表 -->
<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">
<img src="@/assets/large/right2.png" style="width: 15px; height: 15px" alt="" />
<span class="card-title">{{ item.alarmMsg }}</span>
<span class="time">{{ formatDate(item.alarmBeginTime) }}</span>
</div>
<div class="card-content">
{{ item.advice }}
</div>
<div class="card-footer">
<el-tag type="danger" size="small">紧急</el-tag>
<el-tag type="danger" size="small">处理</el-tag>
</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 class="overview">
<div class="left_title">
<div style="display: flex; align-items: center">
@ -73,7 +83,7 @@
<span
class="progress-percent"
:class="{
green1: item.rate >= 99, // 可根据需求调整颜色规则
green1: item.rate >= 99,
orange1: item.rate < 99 && item.rate >= 90
}"
>{{ item.rate }}%</span
@ -84,7 +94,7 @@
class="progress-fg"
:style="{ width: item.rate + '%' }"
:class="{
green: item.rate >= 99, // 可根据需求调整颜色规则
green: item.rate >= 99,
orange: item.rate < 99 && item.rate >= 90
}"
></div>
@ -93,7 +103,6 @@
</div>
<div class="container_item_two">
<div>正常{{ item.normal }}</div>
<div>异常{{ item.abnormal }}</div>
</div>
</div>
@ -103,15 +112,15 @@
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ref, nextTick, onUnmounted } from 'vue';
import { getAlarmListOverview } from '@/api/large';
import { formatDate } from '@/utils/index';
const alarmData: any = ref({});
const alarmData = ref<any[]>([]);
const deviceStats = ref([
{
name: '光伏组件',
icon: '../../../assets/large/right5.png', // 示例图标
icon: '../../../assets/large/right5.png',
total: '25,680',
unit: '块',
rate: 99.2,
@ -155,13 +164,85 @@ const deviceStats = ref([
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 = () => {
getAlarmListOverview().then((res) => {
console.log(res);
alarmData.value = res.data;
handleReadMore(); // 数据渲染后执行文本溢出处理
});
};
// 初始化调用接口
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>
<style scoped lang="scss">
@ -171,10 +252,9 @@ getAlarm();
}
.alarm-container {
border: 1px solid #1e2b3d; /* 深色背景模拟,可替换成项目背景 */
border: 1px solid #1e2b3d;
border-radius: 8px;
color: #fff;
// box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
padding: 10px;
}
@ -203,8 +283,8 @@ getAlarm();
.alarm_list {
width: 100%;
padding: 5px 0;
height: 14vh;
overflow-y: auto; /* 垂直方向超出时显示滚动条 */
height: 17vh;
// overflow-y: auto; /* 垂直方向超出时显示滚动条 */
}
// 滚动条优化
.alarm_list::-webkit-scrollbar {
@ -220,18 +300,19 @@ getAlarm();
}
/* 告警卡片 */
.alarm-card {
height: 100%;
background: rgba(12, 30, 53, 0.3);
color: #fff;
border: none;
border-radius: 8px;
border: 1px solid #f56c6c;
margin-top: 10px;
padding: 5px;
margin-bottom: 8px; /* 增加卡片间距,避免重叠 */
}
.card-header {
display: flex;
align-items: center;
// justify-content: space-between;
margin-bottom: 12px;
margin-bottom: 8px;
}
.card-title {
font-size: 16px;
@ -244,16 +325,49 @@ getAlarm();
color: #909399;
margin-left: auto; /* 右对齐 */
}
/* 关键修改:卡片内容容器(承载文本和按钮) */
.card-content {
font-size: 13px;
color: #dcdfe6;
margin-bottom: 12px;
// margin-bottom: 12px;
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 {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 5px;
}
.left_title {
width: 100%;
@ -290,7 +404,7 @@ img {
}
.overview {
width: 100%;
height: 28vh;
height: 25vh;
padding: 10px;
border-radius: 10px;
border: 1px solid #1e2b3d;
@ -301,7 +415,7 @@ img {
width: 100%;
font-size: 14px;
line-height: 30px;
overflow-y: auto; /* 垂直方向超出时显示滚动条 */
overflow-y: auto;
}
// 滚动条优化
.overview_content::-webkit-scrollbar {
@ -325,15 +439,14 @@ img {
border-radius: 10px;
.stats-container {
width: 100%; /* 可根据实际场景调整宽度 */
width: 100%;
height: 87%;
padding: 10px;
border-radius: 8px;
box-sizing: border-box;
overflow-y: auto; /* 垂直方向超出时显示滚动条 */
overflow-y: auto;
.container_item {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
@ -359,13 +472,10 @@ img {
flex-direction: column;
justify-content: space-between;
padding-left: 10px;
// align-items: center;
}
}
/* 右侧区域:进度条 + 数据 */
.card-right {
display: flex;
margin-left: 10px;
justify-content: space-between;
align-items: center;
@ -381,7 +491,7 @@ img {
font-weight: bold;
}
.abnormal {
color: #ff9900; /* 异常数据颜色 */
color: #ff9900;
}
.progress-bg {
height: 6px;
@ -397,7 +507,6 @@ img {
width: 100px;
transition: width 0.3s;
}
/* 进度条颜色区分(可扩展更多规则) */
.green {
background-color: #28a745;
}
@ -429,14 +538,59 @@ img {
width: 5px;
height: 5px;
}
.stats-container::-webkit-scrollbar-thumb {
background-color: #0ff;
border-radius: 5px;
}
.stats-container::-webkit-scrollbar-track {
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>

View File

@ -0,0 +1,136 @@
<template>
<div class="header">
<div class="header-left">
<div>
<el-date-picker v-model="value1" type="date" placeholder="请选择时间" value-format="YYYY-MM-DD" class="datePicker" />
</div>
<div>
<el-select v-model="value" placeholder="请选择项目" style="width: 100%">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</div>
<div class="header-center">新能源场站智慧运维大数据平台</div>
<div class="header-right">
<div>
<div class="left-section">
<img src="@/assets/large/weather.png" alt="天气图标" />
<span>
<span>多云 9°/18°</span>
</span>
</div>
</div>
<div>{{ date.ymd }} {{ date.hms }}</div>
</div>
</div>
</template>
<script setup lang="ts">
import '@/assets/styles/element.scss';
const date: any = ref({
ymd: '',
hms: '',
week: ''
});
const value1 = ref('');
const value = ref('');
const options = ref([
{
value: 1,
label: '田东县乡村振兴光伏发电项目'
},
{
value: 2,
label: '田东县乡村振兴光伏发电项目(二期)'
},
{
value: 3,
label: '长顺县朝核农业光伏电站'
}
]);
const setTime = () => {
let date1 = new Date();
let year: any = date1.getFullYear();
let month: any = date1.getMonth() + 1;
let day: any = date1.getDate();
let hours: any = date1.getHours();
if (hours < 10) {
hours = '0' + hours;
}
let minutes: any = date1.getMinutes();
if (minutes < 10) {
minutes = '0' + minutes;
}
let seconds: any = date1.getSeconds();
if (seconds < 10) {
seconds = '0' + seconds;
}
date.value.ymd = year + '-' + month + '-' + day;
date.value.hms = hours + ':' + minutes + ':' + seconds;
date.value.week = date1.getDay();
};
// 添加定时器,每秒更新一次时间
const timer = setInterval(setTime, 1000);
// 组件卸载时清除定时器
onUnmounted(() => {
clearInterval(timer);
});
</script>
<style scoped lang="scss">
$vm_base: 1920;
$vh_base: 1080;
// 计算vw
@function vw($px) {
@return calc(($px / $vm_base) * 100vw);
}
// 计算vh
@function vh($px) {
@return calc(($px / $vh_base) * 100vh);
}
.header {
width: 100%;
height: 8vh;
display: grid;
grid-template-columns: 1fr 2fr 1fr;
}
.header-left {
padding-left: vw(40);
display: flex;
align-items: center;
justify-content: space-between;
& > div {
width: vw(240);
margin-right: vw(20);
}
}
.header-center {
display: flex;
align-items: center;
justify-content: center;
font-family: Rang_men_zheng_title;
font-size: vw(32);
letter-spacing: vw(8);
}
.header-right {
padding-right: vw(20);
display: flex;
align-items: center;
justify-content: flex-end;
font-size: vw(15);
.left-section {
display: flex;
align-items: center;
padding-right: vw(20);
// margin-right: auto; /* 让右侧元素(管理系统)居右 */
}
.left-section img {
width: 32px;
height: 32px;
margin-right: 8px; /* 图标与文字间距 */
}
}
</style>

View File

@ -0,0 +1,11 @@
$vm_base: 1920;
$vh_base: 1080;
// 计算vw
@function vw($px) {
@return calc(($px / $vm_base) * 100vw);
}
// 计算vh
@function vh($px) {
@return calc(($px / $vh_base) * 100vh);
}

View File

@ -0,0 +1,29 @@
<template>
<div class="ueScreen">
<Header />
</div>
</template>
<script setup lang="ts">
import Header from './components/header.vue';
</script>
<style scoped lang="scss">
$vm_base: 1920;
$vh_base: 1080;
// 计算vw
@function vw($px) {
@return calc(($px / $vm_base) * 100vw);
}
// 计算vh
@function vh($px) {
@return calc(($px / $vh_base) * 100vh);
}
.ueScreen {
width: 100vw;
height: 100vh;
background-color: rgba(4, 7, 17, 0.8);
color: #fff;
}
</style>

View File

@ -201,15 +201,6 @@ import { ref, computed, onMounted, onUnmounted, nextTick, reactive, toRefs } fro
import router from '@/router';
import * as echarts from 'echarts'; // 导入ECharts
import renwuImage from '@/assets/images/renwu.png';
import { getCurrentInstance } from 'vue';
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue';
import router from '@/router';
import * as echarts from 'echarts'; // 导入ECharts
import renwuImage from '@/assets/images/renwu.png';
import { getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 搜索条件
const searchKeyword = ref('');