|
|
@@ -1,22 +1,11 @@
|
|
|
<script lang="ts" setup>
|
|
|
import {onMounted, reactive, ref} from 'vue'
|
|
|
import {message, Modal} from 'ant-design-vue'
|
|
|
-import {DeleteOutlined, EditOutlined} from '@ant-design/icons-vue'
|
|
|
+import {DeleteOutlined, EditOutlined, PlusOutlined} from '@ant-design/icons-vue'
|
|
|
import {createMqtt, deleteMqtt, getMqttList, updateMqtt} from '@/api/mqtt'
|
|
|
import {createBle, deleteBle, getBleList, updateBle} from '@/api/ble'
|
|
|
import type {BleConfig, BleConfigForm, MqttConfig, MqttConfigForm} from '@/types'
|
|
|
|
|
|
-const activeTab = ref('mqtt')
|
|
|
-
|
|
|
-const tabList = [
|
|
|
- {key: 'mqtt', tab: '消息配置'},
|
|
|
- {key: 'ble', tab: '蓝牙配置'},
|
|
|
-]
|
|
|
-
|
|
|
-function onTabChange(key: string) {
|
|
|
- activeTab.value = key
|
|
|
-}
|
|
|
-
|
|
|
// MQTT 相关
|
|
|
const mqttLoading = ref(false)
|
|
|
const mqttList = ref<MqttConfig[]>([])
|
|
|
@@ -205,181 +194,213 @@ onMounted(() => {
|
|
|
|
|
|
<template>
|
|
|
<div class="server-config-page">
|
|
|
- <a-card
|
|
|
- :active-tab-key="activeTab"
|
|
|
- :tab-list="tabList"
|
|
|
- :body-style="{ padding: '10px' }"
|
|
|
- style="width: 100%; height: 100%;"
|
|
|
- @tabChange="onTabChange"
|
|
|
- >
|
|
|
- <!-- 消息配置 -->
|
|
|
- <template v-if="activeTab === 'mqtt'">
|
|
|
- <a-spin :spinning="mqttLoading">
|
|
|
- <div class="card-grid">
|
|
|
- <a-card v-for="item in mqttList" :key="item.id" class="config-card" size="small">
|
|
|
- <template #title>
|
|
|
- <div class="card-title">服务器 {{ item.id }}</div>
|
|
|
- </template>
|
|
|
- <div class="card-content">
|
|
|
- <a-row :gutter="[0, 4]">
|
|
|
- <a-col :span="8" class="label">地址:</a-col>
|
|
|
- <a-col :span="16" class="value">{{ item.broker }}</a-col>
|
|
|
- <a-col :span="8" class="label">状态:</a-col>
|
|
|
- <a-col :span="16">
|
|
|
- <a-tag :color="item.enabled ? 'success' : 'default'" size="small">{{
|
|
|
- item.enabled ? '启用' : '禁用'
|
|
|
- }}
|
|
|
- </a-tag>
|
|
|
- </a-col>
|
|
|
- <a-col :span="8" class="label">客户端ID:</a-col>
|
|
|
- <a-col :span="16" class="value">{{ item.client_id }}</a-col>
|
|
|
- <a-col :span="8" class="label">主题:</a-col>
|
|
|
- <a-col :span="16" class="value">{{ item.topic }}</a-col>
|
|
|
- </a-row>
|
|
|
- </div>
|
|
|
- <template #actions>
|
|
|
- <a-button class="action-btn" size="small" type="link" @click="openMqttModal(item)">
|
|
|
- <EditOutlined/>
|
|
|
- 编辑
|
|
|
- </a-button>
|
|
|
- <a-button class="action-btn" danger size="small" type="link" @click="onMqttDelete(item)">
|
|
|
- <DeleteOutlined/>
|
|
|
- 删除
|
|
|
- </a-button>
|
|
|
- </template>
|
|
|
- </a-card>
|
|
|
-
|
|
|
- <a-card class="config-card add-card" size="small" @click="openMqttModal()">
|
|
|
- <div class="add-card-content">
|
|
|
- <img alt="add" class="add-icon" src="/plus.svg"/>
|
|
|
- </div>
|
|
|
- </a-card>
|
|
|
- </div>
|
|
|
- </a-spin>
|
|
|
-
|
|
|
- <a-modal
|
|
|
- :ok-text="mqttEditingId !== null ? '更新' : '创建'"
|
|
|
- :open="mqttModalVisible"
|
|
|
- :title="mqttEditingId !== null ? '编辑消息配置' : '新建消息配置'"
|
|
|
- :width="480"
|
|
|
- cancel-text="取消"
|
|
|
- @cancel="mqttModalVisible = false"
|
|
|
- @ok="onMqttSubmit"
|
|
|
- >
|
|
|
- <a-form layout="vertical">
|
|
|
- <a-form-item label="Broker 地址" required>
|
|
|
- <a-input v-model:value="mqttForm.broker" placeholder="tcp://127.0.0.1:1883"/>
|
|
|
- </a-form-item>
|
|
|
- <a-form-item label="客户端ID">
|
|
|
- <a-input v-model:value="mqttForm.client_id" placeholder="agent-monitor"/>
|
|
|
- </a-form-item>
|
|
|
- <a-form-item label="用户名">
|
|
|
- <a-input v-model:value="mqttForm.username" placeholder="可选"/>
|
|
|
- </a-form-item>
|
|
|
- <a-form-item label="密码">
|
|
|
- <a-input-password v-model:value="mqttForm.password" placeholder="可选"/>
|
|
|
- </a-form-item>
|
|
|
- <a-form-item label="主题">
|
|
|
- <a-input v-model:value="mqttForm.topic" placeholder="agent/status"/>
|
|
|
- </a-form-item>
|
|
|
- <a-form-item label="启用">
|
|
|
- <a-switch v-model:checked="mqttForm.enabled"/>
|
|
|
- </a-form-item>
|
|
|
- </a-form>
|
|
|
- </a-modal>
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- 蓝牙配置 -->
|
|
|
- <template v-if="activeTab === 'ble'">
|
|
|
- <a-spin :spinning="bleLoading">
|
|
|
- <div class="card-grid">
|
|
|
- <a-card v-for="item in bleList" :key="item.id" class="config-card" size="small">
|
|
|
- <template #title>
|
|
|
- <div class="card-title">蓝牙 {{ item.id }}</div>
|
|
|
- </template>
|
|
|
- <div class="card-content">
|
|
|
- <a-row :gutter="[0, 4]">
|
|
|
- <a-col :span="8" class="label">设备名称:</a-col>
|
|
|
- <a-col :span="16" class="value">{{ item.device_name }}</a-col>
|
|
|
- <a-col :span="8" class="label">状态:</a-col>
|
|
|
- <a-col :span="16">
|
|
|
- <a-tag :color="item.enabled ? 'success' : 'default'" size="small">{{
|
|
|
- item.enabled ? '启用' : '禁用'
|
|
|
- }}
|
|
|
- </a-tag>
|
|
|
- </a-col>
|
|
|
- <a-col :span="8" class="label">服务码:</a-col>
|
|
|
- <a-col :span="16" :title="item.service_uuid" class="value">{{
|
|
|
- truncate(item.service_uuid, 20)
|
|
|
+ <!-- MQTT 配置 -->
|
|
|
+ <div class="section-card">
|
|
|
+ <div class="section-header">
|
|
|
+ <span class="section-title">消息配置 (MQTT)</span>
|
|
|
+ <a-button size="small" type="primary" @click="openMqttModal()">
|
|
|
+ <template #icon>
|
|
|
+ <PlusOutlined/>
|
|
|
+ </template>
|
|
|
+ 新建
|
|
|
+ </a-button>
|
|
|
+ </div>
|
|
|
+ <a-spin :spinning="mqttLoading">
|
|
|
+ <div v-if="mqttList.length === 0" class="empty-text">暂无配置</div>
|
|
|
+ <div v-else class="card-grid">
|
|
|
+ <a-card v-for="item in mqttList" :key="item.id" class="config-card" size="small">
|
|
|
+ <template #title>
|
|
|
+ <div class="card-title">服务器 {{ item.id }}</div>
|
|
|
+ </template>
|
|
|
+ <div class="card-content">
|
|
|
+ <a-row :gutter="[0, 4]">
|
|
|
+ <a-col :span="8" class="label">地址:</a-col>
|
|
|
+ <a-col :span="16" class="value">{{ item.broker }}</a-col>
|
|
|
+ <a-col :span="8" class="label">状态:</a-col>
|
|
|
+ <a-col :span="16">
|
|
|
+ <a-tag :color="item.enabled ? 'success' : 'default'" size="small">{{
|
|
|
+ item.enabled ? '启用' : '禁用'
|
|
|
}}
|
|
|
- </a-col>
|
|
|
- <a-col :span="8" class="label">模式码:</a-col>
|
|
|
- <a-col :span="16" :title="item.mode_char_uuid" class="value">{{
|
|
|
- truncate(item.mode_char_uuid, 20)
|
|
|
+ </a-tag>
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="8" class="label">客户端ID:</a-col>
|
|
|
+ <a-col :span="16" class="value">{{ item.client_id }}</a-col>
|
|
|
+ <a-col :span="8" class="label">主题:</a-col>
|
|
|
+ <a-col :span="16" class="value">{{ item.topic }}</a-col>
|
|
|
+ </a-row>
|
|
|
+ </div>
|
|
|
+ <template #actions>
|
|
|
+ <a-button class="action-btn" size="small" type="link" @click="openMqttModal(item)">
|
|
|
+ <EditOutlined/>
|
|
|
+ 编辑
|
|
|
+ </a-button>
|
|
|
+ <a-button class="action-btn" danger size="small" type="link" @click="onMqttDelete(item)">
|
|
|
+ <DeleteOutlined/>
|
|
|
+ 删除
|
|
|
+ </a-button>
|
|
|
+ </template>
|
|
|
+ </a-card>
|
|
|
+ </div>
|
|
|
+ </a-spin>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- BLE 配置 -->
|
|
|
+ <div class="section-card">
|
|
|
+ <div class="section-header">
|
|
|
+ <span class="section-title">蓝牙配置 (BLE)</span>
|
|
|
+ <a-button size="small" type="primary" @click="openBleModal()">
|
|
|
+ <template #icon>
|
|
|
+ <PlusOutlined/>
|
|
|
+ </template>
|
|
|
+ 新建
|
|
|
+ </a-button>
|
|
|
+ </div>
|
|
|
+ <a-spin :spinning="bleLoading">
|
|
|
+ <div v-if="bleList.length === 0" class="empty-text">暂无配置</div>
|
|
|
+ <div v-else class="card-grid">
|
|
|
+ <a-card v-for="item in bleList" :key="item.id" class="config-card" size="small">
|
|
|
+ <template #title>
|
|
|
+ <div class="card-title">蓝牙 {{ item.id }}</div>
|
|
|
+ </template>
|
|
|
+ <div class="card-content">
|
|
|
+ <a-row :gutter="[0, 4]">
|
|
|
+ <a-col :span="8" class="label">设备名称:</a-col>
|
|
|
+ <a-col :span="16" class="value">{{ item.device_name }}</a-col>
|
|
|
+ <a-col :span="8" class="label">状态:</a-col>
|
|
|
+ <a-col :span="16">
|
|
|
+ <a-tag :color="item.enabled ? 'success' : 'default'" size="small">{{
|
|
|
+ item.enabled ? '启用' : '禁用'
|
|
|
}}
|
|
|
- </a-col>
|
|
|
- </a-row>
|
|
|
- </div>
|
|
|
- <template #actions>
|
|
|
- <a-button class="action-btn" size="small" type="link" @click="openBleModal(item)">
|
|
|
- <EditOutlined/>
|
|
|
- 编辑
|
|
|
- </a-button>
|
|
|
- <a-button class="action-btn" danger size="small" type="link" @click="onBleDelete(item)">
|
|
|
- <DeleteOutlined/>
|
|
|
- 删除
|
|
|
- </a-button>
|
|
|
- </template>
|
|
|
- </a-card>
|
|
|
-
|
|
|
- <a-card class="config-card add-card" size="small" @click="openBleModal()">
|
|
|
- <div class="add-card-content">
|
|
|
- <img alt="add" class="add-icon" src="/plus.svg"/>
|
|
|
- </div>
|
|
|
- </a-card>
|
|
|
- </div>
|
|
|
- </a-spin>
|
|
|
-
|
|
|
- <a-modal
|
|
|
- :ok-text="bleEditingId !== null ? '更新' : '创建'"
|
|
|
- :open="bleModalVisible"
|
|
|
- :title="bleEditingId !== null ? '编辑蓝牙配置' : '新建蓝牙配置'"
|
|
|
- :width="520"
|
|
|
- cancel-text="取消"
|
|
|
- @cancel="bleModalVisible = false"
|
|
|
- @ok="onBleSubmit"
|
|
|
- >
|
|
|
- <a-form layout="vertical">
|
|
|
- <a-form-item label="设备名称" required>
|
|
|
- <a-input v-model:value="bleForm.device_name" placeholder="AI-Light"/>
|
|
|
- </a-form-item>
|
|
|
- <a-form-item label="服务码" required>
|
|
|
- <a-input v-model:value="bleForm.service_uuid" :placeholder="DEFAULT_SERVICE_UUID"/>
|
|
|
- </a-form-item>
|
|
|
- <a-form-item label="模式码" required>
|
|
|
- <a-input v-model:value="bleForm.mode_char_uuid" :placeholder="DEFAULT_MODE_CHAR_UUID"/>
|
|
|
- <div class="form-hint">读写/通知 - 灯效模式控制</div>
|
|
|
- </a-form-item>
|
|
|
- <a-form-item label="启用">
|
|
|
- <a-switch v-model:checked="bleForm.enabled"/>
|
|
|
- </a-form-item>
|
|
|
- </a-form>
|
|
|
- </a-modal>
|
|
|
- </template>
|
|
|
- </a-card>
|
|
|
+ </a-tag>
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="8" class="label">服务码:</a-col>
|
|
|
+ <a-col :span="16" :title="item.service_uuid" class="value">{{
|
|
|
+ truncate(item.service_uuid, 20)
|
|
|
+ }}
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="8" class="label">模式码:</a-col>
|
|
|
+ <a-col :span="16" :title="item.mode_char_uuid" class="value">{{
|
|
|
+ truncate(item.mode_char_uuid, 20)
|
|
|
+ }}
|
|
|
+ </a-col>
|
|
|
+ </a-row>
|
|
|
+ </div>
|
|
|
+ <template #actions>
|
|
|
+ <a-button class="action-btn" size="small" type="link" @click="openBleModal(item)">
|
|
|
+ <EditOutlined/>
|
|
|
+ 编辑
|
|
|
+ </a-button>
|
|
|
+ <a-button class="action-btn" danger size="small" type="link" @click="onBleDelete(item)">
|
|
|
+ <DeleteOutlined/>
|
|
|
+ 删除
|
|
|
+ </a-button>
|
|
|
+ </template>
|
|
|
+ </a-card>
|
|
|
+ </div>
|
|
|
+ </a-spin>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- MQTT 弹窗 -->
|
|
|
+ <a-modal
|
|
|
+ :ok-text="mqttEditingId !== null ? '更新' : '创建'"
|
|
|
+ :open="mqttModalVisible"
|
|
|
+ :title="mqttEditingId !== null ? '编辑消息配置' : '新建消息配置'"
|
|
|
+ :width="480"
|
|
|
+ cancel-text="取消"
|
|
|
+ @cancel="mqttModalVisible = false"
|
|
|
+ @ok="onMqttSubmit"
|
|
|
+ >
|
|
|
+ <a-form layout="vertical">
|
|
|
+ <a-form-item label="Broker 地址" required>
|
|
|
+ <a-input v-model:value="mqttForm.broker" placeholder="tcp://127.0.0.1:1883"/>
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="客户端ID">
|
|
|
+ <a-input v-model:value="mqttForm.client_id" placeholder="agent-monitor"/>
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="用户名">
|
|
|
+ <a-input v-model:value="mqttForm.username" placeholder="可选"/>
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="密码">
|
|
|
+ <a-input-password v-model:value="mqttForm.password" placeholder="可选"/>
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="主题">
|
|
|
+ <a-input v-model:value="mqttForm.topic" placeholder="agent/status"/>
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="启用">
|
|
|
+ <a-switch v-model:checked="mqttForm.enabled"/>
|
|
|
+ </a-form-item>
|
|
|
+ </a-form>
|
|
|
+ </a-modal>
|
|
|
+
|
|
|
+ <!-- BLE 弹窗 -->
|
|
|
+ <a-modal
|
|
|
+ :ok-text="bleEditingId !== null ? '更新' : '创建'"
|
|
|
+ :open="bleModalVisible"
|
|
|
+ :title="bleEditingId !== null ? '编辑蓝牙配置' : '新建蓝牙配置'"
|
|
|
+ :width="520"
|
|
|
+ cancel-text="取消"
|
|
|
+ @cancel="bleModalVisible = false"
|
|
|
+ @ok="onBleSubmit"
|
|
|
+ >
|
|
|
+ <a-form layout="vertical">
|
|
|
+ <a-form-item label="设备名称" required>
|
|
|
+ <a-input v-model:value="bleForm.device_name" placeholder="AI-Light"/>
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="服务码" required>
|
|
|
+ <a-input v-model:value="bleForm.service_uuid" :placeholder="DEFAULT_SERVICE_UUID"/>
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="模式码" required>
|
|
|
+ <a-input v-model:value="bleForm.mode_char_uuid" :placeholder="DEFAULT_MODE_CHAR_UUID"/>
|
|
|
+ <div class="form-hint">读写/通知 - 灯效模式控制</div>
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="启用">
|
|
|
+ <a-switch v-model:checked="bleForm.enabled"/>
|
|
|
+ </a-form-item>
|
|
|
+ </a-form>
|
|
|
+ </a-modal>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<style scoped>
|
|
|
.server-config-page {
|
|
|
color: var(--text-color);
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.section-card {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ background: var(--card-bg);
|
|
|
+ border: 1px solid var(--border-color);
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
+ padding: 16px 20px;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
+ overflow: auto;
|
|
|
}
|
|
|
|
|
|
-.server-config-page :deep(.ant-card-head) {
|
|
|
- padding: 0 15px;
|
|
|
+.section-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.section-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--text-secondary);
|
|
|
+}
|
|
|
+
|
|
|
+.empty-text {
|
|
|
+ color: var(--text-secondary);
|
|
|
+ font-size: 13px;
|
|
|
+ text-align: center;
|
|
|
+ padding: 24px 0;
|
|
|
}
|
|
|
|
|
|
.card-grid {
|
|
|
@@ -479,39 +500,4 @@ onMounted(() => {
|
|
|
font-size: 12px;
|
|
|
margin-top: 4px;
|
|
|
}
|
|
|
-
|
|
|
-.add-card {
|
|
|
- cursor: pointer;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- background: transparent;
|
|
|
- border: 1px dashed var(--border-color);
|
|
|
- box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px 0 rgba(0, 0, 0, 0.02);
|
|
|
- transition: all 0.3s;
|
|
|
-}
|
|
|
-
|
|
|
-.add-card:hover {
|
|
|
- border-color: #1890ff;
|
|
|
- box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.06), 0 6px 16px 0 rgba(0, 0, 0, 0.04), 0 9px 28px 8px rgba(0, 0, 0, 0.03);
|
|
|
-}
|
|
|
-
|
|
|
-.add-card-content {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
-}
|
|
|
-
|
|
|
-.add-icon {
|
|
|
- width: 48px;
|
|
|
- height: 48px;
|
|
|
- opacity: 0.3;
|
|
|
- transition: opacity 0.3s;
|
|
|
-}
|
|
|
-
|
|
|
-.add-card:hover .add-icon {
|
|
|
- opacity: 0.6;
|
|
|
-}
|
|
|
</style>
|