Selaa lähdekoodia

feat:对接实时播报的接口和功能

wangsisi 1 viikko sitten
vanhempi
commit
945cd5445b
3 muutettua tiedostoa jossa 169 lisäystä ja 77 poistoa
  1. 4 0
      src/api/modules/monitor.js
  2. 2 1
      src/components/weatherInfo.vue
  3. 163 76
      src/views/old_mini/monitor/index.vue

+ 4 - 0
src/api/modules/monitor.js

@@ -4,5 +4,9 @@ module.exports = {
     getCountByStatusAndFarmId: {
         url: config.base_dev_url + "z_farm_work_record/getCountByStatusAndFarmId",
         type: "get",
+    },
+    broadcastPage: {
+        url: config.base_dev_url + "farm_broadcast/page/{limit}/{page}",
+        type: "get",
     }
 }

+ 2 - 1
src/components/weatherInfo.vue

@@ -147,7 +147,8 @@ const handleAddGarden = () => {
                         font-weight: 500;
                         color: #000000;
                         span {
-                            max-width: fit-content;
+                            // max-width: fit-content;
+                            width: 100%;
                             display: inline-block;
                         }
 

+ 163 - 76
src/views/old_mini/monitor/index.vue

@@ -3,7 +3,13 @@
         <!-- 天气遮罩 -->
         <div class="weather-mask" v-show="isExpanded"></div>
         <!-- 天气 -->
-        <weather-info class="weather-info" @weatherExpanded="weatherExpanded" @changeGarden="changeGarden" :isGarden="true"></weather-info>        <!-- 操作按钮 -->
+        <weather-info
+            class="weather-info"
+            @weatherExpanded="weatherExpanded"
+            @changeGarden="changeGarden"
+            :isGarden="true"
+        ></weather-info>
+        <!-- 操作按钮 -->
         <div class="operation-button">
             <div class="button-group">
                 <div class="button-item" @click="toFarmInfo">
@@ -43,23 +49,24 @@
             <div class="broadcast-header">
                 <div class="header-left">
                     <span class="broadcast-title">实时播报</span>
-                    <div class="broadcast-action" @click="handleBroadcast">
+                    <div class="broadcast-action" :class="{ speaking: isSpeaking }" @click="handleBroadcast">
                         <img class="speaker-icon" src="@/assets/img/monitor/speaker.png" alt="播报" />
-                        <span class="broadcast-text">点击播报</span>
+                        <span class="broadcast-text">{{ isSpeaking ? '停止播报' : '点击播报' }}</span>
                     </div>
                 </div>
-                <div class="more-link" @click="handleMoreBroadcast">
-                    <span>更多</span>
-                    <el-icon size="12"><ArrowRightBold /></el-icon>
-                </div>
             </div>
 
-            <div class="broadcast-list">
+            <list
+                v-model:loading="loading"
+                :finished="finished"
+                finished-text="暂无更多播报"
+                @load="onLoad"
+                class="broadcast-list"
+            >
                 <div
                     v-for="(item, index) in broadcastList"
                     :key="index"
                     class="broadcast-item"
-                    @click="handleBroadcastItem(item)"
                 >
                     <div class="item-content">
                         <div class="content-top">
@@ -68,16 +75,14 @@
                             </div>
                             <div class="item-title">{{ item.title }}</div>
                         </div>
-                        <div class="item-status">
-                            距离执行还差 <span class="countdown">{{ item.daysLeft }}</span> 天
-                        </div>
+                        <div class="item-status van-multi-ellipsis--l2">{{ item.content }}</div>
                     </div>
                     <div class="item-zone">
                         <div class="point"></div>
-                        <span>{{ item.zone }}</span>
+                        <span>{{ item.regionId }}</span>
                     </div>
                 </div>
-            </div>
+            </list>
         </div>
     </div>
     <!-- 农场信息 -->
@@ -85,9 +90,9 @@
 </template>
 
 <script setup>
-import { ref, computed, onMounted } from "vue";
+import { ref, computed, onMounted, onUnmounted } from "vue";
 import { useStore } from "vuex";
-import { Badge } from "vant";
+import { Badge, List } from "vant";
 import weatherInfo from "@/components/weatherInfo.vue";
 import { useRouter } from "vue-router";
 import farmInfoPopup from "../home/components/farmInfoPopup.vue";
@@ -96,7 +101,6 @@ const store = useStore();
 const tabBarHeight = computed(() => store.state.home.tabBarHeight);
 const router = useRouter();
 
-
 const farmInfoRef = ref(null);
 function toFarmInfo() {
     farmInfoRef.value.handleShow();
@@ -127,65 +131,127 @@ const functionCards = ref([
 ]);
 
 const getStayCount = () => {
-    VE_API.monitor.getCountByStatusAndFarmId({
-        farmId: gardenId.value,
-        startStatus:1,
-        endStatus:3,
-    }).then(res => {
-        functionCards.value[0].status = null;
-        if(res.data && res.data != 0){
-            functionCards.value[0].status = res.data + ' 待执行';
-        }
-    });
-}
+    VE_API.monitor
+        .getCountByStatusAndFarmId({
+            farmId: gardenId.value,
+            startStatus: 1,
+            endStatus: 3,
+        })
+        .then((res) => {
+            functionCards.value[0].status = null;
+            if (res.data && res.data != 0) {
+                functionCards.value[0].status = res.data + " 待执行";
+            }
+        });
+};
 
 // 实时播报数据
-const broadcastList = ref([
-    {
-        title: "某莫普农事未执行未执行",
-        daysLeft: 3,
-        zone: "2区",
-    },
-    {
-        title: "某莫普农事未执行未执行",
-        daysLeft: 3,
-        zone: "2区",
-    },
-    {
-        title: "某莫普农事未执行未执行",
-        daysLeft: 3,
-        zone: "2区",
-    },
-    {
-        title: "某莫普农事未执行未执行",
-        daysLeft: 3,
-        zone: "2区",
-    },
-]);
+const broadcastList = ref([]);
+const loading = ref(false);
+const finished = ref(false);
+const currentPage = ref(1);
+const pageSize = ref(10);
+
+const getBroadcastList = (page = 1, isLoadMore = false) => {
+    loading.value = true;
+    VE_API.monitor
+        .broadcastPage({
+            farmId: gardenId.value,
+            limit: pageSize.value,
+            page: page,
+        })
+        .then((res) => {
+            const newData = res.data || [];
+            if (isLoadMore) {
+                broadcastList.value = [...broadcastList.value, ...newData];
+            } else {
+                broadcastList.value = newData;
+            }
+
+            // 判断是否还有更多数据
+            if (newData.length < pageSize.value) {
+                finished.value = true;
+            }
+
+            loading.value = false;
+        })
+        .catch(() => {
+            loading.value = false;
+        });
+};
+
+// 滚动加载更多
+const onLoad = () => {
+    if (finished.value) return;
 
+    currentPage.value += 1;
+    getBroadcastList(currentPage.value, true);
+};
 // 卡片点击事件
 const handleCardClick = (card) => {
     router.push(card.route);
 };
 
 // 播报相关事件
+const isSpeaking = ref(false);
+const speechSynthesis = window.speechSynthesis;
+
 const handleBroadcast = () => {
-    console.log("点击播报");
-    // TODO: 实现播报功能
-};
+    if (isSpeaking.value) {
+        // 如果正在播放,则停止
+        speechSynthesis.cancel();
+        isSpeaking.value = false;
+        return;
+    }
 
-const handleMoreBroadcast = () => {
-    console.log("查看更多播报");
-    // TODO: 跳转到播报列表页面
-};
+    // 构建播报文本
+    let broadcastText = "实时播报:";
+    
+    if (broadcastList.value.length === 0) {
+        broadcastText += "暂无更多播报";
+    } else {
+        broadcastList.value.forEach((item, index) => {
+            broadcastText += `${index + 1}、${item.title}。${item.content}。`;
+        });
+    }
 
-const handleBroadcastItem = (item) => {
-    console.log("播报项点击:", item);
-    // TODO: 处理播报项点击事件
+    // 创建语音合成对象
+    const utterance = new SpeechSynthesisUtterance(broadcastText);
+    
+    // 设置语音参数
+    utterance.lang = 'zh-CN';
+    utterance.rate = 0.8; // 语速
+    utterance.pitch = 1; // 音调
+    utterance.volume = 1; // 音量
+
+    // 播放开始事件
+    utterance.onstart = () => {
+        isSpeaking.value = true;
+    };
+
+    // 播放结束事件
+    utterance.onend = () => {
+        isSpeaking.value = false;
+    };
+
+    // 播放错误事件
+    utterance.onerror = (event) => {
+        isSpeaking.value = false;
+        console.error("播报错误:", event.error);
+    };
+
+    // 开始播报
+    speechSynthesis.speak(utterance);
 };
 
-onMounted(() => {
-    
+onMounted(() => {});
+
+// 组件卸载时停止语音播放
+onUnmounted(() => {
+    if (isSpeaking.value) {
+        speechSynthesis.cancel();
+        isSpeaking.value = false;
+    }
 });
 
 const isExpanded = ref(false);
@@ -196,8 +262,13 @@ const weatherExpanded = (isExpandedValue) => {
 const gardenId = ref(null);
 const changeGarden = (id) => {
     gardenId.value = id;
+    // 重置分页状态
+    currentPage.value = 1;
+    finished.value = false;
+    broadcastList.value = [];
     getStayCount();
-}
+    getBroadcastList();
+};
 
 function handlePage(url) {
     router.push({
@@ -212,7 +283,7 @@ function handlePage(url) {
     height: 100%;
     padding: 13px 10px;
     box-sizing: border-box;
-    background-image: linear-gradient(250deg, #CBEBFF 0% ,#dceffd 50%,#e7f3fd 100%);
+    background-image: linear-gradient(250deg, #cbebff 0%, #dceffd 50%, #e7f3fd 100%);
     .weather-mask {
         position: fixed;
         top: 0;
@@ -302,7 +373,7 @@ function handlePage(url) {
             .card-title {
                 font-size: 16px;
                 font-weight: 500;
-                color: #1D2129;
+                color: #1d2129;
             }
             .card-status {
                 position: absolute;
@@ -314,10 +385,10 @@ function handlePage(url) {
                 background: #2199f8;
                 color: #fff;
                 &.yellow {
-                    background: #FDCF4C;
+                    background: #fdcf4c;
                 }
                 &.blue {
-                    background: #8A87FF;
+                    background: #8a87ff;
                 }
             }
         }
@@ -333,7 +404,6 @@ function handlePage(url) {
         .broadcast-header {
             display: flex;
             align-items: center;
-            justify-content: space-between;
             margin-bottom: 4px;
 
             .header-left {
@@ -351,28 +421,44 @@ function handlePage(url) {
                     display: flex;
                     align-items: center;
                     gap: 4px;
+                    cursor: pointer;
+                    transition: all 0.3s ease;
+
+                    &:hover {
+                        opacity: 0.8;
+                    }
 
                     .speaker-icon {
                         width: 16px;
                         height: 14px;
+                        transition: transform 0.3s ease;
                     }
 
                     .broadcast-text {
                         color: #2199f8;
+                        font-size: 14px;
+                    }
+
+                    // 播放状态下的样式
+                    &.speaking {
+                        .speaker-icon {
+                            animation: pulse 1s infinite;
+                        }
+                        .broadcast-text {
+                            color: #ff4757;
+                        }
                     }
                 }
-            }
 
-            .more-link {
-                display: flex;
-                align-items: center;
-                color: #4e5969;
+                @keyframes pulse {
+                    0% { transform: scale(1); }
+                    50% { transform: scale(1.1); }
+                    100% { transform: scale(1); }
+                }
             }
         }
 
         .broadcast-list {
-            overflow: auto;
-
             .broadcast-item {
                 display: flex;
                 align-items: center;
@@ -385,7 +471,8 @@ function handlePage(url) {
                 }
 
                 .item-content {
-                    .content-top{
+                    width: calc(100% - 50px);
+                    .content-top {
                         display: flex;
                         align-items: center;
                     }