feat():首页闸门控制替换

master
李神峰 2025-12-31 17:35:45 +08:00
parent 7d67c27718
commit 2a31f17099
7 changed files with 396 additions and 7 deletions

BIN
public/assets/centerZm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
src/assets/ykzImg/ykbg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,152 @@
# 闸门标注功能使用说明
## 功能概述
本功能在水闸大屏上实现了五个闸门的标注显示,每个标注包含工作闸和检修闸的开关状态。标注使用百分比定位,确保在不同屏幕分辨率下位置保持不变。
## 文件结构
```
src/views/Home/
├── index.js # 主组件文件
├── index.less # 样式文件
└── gateMarkersConfig.js # 闸门标注配置文件
```
## 如何调整闸门位置
### 1. 打开配置文件
编辑 `src/views/Home/gateMarkersConfig.js` 文件
### 2. 修改位置参数
每个闸门对象都有一个 `position` 属性,包含两个百分比值:
- `left`: 从左到右的百分比位置 (0-100)
- `top`: 从上到下的百分比位置 (0-100)
```javascript
{
id: 1,
name: '1号闸门',
workGate: '开启',
repairGate: '关闭',
position: { left: 15, top: 45 } // 调整这两个值
}
```
### 3. 调整建议
- **left值**: 从左到右依次增加,建议间隔 15% 左右
- 1号闸门: left: 15
- 2号闸门: left: 30
- 3号闸门: left: 45
- 4号闸门: left: 60
- 5号闸门: left: 75
- **top值**: 根据实际图片中闸门的位置调整
- 如果闸门在图片上方,使用较小的值(如 35-40
- 如果闸门在图片中间,使用中等值(如 45-55
- 如果闸门在图片下方,使用较大的值(如 60-70
### 4. 精确定位方法
1. 打开浏览器开发者工具 (F12)
2. 选择元素选择工具
3. 点击大屏区域,查看容器的实际尺寸
4. 计算闸门在图片中的相对位置:
```
left百分比 = (闸门X坐标 / 容器宽度) × 100
top百分比 = (闸门Y坐标 / 容器高度) × 100
```
## 如何修改闸门状态
`gateMarkersConfig.js` 中修改每个闸门的 `workGate``repairGate` 属性:
- `'开启'`: 显示为绿色
- `'关闭'`: 显示为红色
```javascript
{
id: 1,
name: '1号闸门',
workGate: '开启', // 修改为 '开启' 或 '关闭'
repairGate: '关闭', // 修改为 '开启' 或 '关闭'
position: { left: 15, top: 45 }
}
```
## 样式自定义
如需修改标注样式,编辑 `src/views/Home/index.less` 文件中的以下部分:
### 标注点样式
```less
.marker-dot {
width: 12px; // 点的大小
height: 12px;
background: #ff4d4f; // 点的颜色
// ...
}
```
### 信息框样式
```less
.marker-info {
background: rgba(0, 0, 0, 0.75); // 背景颜色和透明度
border: 1px solid #ff4d4f; // 边框颜色
min-width: 120px; // 最小宽度
// ...
}
```
### 动画效果
```less
@keyframes pulse {
// 修改脉冲动画效果
}
```
## 响应式特性
- 标注使用百分比定位,自动适应不同屏幕尺寸
- 配合 `autofit.js` 实现整体大屏的缩放适配
- 标注信息框会自动调整位置,避免超出屏幕边界
## 交互功能
- **悬停效果**: 鼠标悬停时标注会放大 1.05 倍
- **点击事件**: 点击标注会触发 `handleGateClick` 函数,可在控制台查看点击的闸门信息
- **脉冲动画**: 标注点有持续的脉冲动画效果,吸引注意力
## 注意事项
1. 修改配置文件后需要刷新页面才能看到效果
2. 确保百分比值的总和不超过 100%
3. 如果标注信息框超出屏幕边界,可以调整 `left` 值或修改样式中的 `left` 偏移量
4. 在生产环境中,建议将闸门状态改为从后端 API 获取
## 示例从API获取闸门状态
```javascript
// 在 index.js 中添加获取闸门数据的函数
const getGateData = async () => {
try {
const res = await httppost2(apiurl.gate.getGateStatus)
if (res.code == 200) {
// 更新闸门状态
const updatedGateData = gateMarkersConfig.map(gate => {
const apiData = res.data.find(item => item.id === gate.id)
return {
...gate,
workGate: apiData?.workGate || '关闭',
repairGate: apiData?.repairGate || '关闭'
}
})
setGateData(updatedGateData)
}
} catch (error) {
console.log(error)
}
}
// 在 useEffect 中调用
useEffect(() => {
// ... 其他初始化代码
getGateData()
}, [])
```

View File

@ -0,0 +1,64 @@
// 闸门标注配置文件
// 使用百分比定位,确保在不同分辨率下标注位置保持不变
// left: 从左到右的百分比位置 (0-100)
// top: 从上到下的百分比位置 (0-100)
export const gateMarkersConfig = [
{
id: 1,
name: '1号闸门',
workGate: '-',
repairGate: '-',
position: { left: 33, top: 45 } // 根据实际图片调整这些值
},
{
id: 2,
name: '2号闸门',
workGate: '-',
repairGate: '-',
position: { left: 40, top: 45 } // 根据实际图片调整这些值
},
{
id: 3,
name: '3号闸门',
workGate: '-',
repairGate: '-',
position: { left: 47, top: 45 } // 根据实际图片调整这些值
},
{
id: 4,
name: '4号闸门',
workGate: '-',
repairGate: '-',
position: { left: 54, top: 45 } // 根据实际图片调整这些值
},
{
id: 5,
name: '5号闸门',
workGate: '-',
repairGate: '-',
position: { left: 61, top: 45 } // 根据实际图片调整这些值
}
]
// 标注样式配置
export const markerStyleConfig = {
// 标注点大小
dotSize: 12,
// 标注线高度
lineHeight: 30,
// 信息框最小宽度
infoBoxMinWidth: 120,
// 信息框内边距
infoBoxPadding: '8px 12px',
// 开启状态颜色
openColor: '#52c41a',
// 关闭状态颜色
closedColor: '#ff4d4f',
// 标注点颜色
dotColor: '#ff4d4f',
// 边框颜色
borderColor: '#ff4d4f',
// 背景透明度
backgroundOpacity: 0.75
}

View File

@ -16,6 +16,43 @@ import { useDispatch, useSelector } from 'react-redux';
import HFivePlayer from '../../components/video1Plary';
import moment from 'moment';
import { loadMenu, loadRole } from '../../models/auth/_'
import { gateMarkersConfig } from './gateMarkersConfig'
// 闸门标注组件
const GateMarker = ({ gateInfo, position, onClick }) => {
const getStatusColor = (status) => {
return status === '开启' ? '#52c41a' : '#ff4d4f'
}
return (
<div
className="gate-marker"
style={{
left: `${position.left}%`,
top: `${position.top}%`
}}
onClick={() => onClick && onClick(gateInfo)}
>
{/* <div className="marker-dot"></div>
<div className="marker-line"></div> */}
<div className="marker-info">
<div className="marker-title">{gateInfo.name}</div>
<div className="marker-item">
<span className="marker-label">工作闸开度:</span>
<span className="marker-value" style={{ color: getStatusColor(gateInfo.workGate) }}>
{gateInfo.workGate}
</span>
</div>
<div className="marker-item">
<span className="marker-label">检修闸开度:</span>
<span className="marker-value" style={{ color: getStatusColor(gateInfo.repairGate) }}>
{gateInfo.repairGate}
</span>
</div>
</div>
</div>
)
}
const MenuTitleCard = ({ key, title }) => {
return (
<div className='menuItem_style' key={key} title={title}>
@ -223,12 +260,12 @@ export default function Home() {
const delClick = () => {
let idx = index - 1
setIndex(idx)
getVideoSrc(videoList[idx].indexCode)
getVideoSrc(videoList[idx]?.indexCode)
}
const addClick = () => {
let idx = index + 1
setIndex(idx)
getVideoSrc(videoList[idx].indexCode)
getVideoSrc(videoList[idx]?.indexCode)
}
@ -242,6 +279,15 @@ export default function Home() {
}
}
const [activeOne, setActiveOne] = useState(0)
// 闸门数据 - 使用配置文件
const [gateData, setGateData] = useState(gateMarkersConfig)
// 处理闸门标注点击
const handleGateClick = (gateInfo) => {
console.log('点击闸门:', gateInfo)
// 这里可以添加点击后的逻辑,比如显示详细信息等
}
useEffect(() => {
autofit.init({
dh: 1080,
@ -261,7 +307,7 @@ export default function Home() {
<div className='daping-body' id='daping-body'>
<div className='topMenu'>
<div className='title'></div>
<div className='title_name'></div>
{/* <div className='title_name'></div> */}
{title.map((item, i) => (
<div key={item.key} className={'styles' + i} onClick={() => jumpMenu(item)} style={{ cursor: 'pointer' }}>
<MenuTitleCard title={item.title} />
@ -269,7 +315,21 @@ export default function Home() {
))}
</div>
<div className='content-box'>
<Zmjk water={ RealData.find(item => item?.type == 2)} />
{/* <Zmjk water={ RealData.find(item => item?.type == 2)} /> */}
<div className='content-center'>
{/* <img alt='' src={`${process.env.PUBLIC_URL}/assets/centerZm.png`} /> */}
{/* 闸门标注 */}
<div className="gate-markers-container">
{gateData.map((gate) => (
<GateMarker
key={gate.id}
gateInfo={gate}
position={gate.position}
onClick={handleGateClick}
/>
))}
</div>
</div>
</div>
{showTabLeft &&
<div

View File

@ -2,7 +2,8 @@
position: relative;
width: 100%;
height: 100vh;
background:url(../../assets/ykzImg/bp.png) no-repeat center;
// background:url(../../assets/ykzImg/bp.png) no-repeat center;
background:url(../../assets/ykzImg/ykbg.png) no-repeat center;
background-size: 100% 100%;
.topMenu{
display: flex;
@ -73,7 +74,119 @@
.content-box{
display: flex;
justify-content: center;
position: relative;
}
.content-center{
position: relative;
width: 100%;
height: 800px;
}
// 闸门标注容器
.gate-markers-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
// 闸门标注
.gate-marker {
position: absolute;
pointer-events: auto;
cursor: pointer;
z-index: 100;
transition: all 0.3s ease;
&:hover {
transform: scale(1.05);
}
.marker-dot {
position: absolute;
width: 12px;
height: 12px;
background: #ff4d4f;
border: 2px solid #fff;
border-radius: 50%;
box-shadow: 0 0 10px rgba(255, 77, 79, 0.8);
animation: pulse 2s infinite;
left: -6px;
top: -6px;
}
.marker-line {
position: absolute;
width: 2px;
height: 30px;
background: linear-gradient(to bottom, #ff4d4f, transparent);
left: -1px;
top: 6px;
}
.marker-info {
position: absolute;
left: 10px;
top: -40px;
// background: rgba(0, 0, 0, 0.65);
// border: 1px solid #ff4d4f;
// border-radius: 4px;
background:url(../../assets/ykzImg/zmCard.png) no-repeat center;
background-size: 100% 100%;
padding: 8px 12px;
min-width: 120px;
backdrop-filter: blur(4px);
.marker-title {
color: #fff;
font-size: 14px;
font-weight: bold;
margin-bottom: 6px;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
padding-bottom: 4px;
margin-left: 20px;
margin-top: 5px;
}
.marker-item {
display: flex;
justify-content: space-between;
margin: 4px 0;
.marker-label {
color: #fff;
font-size: 12px;
font-weight: 700;
}
.marker-value {
color: #52c41a;
font-size: 12px;
font-weight: bold;
&.closed {
color: #ff4d4f;
}
}
}
}
}
@keyframes pulse {
0% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(255, 77, 79, 0.7);
}
50% {
transform: scale(1.1);
box-shadow: 0 0 0 10px rgba(255, 77, 79, 0);
}
100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(255, 77, 79, 0);
}
}
.content-left{
position: absolute;