|
@@ -3,7 +3,13 @@
|
|
|
<!-- 天气遮罩 -->
|
|
<!-- 天气遮罩 -->
|
|
|
<div class="weather-mask" v-show="isExpanded"></div>
|
|
<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="operation-button">
|
|
|
<div class="button-group">
|
|
<div class="button-group">
|
|
|
<div class="button-item" @click="toFarmInfo">
|
|
<div class="button-item" @click="toFarmInfo">
|
|
@@ -43,23 +49,24 @@
|
|
|
<div class="broadcast-header">
|
|
<div class="broadcast-header">
|
|
|
<div class="header-left">
|
|
<div class="header-left">
|
|
|
<span class="broadcast-title">实时播报</span>
|
|
<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="播报" />
|
|
<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>
|
|
</div>
|
|
|
- <div class="more-link" @click="handleMoreBroadcast">
|
|
|
|
|
- <span>更多</span>
|
|
|
|
|
- <el-icon size="12"><ArrowRightBold /></el-icon>
|
|
|
|
|
- </div>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="broadcast-list">
|
|
|
|
|
|
|
+ <list
|
|
|
|
|
+ v-model:loading="loading"
|
|
|
|
|
+ :finished="finished"
|
|
|
|
|
+ finished-text="暂无更多播报"
|
|
|
|
|
+ @load="onLoad"
|
|
|
|
|
+ class="broadcast-list"
|
|
|
|
|
+ >
|
|
|
<div
|
|
<div
|
|
|
v-for="(item, index) in broadcastList"
|
|
v-for="(item, index) in broadcastList"
|
|
|
:key="index"
|
|
:key="index"
|
|
|
class="broadcast-item"
|
|
class="broadcast-item"
|
|
|
- @click="handleBroadcastItem(item)"
|
|
|
|
|
>
|
|
>
|
|
|
<div class="item-content">
|
|
<div class="item-content">
|
|
|
<div class="content-top">
|
|
<div class="content-top">
|
|
@@ -68,16 +75,14 @@
|
|
|
</div>
|
|
</div>
|
|
|
<div class="item-title">{{ item.title }}</div>
|
|
<div class="item-title">{{ item.title }}</div>
|
|
|
</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>
|
|
|
<div class="item-zone">
|
|
<div class="item-zone">
|
|
|
<div class="point"></div>
|
|
<div class="point"></div>
|
|
|
- <span>{{ item.zone }}</span>
|
|
|
|
|
|
|
+ <span>{{ item.regionId }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
|
+ </list>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<!-- 农场信息 -->
|
|
<!-- 农场信息 -->
|
|
@@ -85,9 +90,9 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { ref, computed, onMounted } from "vue";
|
|
|
|
|
|
|
+import { ref, computed, onMounted, onUnmounted } from "vue";
|
|
|
import { useStore } from "vuex";
|
|
import { useStore } from "vuex";
|
|
|
-import { Badge } from "vant";
|
|
|
|
|
|
|
+import { Badge, List } from "vant";
|
|
|
import weatherInfo from "@/components/weatherInfo.vue";
|
|
import weatherInfo from "@/components/weatherInfo.vue";
|
|
|
import { useRouter } from "vue-router";
|
|
import { useRouter } from "vue-router";
|
|
|
import farmInfoPopup from "../home/components/farmInfoPopup.vue";
|
|
import farmInfoPopup from "../home/components/farmInfoPopup.vue";
|
|
@@ -96,7 +101,6 @@ const store = useStore();
|
|
|
const tabBarHeight = computed(() => store.state.home.tabBarHeight);
|
|
const tabBarHeight = computed(() => store.state.home.tabBarHeight);
|
|
|
const router = useRouter();
|
|
const router = useRouter();
|
|
|
|
|
|
|
|
-
|
|
|
|
|
const farmInfoRef = ref(null);
|
|
const farmInfoRef = ref(null);
|
|
|
function toFarmInfo() {
|
|
function toFarmInfo() {
|
|
|
farmInfoRef.value.handleShow();
|
|
farmInfoRef.value.handleShow();
|
|
@@ -127,65 +131,127 @@ const functionCards = ref([
|
|
|
]);
|
|
]);
|
|
|
|
|
|
|
|
const getStayCount = () => {
|
|
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) => {
|
|
const handleCardClick = (card) => {
|
|
|
router.push(card.route);
|
|
router.push(card.route);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 播报相关事件
|
|
// 播报相关事件
|
|
|
|
|
+const isSpeaking = ref(false);
|
|
|
|
|
+const speechSynthesis = window.speechSynthesis;
|
|
|
|
|
+
|
|
|
const handleBroadcast = () => {
|
|
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);
|
|
const isExpanded = ref(false);
|
|
@@ -196,8 +262,13 @@ const weatherExpanded = (isExpandedValue) => {
|
|
|
const gardenId = ref(null);
|
|
const gardenId = ref(null);
|
|
|
const changeGarden = (id) => {
|
|
const changeGarden = (id) => {
|
|
|
gardenId.value = id;
|
|
gardenId.value = id;
|
|
|
|
|
+ // 重置分页状态
|
|
|
|
|
+ currentPage.value = 1;
|
|
|
|
|
+ finished.value = false;
|
|
|
|
|
+ broadcastList.value = [];
|
|
|
getStayCount();
|
|
getStayCount();
|
|
|
-}
|
|
|
|
|
|
|
+ getBroadcastList();
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
function handlePage(url) {
|
|
function handlePage(url) {
|
|
|
router.push({
|
|
router.push({
|
|
@@ -212,7 +283,7 @@ function handlePage(url) {
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
padding: 13px 10px;
|
|
padding: 13px 10px;
|
|
|
box-sizing: border-box;
|
|
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 {
|
|
.weather-mask {
|
|
|
position: fixed;
|
|
position: fixed;
|
|
|
top: 0;
|
|
top: 0;
|
|
@@ -302,7 +373,7 @@ function handlePage(url) {
|
|
|
.card-title {
|
|
.card-title {
|
|
|
font-size: 16px;
|
|
font-size: 16px;
|
|
|
font-weight: 500;
|
|
font-weight: 500;
|
|
|
- color: #1D2129;
|
|
|
|
|
|
|
+ color: #1d2129;
|
|
|
}
|
|
}
|
|
|
.card-status {
|
|
.card-status {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
@@ -314,10 +385,10 @@ function handlePage(url) {
|
|
|
background: #2199f8;
|
|
background: #2199f8;
|
|
|
color: #fff;
|
|
color: #fff;
|
|
|
&.yellow {
|
|
&.yellow {
|
|
|
- background: #FDCF4C;
|
|
|
|
|
|
|
+ background: #fdcf4c;
|
|
|
}
|
|
}
|
|
|
&.blue {
|
|
&.blue {
|
|
|
- background: #8A87FF;
|
|
|
|
|
|
|
+ background: #8a87ff;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -333,7 +404,6 @@ function handlePage(url) {
|
|
|
.broadcast-header {
|
|
.broadcast-header {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
|
|
margin-bottom: 4px;
|
|
margin-bottom: 4px;
|
|
|
|
|
|
|
|
.header-left {
|
|
.header-left {
|
|
@@ -351,28 +421,44 @@ function handlePage(url) {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
gap: 4px;
|
|
gap: 4px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ opacity: 0.8;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
.speaker-icon {
|
|
.speaker-icon {
|
|
|
width: 16px;
|
|
width: 16px;
|
|
|
height: 14px;
|
|
height: 14px;
|
|
|
|
|
+ transition: transform 0.3s ease;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.broadcast-text {
|
|
.broadcast-text {
|
|
|
color: #2199f8;
|
|
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 {
|
|
.broadcast-list {
|
|
|
- overflow: auto;
|
|
|
|
|
-
|
|
|
|
|
.broadcast-item {
|
|
.broadcast-item {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
@@ -385,7 +471,8 @@ function handlePage(url) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.item-content {
|
|
.item-content {
|
|
|
- .content-top{
|
|
|
|
|
|
|
+ width: calc(100% - 50px);
|
|
|
|
|
+ .content-top {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
}
|
|
}
|