5.30
This commit is contained in:
parent
b4a2661c04
commit
d832944320
@ -130,3 +130,7 @@ Element.Dialog.props.closeOnClickModal.default = false;
|
||||
|
||||
|
||||
|
||||
|
||||
// 添加 token 打印(从 Cookies 或 localStorage 获取)
|
||||
const token = Cookies.get('Admin-Token') || localStorage.getItem('Admin-Token');
|
||||
console.log(' 当前 Token:', token); // 打印 token 到控制台
|
||||
|
1243
src/views/iot/device/acousto_optic.vue
Normal file
1243
src/views/iot/device/acousto_optic.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -69,7 +69,7 @@
|
||||
<template slot="prepend">Version</template>
|
||||
<template slot="append">{{ form.firmwareType === 1 ?
|
||||
$t('firmware.index.222541-52') : $t('firmware.index.222541-53')
|
||||
}}</template>
|
||||
}}</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- 设备影子 -->
|
||||
@ -187,8 +187,12 @@
|
||||
ref="gatewayRunningStatus" :device="form" @statusEvent="getDeviceStatusData($event)" />
|
||||
<relay v-else-if="form.productName && form.productName.toLowerCase().includes('多路控制器')" ref="relay"
|
||||
:device="form" @statusEvent="getDeviceStatusData($event)" />
|
||||
<gatewaypre v-else-if="form.productName && form.productName.toLowerCase().includes('网关卡预配置')" ref="gatewaypre"
|
||||
:device="form" @statusEvent="getDeviceStatusData($event)" />
|
||||
<gatewaypre v-else-if="form.productName && form.productName.toLowerCase().includes('网关卡预配置')"
|
||||
ref="gatewaypre" :device="form" @statusEvent="getDeviceStatusData($event)" />
|
||||
<acousto_optic v-else-if="form.productName && form.productName.toLowerCase().includes('声光')"
|
||||
ref="acousto_optic" :device="form" @statusEvent="getDeviceStatusData($event)" />
|
||||
<voicecard v-else-if="form.productName && form.productName.toLowerCase().includes('声卡')"
|
||||
ref="voicecard" :device="form" @statusEvent="getDeviceStatusData($event)" />
|
||||
<running-status v-else ref="runningStatus" :device="form"
|
||||
@statusEvent="getDeviceStatusData($event)" />
|
||||
|
||||
@ -407,7 +411,7 @@
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button class="btns" type="primary" @click="doCopy(2)">{{ $t('device.device-edit.148398-59')
|
||||
}}</el-button>
|
||||
}}</el-button>
|
||||
<el-button @click="closeSummaryDialog">{{ $t('device.device-edit.148398-57') }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
@ -453,6 +457,8 @@ import defaultSettings from '@/settings';
|
||||
import gatewayRunningStatus from './gatewayrunning-status.vue';
|
||||
import relay from './relay.vue'
|
||||
import gatewaypre from './gatewaypre.vue'
|
||||
import acousto_optic from'./acousto_optic.vue'
|
||||
import voicecard from './voicecard.vue';
|
||||
export default {
|
||||
name: 'DeviceEdit',
|
||||
dicts: ['iot_device_status', 'iot_location_way'],
|
||||
@ -486,7 +492,9 @@ export default {
|
||||
deviceInlineVideo,
|
||||
gatewayRunningStatus,
|
||||
relay,
|
||||
gatewaypre
|
||||
gatewaypre,
|
||||
acousto_optic,
|
||||
voicecard
|
||||
},
|
||||
watch: {
|
||||
activeName(val) {
|
||||
|
906
src/views/iot/device/voicecard.vue
Normal file
906
src/views/iot/device/voicecard.vue
Normal file
@ -0,0 +1,906 @@
|
||||
<template>
|
||||
<div class="running-status">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="14" class="status-col">
|
||||
<!-- 设备模式和OTA升级部分 -->
|
||||
<el-row :gutter="20" class="mode-section">
|
||||
<!-- 设备模式 -->
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-card class="mode-card" shadow="hover">
|
||||
<div class="mode-header">
|
||||
<i class="el-icon-menu"></i>
|
||||
<span class="mode-title">{{ $t('device.running-status.866086-0') }}</span>
|
||||
</div>
|
||||
<div class="mode-content">
|
||||
<span class="title" :style="{ color: statusColor.background }">{{ title }}</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 设备升级 -->
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-card class="mode-card" shadow="hover">
|
||||
<div class="mode-header">
|
||||
<svg-icon icon-class="ota" />
|
||||
<span class="mode-title">{{ $t('device.running-status.866086-1') }}</span>
|
||||
</div>
|
||||
<div class="mode-content">
|
||||
<el-button type="primary" size="mini" :plain="true" @click="viewVersion()">
|
||||
{{ $t('device.running-status.866086-44') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 声卡基础设置 -->
|
||||
<el-card class="settings-card" shadow="hover">
|
||||
<div slot="header" class="settings-header">
|
||||
<span class="settings-title">基础设置</span>
|
||||
</div>
|
||||
<el-form :model="basicSettings" label-width="100px">
|
||||
<el-form-item label="音量设置">
|
||||
<el-slider v-model="basicSettings.volume" :min="0" :max="100" :format-tooltip="formatVolume"
|
||||
@change="handleVolumeChange" style="width: 80%">
|
||||
</el-slider>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 音频列表 -->
|
||||
<el-card class="audio-list-card" shadow="hover">
|
||||
<div slot="header" class="audio-list-header">
|
||||
<span class="audio-list-title">音频列表</span>
|
||||
</div>
|
||||
<el-table :data="audioList" style="width: 100%" :header-cell-style="{ background: '#f5f7fa' }"
|
||||
border>
|
||||
<el-table-column prop="id" label="序号" width="80" align="center">
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="音频名称" min-width="150">
|
||||
</el-table-column>
|
||||
<el-table-column prop="duration" label="时长" width="120" align="center">
|
||||
</el-table-column>
|
||||
<el-table-column prop="size" label="大小" width="120" align="center">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 默认列表 -->
|
||||
<el-card class="default-list-card" shadow="hover">
|
||||
<div slot="header" class="default-list-header">
|
||||
<span class="default-list-title">默认列表</span>
|
||||
</div>
|
||||
<el-table :data="defaultList" style="width: 100%" :header-cell-style="{ background: '#f5f7fa' }"
|
||||
border>
|
||||
<el-table-column prop="id" label="序号" width="80" align="center">
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="音频名称" min-width="150">
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="类型" width="120" align="center">
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === '启用' ? 'success' : 'info'">
|
||||
{{ scope.row.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
|
||||
<!-- 远程喊话控制面板 -->
|
||||
<el-card class="voice-control-card" shadow="hover">
|
||||
<div slot="header" class="voice-control-header">
|
||||
<span class="voice-control-title">远程喊话</span>
|
||||
</div>
|
||||
<div class="voice-control-content">
|
||||
<div class="recorder-status">
|
||||
<div class="status-indicator" :class="{ 'recording': isRecording }">
|
||||
<i class="el-icon-microphone"></i>
|
||||
</div>
|
||||
<span class="status-text">{{ recordingStatus }}</span>
|
||||
</div>
|
||||
<div class="timer-display" v-if="isRecording">
|
||||
{{ formatTime(recordingTime) }}
|
||||
</div>
|
||||
<div class="control-buttons">
|
||||
<el-button type="primary" icon="el-icon-video-play" circle @click="startRecording"
|
||||
:disabled="isRecording">
|
||||
</el-button>
|
||||
<el-button type="danger" icon="el-icon-video-pause" circle @click="stopRecording"
|
||||
:disabled="!isRecording">
|
||||
</el-button>
|
||||
<el-button type="success" icon="el-icon-upload2" circle @click="uploadRecording"
|
||||
:disabled="!hasRecording || isRecording">
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 录音预览 -->
|
||||
<div class="recording-preview" v-if="hasRecording">
|
||||
<div class="preview-title">录音预览</div>
|
||||
<div class="audio-player">
|
||||
<audio ref="audioPlayer" :src="audioUrl" controls></audio>
|
||||
<div class="preview-controls">
|
||||
<el-button type="text" icon="el-icon-refresh" @click="reRecord"
|
||||
:disabled="isRecording">
|
||||
重新录制
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="recording-list" v-if="recordings.length > 0">
|
||||
<div class="list-title">最近录音</div>
|
||||
<el-scrollbar style="height: 200px">
|
||||
<div v-for="(recording, index) in recordings" :key="index" class="recording-item">
|
||||
<span class="recording-name">{{ recording.name }}</span>
|
||||
<span class="recording-time">{{ recording.time }}</span>
|
||||
<el-button type="text" icon="el-icon-delete" @click="deleteRecording(index)">
|
||||
</el-button>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 设备监测图表-->
|
||||
<el-row :gutter="20" v-if="deviceInfo.chartList.length > 0">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="24" :xl="12" v-for="(item, index) in deviceInfo.chartList"
|
||||
:key="index">
|
||||
<el-card shadow="hover" style="border-radius: 8px; margin-bottom: 20px">
|
||||
<div ref="map" style="height: 230px; width: 185px; margin: 0 auto; margin-bottom: 15px">
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 固件版本查看对话框 -->
|
||||
<el-dialog :title="$t('device.running-status.866086-10')" :visible.sync="openVersion" width="550px"
|
||||
append-to-body>
|
||||
<el-form ref="firmwareForm" label-width="100px" :model="firmwareParams" :inline="true" :rules="rules">
|
||||
<el-form-item :label="$t('device.running-status.866086-38')" prop="firmwareType">
|
||||
<el-select v-model="deviceInfo.firmwareType" :placeholder="$t('firmware.index.222541-51')"
|
||||
@change="handleVersionInputChange" style="width: 350px" disabled>
|
||||
<el-option v-for="item in firmwareTypeList" :key="item.value" :label="item.label"
|
||||
:value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('device.running-status.866086-39')" prop="">
|
||||
<el-input :placeholder="$t('device.running-status.866086-40')" v-model="deviceInfo.firmwareVersion"
|
||||
style="width: 350px" disabled>
|
||||
<template slot="prepend">Version</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-tooltip effect="dark" :content="$t('device.running-status.866086-41')" placement="top-start">
|
||||
<el-button type="primary" @click="getLatestFirmware" :disabled="device.status !== 3">{{
|
||||
$t('device.running-status.866086-42') }}</el-button>
|
||||
</el-tooltip>
|
||||
<el-button @click="cancel1">{{ $t('cancel') }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 添加或修改产品固件对话框 -->
|
||||
<el-dialog :title="$t('device.running-status.866086-10')" :visible.sync="openFirmware" width="600px"
|
||||
append-to-body>
|
||||
<div v-if="firmware == null" style="text-align: center; font-size: 16px">
|
||||
<i class="el-icon-success" style="color: #67c23a"></i>
|
||||
{{ $t('device.running-status.866086-11') }}
|
||||
</div>
|
||||
<el-descriptions :column="1" border size="large"
|
||||
v-if="firmware != null && deviceInfo.firmwareVersion < firmware.version"
|
||||
:labelStyle="{ width: '150px', 'font-weight': 'bold' }">
|
||||
<template slot="title">
|
||||
<el-link icon="el-icon-success" type="success" :underline="false">{{
|
||||
$t('device.running-status.866086-12') }}</el-link>
|
||||
</template>
|
||||
<el-descriptions-item :label="$t('device.running-status.866086-13')">{{ firmware.firmwareName
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('device.device-edit.148398-4')">{{ firmware.productName
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('device.device-edit.148398-12')">Version {{ firmware.version
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('device.running-status.866086-16')">
|
||||
<el-link :href="getDownloadUrl(firmware.filePath)" :underline="false" type="primary">{{
|
||||
getDownloadUrl(firmware.filePath) }}</el-link>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('device.running-status.866086-17')">{{ firmware.remark
|
||||
}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="success" @click="otaUpgrade"
|
||||
v-if="firmware != null && deviceInfo.firmwareVersion < firmware.version">{{
|
||||
$t('device.running-status.866086-18') }}</el-button>
|
||||
<el-button @click="cancel">{{ $t('cancel') }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getLatestFirmware } from '@/api/iot/firmware';
|
||||
import { serviceInvoke, serviceInvokeReply } from '@/api/iot/runstatus';
|
||||
import { getOrderControl } from '@/api/iot/control';
|
||||
|
||||
export default {
|
||||
name: 'running-status',
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
device: {
|
||||
handler(newVal) {
|
||||
if (newVal && newVal.deviceId != 0) {
|
||||
this.deviceInfo = newVal;
|
||||
this.updateDeviceStatus(this.deviceInfo);
|
||||
this.$nextTick(function () {
|
||||
this.MonitorChart();
|
||||
});
|
||||
if (this.deviceInfo.thingsModels && this.deviceInfo.thingsModels.length > 0) {
|
||||
this.deviceInfo.thingsModels = this.device.thingsModels.sort((a, b) => b.order - a.order);
|
||||
}
|
||||
if (this.deviceInfo.chartList && this.deviceInfo.chartList.length > 0) {
|
||||
this.deviceInfo.chartList = this.deviceInfo.chartList.sort((a, b) => b.order - a.order);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
title: '设备控制',
|
||||
shadowUnEnable: false,
|
||||
statusColor: {
|
||||
background: '#67C23A',
|
||||
color: '#fff',
|
||||
maxWidth: '200px',
|
||||
},
|
||||
firmware: {},
|
||||
openFirmware: false,
|
||||
loading: true,
|
||||
deviceInfo: {
|
||||
deviceId: 0,
|
||||
serialNumber: '',
|
||||
productId: '',
|
||||
productName: '',
|
||||
status: 0,
|
||||
isShadow: 0,
|
||||
rssi: 0,
|
||||
firmwareVersion: '',
|
||||
wirelessVersion: '',
|
||||
firmwareType: 1,
|
||||
protocolCode: '',
|
||||
thingsModels: [],
|
||||
chartList: [],
|
||||
},
|
||||
firmwareParams: {
|
||||
firmwareType: '',
|
||||
versionInput: '',
|
||||
},
|
||||
monitorChart: [
|
||||
{
|
||||
chart: {},
|
||||
data: {
|
||||
id: '',
|
||||
name: '',
|
||||
value: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
openVersion: false,
|
||||
firmwareTypeList: [
|
||||
{
|
||||
label: this.$t('firmware.index.222541-52'),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: 'HTTP',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
firmwareType: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('device.running-status.866086-43'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
// 声卡相关数据
|
||||
basicSettings: {
|
||||
volume: 50
|
||||
},
|
||||
audioList: [
|
||||
{ id: 1, name: '音频1', duration: '00:30', size: '2.5MB' },
|
||||
{ id: 2, name: '音频2', duration: '01:15', size: '3.8MB' },
|
||||
{ id: 3, name: '音频3', duration: '00:45', size: '2.1MB' }
|
||||
],
|
||||
defaultList: [
|
||||
{ id: 1, name: '默认音频1', type: '系统', status: '启用' },
|
||||
{ id: 2, name: '默认音频2', type: '用户', status: '启用' },
|
||||
{ id: 3, name: '默认音频3', type: '系统', status: '禁用' }
|
||||
],
|
||||
// 录音相关数据
|
||||
isRecording: false,
|
||||
recordingTime: 0,
|
||||
recordingStatus: '准备就绪',
|
||||
hasRecording: false,
|
||||
mediaRecorder: null,
|
||||
audioChunks: [],
|
||||
recordings: [],
|
||||
timer: null,
|
||||
audioUrl: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.device && this.device.deviceId) {
|
||||
this.handleDeviceChange(this.device);
|
||||
this.initDataStatus();
|
||||
this.initData();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 保留原有的设备状态相关方法
|
||||
handleDeviceChange(device) {
|
||||
if (device && device.deviceId != 0) {
|
||||
const { firmwareVersion, wirelessVersion, firmwareType, ...res } = device;
|
||||
const data = {
|
||||
version: firmwareType === 1 ? firmwareVersion : wirelessVersion,
|
||||
firmwareType,
|
||||
...res,
|
||||
};
|
||||
this.deviceInfo = data;
|
||||
this.updateDeviceStatus(this.deviceInfo);
|
||||
this.$nextTick(() => {
|
||||
this.MonitorChart();
|
||||
});
|
||||
if (this.deviceInfo.thingsModels && this.deviceInfo.thingsModels.length > 0) {
|
||||
this.deviceInfo.thingsModels = this.deviceInfo.thingsModels.sort((a, b) => b.order - a.order);
|
||||
}
|
||||
if (this.deviceInfo.chartList && this.deviceInfo.chartList.length > 0) {
|
||||
this.deviceInfo.chartList = this.deviceInfo.chartList.sort((a, b) => b.order - a.order);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 声卡特有方法
|
||||
formatVolume(val) {
|
||||
return val + '%';
|
||||
},
|
||||
|
||||
handleVolumeChange(val) {
|
||||
// 处理音量变化
|
||||
console.log('音量变化:', val);
|
||||
// TODO: 调用相应的API更新设备音量
|
||||
},
|
||||
|
||||
// 保留其他必要的方法
|
||||
initData() {
|
||||
this.$busEvent.$on('updateData', (params) => {
|
||||
this.updateParam(params);
|
||||
});
|
||||
},
|
||||
|
||||
initDataStatus() {
|
||||
this.$busEvent.$on('updateStatus', (status) => {
|
||||
this.updateStatus(status);
|
||||
});
|
||||
},
|
||||
|
||||
updateStatus(status) {
|
||||
let { serialNumber, productId, data } = status;
|
||||
if (data) {
|
||||
if (this.deviceInfo.serialNumber == serialNumber) {
|
||||
this.deviceInfo.status = data.status;
|
||||
this.deviceInfo.isShadow = data.isShadow;
|
||||
this.deviceInfo.rssi = data.rssi;
|
||||
this.updateDeviceStatus(this.deviceInfo);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateDeviceStatus(device) {
|
||||
if (device.status == 3) {
|
||||
this.statusColor.background = '#12d09f';
|
||||
this.title = this.$t('device.running-status.866086-26');
|
||||
this.shadowUnEnable = false;
|
||||
} else {
|
||||
if (device.isShadow == 1) {
|
||||
this.statusColor.background = '#486FF2';
|
||||
this.title = this.$t('device.running-status.866086-27');
|
||||
this.shadowUnEnable = false;
|
||||
} else {
|
||||
this.statusColor.background = '#909399';
|
||||
this.title = this.$t('device.running-status.866086-28');
|
||||
this.shadowUnEnable = true;
|
||||
}
|
||||
}
|
||||
this.$emit('statusEvent', this.deviceInfo.status);
|
||||
},
|
||||
|
||||
// 保留固件更新相关方法
|
||||
viewVersion() {
|
||||
this.openVersion = true;
|
||||
this.firmwareParams.firmwareType = 1;
|
||||
this.firmwareParams.versionInput = '';
|
||||
this.handleVersionInputChange();
|
||||
},
|
||||
|
||||
handleVersionInputChange() {
|
||||
if (this.firmwareParams.firmwareType == 1) {
|
||||
this.firmwareParams.versionInput = 'Version' + this.device.firmwareVersion;
|
||||
} else {
|
||||
this.firmwareParams.versionInput = 'Version' + this.device.wirelessVersion;
|
||||
}
|
||||
},
|
||||
|
||||
cancel1() {
|
||||
this.openVersion = false;
|
||||
},
|
||||
|
||||
getLatestFirmware() {
|
||||
const { deviceId, firmwareType } = this.deviceInfo;
|
||||
getLatestFirmware(deviceId, firmwareType).then((response) => {
|
||||
if (response.code === 200) {
|
||||
this.firmware = response.data;
|
||||
this.openFirmware = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this.openFirmware = false;
|
||||
},
|
||||
|
||||
getDownloadUrl(path) {
|
||||
return window.location.origin + process.env.VUE_APP_BASE_API + path;
|
||||
},
|
||||
|
||||
MonitorChart() {
|
||||
for (let i = 0; i < this.deviceInfo.chartList.length; i++) {
|
||||
this.monitorChart[i] = {
|
||||
chart: this.$echarts.init(this.$refs.map[i]),
|
||||
data: {
|
||||
id: this.deviceInfo.chartList[i].id,
|
||||
name: this.deviceInfo.chartList[i].name,
|
||||
value: this.deviceInfo.chartList[i].shadow ? this.deviceInfo.chartList[i].shadow : this.deviceInfo.chartList[i].datatype.min,
|
||||
},
|
||||
};
|
||||
var option;
|
||||
option = {
|
||||
tooltip: {
|
||||
formatter: ' {b} <br/> {c}' + this.deviceInfo.chartList[i].datatype.unit,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: this.deviceInfo.chartList[i].datatype.type,
|
||||
type: 'gauge',
|
||||
min: this.deviceInfo.chartList[i].datatype.min,
|
||||
max: this.deviceInfo.chartList[i].datatype.max,
|
||||
colorBy: 'data',
|
||||
splitNumber: 10,
|
||||
radius: '100%',
|
||||
splitLine: {
|
||||
distance: 4,
|
||||
},
|
||||
axisLabel: {
|
||||
fontSize: 10,
|
||||
distance: 10,
|
||||
},
|
||||
axisTick: {
|
||||
distance: 4,
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
width: 8,
|
||||
color: [
|
||||
[0.2, '#409EFF'],
|
||||
[0.8, '#12d09f'],
|
||||
[1, '#F56C6C'],
|
||||
],
|
||||
opacity: 0.3,
|
||||
},
|
||||
},
|
||||
pointer: {
|
||||
icon: 'triangle',
|
||||
length: '60%',
|
||||
width: 7,
|
||||
},
|
||||
progress: {
|
||||
show: true,
|
||||
width: 8,
|
||||
},
|
||||
detail: {
|
||||
valueAnimation: true,
|
||||
formatter: '{value}' + ' ' + this.deviceInfo.chartList[i].datatype.unit,
|
||||
offsetCenter: [0, '80%'],
|
||||
fontSize: 20,
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: this.deviceInfo.chartList[i].shadow ? this.deviceInfo.chartList[i].shadow : this.deviceInfo.chartList[i].datatype.min,
|
||||
name: this.deviceInfo.chartList[i].name,
|
||||
},
|
||||
],
|
||||
title: {
|
||||
offsetCenter: [0, '115%'],
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
option && this.monitorChart[i].chart.setOption(option);
|
||||
}
|
||||
},
|
||||
|
||||
// 录音相关方法
|
||||
async startRecording() {
|
||||
if (this.isRecording) return;
|
||||
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
this.mediaRecorder = new MediaRecorder(stream);
|
||||
this.audioChunks = [];
|
||||
|
||||
this.mediaRecorder.ondataavailable = (event) => {
|
||||
this.audioChunks.push(event.data);
|
||||
};
|
||||
|
||||
this.mediaRecorder.onstop = () => {
|
||||
const audioBlob = new Blob(this.audioChunks, { type: 'audio/mp3' });
|
||||
this.hasRecording = true;
|
||||
this.recordingStatus = '录音完成';
|
||||
// 创建音频预览URL
|
||||
this.audioUrl = URL.createObjectURL(audioBlob);
|
||||
};
|
||||
|
||||
// 设置数据收集间隔为100ms
|
||||
this.mediaRecorder.start(100);
|
||||
this.isRecording = true;
|
||||
this.recordingStatus = '正在录音...';
|
||||
this.recordingTime = 0;
|
||||
|
||||
// 开始计时
|
||||
this.timer = setInterval(() => {
|
||||
this.recordingTime++;
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
this.$message.error('无法访问麦克风');
|
||||
console.error('录音错误:', error);
|
||||
}
|
||||
},
|
||||
|
||||
stopRecording() {
|
||||
if (this.mediaRecorder && this.isRecording) {
|
||||
this.mediaRecorder.stop();
|
||||
this.isRecording = false;
|
||||
clearInterval(this.timer);
|
||||
|
||||
// 停止所有音轨
|
||||
this.mediaRecorder.stream.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
},
|
||||
|
||||
formatTime(seconds) {
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const remainingSeconds = seconds % 60;
|
||||
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
|
||||
},
|
||||
|
||||
reRecord() {
|
||||
// 释放之前的音频URL
|
||||
if (this.audioUrl) {
|
||||
URL.revokeObjectURL(this.audioUrl);
|
||||
this.audioUrl = null;
|
||||
}
|
||||
this.hasRecording = false;
|
||||
this.audioChunks = [];
|
||||
this.recordingStatus = '准备就绪';
|
||||
this.recordingTime = 0; // 重置录音时长
|
||||
},
|
||||
|
||||
async uploadRecording() {
|
||||
if (!this.hasRecording) return;
|
||||
|
||||
try {
|
||||
const audioBlob = new Blob(this.audioChunks, { type: 'audio/mp3' });
|
||||
const formData = new FormData();
|
||||
formData.append('file', audioBlob, `recording_${Date.now()}.mp3`);
|
||||
|
||||
// TODO: 替换为实际的上传API
|
||||
// const response = await uploadFile(formData);
|
||||
|
||||
this.$message.success('上传成功');
|
||||
this.recordings.unshift({
|
||||
name: `录音_${this.formatTime(this.recordingTime)}`,
|
||||
time: new Date().toLocaleString()
|
||||
});
|
||||
|
||||
// 重置录音状态
|
||||
this.reRecord();
|
||||
} catch (error) {
|
||||
this.$message.error('上传失败');
|
||||
console.error('上传错误:', error);
|
||||
}
|
||||
},
|
||||
|
||||
deleteRecording(index) {
|
||||
this.recordings.splice(index, 1);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.running-status {
|
||||
padding: 20px;
|
||||
|
||||
.status-col {
|
||||
.title {
|
||||
line-height: 28px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.mode-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.mode-card {
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.3s;
|
||||
padding: 20px;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.mode-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
|
||||
i,
|
||||
.svg-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 8px;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
.mode-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.mode-content {
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-card,
|
||||
.audio-list-card,
|
||||
.default-list-card {
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.settings-header,
|
||||
.audio-list-header,
|
||||
.default-list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.settings-title,
|
||||
.audio-list-title,
|
||||
.default-list-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-table {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.el-slider {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.voice-control-card {
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.voice-control-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.voice-control-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.voice-control-content {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
|
||||
.recorder-status {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.status-indicator {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
background: #f4f4f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
transition: all 0.3s;
|
||||
|
||||
i {
|
||||
font-size: 30px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
&.recording {
|
||||
background: #fef0f0;
|
||||
animation: pulse 1.5s infinite;
|
||||
|
||||
i {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
|
||||
.timer-display {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.control-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.el-button {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.recording-preview {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
|
||||
.preview-title {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
margin-bottom: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.audio-player {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
audio {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.preview-controls {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 5px;
|
||||
|
||||
.el-button {
|
||||
padding: 8px 15px;
|
||||
|
||||
i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recording-list {
|
||||
text-align: left;
|
||||
border-top: 1px solid #ebeef5;
|
||||
padding-top: 15px;
|
||||
|
||||
.list-title {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.recording-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
.recording-name {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.recording-time {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(245, 108, 108, 0.4);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 0 0 10px rgba(245, 108, 108, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(245, 108, 108, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user