app.json
@@ -2,6 +2,7 @@ "pages": [ "pages/home/home", "pages/stationMonitor/stationMonitor", "pages/valveList/valveList", "pages/feedback/feedback", "pages/wxlogin/wxlogin", @@ -29,5 +30,11 @@ }, "componentFramework": "glass-easel", "sitemapLocation": "sitemap.json", "lazyCodeLoading": "requiredComponents" "lazyCodeLoading": "requiredComponents", "plugins": { "ezplayer": { "version": "1.0.13", "provider": "wxf2b3a0262975d8c2" } } } images/camera.svg
New file @@ -0,0 +1 @@ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1754548431492" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16574" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1024.2 480L861.9 710c-9.3 13.2-21.5 23.7-35.4 30.9-13.9 7.2-29.5 11.1-45.7 11.1-18.7 0-37-5.3-52.8-15.2L98.4 341.5C87.8 334.8 79.2 325.6 73.3 315c-5.9-10.7-9.1-22.8-9.1-35.3 0-15.3 4.8-30.3 13.8-42.8l117.6-163c9.3-12.9 21.4-23.1 35.1-30.1 13.7-7 29.1-10.8 45-10.8 18.4 0 36.4 5.1 52.1 14.8L1024.2 480zM754.1 768c-11.1 0-22-1.5-32.6-4.5-10.6-3-20.7-7.4-30.2-13.2l-611.8-373c-6.7-4.1-15.3 0.7-15.3 8.6v22.6c0 14.6 7.6 28.2 20 35.8l182.5 112.3C241.2 572 224.2 600 224.2 632c0 20.2 6.8 38.8 18.3 53.7a62.518 62.518 0 0 1-44.2 18.3H64.2v-46.8c0-29-19.5-54.4-47.6-61.9L0.2 591l-0.2 369 28.8-14.3c21.7-10.8 35.5-33 35.5-57.3V768h135.5c25.9 0 50.8-10.3 69.2-28.7l3.1-3.1c10.6-10.6 25-16.3 40-16.3h0.2c46.4 0 84.4-35.9 87.8-81.4L674.2 807.2c9.2 5.7 19.9 8.7 30.7 8.7 9.8 0 19.3-2.5 27.7-6.9 8.4-4.5 15.7-11 21.1-19.2l6.8-10.1c3.2-5-0.4-11.7-6.4-11.7zM312.2 656c-13.2 0-24-10.8-24-24s10.8-24 24-24 24 10.8 24 24-10.8 24-24 24z" p-id="16575" fill="#707070"></path></svg> images/fertilizer.svg
New file @@ -0,0 +1 @@ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1754548404907" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15411" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M789.650286 57.161143a99.620571 99.620571 0 0 1 100.352 95.341714v707.584a50.176 50.176 0 0 1 0 100.352H87.076571a50.176 50.176 0 1 1 0-100.352H287.817143V257.901714H187.465143v105.398857a53.357714 53.357714 0 0 1-50.176 45.165715 47.542857 47.542857 0 0 1-50.176-50.176V252.891429a103.350857 103.350857 0 0 1 100.352-95.341715H287.817143a99.620571 99.620571 0 0 1 95.341714-100.388571z m0 702.573714H388.169143v100.352h401.481143zM137.252571 508.818286a47.542857 47.542857 0 0 1 50.176 50.176v150.564571a50.176 50.176 0 0 1-100.352 0v-150.564571a47.542857 47.542857 0 0 1 50.176-50.176z m652.397715-351.268572H388.169143v131.437715h401.481143z" fill="#707070" p-id="15412"></path></svg> images/flow.svg
New file @@ -0,0 +1,3 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-8 14H9v-4h2v4zm4 0h-2V9h2v8zm4 0h-2V7h2v10z" fill="currentColor"/> </svg> images/humidity.svg
New file @@ -0,0 +1,3 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 2l-4.09 4.09L12 10.18l4.09-4.09L12 2zm0 16.36c-2.76 0-5-2.24-5-5 0-1.38.56-2.63 1.46-3.54L12 6.27l3.54 3.55c.9.9 1.46 2.16 1.46 3.54 0 2.76-2.24 5-5 5z" fill="currentColor"/> </svg> images/monitor.svg
New file @@ -0,0 +1 @@ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1754546726528" class="icon" viewBox="0 0 1033 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10882" xmlns:xlink="http://www.w3.org/1999/xlink" width="201.7578125" height="200"><path d="M608 291.2h233.6c12.8 0 25.6-12.8 25.6-25.6 0-16-12.8-25.6-25.6-25.6H608c-12.8 0-25.6 12.8-25.6 25.6-3.2 12.8 9.6 25.6 25.6 25.6z m310.4 73.6H704c-12.8 0-25.6 12.8-25.6 25.6 0 16 12.8 25.6 25.6 25.6h214.4c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6z m-291.2 0H608c-12.8 0-25.6 12.8-25.6 25.6 0 16 12.8 25.6 25.6 25.6h19.2c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6z m291.2 89.6H704c-12.8 0-25.6 12.8-25.6 25.6 0 16 12.8 25.6 25.6 25.6h214.4c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6z m-291.2 0H608c-12.8 0-25.6 12.8-25.6 25.6 0 16 12.8 25.6 25.6 25.6h19.2c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6z m291.2 89.6H704c-12.8 0-25.6 12.8-25.6 25.6 0 16 12.8 25.6 25.6 25.6h214.4c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6z m-291.2 0H608c-12.8 0-25.6 12.8-25.6 25.6 0 16 12.8 25.6 25.6 25.6h19.2c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6z m-236.8-124.8H320v-96c0-12.8-9.6-22.4-22.4-22.4-12.8 0-22.4 9.6-22.4 22.4V448l3.2 3.2v3.2c3.2 6.4 9.6 9.6 19.2 9.6h92.8c12.8 0 22.4-9.6 22.4-22.4 0-12.8-9.6-22.4-22.4-22.4zM297.6 224C246.4 224 198.4 246.4 160 281.6c-38.4 38.4-57.6 86.4-57.6 137.6 0 51.2 19.2 102.4 57.6 137.6 38.4 38.4 86.4 57.6 137.6 57.6 51.2 0 102.4-19.2 137.6-57.6 38.4-38.4 57.6-86.4 57.6-137.6s-19.2-102.4-57.6-137.6C400 246.4 348.8 224 297.6 224z m0 345.6c-41.6 0-76.8-16-105.6-44.8-28.8-28.8-44.8-67.2-44.8-105.6 0-41.6 16-76.8 44.8-105.6 28.8-28.8 67.2-44.8 105.6-44.8 41.6 0 76.8 16 105.6 44.8 28.8 28.8 44.8 67.2 44.8 105.6 0 41.6-16 76.8-44.8 105.6-25.6 28.8-64 44.8-105.6 44.8zM1027.2 124.8c0-32-12.8-60.8-54.4-64-19.2-3.2-38.4-3.2-57.6-3.2H163.2c-38.4 0-76.8 0-115.2 3.2C22.4 64 6.4 86.4 3.2 112c0 19.2-3.2 35.2-3.2 54.4v604.8c0 28.8 19.2 44.8 44.8 51.2 19.2 3.2 35.2 3.2 54.4 3.2h323.2v54.4c0 16 0 22.4-12.8 25.6H272c-6.4 0-9.6 9.6-9.6 19.2v22.4c0 9.6 3.2 19.2 9.6 19.2h483.2c6.4 0 9.6-9.6 9.6-19.2v-22.4c0-9.6-3.2-19.2-9.6-19.2h-137.6c-16 0-12.8-3.2-12.8-28.8v-16-35.2h364.8c9.6 0 22.4-3.2 32-9.6 25.6-12.8 32-35.2 32-60.8v-281.6c-6.4-112-6.4-230.4-6.4-348.8zM486.4 752c0-16 12.8-25.6 25.6-25.6s25.6 12.8 25.6 25.6c0 16-12.8 25.6-25.6 25.6-12.8 3.2-25.6-9.6-25.6-25.6z m480-396.8v320c0 16-9.6 22.4-25.6 25.6H96c-28.8 0-38.4-6.4-38.4-35.2v-131.2-172.8-185.6c0-25.6 6.4-32 32-32h844.8c19.2 0 25.6 6.4 25.6 25.6v92.8c6.4 28.8 6.4 60.8 6.4 92.8z" fill="#3F8BFD" p-id="10883"></path></svg> images/offline.svg
New file @@ -0,0 +1,4 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="currentColor" opacity="0.3"/> <path d="M3 3l18 18-1.41 1.41L3 4.41 3 3z" fill="currentColor"/> </svg> images/pause.svg
New file @@ -0,0 +1,3 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z" fill="currentColor"/> </svg> images/play.svg
New file @@ -0,0 +1,3 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M8 5v14l11-7L8 5z" fill="currentColor"/> </svg> images/pressure.svg
New file @@ -0,0 +1,5 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" fill="currentColor"/> <path d="M12 6c-.55 0-1 .45-1 1v5c0 .55.45 1 1 1s1-.45 1-1V7c0-.55-.45-1-1-1z" fill="currentColor"/> <circle cx="12" cy="16" r="1" fill="currentColor"/> </svg> images/soil.svg
New file @@ -0,0 +1 @@ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1754548305051" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14084" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M981.369394 925.762065l-152.807226-541.729033A70.689032 70.689032 0 0 0 761.572748 330.322581H620.921394v360.051613c0 58.268903-43.668645 105.703226-97.445162 105.703225s-97.379097-47.434323-97.379097-105.703225V330.322581H272.695329a70.755097 70.755097 0 0 0-66.593032 52.389161l-163.047226 541.729032A80.532645 80.532645 0 0 0 52.766555 992.619355a67.782194 67.782194 0 0 0 56.881548 31.380645h804.797936a67.848258 67.848258 0 0 0 56.286967-30.786065 80.400516 80.400516 0 0 0 10.636388-67.45187z" p-id="14085" fill="#707070"></path><path d="M523.872619 766.348387c39.110194 0 70.887226-35.278452 70.887226-78.616774V78.616774C594.495587 35.278452 562.982813 0 523.872619 0S453.051458 35.278452 453.051458 78.616774v608.850581c0 43.338323 31.777032 78.881032 70.821161 78.881032zM494.407845 78.616774a29.662968 29.662968 0 1 1 58.995613 0V112.309677H535.037523a9.909677 9.909677 0 0 0-9.909678 9.909678v3.303226a9.909677 9.909677 0 0 0 9.909678 9.909677h18.101677v85.223226H535.037523a9.909677 9.909677 0 0 0-9.909678 9.909677v3.303226a9.909677 9.909677 0 0 0 9.909678 9.909678h18.101677v85.223225H535.037523a9.909677 9.909677 0 0 0-9.909678 9.909678v3.303226a9.909677 9.909677 0 0 0 9.909678 9.909677h18.101677v85.223226H535.037523a9.909677 9.909677 0 0 0-9.909678 9.909677v3.303226a9.909677 9.909677 0 0 0 9.909678 9.909677h18.101677v85.223226H535.037523a9.909677 9.909677 0 0 0-9.909678 9.909678v3.303225a9.909677 9.909677 0 0 0 9.909678 9.909678h18.101677V654.03871H535.037523a9.909677 9.909677 0 0 0-9.909678 9.909677v3.303226a9.909677 9.909677 0 0 0 9.909678 9.909677h18.101677v10.306065a29.662968 29.662968 0 1 1-58.995613 0z" p-id="14086" fill="#707070"></path></svg> images/stop.svg
New file @@ -0,0 +1,3 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M6 6h12v12H6V6z" fill="currentColor"/> </svg> images/temperature.svg
New file @@ -0,0 +1,3 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M15 13V5c0-1.66-1.34-3-3-3S9 3.34 9 5v8c-1.21.91-2 2.37-2 4 0 2.76 2.24 5 5 5s5-2.24 5-5c0-1.63-.79-3.09-2-4zm-4-8c0-.55.45-1 1-1s1 .45 1 1h-2v1h2v1h-2v1h2v4.51c-.6-.29-1.29-.51-2-.51s-1.4.22-2 .51V5z" fill="currentColor"/> </svg> images/water-level.svg
New file @@ -0,0 +1,4 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 2C8.13 2 5 5.13 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.87-3.13-7-7-7zm0 12.5c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z" fill="currentColor"/> <path d="M12 7c-1.1 0-2 .9-2 2v2c0 .55.45 1 1 1s1-.45 1-1V9c0-.55-.45-1-1-1z" fill="currentColor"/> </svg> images/weather.svg
New file @@ -0,0 +1 @@ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1754548228637" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12830" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M624.870875 191.348713H461.322593V82.375927C461.322593 36.755267 498.062669 0.000342 542.421038 0.000342a82.21223 82.21223 0 0 1 82.449837 82.375585zM966.951569 464.44895h-136.861979v-114.051649a49.481783 49.481783 0 0 1 49.422381-49.422381h36.740076a49.481783 49.481783 0 0 1 49.422381 49.422381v114.051649z" p-id="12831" fill="#707070"></path><path d="M515.808986 163.474371h54.486393v191.363222h-54.486393z" p-id="12832" fill="#707070"></path><path d="M461.307743 287.668894h163.474029V819.746566H461.307743z" p-id="12833" fill="#707070"></path><path d="M461.307743 737.534336h163.474029v286.465664H461.307743zM624.781772 465.07267h368.766997v40.556647H624.781772zM119.152797 690.651386h491.684379v40.556647H119.152797z" p-id="12834" fill="#707070"></path><path d="M119.152797 655.173601h40.556647v43.081228H119.152797zM30.450909 354.837593c0 9.949818 20.790665 19.157113 54.486393 24.132022a398.631302 398.631302 0 0 0 108.987637 0c33.725429-4.974909 54.501244-14.182204 54.501243-24.132022 0-15.399943-48.798661-27.889192-108.987636-27.889192S30.450909 339.43765 30.450909 354.837593z m0 0M30.450909 594.346055c0 9.949818 20.790665 19.157113 54.486393 24.132022a398.631302 398.631302 0 0 0 108.987637 0c33.725429-4.974909 54.501244-14.182204 54.501243-24.132022 0-15.399943-48.798661-27.889192-108.987636-27.889192S30.450909 578.946112 30.450909 594.346055z m0 0" p-id="12835" fill="#707070"></path><path d="M44.395505 396.656531h191.392923v27.874342H44.395505z m0 45.605809h191.392923v27.889192H44.395505z m0 45.63551h191.392923v27.904042H44.395505z m0 45.620659h191.392923v27.874342H44.395505z m46.88295 95.043041h96.305331v34.156092H91.278455zM624.870875 82.375927h128.872423v40.541797h-128.872423z m-337.432494 0h172.592221v40.541797H287.364128z m63.708538 0" p-id="12836" fill="#707070"></path><path d="M214.210688 102.646825a62.089836 62.089836 0 1 0 31.037493-53.77357 62.104687 62.104687 0 0 0-31.037493 53.77357z m0 0M809.803841 163.474371a62.104687 62.104687 0 0 1 0-124.194523c32.953204 1.277141 32.953204 124.194523 0 124.194523zM119.167647 342.155287h40.541797v287.668553h-40.541797z m0 0" p-id="12837" fill="#707070"></path></svg> pages/home/home.js
@@ -496,6 +496,47 @@ icon: 'none' }) }, // 综合站监测 monitor() { const app = getApp(); // 检查当前项目是否需要登录 const currentProject = app.globalData.selectedProject; if (currentProject && PROJECT_CONFIG[currentProject] && PROJECT_CONFIG[currentProject].needLogin) { // 需要登录的项目,检查是否已登录 if (!app.globalData.isLoggedIn) { // 未登录,显示提示并阻止操作 wx.showToast({ title: '请先登录', icon: 'error', duration: 2000 }); // 获取项目配置 const projectConfig = PROJECT_CONFIG[currentProject]; if (projectConfig) { // 询问用户是否前往登录 wx.showModal({ title: '提示', content: '您需要登录后才能使用综合站监测功能,是否立即登录?', confirmText: '前往登录', cancelText: '取消', success: (res) => { if (res.confirm) { // 用户点击确认,直接调用wxLogin方法 this.wxLogin(); } } }); } return; } } // 已登录或不需要登录的项目,跳转到综合站监测页面 wx.navigateTo({ url: '/pages/stationMonitor/stationMonitor', }) }, //解绑用户 unbind() { // 检查当前项目是否需要登录 pages/home/home.wxml
@@ -30,9 +30,13 @@ </view> <view class="center-wrapper"> <view class="center-view" bind:tap="recharge"> <!-- <view class="center-view" bind:tap="recharge"> <image src="/images/wallet.svg" /> <text>充值</text> </view> --> <view class="center-view" bind:tap="monitor"> <image src="/images/monitor.svg" /> <text>综合站监测</text> </view> <view class="center-view" bind:tap="openValve"> <image src="/images/valva.svg" /> pages/stationMonitor/stationMonitor.js
New file @@ -0,0 +1,517 @@ // pages/stationMonitor/stationMonitor.js const { get } = require('../../api/request.js'); Page({ /** * 页面的初始数据 */ data: { activeTab: 'weather', // 默认选中气象站 cameraList: [], isLoading: false }, /** * 生命周期函数--监听页面加载 */ onLoad(options) { // 页面加载时获取摄像头信息 this.getCameraList(); }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady() { }, /** * 生命周期函数--监听页面显示 */ onShow() { }, /** * 生命周期函数--监听页面隐藏 */ onHide() { }, /** * 生命周期函数--监听页面卸载 */ onUnload() { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh() { // 下拉刷新时重新获取摄像头列表 if (this.data.activeTab === 'camera') { this.getCameraList(); } wx.stopPullDownRefresh(); }, /** * 页面上拉触底事件的处理函数 */ onReachBottom() { }, /** * 用户点击右上角分享 */ onShareAppMessage() { }, /** * 切换选项卡 */ switchTab(e) { const tab = e.currentTarget.dataset.tab; console.log('切换到:', tab); this.setData({ activeTab: tab }); // 如果切换到摄像头选项卡,确保有数据 if (tab === 'camera' && this.data.cameraList.length === 0) { this.getCameraList(); } }, /** * 获取摄像头列表 */ getCameraList() { const app = getApp(); // 检查登录状态 if (!app.globalData.isLoggedIn) { wx.showToast({ title: '请先登录', icon: 'error' }); return; } this.setData({ isLoading: true }); // 模拟接口返回数据 setTimeout(() => { const mockResponse = { "code": "0001", "content": { "itemTotal": 4, "obj": [ { "id": "2025070715040300007", "name": "民勤01", "videoUrl4PcLive": "https://open.ys7.com/console/jssdk/pc.html?url=ezopen://open.ys7.com/FX6737162/1.live&accessToken=at.2o04glgs0q36cjugbvddqujz7tqrghx1-1ovr6lmf3k-03pij3c-304ziif7e&themeId=pcLive&env=&date=", "videoUrl4Security": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056878/1.live&themeId=security&date=", "videoUrl4Simple": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056878/1.live&themeId=simple&date=", "videoUrl4Standard": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056878/1.live&themeId=standard&date=" }, { "id": "2025070715040300008", "name": "民勤02", "videoUrl4PcLive": "https://open.ys7.com/console/jssdk/pc.html?url=ezopen://open.ys7.com/FY4056879/1.live&accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&themeId=pcLive&env=&date=", "videoUrl4Security": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056879/1.live&themeId=security&date=", "videoUrl4Simple": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056879/1.live&themeId=simple&date=", "videoUrl4Standard": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056879/1.live&themeId=standard&date=" }, { "id": "2025070715040300009", "name": "民勤03", "videoUrl4PcLive": "https://open.ys7.com/console/jssdk/pc.html?url=ezopen://open.ys7.com/FY4056880/1.live&accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&themeId=pcLive&env=&date=", "videoUrl4Security": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056880/1.live&themeId=security&date=", "videoUrl4Simple": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056880/1.live&themeId=simple&date=", "videoUrl4Standard": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056880/1.live&themeId=standard&date=" }, { "id": "2025070715040300010", "name": "民勤04", "videoUrl4PcLive": "https://open.ys7.com/console/jssdk/pc.html?url=ezopen://open.ys7.com/FY4056881/1.live&accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&themeId=pcLive&env=&date=", "videoUrl4Security": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056881/1.live&themeId=security&date=", "videoUrl4Simple": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056881/1.live&themeId=simple&date=", "videoUrl4Standard": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056881/1.live&themeId=standard&date=" } ], "pageCurr": 1, "pageSize": 4, "pageTotal": 1 }, "msg": "请求成功", "success": true }; console.log('模拟接口返回数据:', mockResponse); if (mockResponse.success && mockResponse.code === '0001') { // 处理返回的摄像头数据 const cameraList = mockResponse.content.obj.map(item => { // 从萤石云URL中提取设备信息并生成RTMP地址 let rtmpUrl = ''; if (item.videoUrl4PcLive) { // 提取设备序列号和通道号 const ezopenMatch = item.videoUrl4PcLive.match(/ezopen:\/\/open\.ys7\.com\/([^\/]+)\/(\d+)\.live/); const tokenMatch = item.videoUrl4PcLive.match(/accessToken=([^&]+)/); if (ezopenMatch && tokenMatch) { const deviceSerial = ezopenMatch[1]; // 设备序列号 const channelNo = ezopenMatch[2]; // 通道号 const accessToken = tokenMatch[1]; // 访问令牌 // 生成RTMP地址 rtmpUrl = `rtmp://open.ys7.com:1935/live/${deviceSerial}/${channelNo}?accessToken=${accessToken}`; // 备用HLS地址 const hlsUrl = `https://open.ys7.com:443/live/${deviceSerial}/${channelNo}.m3u8?accessToken=${accessToken}`; console.log('生成的RTMP地址:', rtmpUrl); console.log('生成的HLS地址:', hlsUrl); } } return { id: item.id, name: item.name, online: true, // 默认在线,实际项目中可能需要额外的状态检查 thumbnail: '/images/camera-thumb1.jpg', // 默认缩略图 lastUpdate: new Date().toLocaleString(), // 当前时间作为最后更新时间 isPlaying: false, // 视频播放状态 rtmpUrl: rtmpUrl, // RTMP流地址 videoUrl4PcLive: item.videoUrl4PcLive, // 原始PC播放地址 videoUrl4Security: item.videoUrl4Security, videoUrl4Simple: item.videoUrl4Simple, videoUrl4Standard: item.videoUrl4Standard }; }); this.setData({ cameraList: cameraList, isLoading: false }); console.log('处理后的摄像头列表:', cameraList); } else { console.error('获取摄像头列表失败:', mockResponse.msg); this.setData({ isLoading: false }); wx.showToast({ title: mockResponse.msg || '获取摄像头列表失败', icon: 'none' }); } }, 1000); // 模拟网络延迟1秒 }, /** * 播放视频 */ playVideo(e) { const camera = e.currentTarget.dataset.camera; console.log('播放摄像头:', camera.name); if (!camera.online) { wx.showToast({ title: '摄像头离线', icon: 'error' }); return; } // 检查视频URL是否有效 if (!camera.videoUrl4PcLive) { wx.showToast({ title: '视频地址无效', icon: 'error' }); return; } console.log('视频URL:', camera.videoUrl4PcLive); // 更新视频播放状态 const cameraList = this.data.cameraList.map(item => { if (item.id === camera.id) { return { ...item, isPlaying: true }; } return item; }); this.setData({ cameraList: cameraList }); // 延迟一下让live-player组件更新 setTimeout(() => { console.log('开始播放直播:', camera.videoUrl4PcLive); }, 100); }, /** * 暂停视频 */ 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 }); }, /** * 测试视频URL */ testVideoUrl(e) { const camera = e.currentTarget.dataset.camera; console.log('测试视频URL:', camera.name); console.log('原始URL:', camera.videoUrl4PcLive); console.log('RTMP URL:', camera.rtmpUrl); // 显示URL信息 wx.showModal({ title: '视频URL信息', content: `摄像头: ${camera.name}\n原始URL: ${camera.videoUrl4PcLive}\nRTMP URL: ${camera.rtmpUrl}`, showCancel: false, confirmText: '确定' }); }, /** * 直播播放器状态变化 */ 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; } // 使用PC直播URL进行全屏播放 if (camera.videoUrl4PcLive) { console.log('全屏视频URL:', camera.videoUrl4PcLive); 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' }); } }); } }) pages/stationMonitor/stationMonitor.json
New file @@ -0,0 +1,6 @@ { "navigationBarTitleText": "综合站监测", "usingComponents": { "ezplayer": "plugin://ezplayer/ezplayer" } } pages/stationMonitor/stationMonitor.wxml
New file @@ -0,0 +1,121 @@ <!--pages/stationMonitor/stationMonitor.wxml--> <view class="container"> <!-- 顶部选项卡 --> <view class="tab-container"> <view class="tab-item {{activeTab === 'weather' ? 'active' : ''}}" bind:tap="switchTab" data-tab="weather"> <image class="tab-icon" src="/images/weather.svg" /> <text class="tab-name">气象站</text> </view> <view class="tab-item {{activeTab === 'soil' ? 'active' : ''}}" bind:tap="switchTab" data-tab="soil"> <image class="tab-icon" src="/images/soil.svg" /> <text class="tab-name">土壤墒情站</text> </view> <view class="tab-item {{activeTab === 'fertilizer' ? 'active' : ''}}" bind:tap="switchTab" data-tab="fertilizer"> <image class="tab-icon" src="/images/fertilizer.svg" /> <text class="tab-name">水肥机</text> </view> <view class="tab-item {{activeTab === 'camera' ? 'active' : ''}}" bind:tap="switchTab" data-tab="camera"> <image class="tab-icon" src="/images/camera.svg" /> <text class="tab-name">摄像头</text> </view> </view> <!-- 内容区域 --> <view class="content-area"> <!-- 气象站内容 --> <view wx:if="{{activeTab === 'weather'}}" class="tab-content"> <text class="content-title">气象站监测</text> <!-- 在这里添加气象站相关内容 --> </view> <!-- 土壤墒情站内容 --> <view wx:elif="{{activeTab === 'soil'}}" class="tab-content"> <text class="content-title">土壤墒情站监测</text> <!-- 在这里添加土壤墒情站相关内容 --> </view> <!-- 水肥机内容 --> <view wx:elif="{{activeTab === 'fertilizer'}}" class="tab-content"> <text class="content-title">水肥机监测</text> <!-- 在这里添加水肥机相关内容 --> </view> <!-- 摄像头内容 --> <view wx:elif="{{activeTab === 'camera'}}" class="tab-content"> <!-- 摄像头列表 --> <view class="camera-list"> <view wx:for="{{cameraList}}" wx:key="id" class="camera-item"> <!-- 摄像头名称 --> <view class="camera-header"> <text class="camera-name">{{item.name}}</text> <view class="camera-status {{item.online ? 'online' : 'offline'}}"> <text>{{item.online ? '在线' : '离线'}}</text> </view> </view> <!-- 摄像头视频 --> <view class="camera-video-container"> <view wx:if="{{item.online}}" class="video-wrapper"> <!-- 直播播放器组件 --> <ezplayer id="ezplayer" accessToken="at.2o04glgs0q36cjugbvddqujz7tqrghx1-1ovr6lmf3k-03pij3c-304ziif7e" url="rtmp://open.ys7.com/BA7248908/1/live" plugins="talk,voice,capture" recPlayTime="" width="360" height="300" watermark="shuiyin" theme="{{ { showFullScreenBtn: true, showHdBtn: true, showTimeLine: true } }}" bind:handleError="handleError" bind:onControlEvent="onControlEvent" /> <!-- 备用HLS播放器 --> <video wx:if="{{item.isPlaying && !item.rtmpUrl}}" class="live-player" src="{{item.hlsUrl}}" autoplay="{{true}}" muted="{{true}}" controls="{{false}}" object-fit="contain" bind:load="onVideoLoad" bind:play="onVideoPlay" bind:pause="onVideoPause" bind:ended="onVideoEnded" bind:error="onVideoError" data-camera="{{item}}" /> <!-- 视频控制覆盖层 --> <view class="video-overlay" wx:if="{{!item.isPlaying}}"> <view class="play-overlay" bind:tap="playVideo" data-camera="{{item}}"> <image class="play-icon" src="/images/play.svg" /> </view> </view> <!-- 视频信息 --> <view class="video-info"> <text class="video-time">{{item.lastUpdate}}</text> <text class="video-status">{{item.isPlaying ? '直播中' : '已停止'}}</text> </view> <!-- 播放控制按钮 --> <view class="video-controls" wx:if="{{item.isPlaying}}"> <view class="control-btn" bind:tap="pauseVideo" data-camera="{{item}}"> <image class="control-icon" src="/images/pause.svg" /> </view> <view class="control-btn" bind:tap="stopVideo" data-camera="{{item}}"> <image class="control-icon" src="/images/stop.svg" /> </view> </view> <!-- 调试按钮 --> <view class="debug-controls"> <view class="debug-btn" bind:tap="testVideoUrl" data-camera="{{item}}"> <text>测试URL</text> </view> </view> </view> <!-- 离线状态 --> <view wx:else class="video-offline"> <image class="offline-icon" src="/images/offline.svg" /> <text class="offline-text">摄像头离线</text> </view> </view> <!-- 摄像头操作按钮 --> <view class="camera-actions"> <button class="action-btn {{item.online ? 'primary' : 'disabled'}}" disabled="{{!item.online}}" bind:tap="fullscreenVideo" data-camera="{{item}}"> 全屏播放 </button> <button class="action-btn secondary" bind:tap="cameraSettings" data-camera="{{item}}"> 设置 </button> </view> </view> </view> </view> </view> </view> pages/stationMonitor/stationMonitor.wxss
New file @@ -0,0 +1,429 @@ /* pages/stationMonitor/stationMonitor.wxss */ /* 重置页面默认样式 */ page { margin: 0; padding: 0; width: 100%; box-sizing: border-box; } .container { padding: 0; margin: 0; background-color: #f5f5f5; min-height: 100vh; width: 100%; box-sizing: border-box; } /* 选项卡容器 */ .tab-container { display: flex; width: 100%; background: white; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); position: sticky; top: 0; z-index: 100; margin: 0; padding: 0; box-sizing: border-box; } /* 选项卡项 */ .tab-item { flex: 1; width: 25%; /* 明确设置每个选项卡占25%宽度 */ display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20rpx 5rpx; position: relative; transition: all 0.3s ease; box-sizing: border-box; } .tab-item.active { background-color: #f0f8ff; } .tab-item.active::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 60rpx; height: 4rpx; background-color: #1890FF; border-radius: 2rpx; } /* 选项卡图标 */ .tab-icon { width: 48rpx; height: 48rpx; margin-bottom: 8rpx; transition: all 0.3s ease; } .tab-item.active .tab-icon { transform: scale(1.1); filter: brightness(0) saturate(100%) invert(41%) sepia(96%) saturate(1408%) hue-rotate(200deg) brightness(96%) contrast(103%); } .tab-item:not(.active) .tab-icon { filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); } /* 选项卡文字 */ .tab-name { font-size: 22rpx; color: #666; text-align: center; line-height: 1.2; transition: color 0.3s ease; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } .tab-item.active .tab-name { color: #1890FF; font-weight: 600; } /* 内容区域 */ .content-area { padding: 0; margin: 0; width: 100%; min-height: calc(100vh - 140rpx); box-sizing: border-box; } /* 选项卡内容 */ .tab-content { width: 100%; background: white; border-radius: 0; padding: 20rpx 0; box-shadow: none; animation: fadeIn 0.3s ease-in-out; box-sizing: border-box; margin: 0; display: block; } @keyframes fadeIn { from { opacity: 0; transform: translateY(20rpx); } to { opacity: 1; transform: translateY(0); } } /* 内容标题 */ .content-title { font-size: 32rpx; font-weight: 600; color: #333; margin: 0 20rpx 20rpx 20rpx; display: block; width: calc(100% - 40rpx); box-sizing: border-box; } /* 响应式适配 */ @media (max-width: 400px) { .tab-name { font-size: 20rpx; } .tab-icon { width: 40rpx; height: 40rpx; } .tab-item { padding: 16rpx 2rpx; width: 25%; /* 确保小屏幕下也均分 */ } } /* 超小屏幕适配 */ @media (max-width: 320px) { .tab-name { font-size: 18rpx; } .tab-icon { width: 36rpx; height: 36rpx; } .tab-item { padding: 12rpx 1rpx; } } /* 摄像头列表样式 */ .camera-list { display: flex; flex-direction: column; gap: 0; margin: 0; width: 100%; } .camera-item { width: 100vw; background: #f8f9fa; border-radius: 0; padding: 20rpx; box-shadow: none; box-sizing: border-box; margin: 0 0 2rpx 0; position: relative; left: 0; right: 0; } /* 摄像头头部 */ .camera-header { width: 100%; display: flex; justify-content: space-between; align-items: center; margin-bottom: 16rpx; box-sizing: border-box; } .camera-name { font-size: 28rpx; font-weight: 600; color: #333; } .camera-status { padding: 8rpx 16rpx; border-radius: 20rpx; font-size: 22rpx; } .camera-status.online { background-color: #f6ffed; color: #52c41a; border: 1rpx solid #b7eb8f; } .camera-status.offline { background-color: #fff2f0; color: #ff4d4f; border: 1rpx solid #ffccc7; } /* 视频容器 */ .camera-video-container { position: relative; width: 100%; height: 400rpx; border-radius: 0; overflow: hidden; margin-bottom: 16rpx; box-sizing: border-box; } .video-wrapper { position: relative; width: 100%; height: 100%; } .live-player { width: 100%; height: 100%; background-color: #000; border-radius: 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; 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 { width: 100%; height: 100%; background-color: #f5f5f5; display: flex; flex-direction: column; align-items: center; justify-content: center; border-radius: 12rpx; } .offline-icon { width: 60rpx; height: 60rpx; opacity: 0.5; margin-bottom: 16rpx; } .offline-text { font-size: 24rpx; color: #999; } /* 操作按钮 */ .camera-actions { width: 100%; display: flex; gap: 16rpx; box-sizing: border-box; } .action-btn { flex: 1; height: 64rpx; border-radius: 32rpx; font-size: 26rpx; border: none; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; } .action-btn.primary { background-color: #1890FF; color: white; } .action-btn.primary:active { background-color: #0050b3; } .action-btn.secondary { background-color: white; color: #1890FF; border: 1rpx solid #1890FF; } .action-btn.secondary:active { background-color: #f0f8ff; } .action-btn.disabled { background-color: #f5f5f5; color: #bfbfbf; cursor: not-allowed; }