Commit 128bf1d7 authored by wanghao's avatar wanghao

1 老化柜 和 老化层 界面实现中;

parent 0f43f216
......@@ -33,6 +33,7 @@
],
"repository": {},
"dependencies": {
"@jiaminghi/data-view": "^2.10.0",
"@riophae/vue-treeselect": "0.4.0",
"axios": "0.21.0",
"clipboard": "2.0.6",
......
......@@ -21,6 +21,10 @@ import Pagination from "@/components/Pagination";
// 自定义表格工具扩展
import RightToolbar from "@/components/RightToolbar"
// 引入 datav
import DataV from '@jiaminghi/data-view'
Vue.use(DataV)
// 全局方法挂载
Vue.prototype.getDicts = getDicts
Vue.prototype.getConfigKey = getConfigKey
......
......@@ -38,6 +38,12 @@ export const constantRoutes = [
}
]
},
{
path: '/bigScreen',
name: 'Screen',
component: () => import('@/views/screen/index.vue'),
meta: { title: '数据看板界面' }
},
{
path: '/login',
component: (resolve) => require(['@/views/login'], resolve),
......
<template>
<div class="app-container">
<!-- 右上角的图例提示 -->
<div class="legend">
<div class="legend-item">
<span class="legend-color green"></span> 运行中
</div>
<div class="legend-item">
<span class="legend-color red"></span> 故障
</div>
<div class="legend-item">
<span class="legend-color gray"></span> 空闲
</div>
</div>
<div class="aging">
老化柜看板
</div>
<!-- <el-row :gutter="20">-->
<!-- <template v-for="(cabinet, index) in agingCabinetList" :key="cabinet.deviceCode">-->
<!-- <el-col :span="4" v-if="index % 6 === 0" style="margin-bottom: 20px;">-->
<!-- <el-row :gutter="10">-->
<!-- <el-col :span="8" v-for="i in 6" :key="(index + i - 1)">-->
<!-- <el-card-->
<!-- :body-style="{ padding: '10px' }"-->
<!-- :class="agingCabinetList[index + i - 1].deviceStatus"-->
<!-- >-->
<!-- {{ agingCabinetList[index + i - 1].deviceCode }}-->
<!-- </el-card>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- </el-col>-->
<!-- </template>-->
<!-- </el-row>-->
<div style="padding: 20px;">
<!-- 渲染每一行 -->
<el-row :gutter="20" v-for="(row, rowIndex) in cabinetRows" :key="rowIndex">
<!-- 每行渲染6个卡片 -->
<el-col
:span="4"
v-for="item in row"
:key="item.id"
style="margin-bottom: 20px; text-align: center;"
>
<!-- 使用 el-tooltip 来提供 hover 提示 -->
<el-tooltip
:content="getTooltipContent(item)"
placement="top"
>
<el-card
:class="statusMap[item.status]"
style="
width: 150px;
height: 150px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: bold;"
@click.native="handleCardClick(item)"
>
{{ item.id + "号柜" }}
</el-card>
</el-tooltip>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import {getAgingCabinetAndPowerCheck} from "@/api/testScheduledTasks/testTasks";
export default {
name: "TestScheduledTasks",
data() {
return {
textarea: null
// 示例数据格式,实际从后端获取
cabinets: [
{ id: 1, status: 'available' },
{ id: 2, status: 'occupied' },
{ id: 3, status: 'default' },
{ id: 4, status: 'default' },
{ id: 5, status: 'default' },
{ id: 6, status: 'default' },
{ id: 7, status: 'default' },
{ id: 8, status: 'default' },
{ id: 9, status: 'default' },
{ id: 10, status: 'default' },
{ id: 11, status: 'default' },
{ id: 12, status: 'default' },
{ id: 13, status: 'default' },
{ id: 14, status: 'default' },
{ id: 15, status: 'default' },
{ id: 16, status: 'default' },
{ id: 17, status: 'default' },
{ id: 18, status: 'default' },
{ id: 19, status: 'default' },
{ id: 20, status: 'default' },
{ id: 21, status: 'default' },
{ id: 22, status: 'default' },
{ id: 23, status: 'default' },
{ id: 24, status: 'default' },
{ id: 25, status: 'default' },
{ id: 26, status: 'default' },
{ id: 27, status: 'default' },
{ id: 28, status: 'default' },
{ id: 29, status: 'default' },
{ id: 30, status: 'default' },
{ id: 31, status: 'default' },
{ id: 32, status: 'default' },
{ id: 33, status: 'default' },
{ id: 34, status: 'default' },
{ id: 35, status: 'default' },
{ id: 36, status: 'default' },
// ...共36个
],
// 状态对应的颜色类名
statusMap: {
default: 'default',
available: 'available',
occupied: 'occupied'
},
agingCabinetList: null
};
},
created() {
},
methods: {}
computed: {
cabinetRows() {
const rows = [];
for (let i = 0; i < 36; i += 6) {
rows.push(this.cabinets.slice(i, i + 6));
}
return rows;
}
},
mounted() {
//this.testAgingCabinetAndPowerCheck();
},
methods: {
handleCardClick(item) {
this.msgSuccess("你想看层信息是吧");
},
getTooltipContent(item) {
switch (item.status) {
case 'occupied':
return item.faultReason || '无故障详情';
case 'available':
return '运行中';
case 'default':
default:
return '空闲';
}
},
testAgingCabinetAndPowerCheck() {
getAgingCabinetAndPowerCheck().then(response => {
this.agingCabinetList = response;
}
);
}
}
};
</script>
<style>
.aging{
text-align: center;
font-size: 20px;
font-weight: bold;
font-size: 40px;
}
.default {
background-color: #f0f0f0;
}
.available {
background-color: #67c23a;
color: white;
}
.occupied {
background-color: #f56c6c;
color: white;
}
/* 可选:悬停效果 */
.el-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
transition: all 0.2s ease;
}
/* 添加更多状态样式 */
.cabinet-container {
position: relative;
padding: 40px;
}
.legend {
position: fixed; /* 关键:固定定位 */
top: 100px;
right: 20px;
display: flex;
gap: 15px;
background-color: #fff;
padding: 10px 15px;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 9999; /* 确保在最上层 */
font-size: 14px;
color: #333;
}
.legend-item {
display: flex;
align-items: center;
gap: 5px;
}
.legend-color {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 2px;
}
.legend-color.red {
background-color: #f56c6c;
}
.legend-color.green {
background-color: #67c23a;
}
.legend-color.gray {
background-color: #f0f0f0;
}
</style>
......@@ -127,7 +127,8 @@ export default {
Cookies.remove('rememberMe');
}
this.$store.dispatch("Login", this.loginForm).then(() => {
this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
// this.$router.push({ path: this.redirect || "/bigScreen" }).catch(()=>{});
this.$router.push({ path:"/bigScreen" }).catch(()=>{});
}).catch(() => {
this.loading = false;
this.getCode();
......
<template>
<div class="app-container">
<!-- 右上角的图例提示 -->
<div class="legend">
<div class="legend-item">
<span class="legend-color green"></span> 运行中
</div>
<div class="legend-item">
<span class="legend-color red"></span> 故障
</div>
<div class="legend-item">
<span class="legend-color gray"></span> 空闲
</div>
</div>
<div style="padding: 20px;">
<!-- 渲染每一行 -->
<el-row :gutter="20" v-for="(row, rowIndex) in cabinetRows" :key="rowIndex">
<!-- 每行渲染6个卡片 -->
<el-col
:span="4"
v-for="item in row"
:key="item.id"
style="margin-bottom: 20px; text-align: center;"
>
<!-- 使用 el-tooltip 来提供 hover 提示 -->
<el-tooltip
:content="getTooltipContent(item)"
placement="top"
>
<el-card
:class="statusMap[item.status]"
style="
width: 150px;
height: 150px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: bold;"
@click.native="handleCardClick(item)"
>
{{ item.id + "号柜" }}
</el-card>
</el-tooltip>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import {getAgingCabinetAndPowerCheck} from "@/api/testScheduledTasks/testTasks";
export default {
name: "AgingCabinetBoard",
data() {
return {
// 示例数据格式,实际从后端获取
cabinets: [
{ id: 1, status: 'available' },
{ id: 2, status: 'occupied' },
{ id: 3, status: 'default' },
{ id: 4, status: 'default' },
{ id: 5, status: 'default' },
{ id: 6, status: 'default' },
{ id: 7, status: 'default' },
{ id: 8, status: 'default' },
{ id: 9, status: 'default' },
{ id: 10, status: 'default' },
{ id: 11, status: 'default' },
{ id: 12, status: 'default' },
{ id: 13, status: 'default' },
{ id: 14, status: 'default' },
{ id: 15, status: 'default' },
{ id: 16, status: 'default' },
{ id: 17, status: 'default' },
{ id: 18, status: 'default' },
{ id: 19, status: 'default' },
{ id: 20, status: 'default' },
{ id: 21, status: 'default' },
{ id: 22, status: 'default' },
{ id: 23, status: 'default' },
{ id: 24, status: 'default' },
{ id: 25, status: 'default' },
{ id: 26, status: 'default' },
{ id: 27, status: 'default' },
{ id: 28, status: 'default' },
{ id: 29, status: 'default' },
{ id: 30, status: 'default' },
{ id: 31, status: 'default' },
{ id: 32, status: 'default' },
{ id: 33, status: 'default' },
{ id: 34, status: 'default' },
{ id: 35, status: 'default' },
{ id: 36, status: 'default' },
// ...共36个
],
// 状态对应的颜色类名
statusMap: {
default: 'default',
available: 'available',
occupied: 'occupied'
},
agingCabinetList: null
};
},
created() {
},
computed: {
cabinetRows() {
const rows = [];
for (let i = 0; i < 36; i += 6) {
rows.push(this.cabinets.slice(i, i + 6));
}
return rows;
}
},
mounted() {
//this.testAgingCabinetAndPowerCheck();
},
methods: {
handleCardClick(item) {
this.msgSuccess("你想看层信息是吧");
},
getTooltipContent(item) {
switch (item.status) {
case 'occupied':
return item.faultReason || '无故障详情';
case 'available':
return '运行中';
case 'default':
default:
return '空闲';
}
},
testAgingCabinetAndPowerCheck() {
getAgingCabinetAndPowerCheck().then(response => {
this.agingCabinetList = response;
}
);
}
}
};
</script>
<style>
.aging{
text-align: center;
font-size: 40px;
}
.default {
background-color: #f0f0f0;
}
.available {
background-color: #67c23a;
color: white;
}
.occupied {
background-color: #f56c6c;
color: white;
}
/* 可选:悬停效果 */
.el-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
transition: all 0.2s ease;
}
/* 添加更多状态样式 */
.legend {
position: fixed; /* 关键:固定定位 */
top: 110px;
right: 60px;
display: flex;
gap: 15px;
background-color: #fff;
padding: 10px 15px;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 9999; /* 确保在最上层 */
font-size: 14px;
color: #333;
}
.legend-item {
display: flex;
align-items: center;
gap: 5px;
}
.legend-color {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 2px;
}
.legend-color.red {
background-color: #f56c6c;
}
.legend-color.green {
background-color: #67c23a;
}
.legend-color.gray {
background-color: #f0f0f0;
}
</style>
<template>
<div class="screen-container">
<!-- 标题 -->
<div class="title" ref="title">2号柜</div>
<!-- 柜体容器 -->
<div class="cabinet-body">
<div class="cabinet" ref="cabinetContainer">
<div
v-for="(layer, index) in layers"
:key="layer.name"
class="layer"
:style="getLayerStyle(index)"
:class="['layer-depth', `layer-${index}`]"
>
{{ layer.name }}
</div>
</div>
<!-- 新增:竖直连线容器 -->
<div class="vertical-line-container" ref="verticalLineContainer">
<div
v-for="(_, index) in layers"
:key="'vline'+index"
class="vertical-connector-line"
:ref="el => setVerticalLineRef(el, index)"
></div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "AgingLayer",
data() {
return {
layers: Array.from({ length: 10 }, (_, i) => ({
name: `第${i + 1}层`,
})),
verticalLineRefs: [],
};
},
mounted() {
this.updateVerticalLines();
window.addEventListener('resize', this.updateVerticalLines);
},
beforeDestroy() {
window.removeEventListener('resize', this.updateVerticalLines);
},
methods: {
getLayerStyle(index) {
const verticalSpacing = 70;
return {
bottom: `${index * verticalSpacing}px`,
width: '180px',
height: '60px',
zIndex: 10 - index,
};
},
setVerticalLineRef(el, index) {
this.verticalLineRefs[index] = el;
},
updateVerticalLines() {
const titleEl = this.$refs.title;
const cabinetEl = this.$refs.cabinetContainer;
if (!titleEl || !cabinetEl) return;
const titleRect = titleEl.getBoundingClientRect();
// 标题中心右侧点
const startX = titleRect.right;
const startY = titleRect.top + titleRect.height / 2;
for (let i = 0; i < this.layers.length; i++) {
const layerEl = cabinetEl.children[i];
if (!layerEl) continue;
const layerRect = layerEl.getBoundingClientRect();
// 层左侧中点
const endX = layerRect.left;
const endY = layerRect.top + layerRect.height / 2;
const deltaX = endX - startX;
const deltaY = endY - startY;
const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
const angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
const line = this.verticalLineRefs[i];
if (!line) continue;
line.style.width = `${length}px`;
line.style.height = '2px';
line.style.backgroundColor = 'rgba(100, 200, 255, 0.9)';
line.style.position = 'absolute';
line.style.left = `${startX}px`;
line.style.top = `${startY}px`;
line.style.transformOrigin = 'left center';
line.style.transform = `rotate(${angle}deg)`;
}
}
}
};
</script>
<style scoped>
.screen-container {
background-color: #030b2a;
color: white;
font-family: Arial, sans-serif;
position: relative;
height: 100vh;
display: flex;
align-items: center;
padding-left: 100px;
overflow: hidden;
transform-origin: center center;
}
/* 标题样式:增加边框+背景 */
.title {
font-size: 24px;
font-weight: bold;
margin-right: 50px;
z-index: 20;
position: relative;
padding: 10px 20px;
border-radius: 8px;
background: linear-gradient(to bottom, rgba(255,255,255,0.1), rgba(0,0,0,0.2));
border: 1px solid rgba(100, 180, 255, 0.6);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.6),
inset 0 0 8px rgba(100, 200, 255, 0.3);
}
/* 新增:柜体容器 */
.cabinet-body {
position: relative;
height: 100%;
display: flex;
align-items: flex-end;
}
.cabinet {
position: relative;
width: 180px;
height: 80vh;
margin-left: 20px;
}
.layer {
position: absolute;
left: 0;
background: linear-gradient(to bottom, rgba(255,255,255,0.15), rgba(0,0,0,0.2));
border: 1px solid rgba(100, 180, 255, 0.6);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.6),
inset 0 0 8px rgba(100, 200, 255, 0.3);
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
border-radius: 8px;
transition: all 0.3s ease;
}
.layer-depth {
border-width: 1px 3px 3px 1px;
border-color:
rgba(100, 180, 255, 0.4)
rgba(100, 180, 255, 0.8)
rgba(100, 180, 255, 0.8)
rgba(100, 180, 255, 0.4);
}
.layer-9 {
box-shadow:
0 8px 25px rgba(0, 150, 255, 0.5),
inset 0 0 15px rgba(100, 220, 255, 0.4);
}
/* 竖直连线容器 */
.vertical-line-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
.vertical-connector-line {
position: absolute;
content: '';
height: 2px;
}
</style>
<template>
<div class="app-container">
<div>我是实时数据看板</div>
</div>
</template>
<script>
export default {
name: "RealTimeData",
data() {
return {
};
},
created() {
},
computed: {
},
mounted() {
},
methods: {
}
};
</script>
<style>
</style>
<template>
<dv-full-screen-container class="screen-container">
<!-- 管理系统入口按钮 -->
<div @click="goToAdmin" class="management-entry" style="width:100px;height:30px;">进入管理系统</div>
<!-- 顶部导航栏 -->
<!-- 顶部导航栏 -->
<div class="header">
<div class="system-title-wrapper">
<div class="system-title">监控数字化视频管控平台</div>
<div class="title-line"></div> <!-- 标题下的横线 -->
</div>
<!-- 右侧菜单组 -->
<div class="menu-group">
<div
v-for="(item, index) in menuItems"
:key="index"
class="diamond-box"
@click="selectMenu(index)"
:class="{ active: selectedMenu === index }"
>
<span class="icon">{{ item.icon }}</span>
<span class="text">{{ item.text }}</span>
<!-- 选中时显示的底部线条 -->
<div v-if="selectedMenu === index" class="active-line"></div>
</div>
</div>
<!-- 外部装饰线 -->
<div class="decoration-line top-line"></div>
<div class="decoration-line right-line"></div>
<div class="decoration-line bottom-line"></div>
</div>
<!-- 内容展示区域 -->
<div class="content-area">
<div class="scroll-container">
<transition name="fade" mode="out-in">
<component :is="currentComponent" />
</transition>
</div>
</div>
</dv-full-screen-container>
</template>
<script>
import AgingCabinetBoard from './components/AgingCabinetBoard.vue'
import AgingLayer from './components/AgingLayer'
import RealTimeData from './components/RealTimeData'
export default {
components: {
AgingCabinetBoard,
AgingLayer,
RealTimeData
},
data() {
return {
selectedMenu: 0, // 默认选中第一个
menuItems: [
{ icon: '📷', text: '老化柜看板', component: 'AgingCabinetBoard' },
{ icon: '📊', text: '老化层看板', component: 'AgingLayer' },
{ icon: '📦', text: '实时数据', component: 'RealTimeData' }
],
}
},
computed: {
currentComponent() {
return this.menuItems[this.selectedMenu].component;
},
},
methods: {
selectMenu(index) {
// if(index === 3) {
// this.goToAdmin();
// } else if( index === 2) {
// this.msgSuccess("你选中了第三个");
// } else if( index === 1) {
// this.msgSuccess("你选中了第二个");
// } else if( index === 0) {
// this.msgSuccess("你选中了第一个");
// }
this.selectedMenu = index;
},
goToAdmin() {
this.$router.push('/index') // 或者 '/dashboard' 如果已经登录
},
handleMenuSelect(index) {
// 可以在这里根据 index 做更多操作,比如加载数据等
this.activeMenu = index;
}
}
}
</script>
<style scoped>
/* “进入管理系统”正方形按钮 */
.management-entry {
position: fixed;
top: 20px;
right: 60px; /* 根据图例的位置调整,避免重叠 */
width: 130px;
height: 40px;
background-color: #409EFF;
color: #fff; /* Element UI 主色调 */
border: 1px solid #fff;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
z-index: 9999;
transition: all 0.2s ease-in-out;
}
.screen-container {
background-color: #030b2a;
color: white;
font-family: Arial, sans-serif;
}
.management-entry:hover {
background-color: #ecf5ff;
color: #1a73e8;
}
.header {
display: flex;
align-items: center;
padding: 30px 40px;
position: relative;
}
/* 标题 */
.system-title-wrapper {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.system-title {
margin-right: 20px;
font-size: 24px;
font-weight: bold;
color: #ffffff;
}
.title-line {
width: 100%;
height: 2px;
background: linear-gradient(to right, transparent, #1a73e8, transparent);
margin-top: 10px;
}
/* 菜单组 */
.menu-group {
display: flex;
gap: 20px;
align-items: center;
}
/* 菱形盒子 */
.diamond-box {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 20px;
position: relative;
transform: rotate(-2deg); /* 整体倾斜一点,模拟菱形外观 */
font-family: Arial, sans-serif;
color: white;
z-index: 1;
cursor: pointer;
transition: transform 0.3s ease;
}
.diamond-box:hover {
transform: rotate(-2deg) scale(1.05);
}
.diamond-box::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 1px solid #409EFF;
transform: rotate(2deg);
box-sizing: border-box;
z-index: -1;
background-color: rgba(64, 158, 255, 0.1);
border-radius: 4px;
}
/* 装饰线样式 */
.decoration-line {
position: absolute;
background: linear-gradient(to right, transparent, #409EFF, transparent);
height: 2px;
opacity: 0.8;
animation: lineMove 4s infinite alternate ease-in-out;
}
.top-line {
top: 0;
left: -30%;
width: 160%;
}
.right-line {
right: -20%;
width: 140%;
top: 0;
bottom: 0;
transform: rotate(90deg);
transform-origin: right top;
}
.bottom-line {
bottom: 0;
left: -30%;
width: 160%;
}
/* 动画效果 */
@keyframes lineMove {
0% { transform: scaleX(1); }
100% { transform: scaleX(0.9); }
}
/* 选中时的底部线条 */
.active-line {
position: absolute;
bottom: -6px; /* 略微低于盒子 */
left: 0;
right: 0;
height: 2px;
background: linear-gradient(to right, transparent, #409EFF, transparent);
animation: lineGrow 0.3s forwards;
}
/* 动画:从中间向两边展开 */
@keyframes lineGrow {
0% {
width: 0%;
margin: 0 auto;
}
100% {
width: 100%;
margin: 0 auto;
}
}
.content-area {
padding: 20px 40px;
flex: 1;
min-height: calc(100vh - 150px); /* 根据实际布局调整 */
overflow-y: auto;
}
.scroll-container {
max-height: calc(100vh - 180px);
overflow-y: auto;
padding: 0 10px;
}
.scroll-container::-webkit-scrollbar {
width: 6px;
}
.scroll-container::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.3);
border-radius: 3px;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment