pages/stationMonitor/stationMonitor.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
pages/stationMonitor/stationMonitor.wxml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
pages/stationMonitor/stationMonitor.wxss | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
project.private.config.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
utils/projectConfig.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
pages/stationMonitor/stationMonitor.js
@@ -387,7 +387,7 @@ return { id: item.id, name: item.name, online: true, // 默认在线,实际项目中可能需要额外的状态检查 onLine: true, // 默认在线,实际项目中可能需要额外的状态检查 lastUpdate: new Date().toLocaleString(), // 当前时间作为最后更新时间 isPlaying: false, // 视频播放状态 hslUrl: rtmpUrl, // 使用生成的RTMP URL @@ -432,356 +432,12 @@ /** * 批量获取所有摄像头的HLS播放地址 */ batchGetHlsUrls(cameraList) { if (!cameraList || cameraList.length === 0) { console.log('摄像头列表为空,无需获取播放地址'); return; } console.log('开始批量获取播放地址,摄像头数量:', cameraList.length); // 为每个摄像头获取播放地址 cameraList.forEach((camera, index) => { // 延迟获取,避免同时发起太多请求 setTimeout(() => { this.getHlsUrlForCamera(camera); }, index * 500); // 每个请求间隔500ms }); }, /** * 播放视频 */ playVideo(e) { const camera = e.currentTarget.dataset.camera; console.log('=== 播放视频开始 ==='); console.log('摄像头信息:', camera); console.log('设备信息:', this.data.deviceInfo); console.log('设备特定配置:', this.data.deviceSpecificConfig); if (!camera.online) { console.log('摄像头离线,无法播放'); wx.showToast({ title: '设备离线,无法播放', icon: 'error' }); return; } if (!camera.hslUrl) { console.log('无播放地址,无法播放'); wx.showToast({ title: '暂无播放地址,请稍后重试', icon: 'error' }); return; } if (camera.isLoadingUrl) { console.log('正在加载URL,无法播放'); wx.showToast({ title: '正在获取播放地址,请稍候', icon: 'none' }); return; } console.log('开始播放视频,播放地址:', camera.hslUrl); console.log('当前页面数据状态:', { cameraList: this.data.cameraList, activeTab: this.data.activeTab, isLoading: this.data.isLoading }); // 检查URL格式 if (camera.hslUrl.startsWith('rtmp://')) { console.log('检测到RTMP URL格式,开始播放'); // 更新播放状态 const cameraList = this.data.cameraList.map(item => { if (item.id === camera.id) { return { ...item, isPlaying: true }; } return item; }); this.setData({ cameraList: cameraList }); // 显示播放成功提示 wx.showToast({ title: `开始播放 ${camera.name}`, icon: 'success' }); console.log(`播放状态已更新,摄像头 ${camera.name} 开始播放RTMP流`); } else { console.log('URL格式不是RTMP,无法播放'); wx.showToast({ title: '播放地址格式不正确', icon: 'error' }); } console.log('=== 播放视频结束 ==='); }, /** * 暂停视频 */ pauseVideo(e) { const camera = e.currentTarget.dataset.camera; console.log('暂停摄像头:', camera.name); // 更新视频播放状态 const cameraList = this.data.cameraList.map(item => { if (item.id === camera.id) { return { ...item, isPlaying: false }; } return item; }); this.setData({ cameraList: cameraList }); }, /** * 停止视频 */ stopVideo(e) { const camera = e.currentTarget.dataset.camera; console.log('停止摄像头:', camera.name); // 更新视频播放状态 const cameraList = this.data.cameraList.map(item => { if (item.id === camera.id) { return { ...item, isPlaying: false }; } return item; }); this.setData({ cameraList: cameraList }); }, /** * 直播播放器状态变化 */ onLivePlayerStateChange(e) { const camera = e.currentTarget.dataset.camera; console.log('直播播放器状态变化:', camera.name, e.detail); const { code } = e.detail; // 显示状态信息给用户 let statusText = ''; let isPlaying = false; switch (code) { case 2001: statusText = '已经连接服务器'; break; case 2002: statusText = '已经连接 RTMP 服务器,开始拉流'; isPlaying = true; break; case 2003: statusText = '网络接收到首个视频数据包(IDR)'; isPlaying = true; break; case 2004: statusText = '视频播放开始'; isPlaying = true; break; case 2005: statusText = '视频播放进度'; isPlaying = true; break; case 2006: statusText = '视频播放结束'; isPlaying = false; break; case 2007: statusText = '视频播放Loading'; isPlaying = true; break; case 2008: statusText = '解码器启动'; isPlaying = true; break; case 2009: statusText = '视频分辨率改变'; isPlaying = true; break; case 2101: statusText = '网络断连,且经多次重连抢救无效'; isPlaying = false; break; case 2102: statusText = '获取加速拉流地址失败'; isPlaying = false; break; case 2103: statusText = '当前视频帧解码失败'; isPlaying = false; break; case 2104: statusText = '网络断连, 已启动自动重连'; isPlaying = false; break; case 2105: statusText = '网络来断连, 且经多次重连抢救无效'; isPlaying = false; break; case 2106: statusText = '网络来断连, 且经多次重连抢救无效'; isPlaying = false; break; default: statusText = `未知状态码: ${code}`; break; } console.log(`摄像头 ${camera.name} 状态: ${statusText}`); // 更新播放状态 const cameraList = this.data.cameraList.map(item => { if (item.id === camera.id) { return { ...item, isPlaying: isPlaying }; } return item; }); this.setData({ cameraList: cameraList }); }, /** * 直播播放器网络状态 */ onLivePlayerNetStatus(e) { const camera = e.currentTarget.dataset.camera; console.log('直播播放器网络状态:', camera.name, e.detail); }, /** * 直播播放器错误 */ onLivePlayerError(e) { const camera = e.currentTarget.dataset.camera; console.error('直播播放器错误:', camera.name, e.detail); wx.showToast({ title: '直播播放失败', icon: 'error' }); // 更新播放状态 const cameraList = this.data.cameraList.map(item => { if (item.id === camera.id) { return { ...item, isPlaying: false }; } return item; }); this.setData({ cameraList: cameraList }); }, /** * 全屏播放视频 */ fullscreenVideo(e) { const camera = e.currentTarget.dataset.camera; console.log('全屏播放:', camera.name); if (!camera.online) { wx.showToast({ title: '摄像头离线', icon: 'error' }); return; } // 使用RTMP URL进行全屏播放 if (camera.hslUrl) { console.log('全屏视频URL:', camera.hslUrl); wx.showModal({ title: '全屏播放', content: `即将全屏播放 ${camera.name} 的视频流`, confirmText: '开始播放', success: (res) => { if (res.confirm) { // 这里可以跳转到全屏视频播放页面 wx.showToast({ title: '正在加载全屏视频...', icon: 'loading' }); // 模拟全屏视频加载 setTimeout(() => { wx.showToast({ title: '全屏播放中', icon: 'success' }); }, 1500); } } }); } else { wx.showToast({ title: '全屏视频地址无效', icon: 'error' }); } }, /** * 摄像头设置 */ cameraSettings(e) { const camera = e.currentTarget.dataset.camera; console.log('摄像头设置:', camera.name); wx.showActionSheet({ itemList: ['云台控制', '录像设置', '画质调节', '报警设置'], success: (res) => { const actions = ['云台控制', '录像设置', '画质调节', '报警设置']; wx.showToast({ title: `${actions[res.tapIndex]}功能开发中`, icon: 'none' }); } }); }, /** * 获取气象站列表(兼容性方法,现在调用统一接口) @@ -849,8 +505,6 @@ windDirectionAngle: content.windDirection, // 风向角度 // 在线状态 onLine: content.onLine, // 计算在线状态 online: content.onLine === 1, // 格式化显示数据 lastUpdate: content.dt || new Date().toLocaleString() }; @@ -903,7 +557,7 @@ windSpeed: 0.00, windDirection: '北', windDirectionAngle: 0, onLine: 1, onLine: false, online: true, lastUpdate: new Date().toLocaleString() }; @@ -1071,8 +725,7 @@ soilTemperature4: 0.00, soilTemperature5: 0.00, // 在线状态 onLine: 1, online: true, onLine: false, lastUpdate: new Date().toLocaleString() }; @@ -1240,7 +893,7 @@ stirTime: 0, stirDuration: 300, injectDuration: 300, onLine: 1, onLine: false, mixingEnabled: false, fertilizingEnabled: false, lastUpdate: new Date().toLocaleString() pages/stationMonitor/stationMonitor.wxml
@@ -47,8 +47,8 @@ <!-- 状态栏 --> <view class="status-bar"> <view class="status-item"> <view class="status-indicator {{currentWeatherStation.online ? 'online' : 'offline'}}"></view> <text class="status-text">{{currentWeatherStation.online ? '在线' : '离线'}}</text> <view class="status-indicator {{currentWeatherStation.onLine === true ? 'online' : 'offline'}}"></view> <text class="status-text">{{currentWeatherStation.onLine === true ? '在线' : '离线'}}</text> </view> <view class="refresh-btn" bind:tap="refreshWeatherData"> <image class="refresh-icon" src="/images/refresh.svg" /> @@ -65,7 +65,7 @@ </view> <view class="data-content"> <text class="data-label">空气湿度(%)</text> <text class="data-value">{{currentWeatherStation.humidity || '--'}}%</text> <text class="data-value">{{currentWeatherStation.humidity !== null && currentWeatherStation.humidity !== undefined ? currentWeatherStation.humidity : '--'}}%</text> </view> </view> @@ -76,7 +76,7 @@ </view> <view class="data-content"> <text class="data-label">空气温度(℃)</text> <text class="data-value">{{currentWeatherStation.temperature || '--'}}°C</text> <text class="data-value">{{currentWeatherStation.temperature !== null && currentWeatherStation.temperature !== undefined ? currentWeatherStation.temperature : '--'}}°C</text> </view> </view> @@ -87,7 +87,7 @@ </view> <view class="data-content"> <text class="data-label">紫外线(mW/m²)</text> <text class="data-value">{{currentWeatherStation.uv || '--'}}</text> <text class="data-value">{{currentWeatherStation.uv !== null && currentWeatherStation.uv !== undefined ? currentWeatherStation.uv : '--'}}</text> </view> </view> @@ -98,7 +98,7 @@ </view> <view class="data-content"> <text class="data-label">光照强度(lm/㎡)</text> <text class="data-value">{{currentWeatherStation.light || '--'}} lux</text> <text class="data-value">{{currentWeatherStation.light !== null && currentWeatherStation.light !== undefined ? currentWeatherStation.light : '--'}} lux</text> </view> </view> @@ -109,7 +109,7 @@ </view> <view class="data-content"> <text class="data-label">雨量(mm)</text> <text class="data-value">{{currentWeatherStation.rainfall || '--'}} mm</text> <text class="data-value">{{currentWeatherStation.rainfall !== null && currentWeatherStation.rainfall !== undefined ? currentWeatherStation.rainfall : '--'}} mm</text> </view> </view> @@ -120,7 +120,7 @@ </view> <view class="data-content"> <text class="data-label">风速(m/s)</text> <text class="data-value">{{currentWeatherStation.windSpeed || '--'}} m/s</text> <text class="data-value">{{currentWeatherStation.windSpeed !== null && currentWeatherStation.windSpeed !== undefined ? currentWeatherStation.windSpeed : '--'}} m/s</text> </view> </view> @@ -133,7 +133,7 @@ <text class="data-label">风向</text> <view class="wind-direction-display"> <!-- <image class="wind-arrow" src="/images/wind-arrow.svg" style="transform: rotate({{currentWeatherStation.windDirectionAngle || 0}}deg)" /> --> <text class="data-value">{{currentWeatherStation.windDirection || '--'}}</text> <text class="data-value">{{currentWeatherStation.windDirection !== null && currentWeatherStation.windDirection !== undefined ? currentWeatherStation.windDirection : '--'}}</text> </view> </view> </view> @@ -172,8 +172,8 @@ <!-- 状态栏 --> <view class="status-bar"> <view class="status-item"> <view class="status-indicator {{currentSoilStation.online ? 'online' : 'offline'}}"></view> <text class="status-text">{{currentSoilStation.online ? '在线' : '离线'}}</text> <view class="status-indicator {{currentSoilStation.onLine === true ? 'online' : 'offline'}}"></view> <text class="status-text">{{currentSoilStation.onLine === true ? '在线' : '离线'}}</text> </view> <view class="refresh-btn" bind:tap="refreshSoilData"> <image class="refresh-icon" src="/images/refresh.svg" /> @@ -190,7 +190,7 @@ </view> <view class="data-content"> <text class="data-label">土壤湿度1(%)</text> <text class="data-value">{{currentSoilStation.soilHumidity1 || '--'}}%</text> <text class="data-value">{{currentSoilStation.soilHumidity1 !== null && currentSoilStation.soilHumidity1 !== undefined ? currentSoilStation.soilHumidity1 : '--'}}%</text> </view> </view> @@ -200,7 +200,7 @@ </view> <view class="data-content"> <text class="data-label">土壤温度1(℃)</text> <text class="data-value">{{currentSoilStation.soilTemperature1 || '--'}}°C</text> <text class="data-value">{{currentSoilStation.soilTemperature1 !== null && currentSoilStation.soilTemperature1 !== undefined ? currentSoilStation.soilTemperature1 : '--'}}°C</text> </view> </view> @@ -211,7 +211,7 @@ </view> <view class="data-content"> <text class="data-label">土壤湿度2(%)</text> <text class="data-value">{{currentSoilStation.soilHumidity2 || '--'}}%</text> <text class="data-value">{{currentSoilStation.soilHumidity2 !== null && currentSoilStation.soilHumidity2 !== undefined ? currentSoilStation.soilHumidity2 : '--'}}%</text> </view> </view> @@ -221,7 +221,7 @@ </view> <view class="data-content"> <text class="data-label">土壤温度2(℃)</text> <text class="data-value">{{currentSoilStation.soilTemperature2 || '--'}}°C</text> <text class="data-value">{{currentSoilStation.soilTemperature2 !== null && currentSoilStation.soilTemperature2 !== undefined ? currentSoilStation.soilTemperature2 : '--'}}°C</text> </view> </view> @@ -232,7 +232,7 @@ </view> <view class="data-content"> <text class="data-label">土壤湿度3(%)</text> <text class="data-value">{{currentSoilStation.soilHumidity3 || '--'}}%</text> <text class="data-value">{{currentSoilStation.soilHumidity3 !== null && currentSoilStation.soilHumidity3 !== undefined ? currentSoilStation.soilHumidity3 : '--'}}%</text> </view> </view> @@ -242,7 +242,7 @@ </view> <view class="data-content"> <text class="data-label">土壤温度3(℃)</text> <text class="data-value">{{currentSoilStation.soilTemperature3 || '--'}}°C</text> <text class="data-value">{{currentSoilStation.soilTemperature3 !== null && currentSoilStation.soilTemperature3 !== undefined ? currentSoilStation.soilTemperature3 : '--'}}°C</text> </view> </view> @@ -253,7 +253,7 @@ </view> <view class="data-content"> <text class="data-label">土壤湿度4(%)</text> <text class="data-value">{{currentSoilStation.soilHumidity4 || '--'}}%</text> <text class="data-value">{{currentSoilStation.soilHumidity4 !== null && currentSoilStation.soilHumidity4 !== undefined ? currentSoilStation.soilHumidity4 : '--'}}%</text> </view> </view> @@ -263,7 +263,7 @@ </view> <view class="data-content"> <text class="data-label">土壤温度4(℃)</text> <text class="data-value">{{currentSoilStation.soilTemperature4 || '--'}}°C</text> <text class="data-value">{{currentSoilStation.soilTemperature4 !== null && currentSoilStation.soilTemperature4 !== undefined ? currentSoilStation.soilTemperature4 : '--'}}°C</text> </view> </view> @@ -274,7 +274,7 @@ </view> <view class="data-content"> <text class="data-label">土壤湿度5(%)</text> <text class="data-value">{{currentSoilStation.soilHumidity5 || '--'}}%</text> <text class="data-value">{{currentSoilStation.soilHumidity5 !== null && currentSoilStation.soilHumidity5 !== undefined ? currentSoilStation.soilHumidity5 : '--'}}%</text> </view> </view> @@ -284,7 +284,7 @@ </view> <view class="data-content"> <text class="data-label">土壤温度5(℃)</text> <text class="data-value">{{currentSoilStation.soilTemperature5 || '--'}}°C</text> <text class="data-value">{{currentSoilStation.soilTemperature5 !== null && currentSoilStation.soilTemperature5 !== undefined ? currentSoilStation.soilTemperature5 : '--'}}°C</text> </view> </view> </view> @@ -324,8 +324,8 @@ <!-- 状态栏 --> <view class="status-bar"> <view class="status-item"> <view class="status-indicator {{currentFertilizerStation.onLine === 1 ? 'online' : 'offline'}}"></view> <text class="status-text">{{currentFertilizerStation.onLine === 1 ? '在线' : '离线'}}</text> <view class="status-indicator {{currentFertilizerStation.onLine === true ? 'online' : 'offline'}}"></view> <text class="status-text">{{currentFertilizerStation.onLine === true ? '在线' : '离线'}}</text> </view> <view class="refresh-btn" bind:tap="refreshFertilizerData"> <image class="refresh-icon" src="/images/refresh.svg" /> @@ -340,13 +340,13 @@ <!-- 搅拌开关 --> <view class="switch-item"> <text class="switch-label">搅拌</text> <switch class="custom-switch" checked="{{currentFertilizerStation.mixingEnabled}}" bindchange="toggleMixing" disabled="{{currentFertilizerStation.onLine !== 1}}" color="#07c160" /> <switch class="custom-switch" checked="{{currentFertilizerStation.mixingEnabled}}" bindchange="toggleMixing" disabled="{{currentFertilizerStation.onLine !== true}}" color="#07c160" /> </view> <!-- 注肥开关 --> <view class="switch-item"> <text class="switch-label">注肥</text> <switch class="custom-switch" checked="{{currentFertilizerStation.fertilizingEnabled}}" bindchange="toggleFertilizing" disabled="{{currentFertilizerStation.onLine !== 1}}" color="#07c160" /> <switch class="custom-switch" checked="{{currentFertilizerStation.fertilizingEnabled}}" bindchange="toggleFertilizing" disabled="{{currentFertilizerStation.onLine !== true}}" color="#07c160" /> </view> </view> </view> @@ -362,7 +362,7 @@ </view> <view class="data-content"> <text class="data-label">肥料流量(升)</text> <text class="data-value">{{currentFertilizerStation.manureFlow || '--'}} L</text> <text class="data-value">{{currentFertilizerStation.manureFlow !== null && currentFertilizerStation.manureFlow !== undefined ? currentFertilizerStation.manureFlow : '--'}} L</text> </view> </view> @@ -373,7 +373,7 @@ </view> <view class="data-content"> <text class="data-label">注肥时长(秒)</text> <text class="data-value">{{currentFertilizerStation.manureTime || '--'}} s</text> <text class="data-value">{{currentFertilizerStation.manureTime !== null && currentFertilizerStation.manureTime !== undefined ? currentFertilizerStation.manureTime : '--'}} s</text> </view> </view> @@ -384,7 +384,7 @@ </view> <view class="data-content"> <text class="data-label">搅拌时长(秒)</text> <text class="data-value">{{currentFertilizerStation.stirTime || '--'}} s</text> <text class="data-value">{{currentFertilizerStation.stirTime !== null && currentFertilizerStation.stirTime !== undefined ? currentFertilizerStation.stirTime : '--'}} s</text> </view> </view> @@ -395,7 +395,7 @@ </view> <view class="data-content"> <text class="data-label">搅拌设定时间(秒)</text> <text class="data-value">{{currentFertilizerStation.stirDuration || '--'}} s</text> <text class="data-value">{{currentFertilizerStation.stirDuration !== null && currentFertilizerStation.stirDuration !== undefined ? currentFertilizerStation.stirDuration : '--'}} s</text> </view> </view> @@ -406,7 +406,7 @@ </view> <view class="data-content"> <text class="data-label">注肥设定时间(秒)</text> <text class="data-value">{{currentFertilizerStation.injectDuration || '--'}} s</text> <text class="data-value">{{currentFertilizerStation.injectDuration !== null && currentFertilizerStation.injectDuration !== undefined ? currentFertilizerStation.injectDuration : '--'}} s</text> </view> </view> </view> @@ -435,13 +435,13 @@ <!-- 摄像头名称 --> <view class="camera-header"> <text class="camera-name">{{item.name}}</text> <view class="camera-status {{item.online ? 'online' : 'offline'}}"> <text>{{item.online ? '在线' : '离线'}}</text> <view class="camera-status {{item.onLine === true ? 'online' : 'offline'}}"> <text>{{item.onLine === true ? '在线' : '离线'}}</text> </view> </view> <!-- 摄像头视频 --> <view class="camera-video-container" style="height: {{deviceSpecificConfig.videoHeight}}rpx;"> <view class="camera-video-container" > <!-- 加载状态 --> <view wx:if="{{item.isLoadingUrl}}" class="video-loading"> <view class="loading-spinner"></view> @@ -459,28 +459,13 @@ </view> <!-- 正常播放状态 --> <view wx:elif="{{item.online && item.hslUrl}}" class="video-wrapper"> <view wx:elif="{{item.onLine && item.hslUrl}}" class="video-wrapper"> <!-- 直播播放器组件 --> <ezplayer id="ezplayer-{{item.id}}" accessToken="{{item.accessToken}}" url="{{item.hslUrl}}" deviceSerial="{{item.deviceSerial}}" channelNo="1" width="300rpx" height="300rpx" plugins="talk,voice,capture,ptz,privacy,mirror" watermark="大禹" autoPlay="{{true}}" theme="{{ { showFullScreenBtn: true, showHdBtn: true, showTimeLine: true } }}" bind:handleError="handleError" bind:onControlEvent="onControlEvent" style="width: 100vw; height: 100%; max-width: 100vw; min-width: 100vw; position: absolute; left: 0; right: 0; top: 0; bottom: 0; overflow: hidden; margin: 0; padding: 0; transform: none; border: none; border-radius: 0; box-shadow: none; background: transparent;" /> <ezplayer class="video-wrapper-ezplayer" id="ezplayer-{{item.id}}" accessToken="{{item.accessToken}}" url="{{item.hslUrl}}" deviceSerial="{{item.deviceSerial}}" channelNo="1" plugins="capture,ptz,mirror" watermark="大禹" autoPlay="{{true}}" theme="{{ { showFullScreenBtn: true, showHdBtn: true, showTimeLine: true } }}" bind:handleError="handleError" bind:onControlEvent="onControlEvent" /> </view> <!-- 离线状态显示 --> <view wx:elif="{{!item.online}}" class="video-offline"> <view wx:elif="{{!item.onLine}}" class="video-offline"> <image class="offline-icon" src="/images/camera.svg" /> <text class="offline-text">设备离线</text> </view> pages/stationMonitor/stationMonitor.wxss
@@ -18,7 +18,8 @@ padding: 0; margin: 0; background-color: #f5f5f5; height: 100vh; /* 固定高度为视口高度 */ height: 100vh; /* 固定高度为视口高度 */ width: 100%; box-sizing: border-box; /* 防止任何滚动 */ @@ -40,13 +41,16 @@ background-color: #fff; padding: 4rpx 0; width: 100%; flex-shrink: 0; /* 防止被压缩 */ flex-shrink: 0; /* 防止被压缩 */ box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.05); border-bottom: 1rpx solid #eaeaea; position: sticky; /* 使用sticky定位 */ position: sticky; /* 使用sticky定位 */ top: 0; z-index: 9999; background-color: #fff; /* 确保背景色 */ background-color: #fff; /* 确保背景色 */ /* 减小tabs高度 */ height: 100rpx; box-sizing: border-box; @@ -152,12 +156,17 @@ box-sizing: border-box; /* 使用flex: 1填充剩余空间 */ flex: 1; min-height: 0; /* 允许flex项目收缩 */ overflow-y: auto; /* 垂直滚动 */ overflow-x: hidden; /* 隐藏水平滚动 */ min-height: 0; /* 允许flex项目收缩 */ overflow-y: auto; /* 垂直滚动 */ overflow-x: hidden; /* 隐藏水平滚动 */ /* 隐藏滚动条 */ -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ /* 为tabs留出空间 */ margin-top: 0; } @@ -186,6 +195,7 @@ opacity: 0; transform: translateY(20rpx); } to { opacity: 1; transform: translateY(0); @@ -474,7 +484,8 @@ .tab-item { padding: 16rpx 2rpx; width: 25%; /* 确保小屏幕下也均分 */ width: 25%; /* 确保小屏幕下也均分 */ } /* 气象站数据项响应式字体 */ @@ -536,81 +547,44 @@ .camera-list { display: flex; flex-direction: column; /* gap: 20rpx; */ margin: 0; width: 100%; box-sizing: border-box; /* 确保在不同设备上的一致性 */ max-width: 100vw; overflow-x: hidden; /* 新增:移除左右内边距,确保完全填充 */ /* padding: 0; */ /* 新增:强制约束,防止ezplayer超出 */ /* contain: layout style paint; */ /* 新增:强制左对齐,无任何边距 */ /* left: 0 !important; right: 0 !important; */ gap: 20rpx; } .camera-item { width: 100%; max-width: 100%; min-width: 0; background: white; border-radius: 16rpx; /* 移除左右内边距,确保完全填充 */ padding: 24rpx 0; box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08); box-sizing: border-box; margin: 0; position: relative; overflow: hidden; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; flex-grow: 0; /* 新增:强制约束,防止ezplayer超出 */ contain: layout style paint; /* 新增:强制左对齐,无任何边距 */ left: 0 !important; right: 0 !important; border-radius: 12rpx; margin-bottom: 10rpx; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); } /* 摄像头头部 */ .camera-header { width: 100%; max-width: 100%; min-width: 0; display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; box-sizing: border-box; /* 防止文字溢出 */ overflow: hidden; margin: 20rpx 10rpx; margin-bottom: 16rpx; margin-top: 15rpx; margin-left: 15rpx; margin-right: 15rpx; } .camera-name { font-size: 32rpx; font-size: 28rpx; font-weight: 600; color: #333; flex: 1; min-width: 0; /* 文字溢出处理 */ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .camera-status { padding: 8rpx 16rpx; border-radius: 20rpx; font-size: 24rpx; padding: 6rpx 12rpx; border-radius: 16rpx; font-size: 22rpx; font-weight: 500; white-space: nowrap; flex-shrink: 0; /* 确保状态标签不被压缩 */ min-width: fit-content; } .camera-status.online { @@ -629,52 +603,42 @@ .camera-video-container { position: relative; width: 100%; max-width: 100%; min-width: 0; /* height: 400rpx; */ /* 移除所有装饰性样式,确保完全填充 */ border-radius: 0; height: 100%; border-radius: 8rpx; overflow: hidden; margin: 0; padding: 0; box-sizing: border-box; background-color: transparent; border: none; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; /* 防止ezplayer超出容器 - 强化约束 */ overflow: hidden !important; /* 新增:绝对定位约束 */ position: relative !important; /* 新增:强制宽度约束,防止ezplayer超出 */ max-width: 100vw !important; /* 新增:确保容器不会超出父元素 */ contain: layout style paint !important; /* 新增:强制左对齐,防止右移 */ left: 0 !important; right: 0 !important; /* 新增:强制填充整个屏幕 */ /* width: 100vw !important; min-width: 100vw !important; */ background-color: #f5f5f5; } /* 移除调试边框,避免影响ezplayer显示 */ /* 加载状态 */ .video-loading { width: 100%; height: 100%; max-width: 100%; min-width: 0; /* 视频状态样式 */ .video-loading, .video-error, .video-offline, .video-no-url { display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: #f8f9fa; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; height: 100%; text-align: center; } .video-loading { background-color: #f8f9fa; } .video-error { background-color: #fff2f0; border: 1rpx solid #ffccc7; } .video-offline, .video-no-url { background-color: #f5f5f5; } /* 加载动画 */ .loading-spinner { width: 60rpx; height: 60rpx; @@ -683,8 +647,6 @@ border-radius: 50%; animation: spin 1s linear infinite; margin-bottom: 16rpx; /* 确保动画在不同设备上的一致性 */ flex-shrink: 0; } @keyframes spin { @@ -692,650 +654,75 @@ 100% { transform: rotate(360deg); } } .loading-text { font-size: 26rpx; .loading-text, .error-text, .offline-text, .no-url-text { font-size: 24rpx; color: #666; font-weight: 500; text-align: center; /* 确保文字在不同设备上的一致性 */ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } /* 错误状态 */ .video-error { width: 100%; height: 100%; max-width: 100%; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: #fff2f0; border: 1rpx solid #ffccc7; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; } .error-icon { width: 80rpx; height: 80rpx; opacity: 0.6; margin-bottom: 16rpx; filter: grayscale(100%) brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%); /* 确保图标在不同设备上的一致性 */ flex-shrink: 0; margin-top: 8rpx; } .error-text { font-size: 26rpx; color: #ff4d4f; font-weight: 500; margin-bottom: 20rpx; text-align: center; /* 确保文字在不同设备上的一致性 */ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } /* 图标样式 */ .error-icon, .offline-icon, .no-url-icon { width: 60rpx; height: 60rpx; opacity: 0.5; } /* 重试按钮 */ .retry-btn { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 24rpx; gap: 6rpx; padding: 8rpx 16rpx; background-color: #ff4d4f; color: white; border: none; border-radius: 20rpx; font-size: 24rpx; transition: all 0.3s ease; /* 确保按钮在不同设备上的一致性 */ flex-shrink: 0; min-width: fit-content; border-radius: 16rpx; font-size: 22rpx; margin-top: 12rpx; } .retry-btn:active { background-color: #cf1322; transform: scale(0.98); } .retry-icon { width: 24rpx; height: 24rpx; filter: brightness(0) invert(1); /* 确保图标在不同设备上的一致性 */ flex-shrink: 0; } /* 无播放地址状态 */ .video-no-url { width: 100%; height: 100%; max-width: 100%; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: #f5f5f5; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; } .no-url-icon { width: 80rpx; height: 80rpx; opacity: 0.4; margin-bottom: 16rpx; filter: grayscale(100%); /* 确保图标在不同设备上的一致性 */ flex-shrink: 0; } .no-url-text { font-size: 26rpx; color: #999; font-weight: 500; text-align: center; /* 确保文字在不同设备上的一致性 */ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } .video-wrapper { position: relative; width: 100%; height: 100%; max-width: 100%; min-width: 0; display: flex; align-items: center; justify-content: center; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; /* 防止内容溢出 */ overflow: hidden !important; /* 新增:绝对定位约束 */ position: relative !important; /* 新增:移除所有边距和内边距 */ margin: 0 !important; padding: 0 !important; /* 新增:强制左对齐 */ left: 0 !important; right: 0 !important; } /* ezplayer组件样式优化 - 修复超出屏幕问题 */ .video-wrapper ezplayer { width: 100% !important; height: 100% !important; max-width: 100% !important; min-width: 0 !important; border-radius: 12rpx; overflow: hidden !important; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; /* 防止超出屏幕的关键设置 */ position: absolute !important; left: 0 !important; right: 0 !important; top: 0 !important; bottom: 0 !important; /* 强制约束尺寸 - 修复最小宽度问题 */ max-width: 100% !important; max-height: 100% !important; /* 新增:强制约束到父容器 */ transform: none !important; transform-origin: center center !important; /* 新增:确保不超出边界 */ clip-path: inset(0 0 0 0) !important; /* 新增:防止任何形式的溢出 */ contain: layout style paint !important; /* 新增:强制宽度约束,覆盖ezplayer的最小宽度限制 */ min-width: 0 !important; min-height: 0 !important; /* 新增:确保组件完全约束在容器内 */ box-sizing: border-box !important; /* 新增:移除所有边距和内边距 */ margin: 0 !important; padding: 0 !important; /* 新增:强制完全填充容器 */ inset: 0 !important; } /* 针对ezplayer组件的特殊约束 */ .video-wrapper ezplayer { /* 确保组件不会超出父容器 */ box-sizing: border-box !important; /* 防止水平滚动 */ overflow-x: hidden !important; overflow-y: hidden !important; /* 确保在flex容器中的行为 */ flex: 0 0 auto !important; /* 防止缩放问题 */ transform-origin: top left !important; /* 确保边框圆角生效 */ border-radius: 12rpx !important; /* 新增:强制尺寸约束 */ min-width: 0 !important; min-height: 0 !important; /* 新增:防止任何形式的拉伸 */ flex-basis: auto !important; flex-grow: 0 !important; flex-shrink: 0 !important; /* 新增:确保定位正确 */ position: absolute !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; /* 新增:强制宽度和高度 */ width: 100% !important; height: 100% !important; } /* 新增:专门处理ezplayer暂停状态的样式 */ .video-wrapper ezplayer[data-paused="true"], .video-wrapper ezplayer.paused { /* 强制约束宽度,防止超出屏幕 */ width: 100% !important; max-width: 100% !important; min-width: 0 !important; /* 确保组件完全在容器内 */ position: absolute !important; left: 0 !important; right: 0 !important; /* 防止任何形式的溢出 */ overflow: hidden !important; /* 强制约束到父容器 */ contain: layout style paint !important; } /* 新增:使用CSS Grid强制约束ezplayer */ .video-wrapper { display: grid !important; grid-template-columns: 1fr !important; grid-template-rows: 1fr !important; place-items: stretch !important; } /* 新增:只针对摄像头相关元素强制约束 */ .camera-list, .camera-item, .camera-video-container, .video-wrapper, .camera-header, .camera-name, .camera-status, .camera-video-container > ezplayer, .video-wrapper > ezplayer { max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; overflow: hidden !important; } /* 新增:强制移除所有可能的边距和装饰 */ .camera-list, .camera-item, .camera-video-container, .video-wrapper { margin: 0 !important; padding: 0 !important; border: none !important; border-radius: 0 !important; box-shadow: none !important; background: transparent !important; } /* 新增:特别针对ezplayer的强制约束 */ ezplayer { width: 100vw !important; height: 100% !important; max-width: 100vw !important; max-height: 100% !important; min-width: 100vw !important; min-height: 0 !important; position: absolute !important; left: 0 !important; right: 0 !important; top: 0 !important; bottom: 0 !important; overflow: hidden !important; box-sizing: border-box !important; contain: layout style paint !important; /* 新增:强制填充整个屏幕宽度 */ margin: 0 !important; padding: 0 !important; transform: none !important; transform-origin: center center !important; /* 新增:强制移除所有装饰 */ border: none !important; border-radius: 0 !important; box-shadow: none !important; background: transparent !important; /* 新增:强制约束到屏幕边缘 */ inset: 0 !important; } .video-wrapper ezplayer { grid-column: 1 !important; grid-row: 1 !important; place-self: stretch !important; /* 强制完全填充网格单元格 */ width: 100% !important; height: 100% !important; max-width: 100% !important; max-height: 100% !important; min-width: 0 !important; min-height: 0 !important; } /* 视频包装器的绝对定位约束 */ .video-wrapper { position: relative; width: 100%; height: 100%; max-width: 100%; min-width: 0; display: flex; align-items: center; justify-content: center; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; /* 防止内容溢出 */ overflow: hidden !important; /* 新增:绝对定位约束 */ position: relative !important; } .live-player { width: 100%; height: 100%; max-width: 100%; min-width: 0; background-color: #000; border-radius: 12rpx; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; } .video-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.3); display: flex; align-items: center; justify-content: center; z-index: 10; } .play-overlay { width: 80rpx; height: 80rpx; background: rgba(0, 0, 0, 0.6); border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; } .play-overlay:active { transform: scale(0.9); } .play-icon { width: 40rpx; height: 40rpx; width: 20rpx; height: 20rpx; filter: brightness(0) invert(1); } .video-info { position: absolute; bottom: 0; left: 0; right: 0; background: linear-gradient(transparent, rgba(0, 0, 0, 0.7)); padding: 20rpx 16rpx 16rpx; color: white; display: flex; justify-content: space-between; align-items: center; } .video-time { font-size: 24rpx; opacity: 0.9; } .video-status { font-size: 22rpx; opacity: 0.8; color: #52c41a; } /* 播放控制按钮 */ .video-controls { position: absolute; top: 16rpx; right: 16rpx; display: flex; gap: 12rpx; z-index: 20; } .control-btn { width: 60rpx; height: 60rpx; background: rgba(0, 0, 0, 0.6); border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; } .control-btn:active { transform: scale(0.9); background: rgba(0, 0, 0, 0.8); } .control-icon { width: 32rpx; height: 32rpx; filter: brightness(0) invert(1); } /* 调试按钮 */ .debug-controls { position: absolute; bottom: 16rpx; right: 16rpx; z-index: 20; } .debug-btn { padding: 8rpx 16rpx; background: rgba(255, 0, 0, 0.7); border-radius: 20rpx; font-size: 20rpx; color: white; } /* 离线状态 */ .video-offline { /* 视频播放器容器 */ .video-wrapper { width: 100%; height: 100%; max-width: 100%; min-width: 0; background-color: #f5f5f5; display: flex; flex-direction: column; align-items: center; justify-content: center; border-radius: 12rpx; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; } .offline-icon { width: 80rpx; height: 80rpx; opacity: 0.4; margin-bottom: 16rpx; filter: grayscale(100%); /* 确保图标在不同设备上的一致性 */ flex-shrink: 0; } .offline-text { font-size: 28rpx; color: #999; font-weight: 500; text-align: center; /* 确保文字在不同设备上的一致性 */ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } /* 操作按钮 */ .camera-actions { .video-wrapper-ezplayer { width: 100%; max-width: 100%; min-width: 0; display: flex; gap: 16rpx; box-sizing: border-box; /* 确保在不同设备上的显示一致性 */ flex-shrink: 0; height: 100%; } .action-btn { flex: 1; height: 72rpx; border-radius: 36rpx; font-size: 26rpx; border: none; display: flex; align-items: center; justify-content: center; gap: 8rpx; transition: all 0.3s ease; box-sizing: border-box; /* 确保按钮在不同设备上的一致性 */ min-width: 0; overflow: hidden; } .action-btn.primary { background-color: #1890FF; color: white; } .action-btn.primary:active { background-color: #096dd9; transform: scale(0.98); } .action-btn.secondary { background-color: #f5f5f5; color: #666; border: 1rpx solid #d9d9d9; } .action-btn.secondary:active { background-color: #e8e8e8; transform: scale(0.98); } /* 禁用状态 */ .action-btn[disabled] { background-color: #f5f5f5 !important; color: #bfbfbf !important; border-color: #d9d9d9 !important; cursor: not-allowed; opacity: 0.6; /* 确保禁用状态在不同设备上的一致性 */ transform: none !important; } .action-btn[disabled]:active { transform: none !important; background-color: #f5f5f5 !important; } .action-btn.primary[disabled] { background-color: #d9d9d9 !important; color: #bfbfbf !important; } .action-btn.secondary[disabled] { background-color: #f5f5f5 !important; color: #bfbfbf !important; border-color: #d9d9d9 !important; } .action-icon { width: 32rpx; height: 32rpx; /* 确保图标在不同设备上的一致性 */ flex-shrink: 0; } /* 响应式优化 - 确保在不同设备上的一致性 */ @media (max-width: 400px) { .camera-item { padding: 20rpx; margin-bottom: 16rpx; } .camera-header { margin-bottom: 16rpx; } .camera-name { font-size: 28rpx; } .camera-status { font-size: 22rpx; padding: 6rpx 12rpx; } .camera-video-container { height: 320rpx; margin-bottom: 16rpx; } .action-btn { height: 64rpx; font-size: 24rpx; } .action-icon { width: 28rpx; height: 28rpx; } } /* 确保在不同设备上的一致性 - 额外的兼容性处理 */ .camera-item { /* 防止在不同设备上的布局差异 */ transform: translateZ(0); backface-visibility: hidden; perspective: 1000px; } .camera-video-container { /* 防止在不同设备上的显示差异 */ transform: translateZ(0); backface-visibility: hidden; } /* 修复可能的溢出问题 */ .camera-list { max-width: 100%; overflow: hidden; /* 确保在不同设备上的一致性 */ transform: translateZ(0); } .camera-item { overflow: hidden; /* 确保在不同设备上的一致性 */ transform: translateZ(0); } /* 针对真机的特殊优化 */ @media screen and (max-device-width: 750px) { .camera-item { /* 真机上可能需要稍微调整间距 */ margin-bottom: 16rpx; } .camera-video-container { /* 真机上可能需要稍微调整高度 */ height: 380rpx; } .action-btn { /* 真机上可能需要稍微调整高度 */ height: 68rpx; } } /* 土壤墒情站专用样式 */ .weather-data-item.soil-item { @@ -1400,8 +787,10 @@ display: flex; flex-direction: column; gap: 8rpx; min-width: 0; /* 防止内容溢出 */ overflow: hidden; /* 隐藏溢出内容 */ min-width: 0; /* 防止内容溢出 */ overflow: hidden; /* 隐藏溢出内容 */ } .weather-data-item.soil-item .data-label { @@ -1409,9 +798,12 @@ color: #333; font-weight: 500; line-height: 1.4; white-space: nowrap; /* 防止标签换行 */ overflow: hidden; /* 隐藏溢出内容 */ text-overflow: ellipsis; /* 显示省略号 */ white-space: nowrap; /* 防止标签换行 */ overflow: hidden; /* 隐藏溢出内容 */ text-overflow: ellipsis; /* 显示省略号 */ } .weather-data-item.soil-item .data-value { @@ -1419,9 +811,12 @@ color: #1890ff; font-weight: 700; line-height: 1.2; white-space: nowrap; /* 防止数值换行 */ overflow: hidden; /* 隐藏溢出内容 */ text-overflow: ellipsis; /* 显示省略号 */ white-space: nowrap; /* 防止数值换行 */ overflow: hidden; /* 隐藏溢出内容 */ text-overflow: ellipsis; /* 显示省略号 */ } /* 土壤墒情站图标优化 */ @@ -1755,90 +1150,4 @@ width: 32rpx; height: 32rpx; } } /* ezplayer组件样式 - 防止变形和超出屏幕 */ .video-wrapper ezplayer { /* 基础尺寸约束 */ width: 100% !important; height: 100% !important; max-width: 100% !important; max-height: 100% !important; min-width: 0 !important; min-height: 0 !important; /* 位置约束 */ position: relative !important; left: 0 !important; right: 0 !important; top: 0 !important; bottom: 0 !important; /* 溢出控制 */ overflow: hidden !important; clip-path: inset(0 0 0 0) !important; /* 变换约束 */ transform: none !important; transform-origin: center center !important; /* 布局约束 */ contain: layout style paint !important; flex-basis: auto !important; flex-grow: 0 !important; flex-shrink: 0 !important; /* 盒模型约束 */ box-sizing: border-box !important; margin: 0 !important; padding: 0 !important; /* 渲染优化 */ backface-visibility: hidden !important; perspective: 1000px !important; will-change: auto !important; /* 真机特殊处理 */ -webkit-transform: none !important; -webkit-transform-origin: center center !important; -webkit-backface-visibility: hidden !important; -webkit-perspective: 1000px !important; } /* 真机上的特殊约束 */ @media screen and (max-device-width: 750px) { .video-wrapper ezplayer { /* 真机上更严格的约束 */ max-width: 100vw !important; max-height: 100vh !important; left: 0 !important; right: 0 !important; top: 0 !important; bottom: 0 !important; /* 防止真机上的缩放问题 */ -webkit-transform: scale(1) !important; transform: scale(1) !important; /* 真机上的溢出控制 */ overflow: hidden !important; clip: rect(0, auto, auto, 0) !important; } } /* 暂停状态的特殊处理 */ .video-wrapper ezplayer[data-paused="true"] { /* 暂停时保持尺寸 */ width: 100% !important; height: 100% !important; max-width: 100% !important; max-height: 100% !important; /* 暂停时防止变形 */ transform: none !important; -webkit-transform: none !important; /* 暂停时的溢出控制 */ overflow: hidden !important; clip-path: inset(0 0 0 0) !important; } project.private.config.json
@@ -3,7 +3,7 @@ "projectname": "irrigate_user", "setting": { "compileHotReLoad": true, "urlCheck": false "urlCheck": true }, "libVersion": "3.9.1" } utils/projectConfig.js
@@ -4,8 +4,8 @@ URL_233: 'https://wanzheng.dayuyanjiuyuan.top/', URL_55: 'https://irrigate.dayuyanjiuyuan.top/', URL_166: 'https://wanzheng.dayuyanjiuyuan.top/frp/', // URL_121: 'https://shifanqu1.dayuyanjiuyuan.top/', URL_121: 'https://wanzheng.dayuyanjiuyuan.top/frp/', URL_121: 'https://shifanqu1.dayuyanjiuyuan.top/', // URL_121: 'https://wanzheng.dayuyanjiuyuan.top/frp/', URL_87: 'http://192.168.10.87:54321/' };