From 2800fa4f32f3900509cb4d6eefaf2bfaf54efdd7 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Mon, 21 Apr 2025 18:29:09 +0800
Subject: [PATCH] fix: 天气显示
---
src/components/CurrentTaskDetails/TaskDetailsLeft.vue | 418 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 399 insertions(+), 19 deletions(-)
diff --git a/src/components/CurrentTaskDetails/TaskDetailsLeft.vue b/src/components/CurrentTaskDetails/TaskDetailsLeft.vue
index 89b9174..336d9f1 100644
--- a/src/components/CurrentTaskDetails/TaskDetailsLeft.vue
+++ b/src/components/CurrentTaskDetails/TaskDetailsLeft.vue
@@ -3,7 +3,7 @@
<div class="title">负载控制</div>
<div
class="singleCol"
- v-for="item in list1"
+ v-for="item in list1.filter(i => i.show)"
@click="cameraType(item)"
:key="item.key"
:class="{ active: item.key === cameraParams.camera_type }"
@@ -21,11 +21,13 @@
<div @click="takePictures">拍照</div>
<div @click="recordFun">{{ isRecording ? '录像中...' : '录像' }}</div>
</div>
- <div class="multiCol">
- <div @click="ElMessage.warning('加急开发中...')">喊话</div>
- <div @click="ElMessage.warning('加急开发中...')">广播</div>
+ <div class="multiCol" v-if="wsInfo?.psdk_widget_values">
+ <div @click="shoutFun">{{ isRecordShouting ? '喊话' : '停止喊话' }}</div>
+ <div @click="broadcastFun">广播</div>
</div>
-
+ <div class="percent" v-if="isRecordShouting && percent > 0">
+ <el-progress :text-inside="true" :stroke-width="10" :percentage="percent"></el-progress>
+ </div>
<div class="cameraZoom">
<el-slider
v-model="cameraParams.zoom_factor"
@@ -34,23 +36,84 @@
:max="cameraParams.camera_type === 'ir' ? 200 : 200"
@change="sliderChange"
/>
+ <div class="cameraZoomText">{{cameraParams.zoom_factor}}X</div>
</div>
+ </div>
+ <!-- 广播列表 -->
+ <div class="broadcast" v-if="isBroadcast">
+ <div class="title">
+ <span>语音内容</span>
+ <img src="@/assets/images/taskManagement/taskIntermediateContent/broadcast-list.png" alt="" />
+ <img @click="isBroadcast = !isBroadcast" class="close" src="@/assets/images/taskManagement/taskIntermediateContent/close.png" alt="">
+ </div>
+ <div class="search-btn">
+ <div class="input-set">
+ <label>文件名称:</label><el-input v-model="searchParams.name" placeholder="请输入广播名称"></el-input>
+ </div>
+ <div class="btn-set">
+ <div class="btn" @click="clearData">清空</div>
+ <div class="btn" @click="getFileList">搜索</div>
+ <el-upload
+ class="btn"
+ action="/upload"
+ :show-file-list="false"
+ :before-upload="handleUpload">
+ 音频上传
+ </el-upload>
+ </div>
+ </div>
+ <div class="video-table">
+ <div class="table-header">
+ <div class="name">音频名称</div>
+ <div class="operate">操作</div>
+ </div>
+ <div class="table-body" v-for="item in tableList">
+ <div class="name">{{ item.name }}</div>
+ <div class="operate"><span class="btn" @click="playVoice(item)">播放</span></div>
+ </div>
+ </div>
+ <div class="pagination">
+ <el-pagination
+ background
+ :page-sizes="[10, 20, 30, 50]"
+ v-model:current-page="searchParams.page"
+ v-model:page-size="searchParams.page_size"
+ layout=" prev, pager, next"
+ :total="total"
+ @change="pageChange"
+ />
+ </div>
</div>
</template>
<script setup>
import EventBus from '@/event-bus'
-import { callPhotoAndVideoCmd, cameraParamsChangeApi, ptzResetModeApi } from '@/api/payload'
-import { ElMessage } from 'element-plus'
+import {
+ callPhotoAndVideoCmd,
+ cameraParamsChangeApi,
+ ptzResetModeApi,
+ startVoice,
+ stayAwayRiver,
+ getVoiceFile,
+ playAudio,
+ uploadSpeak } from '@/api/payload'
+import { ElMessage, ElMessageBox } from 'element-plus'
import { throttle } from 'lodash'
+import Recorder from 'js-audio-recorder';
+import dayjs from 'dayjs'
+const wsInfo = inject('wsInfo')
+// 初始化喊话
+let globalShout = null
const isRecording = ref(false)
-
-const list1 = ref([
- { name: '广角', key: 'wide' },
- { name: '变焦', key: 'zoom' },
- { name: '红外', key: 'ir' },
-])
+const hasIr = inject('hasIr')
+const list1 = computed(() => {
+ return [
+ { name: '广角', key: 'wide',show:true },
+ { name: '变焦', key: 'zoom', show: true },
+ { name: '红外', key: 'ir',show: hasIr.value },
+ ]
+})
const cameraParams = ref({
zoom_factor: 0,
camera_type: 'wide',
@@ -83,7 +146,7 @@
// 修改相机参数
function cameraParamsChange() {
- const { camera_type, zoom_factor} = cameraParams.value
+ const { camera_type, zoom_factor } = cameraParams.value
cameraParamsChangeApi({
sn: dockSn.value,
...cameraParams.value,
@@ -125,11 +188,156 @@
// 拍照
function takePictures() {
+ if (isRecording.value) {
+ return ElMessage.warning('请先结束录像')
+ }
// photo拍照,video_start开始录像,video_stop结束录像
callPhotoAndVideoCmd(dockSn.value, 'photo').then(res => {
ElMessage.success('拍照成功')
})
}
+
+// 记录喊话是否开始阶段
+const isRecordShouting = ref(true);
+const isFlag = ref(false);
+const percent = ref(0); // 滚动条滚动百分比
+// 初始化喊话
+function initShout() {
+ destroyShout();
+ globalShout = new Recorder({
+ sampleBits: 16,
+ sampleRate: 16000,
+ numChannels: 1,
+ })
+}
+
+// 销毁喊话
+function destroyShout() {
+ if (globalShout) {
+ globalShout.pause()
+ globalShout.destroy() // 销毁实例
+ globalShout = null
+ }
+}
+
+// 喊话
+function shoutFun() {
+ // 是否需要判断有控制权限
+ if (isFlag.value) return
+ isFlag.value = true;
+ if (isRecordShouting.value) {
+ // 初始化喊话
+ initShout();
+ globalShout.start().then(() => {
+ ElMessage.success('开始喊话')
+ isRecordShouting.value = false
+ isFlag.value = false
+ }).catch((err) => {
+ isFlag.value = false
+ })
+ } else {
+ let formData = new FormData();
+ const wavBlob = globalShout.getWAVBlob()
+ const wavform = new File([wavBlob], new Date().getTime() + '.wav')
+ formData.append('file', wavform)
+ formData.append('sn', '8UUXN2A00A00MM')
+ formData.append('psdk_index', 2)
+ formData.append('name', '喊话' + dayjs().format('YYYY.MM.DD HH:mm:ss'))
+ formData.append('volumn', 100)
+ startVoice(formData).then(res => {
+ ElMessage.success('喊话成功');
+ isRecordShouting.value = true
+ isFlag.value = false
+ destroyShout();
+ }).catch(err => {
+ isRecordShouting.value = true
+ isFlag.value = false
+ ElMessage.error('喊话失败')
+ destroyShout();
+ });
+ }
+}
+const tableList = ref([]);
+// 分页相关
+const searchParams = ref({
+ sn: droneSn.value,
+ name: '',
+ page: 1,
+ page_size: 1
+});
+const isBroadcast = ref(false);
+const total = ref(0);
+// 广播
+function broadcastFun() {
+ // 是否需要判断有控制权限
+ // 弹出框选择文件
+ isBroadcast.value = true;
+ // 获取文件接口
+ getFileList();
+}
+
+// 获取音频文件列表
+function getFileList() {
+ getVoiceFile(searchParams.value).then((res) => {
+ if (res.data.code !== 0) return ElMessage.error(res.data.msg);
+ tableList.value = res.data.data.records;
+ total.value = res.data.data.total;
+ });
+}
+
+// 清除数据
+function clearData() {
+ searchParams.value.name = '';
+ searchParams.value.page = 1;
+ getFileList();
+}
+
+function pageChange(val) {
+ searchParams.value.page = val;
+ getFileList();
+}
+// 上传
+function handleUpload(file) {
+ let formData = new FormData();
+ formData.append('file', file)
+ formData.append('sn', droneSn.value)
+ uploadSpeak(formData).then(res => {
+ if (res.data.code !== 0) return ElMessage.error('上传失败');
+ ElMessage.success('上传成功');
+ // 获取文件接口
+ getFileList();
+ }).catch(err => {
+ ElMessage.error('上传失败')
+ })
+}
+
+// 播放操作
+function playVoice(item) {
+ ElMessageBox.confirm('是否确认播放该音频?', '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ const { sn, name, url, md5,voice } = item
+ playAudio({sn, name, url, md5,voice}).then((res) => {
+ if (res.code !== 0) return ElMessage.error("播放失败");
+ ElMessage.success("播放成功");
+ });
+ }).catch(() => {
+ ElMessage({
+ type: 'info',
+ message: '已取消'
+ });
+ });
+}
+onMounted(() => {
+ // 初始化喊话
+ initShout();
+});
+onUnmounted(() => {
+ // 销毁喊话
+ destroyShout();
+});
</script>
<style scoped lang="scss">
@@ -140,7 +348,7 @@
top: 50%;
transform: translateY(-60%);
width: 178px;
- height: 409px;
+ padding: 20px 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
border-radius: 20px 20px 20px 20px;
@@ -158,16 +366,27 @@
.cameraZoom {
position: absolute;
- padding: 16px 0;
+ padding: 20px 0 15px 0;
left: 1600px;
top: -59px;
- width: 112px;
+ width: 90px;
height: 490px;
- background: rgba(64, 64, 64, 0.15);
+ background: rgba(0, 0, 0, 0.4);
+ backdrop-filter: blur(5rem);
border-radius: 20px 20px 20px 20px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
.el-slider {
- height: 100%;
+ flex: 1;
+ }
+ .cameraZoomText{
+ font-family: Segoe UI, Segoe UI;
+ font-weight: 400;
+ font-size: 22px;
+ color: #ffffff;
+ margin-top: 20px;
}
}
@@ -242,5 +461,166 @@
}
}
}
+ .percent {
+ width: 146px;
+ :deep(.el-progress-bar__outer) {
+ height: 6px;
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ :deep(.el-progress-bar__inner) {
+ height: 6px;
+ background: #FFFFFF;
+ border-radius: 10px 10px 10px 10px;
+ transition: all 0.3s;
+ }
+ :deep(.el-progress-bar__innerText) {
+ // color: red;
+ }
+ }
+}
+
+.broadcast {
+ z-index: 2;
+ position: absolute;
+ left: 204px;
+ top: 60%;
+ transform: translateY(-60%);
+ backdrop-filter: blur(5px);
+ width: 608px;
+ height: 256px;
+ // background: rgba(64,64,64,0.25);
+ background: rgba(0, 0, 0, 0.5);
+ border-radius: 20px 20px 20px 20px;
+ .title {
+ margin: 18px 8px;
+ span {
+ width: 66px;
+ height: 18px;
+ font-family: Segoe UI, Segoe UI;
+ font-weight: 400;
+ font-size: 14px;
+ color: #EDEDED;
+ line-height: 18px;
+ text-align: left;
+ font-style: normal;
+ text-transform: none;
+ margin-left: 16px;
+ }
+ img {
+ width: 584px;
+ height: 14px;
+ }
+ .close {
+ width: 16px;
+ height: 16px;
+ text-align: right;
+ position: absolute;
+ right: 16px;
+ top: 16px;
+ cursor: pointer;
+ }
+ }
+ .search-btn {
+ display: flex;
+ justify-content: space-between;
+ color: #EDEDED;
+ margin: 0 14px;
+ .input-set {
+ display: flex;
+ align-items: center;
+ width: 214px;
+ height: 32px;
+ label {width: 90px;}
+ :deep(.el-input__wrapper) {
+ background: rgba(58,58,58,0.75);
+ border-radius: 8px 8px 8px 8px;
+ }
+ :deep(.el-input__inner) {
+ color: #EDEDED;
+ }
+ }
+ .btn-set {
+ display: flex;
+ gap: 12px;
+ .btn {
+ width: 66px;
+ height: 32px;
+ line-height: 32px;
+ text-align: center;
+ background: rgba(58,58,58,0.75);
+ box-shadow: 0px 4px 72px 0px rgba(0,0,0,0.25);
+ border-radius: 8px 8px 8px 8px;
+ color: #EDEDED;
+ cursor: pointer;
+ }
+ }
+ }
+ .video-table {
+ margin: 14px 10px;
+ .table-header {
+ height: 44px;
+ line-height: 44px;
+ background: rgba(66,66,66,0.85);
+ border-radius: 8px 8px 0px 0px;
+ display: flex;
+ color: #BDBDBD;
+ }
+ .table-body {
+ height: 44px;
+ line-height: 44px;
+ background: rgba(74,72,72,0.6);
+ border-radius: 0px 0px 8px 8px;
+ display: flex;
+ color: #EDEDED;
+ }
+ .name {
+ width: 60%;
+ text-align: center
+ }
+ .operate {
+ width:40%;
+ text-align: center;
+ .btn {
+ display: inline-block;
+ cursor: pointer;
+ width: 66px;
+ height: 32px;
+ line-height: 32px;
+ text-align: center;
+ background: rgba(58,58,58,0.75);
+ box-shadow: 0px 4px 72px 0px rgba(0,0,0,0.25);
+ border-radius: 8px 8px 8px 8px;
+ color: #EDEDED;
+ }
+ }
+ }
+ .pagination {
+ position: absolute;
+ right: 10px;
+ bottom: 10px;
+ height: 32px;
+ display: flex;
+
+ :deep(.number) {
+ color: #EDEDED;
+ }
+ :deep(.btn-prev),
+ :deep(.btn-next) {
+ width: 26px;
+ height: 26px;
+ background: rgba(58,58,58,0.75);
+ box-shadow: 0px 4px 72px 0px rgba(0,0,0,0.25);
+ border-radius: 8px 8px 8px 8px;
+ cursor: pointer;
+ }
+ :deep(.el-pager li) {
+ cursor: pointer;
+ background: rgba(58,58,58,0.75);
+ box-shadow: 0px 4px 72px 0px rgba(0,0,0,0.25);
+ border-radius: 8px 8px 8px 8px;
+ border: 1px solid rgba(255,255,255,0.99);
+ margin: 0 4px;
+ }
+ }
}
</style>
--
Gitblit v1.9.3