feat():首页闸门控制替换
parent
7d67c27718
commit
2a31f17099
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -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()
|
||||
}, [])
|
||||
```
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue