Browse Source

新增需求11,广播管理系统接口前端

wangshuangpan 1 tháng trước cách đây
mục cha
commit
898318ef80

+ 192 - 0
pm_ui/src/api/publicBroadcasting/broadcast.js

@@ -0,0 +1,192 @@
+import request from '@/utils/request'
+
+// 查询广播分区列表
+export function listBroadcastZone(query) {
+  return request({
+    url: '/broadcast/system/zone/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询广播分区详细
+export function getBroadcastZone(id) {
+  return request({
+    url: '/broadcast/system/zone/' + id,
+    method: 'get'
+  })
+}
+
+// 新增广播分区
+export function addBroadcastZone(data) {
+  return request({
+    url: '/broadcast/system/zone',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改广播分区
+export function updateBroadcastZone(data) {
+  return request({
+    url: '/broadcast/system/zone',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除广播分区
+export function delBroadcastZone(id) {
+  return request({
+    url: '/broadcast/system/zone/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出广播分区
+export function exportBroadcastZone(query) {
+  return request({
+    url: '/broadcast/system/zone/export',
+    method: 'post',
+    params: query
+  })
+}
+
+// 更新音量和静音状态
+export function updateZoneVolume(data) {
+  return request({
+    url: '/broadcast/system/zone/volume',
+    method: 'post',
+    data: data
+  })
+}
+
+// 控制广播播放
+export function controlBroadcast(data) {
+  return request({
+    url: '/broadcast/system/zone/control',
+    method: 'post',
+    data: data
+  })
+}
+
+// 紧急广播
+export function emergencyBroadcast(data) {
+  return request({
+    url: '/broadcast/system/zone/emergency',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询广播设备列表
+export function listBroadcastDevice(query) {
+  return request({
+    url: '/broadcast/system/device/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 根据分区ID查询设备
+export function getZoneDevices(zoneId) {
+  return request({
+    url: '/broadcast/system/device/zone/' + zoneId,
+    method: 'get'
+  })
+}
+
+// 查询广播设备详细
+export function getBroadcastDevice(id) {
+  return request({
+    url: '/broadcast/system/device/' + id,
+    method: 'get'
+  })
+}
+
+// 新增广播设备
+export function addBroadcastDevice(data) {
+  return request({
+    url: '/broadcast/system/device',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改广播设备
+export function updateBroadcastDevice(data) {
+  return request({
+    url: '/broadcast/system/device',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除广播设备
+export function delBroadcastDevice(id) {
+  return request({
+    url: '/broadcast/system/device/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出广播设备
+export function exportBroadcastDevice(query) {
+  return request({
+    url: '/broadcast/system/device/export',
+    method: 'post',
+    params: query
+  })
+}
+
+// 查询广播内容列表
+export function listBroadcastContent(query) {
+  return request({
+    url: '/broadcast/system/content/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 获取广播内容(根据类型)
+export function getBroadcastContent(query) {
+  return request({
+    url: '/broadcast/system/content/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询广播内容详细
+export function getBroadcastContentDetail(id) {
+  return request({
+    url: '/broadcast/system/content/' + id,
+    method: 'get'
+  })
+}
+
+// 新增广播内容
+export function addBroadcastContent(data) {
+  return request({
+    url: '/broadcast/system/content',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改广播内容
+export function updateBroadcastContent(data) {
+  return request({
+    url: '/broadcast/system/content',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除广播内容
+export function delBroadcastContent(id) {
+  return request({
+    url: '/broadcast/system/content/' + id,
+    method: 'delete'
+  })
+}

+ 844 - 0
pm_ui/src/views/publicBroadcasting/index5.vue

@@ -0,0 +1,844 @@
+<template>
+  <div class="app-container">
+    <el-tabs v-model="activeTab">
+      <!-- 广播分区管理标签页 -->
+      <el-tab-pane label="广播分区管理" name="zones">
+        <el-form :model="zoneQuery" ref="zoneQueryRef" :inline="true" label-width="80px">
+          <el-form-item label="分区名称" prop="zoneName">
+            <el-input v-model="zoneQuery.zoneName" placeholder="请输入分区名称" clearable />
+          </el-form-item>
+          <el-form-item label="分区类型" prop="zoneType">
+            <el-select v-model="zoneQuery.zoneType" placeholder="请选择分区类型" clearable>
+              <el-option label="公共区域" value="公共区域" />
+              <el-option label="办公区域" value="办公区域" />
+              <el-option label="会议室" value="会议室" />
+              <el-option label="走廊" value="走廊" />
+              <el-option label="停车场" value="停车场" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="楼栋" prop="buildingName">
+            <el-select v-model="zoneQuery.buildingName" placeholder="请选择楼栋" clearable>
+              <el-option label="A栋" value="A栋" />
+              <el-option label="B栋" value="B栋" />
+              <el-option label="C栋" value="C栋" />
+              <el-option label="D栋" value="D栋" />
+              <el-option label="E栋" value="E栋" />
+              <el-option label="室外" value="室外" />
+              <el-option label="地下" value="地下" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="广播状态" prop="broadcastStatus">
+            <el-select v-model="zoneQuery.broadcastStatus" placeholder="请选择状态" clearable>
+              <el-option label="空闲" :value="0" />
+              <el-option label="广播中" :value="1" />
+              <el-option label="紧急广播" :value="2" />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="getZoneList">搜索</el-button>
+            <el-button icon="Refresh" @click="resetZoneQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+
+        <!-- 快捷操作按钮 -->
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button
+              type="danger"
+              plain
+              icon="Notification"
+              @click="handleEmergencyBroadcast"
+              v-hasPermi="['broadcast:zone:emergency']"
+            >紧急广播</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button
+              type="primary"
+              plain
+              icon="Microphone"
+              @click="handleQuickBroadcast"
+              v-hasPermi="['broadcast:zone:broadcast']"
+            >快速广播</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button
+              type="warning"
+              plain
+              icon="Phone"
+              @click="handlePaging"
+              v-hasPermi="['broadcast:zone:paging']"
+            >寻呼找人</el-button>
+          </el-col>
+        </el-row>
+
+        <el-table 
+          v-loading="zoneLoading" 
+          :data="zoneList" 
+          stripe 
+          border 
+          @selection-change="handleSelectionChange"
+          :row-class-name="getRowClassName"
+        >
+          <el-table-column type="selection" width="55" align="center" />
+          <el-table-column label="分区编码" prop="zoneCode" width="150" />
+          <el-table-column label="分区名称" prop="zoneName" width="180" />
+          <el-table-column label="分区类型" prop="zoneType" width="100" />
+          <el-table-column label="位置" width="180">
+            <template #default="scope">
+              <span>{{ scope.row.spaceLocation }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="在线状态" prop="isOnline" width="90" align="center">
+            <template #default="scope">
+              <el-tag :type="scope.row.isOnline === 1 ? 'success' : 'danger'" effect="dark" size="small">
+                {{ scope.row.isOnline === 1 ? '在线' : '离线' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="广播状态" prop="broadcastStatus" width="120" align="center">
+            <template #default="scope">
+              <el-tag 
+                :type="getBroadcastStatusType(scope.row.broadcastStatus)" 
+                effect="dark"
+              >
+                <el-icon v-if="scope.row.broadcastStatus === 1" class="is-loading">
+                  <Loading />
+                </el-icon>
+                {{ getBroadcastStatusText(scope.row.broadcastStatus) }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="当前播放" prop="currentContent" min-width="150" show-overflow-tooltip />
+          <el-table-column label="音量控制" width="200" align="center">
+            <template #default="scope">
+              <div class="volume-control">
+                <el-button
+                  :icon="scope.row.isMute ? 'MuteNotification' : 'Notification'"
+                  circle
+                  size="small"
+                  @click="toggleMute(scope.row)"
+                  :type="scope.row.isMute ? 'danger' : 'primary'"
+                />
+                <el-slider
+                  v-model="scope.row.volume"
+                  :disabled="scope.row.isMute || scope.row.isOnline === 0"
+                  @change="handleVolumeChange(scope.row)"
+                  style="width: 120px; margin: 0 10px;"
+                />
+                <span>{{ scope.row.volume }}%</span>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="150" fixed="right">
+            <template #default="scope">
+              <el-button 
+                link 
+                type="primary" 
+                icon="VideoPlay" 
+                @click="handleControl(scope.row)"
+                :disabled="scope.row.isOnline === 0"
+              >控制</el-button>
+              <el-button link type="primary" icon="View" @click="handleZoneDetail(scope.row)">详情</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <pagination
+          v-show="zoneTotal > 0"
+          :total="zoneTotal"
+          v-model:page="zoneQuery.pageNum"
+          v-model:limit="zoneQuery.pageSize"
+          @pagination="getZoneList"
+        />
+      </el-tab-pane>
+
+      <!-- 设备监控标签页 -->
+      <el-tab-pane label="设备监控" name="devices">
+        <el-form :model="deviceQuery" ref="deviceQueryRef" :inline="true" label-width="80px">
+          <el-form-item label="设备名称" prop="deviceName">
+            <el-input v-model="deviceQuery.deviceName" placeholder="请输入设备名称" clearable />
+          </el-form-item>
+          <el-form-item label="设备类型" prop="deviceType">
+            <el-select v-model="deviceQuery.deviceType" placeholder="请选择设备类型" clearable>
+              <el-option label="功放" value="功放" />
+              <el-option label="音箱" value="音箱" />
+              <el-option label="话筒" value="话筒" />
+              <el-option label="控制器" value="控制器" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="设备状态" prop="status">
+            <el-select v-model="deviceQuery.status" placeholder="请选择状态" clearable>
+              <el-option label="正常" :value="1" />
+              <el-option label="故障" :value="0" />
+              <el-option label="维护中" :value="2" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="所属分区" prop="zoneName">
+            <el-input v-model="deviceQuery.zoneName" placeholder="请输入分区名称" clearable />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="getDeviceList">搜索</el-button>
+            <el-button icon="Refresh" @click="resetDeviceQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+
+        <!-- 设备统计卡片 -->
+        <el-row :gutter="20" class="mb20">
+          <el-col :span="6">
+            <el-card class="stat-card">
+              <div class="stat-item">
+                <div class="stat-title">设备总数</div>
+                <div class="stat-value total">{{ deviceStats.total }}</div>
+              </div>
+            </el-card>
+          </el-col>
+          <el-col :span="6">
+            <el-card class="stat-card">
+              <div class="stat-item">
+                <div class="stat-title">在线设备</div>
+                <div class="stat-value online">{{ deviceStats.online }}</div>
+              </div>
+            </el-card>
+          </el-col>
+          <el-col :span="6">
+            <el-card class="stat-card">
+              <div class="stat-item">
+                <div class="stat-title">故障设备</div>
+                <div class="stat-value fault">{{ deviceStats.fault }}</div>
+              </div>
+            </el-card>
+          </el-col>
+          <el-col :span="6">
+            <el-card class="stat-card">
+              <div class="stat-item">
+                <div class="stat-title">维护中</div>
+                <div class="stat-value maintenance">{{ deviceStats.maintenance }}</div>
+              </div>
+            </el-card>
+          </el-col>
+        </el-row>
+
+        <el-table v-loading="deviceLoading" :data="deviceList" stripe border>
+          <el-table-column label="设备编码" prop="deviceCode" width="150" />
+          <el-table-column label="设备名称" prop="deviceName" width="180" />
+          <el-table-column label="设备类型" prop="deviceType" width="100" align="center">
+            <template #default="scope">
+              <el-tag>{{ scope.row.deviceType }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="所属分区" prop="zoneName" width="180" />
+          <el-table-column label="品牌型号" width="150">
+            <template #default="scope">
+              <div>{{ scope.row.brand }}</div>
+              <div class="text-small">{{ scope.row.model }}</div>
+            </template>
+          </el-table-column>
+          <el-table-column label="设备状态" prop="status" width="100" align="center">
+            <template #default="scope">
+              <el-tag :type="getDeviceStatusType(scope.row.status)" effect="dark">
+                {{ getDeviceStatusText(scope.row.status) }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="在线状态" prop="isOnline" width="100" align="center">
+            <template #default="scope">
+              <el-tag :type="scope.row.isOnline === 1 ? 'success' : 'danger'">
+                {{ scope.row.isOnline === 1 ? '在线' : '离线' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="故障信息" width="200" show-overflow-tooltip>
+            <template #default="scope">
+              <span v-if="scope.row.faultCode" class="fault-info">
+                {{ scope.row.faultCode }}: {{ scope.row.faultDesc }}
+              </span>
+              <span v-else>-</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="最后心跳" prop="lastHeartbeat" width="160">
+            <template #default="scope">
+              <span>{{ parseTime(scope.row.lastHeartbeat) }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <pagination
+          v-show="deviceTotal > 0"
+          :total="deviceTotal"
+          v-model:page="deviceQuery.pageNum"
+          v-model:limit="deviceQuery.pageSize"
+          @pagination="getDeviceList"
+        />
+      </el-tab-pane>
+    </el-tabs>
+
+    <!-- 分区详情抽屉 -->
+    <el-drawer
+      v-model="detailVisible"
+      title="分区详情"
+      direction="rtl"
+      size="45%"
+    >
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="分区编码">{{ currentZone.zoneCode }}</el-descriptions-item>
+        <el-descriptions-item label="分区名称">{{ currentZone.zoneName }}</el-descriptions-item>
+        <el-descriptions-item label="分区类型">{{ currentZone.zoneType }}</el-descriptions-item>
+        <el-descriptions-item label="楼栋">{{ currentZone.buildingName }}</el-descriptions-item>
+        <el-descriptions-item label="楼层">{{ currentZone.floorName }}</el-descriptions-item>
+        <el-descriptions-item label="设备数量">{{ currentZone.deviceCount }}</el-descriptions-item>
+        <el-descriptions-item label="空间位置" :span="2">{{ currentZone.spaceLocation }}</el-descriptions-item>
+        <el-descriptions-item label="音量">{{ currentZone.volume }}%</el-descriptions-item>
+        <el-descriptions-item label="静音状态">
+          <el-tag :type="currentZone.isMute === 1 ? 'danger' : 'success'">
+            {{ currentZone.isMute === 1 ? '已静音' : '正常' }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="在线状态">
+          <el-tag :type="currentZone.isOnline === 1 ? 'success' : 'danger'">
+            {{ currentZone.isOnline === 1 ? '在线' : '离线' }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="广播状态">
+          <el-tag :type="getBroadcastStatusType(currentZone.broadcastStatus)">
+            {{ getBroadcastStatusText(currentZone.broadcastStatus) }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="当前播放内容" :span="2">
+          {{ currentZone.currentContent || '无' }}
+        </el-descriptions-item>
+        <el-descriptions-item label="内容代号">{{ currentZone.contentCode || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="优先级">{{ currentZone.priority }}</el-descriptions-item>
+      </el-descriptions>
+
+      <!-- 分区内设备列表 -->
+      <div class="zone-devices" v-if="zoneDevices && zoneDevices.length > 0">
+        <h4>分区设备列表</h4>
+        <el-table :data="zoneDevices" stripe size="small">
+          <el-table-column label="设备编码" prop="deviceCode" />
+          <el-table-column label="设备名称" prop="deviceName" />
+          <el-table-column label="设备类型" prop="deviceType" />
+          <el-table-column label="状态" prop="status" align="center">
+            <template #default="scope">
+              <el-tag :type="getDeviceStatusType(scope.row.status)" size="small">
+                {{ getDeviceStatusText(scope.row.status) }}
+              </el-tag>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </el-drawer>
+
+    <!-- 广播控制对话框 -->
+    <el-dialog v-model="controlDialogVisible" title="广播控制" width="600px" append-to-body>
+      <el-form ref="controlFormRef" :model="controlForm" :rules="controlRules" label-width="100px">
+        <el-form-item label="分区名称">
+          <el-input v-model="controlForm.zoneName" disabled />
+        </el-form-item>
+        <el-form-item label="广播内容" prop="contentCode">
+          <el-select v-model="controlForm.contentCode" placeholder="请选择广播内容" @change="handleContentChange">
+            <el-option
+              v-for="item in contentList"
+              :key="item.contentCode"
+              :label="item.contentName"
+              :value="item.contentCode"
+            >
+              <span style="float: left">{{ item.contentName }}</span>
+              <span style="float: right; color: #8492a6; font-size: 13px">{{ item.contentType }}</span>
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="播放模式" prop="playMode">
+          <el-radio-group v-model="controlForm.playMode">
+            <el-radio :label="1">单次播放</el-radio>
+            <el-radio :label="2">循环播放</el-radio>
+            <el-radio :label="3">定时播放</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="音量调节">
+          <el-slider v-model="controlForm.volume" :min="0" :max="100" show-input />
+        </el-form-item>
+        <el-form-item label="优先级" prop="priority">
+          <el-rate v-model="controlForm.priority" :max="10" show-text />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="controlDialogVisible = false">取 消</el-button>
+          <el-button type="primary" @click="submitControl">开始播放</el-button>
+          <el-button type="danger" @click="stopBroadcast">停止播放</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
+    <!-- 紧急广播对话框 -->
+    <el-dialog v-model="emergencyDialogVisible" title="紧急广播" width="700px" append-to-body :close-on-click-modal="false">
+      <el-alert
+        title="紧急广播将中断所有正在播放的内容,请谨慎操作!"
+        type="warning"
+        show-icon
+        :closable="false"
+        style="margin-bottom: 20px"
+      />
+      <el-form ref="emergencyFormRef" :model="emergencyForm" :rules="emergencyRules" label-width="100px">
+        <el-form-item label="广播范围" prop="broadcastRange">
+          <el-radio-group v-model="emergencyForm.broadcastRange">
+            <el-radio :label="1">全部分区</el-radio>
+            <el-radio :label="2">选择分区</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="选择分区" v-if="emergencyForm.broadcastRange === 2" prop="selectedZones">
+          <el-select 
+            v-model="emergencyForm.selectedZones" 
+            multiple 
+            placeholder="请选择分区"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="zone in zoneList"
+              :key="zone.id"
+              :label="zone.zoneName"
+              :value="zone.id"
+              :disabled="zone.isOnline === 0"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="广播方式" prop="broadcastType">
+          <el-radio-group v-model="emergencyForm.broadcastType">
+            <el-radio :label="1">预设内容</el-radio>
+            <el-radio :label="2">实时喊话</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="预设内容" v-if="emergencyForm.broadcastType === 1" prop="contentCode">
+          <el-select v-model="emergencyForm.contentCode" placeholder="请选择紧急广播内容">
+            <el-option
+              v-for="item in emergencyContentList"
+              :key="item.contentCode"
+              :label="item.contentName"
+              :value="item.contentCode"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="喊话内容" v-if="emergencyForm.broadcastType === 2" prop="message">
+          <el-input
+            v-model="emergencyForm.message"
+            type="textarea"
+            :rows="4"
+            placeholder="请输入紧急广播内容"
+            maxlength="200"
+            show-word-limit
+          />
+        </el-form-item>
+        <el-form-item label="循环次数" prop="loopCount">
+          <el-input-number v-model="emergencyForm.loopCount" :min="1" :max="10" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="emergencyDialogVisible = false">取 消</el-button>
+          <el-button type="danger" @click="submitEmergency">立即广播</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, computed } from 'vue'
+import { 
+  listBroadcastZone, 
+  listBroadcastDevice, 
+  updateZoneVolume, 
+  controlBroadcast,
+  emergencyBroadcast,
+  getZoneDevices,
+  getBroadcastContent
+} from '@/api/publicBroadcasting/broadcast'
+
+const { proxy } = getCurrentInstance()
+const activeTab = ref('zones')
+
+// 分区查询相关
+const zoneQuery = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  zoneName: null,
+  zoneType: null,
+  buildingName: null,
+  broadcastStatus: null
+})
+const zoneList = ref([])
+const zoneTotal = ref(0)
+const zoneLoading = ref(false)
+const selectedZones = ref([])
+
+// 设备查询相关
+const deviceQuery = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  deviceName: null,
+  deviceType: null,
+  status: null,
+  zoneName: null
+})
+const deviceList = ref([])
+const deviceTotal = ref(0)
+const deviceLoading = ref(false)
+
+// 设备统计
+const deviceStats = ref({
+  total: 0,
+  online: 0,
+  fault: 0,
+  maintenance: 0
+})
+
+// 详情相关
+const detailVisible = ref(false)
+const currentZone = ref({})
+const zoneDevices = ref([])
+
+// 广播控制相关
+const controlDialogVisible = ref(false)
+const controlForm = ref({
+  zoneId: null,
+  zoneName: '',
+  contentCode: '',
+  playMode: 1,
+  volume: 50,
+  priority: 5
+})
+const contentList = ref([])
+
+const controlRules = {
+  contentCode: [
+    { required: true, message: '请选择广播内容', trigger: 'change' }
+  ]
+}
+
+// 紧急广播相关
+const emergencyDialogVisible = ref(false)
+const emergencyForm = ref({
+  broadcastRange: 1,
+  selectedZones: [],
+  broadcastType: 1,
+  contentCode: '',
+  message: '',
+  loopCount: 3
+})
+const emergencyContentList = ref([])
+
+const emergencyRules = {
+  selectedZones: [
+    { required: true, message: '请选择分区', trigger: 'change' }
+  ],
+  contentCode: [
+    { required: true, message: '请选择广播内容', trigger: 'change' }
+  ],
+  message: [
+    { required: true, message: '请输入喊话内容', trigger: 'blur' }
+  ]
+}
+
+// 获取广播状态类型
+const getBroadcastStatusType = (status) => {
+  const types = ['info', 'success', 'danger']
+  return types[status] || 'info'
+}
+
+// 获取广播状态文本
+const getBroadcastStatusText = (status) => {
+  const texts = ['空闲', '广播中', '紧急广播']
+  return texts[status] || '未知'
+}
+
+// 获取设备状态类型
+const getDeviceStatusType = (status) => {
+  const types = ['danger', 'success', 'warning']
+  return types[status] || 'info'
+}
+
+// 获取设备状态文本
+const getDeviceStatusText = (status) => {
+  const texts = ['故障', '正常', '维护中']
+  return texts[status] || '未知'
+}
+
+// 获取行样式
+const getRowClassName = ({ row }) => {
+  if (row.broadcastStatus === 2) {
+    return 'emergency-row'
+  } else if (row.broadcastStatus === 1) {
+    return 'broadcasting-row'
+  }
+  return ''
+}
+
+// 查询分区列表
+function getZoneList() {
+  zoneLoading.value = true
+  listBroadcastZone(zoneQuery).then(response => {
+    zoneList.value = response.rows
+    zoneTotal.value = response.total
+    zoneLoading.value = false
+  })
+}
+
+// 查询设备列表
+function getDeviceList() {
+  deviceLoading.value = true
+  listBroadcastDevice(deviceQuery).then(response => {
+    deviceList.value = response.rows
+    deviceTotal.value = response.total
+    // 更新统计数据
+    if (response.extData) {
+      deviceStats.value = response.extData
+    }
+    deviceLoading.value = false
+  })
+}
+
+// 重置分区查询
+function resetZoneQuery() {
+  proxy.resetForm('zoneQueryRef')
+  zoneQuery.pageNum = 1
+  getZoneList()
+}
+
+// 重置设备查询
+function resetDeviceQuery() {
+  proxy.resetForm('deviceQueryRef')
+  deviceQuery.pageNum = 1
+  getDeviceList()
+}
+
+// 多选框选中数据
+function handleSelectionChange(selection) {
+  selectedZones.value = selection
+}
+
+// 切换静音
+function toggleMute(row) {
+  const newMuteStatus = row.isMute === 1 ? 0 : 1
+  updateZoneVolume({
+    id: row.id,
+    isMute: newMuteStatus,
+    volume: row.volume
+  }).then(() => {
+    row.isMute = newMuteStatus
+    proxy.$modal.msgSuccess(newMuteStatus === 1 ? '已静音' : '已取消静音')
+  })
+}
+
+// 音量调节
+function handleVolumeChange(row) {
+  updateZoneVolume({
+    id: row.id,
+    volume: row.volume,
+    isMute: row.isMute
+  }).then(() => {
+    proxy.$modal.msgSuccess('音量调节成功')
+  })
+}
+
+// 广播控制
+function handleControl(row) {
+  controlForm.value = {
+    zoneId: row.id,
+    zoneName: row.zoneName,
+    contentCode: '',
+    playMode: 1,
+    volume: row.volume,
+    priority: 5
+  }
+  // 获取广播内容列表
+  getBroadcastContent({ contentType: '背景音乐,通知' }).then(response => {
+    contentList.value = response.rows
+  })
+  controlDialogVisible.value = true
+}
+
+// 内容选择变化
+function handleContentChange(value) {
+  const content = contentList.value.find(item => item.contentCode === value)
+  if (content) {
+    controlForm.value.priority = content.priority
+  }
+}
+
+// 提交广播控制
+function submitControl() {
+  proxy.$refs.controlFormRef.validate(valid => {
+    if (valid) {
+      controlBroadcast(controlForm.value).then(() => {
+        proxy.$modal.msgSuccess('广播开始播放')
+        controlDialogVisible.value = false
+        getZoneList()
+      })
+    }
+  })
+}
+
+// 停止广播
+function stopBroadcast() {
+  proxy.$modal.confirm('确认停止当前广播吗?').then(() => {
+    controlBroadcast({
+      zoneId: controlForm.value.zoneId,
+      action: 'stop'
+    }).then(() => {
+      proxy.$modal.msgSuccess('广播已停止')
+      controlDialogVisible.value = false
+      getZoneList()
+    })
+  })
+}
+
+// 紧急广播
+function handleEmergencyBroadcast() {
+  emergencyForm.value = {
+    broadcastRange: 1,
+    selectedZones: [],
+    broadcastType: 1,
+    contentCode: '',
+    message: '',
+    loopCount: 3
+  }
+  // 获取紧急广播内容
+  getBroadcastContent({ contentType: '紧急广播' }).then(response => {
+    emergencyContentList.value = response.rows
+  })
+  emergencyDialogVisible.value = true
+}
+
+// 提交紧急广播
+function submitEmergency() {
+  proxy.$refs.emergencyFormRef.validate(valid => {
+    if (valid) {
+      proxy.$modal.confirm('确认发送紧急广播吗?这将中断所有正在播放的内容!').then(() => {
+        emergencyBroadcast(emergencyForm.value).then(() => {
+          proxy.$modal.msgSuccess('紧急广播已发送')
+          emergencyDialogVisible.value = false
+          getZoneList()
+        })
+      })
+    }
+  })
+}
+
+// 快速广播
+function handleQuickBroadcast() {
+  if (selectedZones.value.length === 0) {
+    proxy.$modal.msgWarning('请先选择要广播的分区')
+    return
+  }
+  // TODO: 实现快速广播功能
+  proxy.$modal.msgWarning('快速广播功能开发中')
+}
+
+// 寻呼找人
+function handlePaging() {
+  // TODO: 实现寻呼功能
+  proxy.$modal.msgWarning('寻呼找人功能开发中')
+}
+
+// 查看分区详情
+function handleZoneDetail(row) {
+  currentZone.value = row
+  // 获取分区内设备
+  getZoneDevices(row.id).then(response => {
+    zoneDevices.value = response.rows
+  })
+  detailVisible.value = true
+}
+
+// 初始化
+getZoneList()
+</script>
+
+<style scoped>
+.volume-control {
+  display: flex;
+  align-items: center;
+}
+
+.stat-card {
+  height: 100px;
+  margin-bottom: 20px;
+}
+
+.stat-item {
+  text-align: center;
+  padding: 10px;
+}
+
+.stat-title {
+  font-size: 14px;
+  color: #909399;
+  margin-bottom: 10px;
+}
+
+.stat-value {
+  font-size: 28px;
+  font-weight: bold;
+}
+
+.stat-value.total {
+  color: #409EFF;
+}
+
+.stat-value.online {
+  color: #67C23A;
+}
+
+.stat-value.fault {
+  color: #F56C6C;
+}
+
+.stat-value.maintenance {
+  color: #E6A23C;
+}
+
+.text-small {
+  font-size: 12px;
+  color: #909399;
+}
+
+.fault-info {
+  color: #F56C6C;
+  font-size: 12px;
+}
+
+.zone-devices {
+  margin: 20px;
+}
+
+.zone-devices h4 {
+  margin-bottom: 15px;
+}
+
+.mb20 {
+  margin-bottom: 20px;
+}
+
+/* 表格行样式 */
+:deep(.emergency-row) {
+  background-color: #fef0f0 !important;
+}
+
+:deep(.broadcasting-row) {
+  background-color: #f0f9ff !important;
+}
+
+/* 加载动画 */
+.is-loading {
+  animation: rotating 2s linear infinite;
+}
+
+@keyframes rotating {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+</style>