管灌系统农户端微信小程序(嘉峪关应用)
添加综合站监测功能,更新首页逻辑以支持登录检查并跳转至监测页面;在app.json中添加新页面和插件配置。
3个文件已修改
18个文件已添加
1165 ■■■■■ 已修改文件
app.json 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/camera.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/fertilizer.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/flow.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/humidity.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/monitor.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/offline.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/pause.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/play.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/pressure.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/soil.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/stop.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/temperature.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/water-level.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/weather.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/home/home.js 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/home/home.wxml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/stationMonitor/stationMonitor.js 517 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/stationMonitor/stationMonitor.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/stationMonitor/stationMonitor.wxml 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/stationMonitor/stationMonitor.wxss 429 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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;
}