|
|
@@ -1,277 +1,341 @@
|
|
|
<template>
|
|
|
- <div class="mqtt-box">
|
|
|
- <h1 class="header">MQTTX在线测试客户端</h1>
|
|
|
- <el-card :model="connection">
|
|
|
- <h1>连接参数(Configuration)</h1>
|
|
|
- <el-form label-position="top" :model="connection">
|
|
|
- <el-row :gutter="20">
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item prop="host" label="协议|主机|端口">
|
|
|
- <el-input v-model="connection.host" :disabled="connSuccess" type="password" show-password>
|
|
|
- <template #prepend>
|
|
|
- <el-select
|
|
|
- v-model="connection.protocol"
|
|
|
- class="w80"
|
|
|
- :disabled="connSuccess"
|
|
|
- @change="handleProtocolChange"
|
|
|
- >
|
|
|
- <el-option label="ws://" value="ws"></el-option>
|
|
|
- <el-option label="wss://" value="wss"></el-option>
|
|
|
- </el-select>
|
|
|
- </template>
|
|
|
- <template #append>
|
|
|
- <el-input
|
|
|
- v-model.number="connection.port"
|
|
|
- type="number"
|
|
|
- class="w80"
|
|
|
- :disabled="connSuccess"
|
|
|
- placeholder="8083/8084"
|
|
|
- ></el-input>
|
|
|
- </template>
|
|
|
- </el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="0">
|
|
|
- <el-form-item prop="clientId" label="标识(Client ID)唯一性">
|
|
|
- <el-input v-model="connection.clientId"> </el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="0">
|
|
|
- <el-form-item prop="username" label="账号(Username)">
|
|
|
- <el-input v-model="connection.username"></el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="0">
|
|
|
- <el-form-item prop="password" label="密码(Password)">
|
|
|
- <el-input v-model="connection.password" type="password" show-password></el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="4">
|
|
|
- <el-form-item prop="regpacket" label="设备包名(Regpacket)">
|
|
|
- <el-input v-model="connection.repacket" :disabled="connSuccess" @input="syncdhtreg" @change="init_topic"></el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="4">
|
|
|
- <el-form-item prop="dhtRegpack" label="共享传感器(dhtRegpacket)">
|
|
|
- <el-input v-model="connection.dhtRegpack" :disabled="connSuccess" @change="init_topic"></el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8" class="text-right">
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- :icon="Setting"
|
|
|
- size="default"
|
|
|
- class="sub-btn"
|
|
|
- :disabled="client.connected"
|
|
|
- @click="createConnection"
|
|
|
- :loading="btnLoadingType === 'connect'"
|
|
|
- :style="{ display: client.connected?'none':'' }"
|
|
|
- >
|
|
|
- {{ client.connected ? "已连接(Connected)" : "连接(Connect)" }}
|
|
|
- </el-button>
|
|
|
- <el-button
|
|
|
- v-if="client.connected"
|
|
|
- type="warning"
|
|
|
- size="default"
|
|
|
- :icon="Discount"
|
|
|
- @click="destroyConnection"
|
|
|
- :loading="btnLoadingType === 'disconnect'"
|
|
|
- >
|
|
|
- 断开(Disconnect)
|
|
|
- </el-button>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </el-form>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <el-card shadow="hover">
|
|
|
- <h1>订阅(Subscribe)</h1>
|
|
|
- <el-form label-position="top" :model="subscription">
|
|
|
- <el-row :gutter="20">
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item prop="topic" label="订阅主题(Topic)">
|
|
|
- <el-input
|
|
|
- v-model="connection.subTopics"
|
|
|
- :disabled="subscribedSuccess"
|
|
|
- type="password" show-password
|
|
|
- ></el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="4">
|
|
|
- <el-form-item prop="qos" label="订阅质量(QoS)">
|
|
|
- <el-select
|
|
|
- v-model="subscription.qos"
|
|
|
- :disabled="subscribedSuccess"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="qos in qosList"
|
|
|
- :key="qos"
|
|
|
- :label="qos==0?'0 至多一次':qos==1?'1 至少一次':'2 仅仅一次'"
|
|
|
- :value="qos"
|
|
|
- ></el-option>
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8" class="text-right">
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- :icon="Connection"
|
|
|
- size="default"
|
|
|
- class="sub-btn"
|
|
|
- :style="{ display: subscribedSuccess?'none':'' }"
|
|
|
- :loading="btnLoadingType === 'subscribe'"
|
|
|
- :disabled="!client.connected || subscribedSuccess"
|
|
|
- @click="doSubscribe"
|
|
|
- >
|
|
|
- {{ subscribedSuccess ? "已订阅(Subscribed)" : "订阅(Subscribe)" }}
|
|
|
- </el-button>
|
|
|
- <el-button
|
|
|
- v-if="subscribedSuccess"
|
|
|
- type="warning"
|
|
|
- :icon="Discount"
|
|
|
- size="default"
|
|
|
- class="sub-btn"
|
|
|
- :loading="btnLoadingType === 'unsubscribe'"
|
|
|
- :disabled="!client.connected"
|
|
|
- @click="doUnSubscribe"
|
|
|
- >
|
|
|
- 取消(Unsubscribe)
|
|
|
- </el-button>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- </el-form>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <el-card shadow="hover">
|
|
|
- <h1>发布(Publish)</h1>
|
|
|
- <el-form label-position="top" :model="publish">
|
|
|
- <el-row :gutter="30">
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item prop="topic" label="发布主题(Topic)">
|
|
|
- <el-input v-model="connection.pubTopic" type="password" show-password></el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="4">
|
|
|
- <el-form-item prop="qos" label="发布质量(QoS)">
|
|
|
- <el-select v-model="publish.qos">
|
|
|
- <el-option
|
|
|
- v-for="qos in qosList"
|
|
|
- :key="qos"
|
|
|
- :label="qos==0?'0 至多一次':qos==1?'1 至少一次':'2 仅仅一次'"
|
|
|
- :value="qos"
|
|
|
- ></el-option>
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="4">
|
|
|
- <el-form-item prop="retain" label="发布保留(Retain)">
|
|
|
- <el-select v-model="publish.retain">
|
|
|
- <el-option value="false" label="false 不保留"></el-option>
|
|
|
- <el-option value="true" label="true 不保留"></el-option>
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <el-row :gutter="30">
|
|
|
- <el-col :span="16">
|
|
|
- <el-form-item prop="payload" label="操作指令(Payload)">
|
|
|
- <el-input v-model="publish.payload" clearable maxlength="64" show-word-limit>
|
|
|
- <!--<template #prepend>
|
|
|
+ <div class="mqtt-box">
|
|
|
+ <h1 class="header">MQTTX在线测试客户端</h1>
|
|
|
+ <el-card :model="connection">
|
|
|
+ <h1>连接参数(Configuration)</h1>
|
|
|
+ <el-form label-position="top" :model="connection">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item prop="host" label="协议|主机|端口">
|
|
|
+ <el-input v-model="connection.host" :disabled="connSuccess" type="password" show-password>
|
|
|
+ <template #prepend>
|
|
|
+ <el-select v-model="connection.protocol" class="w80" :disabled="connSuccess" @change="handleProtocolChange">
|
|
|
+ <el-option label="ws://" value="ws"></el-option>
|
|
|
+ <el-option label="wss://" value="wss"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ <template #append>
|
|
|
+ <el-input v-model.number="connection.port" type="number" class="w80" :disabled="connSuccess" placeholder="8083/8084"></el-input>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="0">
|
|
|
+ <el-form-item prop="clientId" label="标识(Client ID)唯一性">
|
|
|
+ <el-input v-model="connection.clientId"> </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="0">
|
|
|
+ <el-form-item prop="username" label="账号(Username)">
|
|
|
+ <el-input v-model="connection.username"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="0">
|
|
|
+ <el-form-item prop="password" label="密码(Password)">
|
|
|
+ <el-input v-model="connection.password" type="password" show-password></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <el-form-item prop="regpacket" label="设备包名(Regpacket)">
|
|
|
+ <el-input v-model="connection.repacket" :disabled="connSuccess" @input="syncdhtreg" @change="init_topic"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <el-form-item prop="dhtRegpack" label="共享传感器(dhtRegpacket)">
|
|
|
+ <el-input v-model="connection.dhtRegpack" :disabled="connSuccess" @change="init_topic"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" class="text-right">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ :icon="Setting"
|
|
|
+ size="default"
|
|
|
+ class="sub-btn"
|
|
|
+ :disabled="client.connected"
|
|
|
+ @click="createConnection"
|
|
|
+ :loading="btnLoadingType === 'connect'"
|
|
|
+ :style="{ display: client.connected ? 'none' : '' }"
|
|
|
+ >
|
|
|
+ {{ client.connected ? '已连接(Connected)' : '连接(Connect)' }}
|
|
|
+ </el-button>
|
|
|
+ <el-button v-if="client.connected" type="warning" size="default" :icon="Discount" @click="destroyConnection" :loading="btnLoadingType === 'disconnect'"> 断开(Disconnect) </el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <h1>订阅(Subscribe)</h1>
|
|
|
+ <el-form label-position="top" :model="subscription">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item prop="topic" label="订阅主题(Topic)">
|
|
|
+ <el-input v-model="connection.subTopics" :disabled="subscribedSuccess" type="password" show-password></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <el-form-item prop="qos" label="订阅质量(QoS)">
|
|
|
+ <el-select v-model="subscription.qos" :disabled="subscribedSuccess">
|
|
|
+ <el-option v-for="qos in qosList" :key="qos" :label="qos == 0 ? '0 至多一次' : qos == 1 ? '1 至少一次' : '2 仅仅一次'" :value="qos"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" class="text-right">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ :icon="Connection"
|
|
|
+ size="default"
|
|
|
+ class="sub-btn"
|
|
|
+ :style="{ display: subscribedSuccess ? 'none' : '' }"
|
|
|
+ :loading="btnLoadingType === 'subscribe'"
|
|
|
+ :disabled="!client.connected || subscribedSuccess"
|
|
|
+ @click="doSubscribe"
|
|
|
+ >
|
|
|
+ {{ subscribedSuccess ? '已订阅(Subscribed)' : '订阅(Subscribe)' }}
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ v-if="subscribedSuccess"
|
|
|
+ type="warning"
|
|
|
+ :icon="Discount"
|
|
|
+ size="default"
|
|
|
+ class="sub-btn"
|
|
|
+ :loading="btnLoadingType === 'unsubscribe'"
|
|
|
+ :disabled="!client.connected"
|
|
|
+ @click="doUnSubscribe"
|
|
|
+ >
|
|
|
+ 取消(Unsubscribe)
|
|
|
+ </el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <h1>发布(Publish)</h1>
|
|
|
+ <el-form label-position="top" :model="publish">
|
|
|
+ <el-row :gutter="30">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item prop="topic" label="发布主题(Topic)">
|
|
|
+ <el-input v-model="connection.pubTopic" type="password" show-password></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <el-form-item prop="qos" label="发布质量(QoS)">
|
|
|
+ <el-select v-model="publish.qos">
|
|
|
+ <el-option v-for="qos in qosList" :key="qos" :label="qos == 0 ? '0 至多一次' : qos == 1 ? '1 至少一次' : '2 仅仅一次'" :value="qos"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <el-form-item prop="retain" label="发布保留(Retain)">
|
|
|
+ <el-select v-model="publish.retain">
|
|
|
+ <el-option value="false" label="false 不保留"></el-option>
|
|
|
+ <el-option value="true" label="true 不保留"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="30">
|
|
|
+ <el-col :span="16">
|
|
|
+ <el-form-item prop="payload" label="操作指令(Payload)">
|
|
|
+ <el-input v-model="publish.payload" clearable maxlength="64" show-word-limit>
|
|
|
+ <!--<template #prepend>
|
|
|
<el-button :icon="Operation" />
|
|
|
</template> -->
|
|
|
- <template #append>
|
|
|
- <el-select v-model="publish.payload" placeholder="选择指令" style="width: 115px">
|
|
|
- <el-option label="状态查询" value="55 AA AA AA AA 91 CF" />
|
|
|
- <el-option label="全部打开" value="55 AA AA AA AA 81 A4 01" />
|
|
|
- <el-option label="全部关闭" value="55 AA AA AA AA 81 A4 00" />
|
|
|
- <el-option label="一路开关" value="55 AA AA AA AA 81 BA 01" />
|
|
|
- <el-option label="二路开关" value="55 AA AA AA AA 81 BA 02" />
|
|
|
- <el-option label="三路开关" value="55 AA AA AA AA 81 BA 03" />
|
|
|
- <el-option label="四路开关" value="55 AA AA AA AA 81 BA 04" />
|
|
|
- </el-select>
|
|
|
- </template>
|
|
|
- </el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8" class="text-right">
|
|
|
- <el-button
|
|
|
- type="success"
|
|
|
- :icon="Position"
|
|
|
- size="default"
|
|
|
- class="sub-btn"
|
|
|
- :loading="btnLoadingType === 'publish'"
|
|
|
- :disabled="!client.connected"
|
|
|
- @click="doPublish(publish.payload,connection.pubTopic)">
|
|
|
- 发布(Publish)
|
|
|
- </el-button>
|
|
|
+ <template #append>
|
|
|
+ <el-select v-model="publish.payload" placeholder="选择指令" style="width: 115px">
|
|
|
+ <el-option label="状态查询" value="55 AA AA AA AA 91 CF" />
|
|
|
+ <el-option label="全部打开" value="55 AA AA AA AA 81 A4 01" />
|
|
|
+ <el-option label="全部关闭" value="55 AA AA AA AA 81 A4 00" />
|
|
|
+ <el-option label="一路开关" value="55 AA AA AA AA 81 BA 01" />
|
|
|
+ <el-option label="二路开关" value="55 AA AA AA AA 81 BA 02" />
|
|
|
+ <el-option label="三路开关" value="55 AA AA AA AA 81 BA 03" />
|
|
|
+ <el-option label="四路开关" value="55 AA AA AA AA 81 BA 04" />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" class="text-right">
|
|
|
+ <el-button
|
|
|
+ type="success"
|
|
|
+ :icon="Position"
|
|
|
+ size="default"
|
|
|
+ class="sub-btn"
|
|
|
+ :loading="btnLoadingType === 'publish'"
|
|
|
+ :disabled="!client.connected"
|
|
|
+ @click="doPublish(publish.payload, connection.pubTopic)"
|
|
|
+ >
|
|
|
+ 发布(Publish)
|
|
|
+ </el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <h1>
|
|
|
+ <el-button @click="clsmsg" type="Success" size="default" :icon="Delete" title="点击清空历史记录">接收(Receive)</el-button>
|
|
|
+ <el-tag size="default" title="接收次数">收 {{ recvnum }}</el-tag>
|
|
|
+ <el-tag size="default" :title="dht_tm">{{ dht_wsd }}</el-tag>
|
|
|
+ <el-tag size="default" title="设备已工作时长">{{ parseInt(runSeconds) }} 秒</el-tag>
|
|
|
+ <el-button
|
|
|
+ type="success"
|
|
|
+ title="关闭一路"
|
|
|
+ :disabled="!connection.onlineStatus | !client.connected"
|
|
|
+ v-if="connection.ch1_Status"
|
|
|
+ icon="ele-Check"
|
|
|
+ size="default"
|
|
|
+ id="ch1"
|
|
|
+ v-preventReClick="2000"
|
|
|
+ @click="switchLight('55 AA AA AA AA 81 01 00')"
|
|
|
+ >关闭</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="warning"
|
|
|
+ title="打开一路"
|
|
|
+ :disabled="!connection.onlineStatus | !client.connected"
|
|
|
+ v-else="!connection.ch1_Status"
|
|
|
+ icon="ele-CloseBold"
|
|
|
+ size="default"
|
|
|
+ id="ch1"
|
|
|
+ v-preventReClick="2000"
|
|
|
+ @click="switchLight('55 AA AA AA AA 81 01 01')"
|
|
|
+ >打开</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="success"
|
|
|
+ title="关闭二路"
|
|
|
+ :disabled="!connection.onlineStatus | !client.connected"
|
|
|
+ v-if="connection.ch2_Status"
|
|
|
+ icon="ele-Check"
|
|
|
+ size="default"
|
|
|
+ id="ch2"
|
|
|
+ v-preventReClick="2000"
|
|
|
+ @click="switchLight('55 AA AA AA AA 81 02 00')"
|
|
|
+ >关闭</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="warning"
|
|
|
+ title="打开二路"
|
|
|
+ :disabled="!connection.onlineStatus | !client.connected"
|
|
|
+ v-else="!connection.ch2_Status"
|
|
|
+ icon="ele-CloseBold"
|
|
|
+ size="default"
|
|
|
+ id="ch2"
|
|
|
+ v-preventReClick="2000"
|
|
|
+ @click="switchLight('55 AA AA AA AA 81 02 01')"
|
|
|
+ >打开</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="success"
|
|
|
+ title="关闭三路"
|
|
|
+ :disabled="!connection.onlineStatus | !client.connected"
|
|
|
+ v-if="connection.ch3_Status"
|
|
|
+ icon="ele-Check"
|
|
|
+ size="default"
|
|
|
+ id="ch3"
|
|
|
+ v-preventReClick="2000"
|
|
|
+ @click="switchLight('55 AA AA AA AA 81 03 00')"
|
|
|
+ >关闭</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="warning"
|
|
|
+ title="打开三路"
|
|
|
+ :disabled="!connection.onlineStatus | !client.connected"
|
|
|
+ v-else="!connection.ch3_Status"
|
|
|
+ icon="ele-CloseBold"
|
|
|
+ size="default"
|
|
|
+ id="ch3"
|
|
|
+ v-preventReClick="2000"
|
|
|
+ @click="switchLight('55 AA AA AA AA 81 03 01')"
|
|
|
+ >打开</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="success"
|
|
|
+ title="关闭四路"
|
|
|
+ :disabled="!connection.onlineStatus | !client.connected"
|
|
|
+ v-if="connection.ch4_Status"
|
|
|
+ icon="ele-Check"
|
|
|
+ size="default"
|
|
|
+ id="ch4"
|
|
|
+ v-preventReClick="2000"
|
|
|
+ @click="switchLight('55 AA AA AA AA 81 04 00')"
|
|
|
+ >关闭</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="warning"
|
|
|
+ title="打开四路"
|
|
|
+ :disabled="!connection.onlineStatus | !client.connected"
|
|
|
+ v-else="!connection.ch4_Status"
|
|
|
+ icon="ele-CloseBold"
|
|
|
+ size="default"
|
|
|
+ id="ch4"
|
|
|
+ v-preventReClick="2000"
|
|
|
+ @click="switchLight('55 AA AA AA AA 81 04 01')"
|
|
|
+ >打开</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ title="四路全部关闭"
|
|
|
+ :disabled="!connection.onlineStatus | !client.connected"
|
|
|
+ v-if="connection.all_Status"
|
|
|
+ icon="ele-SwitchButton"
|
|
|
+ size="default"
|
|
|
+ id="ch5"
|
|
|
+ @click="switchLight('55 AA AA AA AA 81 A4 00')"
|
|
|
+ >全关</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="success"
|
|
|
+ title="四路全部打开"
|
|
|
+ :disabled="!connection.onlineStatus | !client.connected"
|
|
|
+ v-else="!connection.all_Status"
|
|
|
+ icon="ele-Switch"
|
|
|
+ size="default"
|
|
|
+ id="ch5"
|
|
|
+ @click="switchLight('55 AA AA AA AA 81 A4 01')"
|
|
|
+ >全开</el-button
|
|
|
+ >
|
|
|
+
|
|
|
+ <el-alert v-if="!client.connected || !connection.onlineStatus" title="网络服务断开或设备离线!" center type="warning" effect="light" />
|
|
|
+ </h1>
|
|
|
+ <!-- 绑定接收日志,只读 -->
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-input type="textarea" :rows="8" id="recv" v-model="receivedMessages" readonly class="log"></el-input>
|
|
|
</el-col>
|
|
|
- </el-row>
|
|
|
- </el-form>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <el-card shadow="hover">
|
|
|
- <h1>
|
|
|
- <el-button @click="clsmsg" type="Success" size="default" :icon="Delete" title="点击清空历史记录">接收(Receive)</el-button>
|
|
|
- <el-tag size="default" title="接收次数">收 {{recvnum}}</el-tag>
|
|
|
- <el-tag size="default" :title="dht_tm">{{dht_wsd}}</el-tag>
|
|
|
- <el-tag size="default" title="设备已工作时长">{{parseInt(runSeconds)}} 秒</el-tag>
|
|
|
- <el-button type="success" title="关闭一路" :disabled="!connection.onlineStatus|!client.connected" v-if="connection.ch1_Status" icon="ele-Check" size="default" id="ch1" v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 01 00')">关闭</el-button>
|
|
|
- <el-button type="warning" title="打开一路" :disabled="!connection.onlineStatus|!client.connected" v-else="!connection.ch1_Status" icon="ele-CloseBold" size="default" id="ch1" v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 01 01')">打开</el-button>
|
|
|
- <el-button type="success" title="关闭二路" :disabled="!connection.onlineStatus|!client.connected" v-if="connection.ch2_Status" icon="ele-Check" size="default" id="ch2" v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 02 00')">关闭</el-button>
|
|
|
- <el-button type="warning" title="打开二路" :disabled="!connection.onlineStatus|!client.connected" v-else="!connection.ch2_Status" icon="ele-CloseBold" size="default" id="ch2" v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 02 01')">打开</el-button>
|
|
|
- <el-button type="success" title="关闭三路" :disabled="!connection.onlineStatus|!client.connected" v-if="connection.ch3_Status" icon="ele-Check" size="default" id="ch3" v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 03 00')">关闭</el-button>
|
|
|
- <el-button type="warning" title="打开三路" :disabled="!connection.onlineStatus|!client.connected" v-else="!connection.ch3_Status" icon="ele-CloseBold" size="default" id="ch3" v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 03 01')">打开</el-button>
|
|
|
- <el-button type="success" title="关闭四路" :disabled="!connection.onlineStatus|!client.connected" v-if="connection.ch4_Status" icon="ele-Check" size="default" id="ch4" v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 04 00')">关闭</el-button>
|
|
|
- <el-button type="warning" title="打开四路" :disabled="!connection.onlineStatus|!client.connected" v-else="!connection.ch4_Status" icon="ele-CloseBold" size="default" id="ch4" v-preventReClick="2000" @click="switchLight('55 AA AA AA AA 81 04 01')">打开</el-button>
|
|
|
- <el-button type="danger" title="四路全部关闭" :disabled="!connection.onlineStatus|!client.connected" v-if="connection.all_Status" icon="ele-SwitchButton" size="default" id="ch5" @click="switchLight('55 AA AA AA AA 81 A4 00')">全关</el-button>
|
|
|
- <el-button type="success" title="四路全部打开" :disabled="!connection.onlineStatus|!client.connected" v-else="!connection.all_Status" icon="ele-Switch" size="default" id="ch5" @click="switchLight('55 AA AA AA AA 81 A4 01')">全开</el-button>
|
|
|
-
|
|
|
- <el-alert v-if="!client.connected ||!connection.onlineStatus" title="网络服务断开或设备离线!" center type="warning" effect="light" />
|
|
|
- </h1>
|
|
|
- <!-- 绑定接收日志,只读 -->
|
|
|
- <el-col :span="24">
|
|
|
- <el-input
|
|
|
- type="textarea"
|
|
|
- :rows="8"
|
|
|
- id="recv"
|
|
|
- v-model="receivedMessages"
|
|
|
- readonly
|
|
|
- class="log"
|
|
|
- ></el-input>
|
|
|
- </el-col>
|
|
|
- </el-card>
|
|
|
- </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts" name="mqttx">
|
|
|
- import { reactive, ref,onMounted,nextTick } from "vue";
|
|
|
- import { Search,ChatDotSquare,TopRight,Star,Operation,Setting,Connection,Discount,Open,Delete,Position} from '@element-plus/icons-vue';
|
|
|
- import * as MQTT from "mqtt/dist/mqtt.min";//针对4.3.7版本的引用方法。5.7.x会提示错误 (import * as MQTT from "mqtt")
|
|
|
- import mittBus from '/@/utils/mitt';//事件总线mitt 解决打包后错误Uncaught (in promise) ReferenceError: Cannot access 'oe' before initialization
|
|
|
-
|
|
|
+import { reactive, ref, onMounted, nextTick } from 'vue';
|
|
|
+import { Search, ChatDotSquare, TopRight, Star, Operation, Setting, Connection, Discount, Open, Delete, Position } from '@element-plus/icons-vue';
|
|
|
+import * as MQTT from 'mqtt/dist/mqtt.min'; // 针对4.3.7版本的引用方法。5.7.x会提示错误 (import * as MQTT from "mqtt")
|
|
|
+import mittBus from '/@/utils/mitt'; // 事件总线mitt 解决打包后错误Uncaught (in promise) ReferenceError: Cannot access 'oe' before initialization
|
|
|
+
|
|
|
// vue 3 + vite use MQTT.js refer to https://github.com/mqttjs/MQTT.js/issues/1269
|
|
|
// https://github.com/mqttjs/MQTT.js#qos
|
|
|
-const qosList = [0, 1, 2];//质量
|
|
|
+const qosList = [0, 1, 2]; // 质量
|
|
|
const now = new Date();
|
|
|
-const recvnum=ref(0);
|
|
|
-const dht_wd=ref(0);//温度,湿度
|
|
|
-const dht_sd=ref(0);
|
|
|
-const dht_tm=ref("");//同步时间
|
|
|
-const dht_wsd=ref("温度0℃,湿度0%");
|
|
|
-const runSeconds=ref(0);//工作时长
|
|
|
+const recvnum = ref(0);
|
|
|
+const dht_wd = ref(0); // 温度、湿度
|
|
|
+const dht_sd = ref(0);
|
|
|
+const dht_tm = ref(''); // 同步时间
|
|
|
+const dht_wsd = ref('温度0℃,湿度0%');
|
|
|
+const runSeconds = ref(0); // 工作时长
|
|
|
|
|
|
// mqtt客户端变量 let或const
|
|
|
const client = ref({
|
|
|
- connected: false,//未连接
|
|
|
+ connected: false, //未连接
|
|
|
} as MQTT.MqttClient);
|
|
|
|
|
|
-const receivedMessages = ref("");
|
|
|
-const subscribedSuccess = ref(false);//订阅成功标志
|
|
|
-const connSuccess=ref(false);//连接成功标志
|
|
|
-const btnLoadingType = ref("");
|
|
|
-const retryTimes = ref(0);//重连次数
|
|
|
+const receivedMessages = ref('');
|
|
|
+const subscribedSuccess = ref(false); //订阅成功标志
|
|
|
+const connSuccess = ref(false); //连接成功标志
|
|
|
+const btnLoadingType = ref('');
|
|
|
+const retryTimes = ref(0); //重连次数
|
|
|
|
|
|
/**
|
|
|
* this demo uses EMQX Public MQTT Broker (https://www.emqx.com/en/mqtt/public-mqtt5-broker), here are the details:
|
|
|
@@ -287,457 +351,473 @@ const retryTimes = ref(0);//重连次数
|
|
|
* for more options and details, please refer to https://github.com/mqttjs/MQTT.js#mqttclientstreambuilder-options
|
|
|
*/
|
|
|
const connection = reactive({
|
|
|
- protocol: "ws",
|
|
|
- host: "broker.emqx.io",
|
|
|
- // ws -> 8083; wss -> 8084
|
|
|
- port: 8083,
|
|
|
- clientId: "emqx_vue3_" + Math.random().toString(16).substring(2, 8),
|
|
|
- username: "",
|
|
|
- password: "",
|
|
|
- repacket: "d1ca1ff51f04",//注册包(改为您的注册包)
|
|
|
- dhtRegpack:"d1ca1ff51f04",//温度注册包(可以相同可以共享传感器)
|
|
|
- mqttToken:'0804d4c44c1f1bd11dea461481f19868',//授权TOKEN自己约定
|
|
|
- keepalive: 30,
|
|
|
- clean: true,//清除 clean session
|
|
|
- connectTimeout: 30 * 1000, // ms 超时毫秒
|
|
|
- reconnectPeriod: 5000, // ms 重连毫秒
|
|
|
- resubscribe:true,//重新订阅
|
|
|
- //定义您自己的主题
|
|
|
- subTopic:'mqtt/admintnet/#0#/out',
|
|
|
- willTopic:'mqtt/admintnet/#0#/will',
|
|
|
- dhtTopic:'mqtt/admintnet/#0#/dht',
|
|
|
- pubTopic:'mqtt/admintnet/#0#/into',
|
|
|
- subTopics:[],
|
|
|
- pubPayload:'{"msg":"hellow vue3 mqtt."}',
|
|
|
- onlineStatus:false,
|
|
|
- ch1_Status:false,
|
|
|
- ch2_Status:false,
|
|
|
- ch3_Status:false,
|
|
|
- ch4_Status:false,
|
|
|
- all_Status:false,
|
|
|
- isAC:null,//强电true
|
|
|
-
|
|
|
+ protocol: 'ws',
|
|
|
+ host: 'broker.emqx.io',
|
|
|
+ // ws -> 8083; wss -> 8084
|
|
|
+ port: 8083,
|
|
|
+ clientId: 'emqx_vue3_' + Math.random().toString(16).substring(2, 8),
|
|
|
+ username: '',
|
|
|
+ password: '',
|
|
|
+ repacket: 'd1ca1ff51f04', //注册包(改为您的注册包)
|
|
|
+ dhtRegpack: 'd1ca1ff51f04', //温度注册包(可以相同可以共享传感器)
|
|
|
+ mqttToken: '0804d4c44c1f1bd11dea461481f19868', //授权TOKEN自己约定
|
|
|
+ keepalive: 30,
|
|
|
+ clean: true, //清除 clean session
|
|
|
+ connectTimeout: 30 * 1000, // ms 超时毫秒
|
|
|
+ reconnectPeriod: 5000, // ms 重连毫秒
|
|
|
+ resubscribe: true, //重新订阅
|
|
|
+ //定义您自己的主题
|
|
|
+ subTopic: 'mqtt/admintnet/#0#/out',
|
|
|
+ willTopic: 'mqtt/admintnet/#0#/will',
|
|
|
+ dhtTopic: 'mqtt/admintnet/#0#/dht',
|
|
|
+ pubTopic: 'mqtt/admintnet/#0#/into',
|
|
|
+ subTopics: [],
|
|
|
+ pubPayload: '{"msg":"hellow vue3 mqtt."}',
|
|
|
+ onlineStatus: false,
|
|
|
+ ch1_Status: false,
|
|
|
+ ch2_Status: false,
|
|
|
+ ch3_Status: false,
|
|
|
+ ch4_Status: false,
|
|
|
+ all_Status: false,
|
|
|
+ isAC: null, //强电true
|
|
|
});
|
|
|
// 初始化主题
|
|
|
-const init_topic=()=>{
|
|
|
- let st='mqtt/admintnet/#0#/out';//订阅主题
|
|
|
- let pt='mqtt/admintnet/#0#/into';//发布主题
|
|
|
- let ptbody='{"token":"{0}","cmd":"{1}","cmdpara":"{2}","clientid":"{3}"}';
|
|
|
- let wt='mqtt/admintnet/#0#/will';//遗嘱主题
|
|
|
- let dh='mqtt/admintnet/#0#/dht';//温湿度
|
|
|
- connection.subTopic=st.replace("#0#",connection.repacket);
|
|
|
- connection.willTopic=wt.replace("#0#",connection.repacket);
|
|
|
- connection.dhtTopic=dh.replace("#0#",connection.dhtRegpack);//温湿度
|
|
|
- connection.pubTopic=pt.replace("#0#",connection.repacket);
|
|
|
- connection.subTopics=[connection.subTopic,connection.willTopic,connection.dhtTopic];
|
|
|
- connection.pubPayload=ptbody;
|
|
|
+const init_topic = () => {
|
|
|
+ let st = 'mqtt/admintnet/#0#/out'; //订阅主题
|
|
|
+ let pt = 'mqtt/admintnet/#0#/into'; //发布主题
|
|
|
+ let ptbody = '{"token":"{0}","cmd":"{1}","cmdpara":"{2}","clientid":"{3}"}';
|
|
|
+ let wt = 'mqtt/admintnet/#0#/will'; //遗嘱主题
|
|
|
+ let dh = 'mqtt/admintnet/#0#/dht'; //温湿度
|
|
|
+ connection.subTopic = st.replace('#0#', connection.repacket);
|
|
|
+ connection.willTopic = wt.replace('#0#', connection.repacket);
|
|
|
+ connection.dhtTopic = dh.replace('#0#', connection.dhtRegpack); //温湿度
|
|
|
+ connection.pubTopic = pt.replace('#0#', connection.repacket);
|
|
|
+ connection.subTopics = [connection.subTopic, connection.willTopic, connection.dhtTopic];
|
|
|
+ connection.pubPayload = ptbody;
|
|
|
//console.log(connection.subTopics);
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
-//默认 注册包同步和传感器包名一致,反之不动
|
|
|
-const syncdhtreg=()=>{
|
|
|
- connection.dhtRegpack=connection.repacket;
|
|
|
-}
|
|
|
+// 默认注册包同步和传感器包名一致,反之不动
|
|
|
+const syncdhtreg = () => {
|
|
|
+ connection.dhtRegpack = connection.repacket;
|
|
|
+};
|
|
|
|
|
|
-//字符串替换模拟 string.format(str,ar1,arn)
|
|
|
-const stringFormat =(formatted, args)=> {
|
|
|
- for (let i = 0; i < args.length; i++) {
|
|
|
- let regexp = new RegExp('\\{' + i + '\\}', 'gi')
|
|
|
- formatted = formatted.replace(regexp, args[i])
|
|
|
- }
|
|
|
- return formatted
|
|
|
-}
|
|
|
+// 字符串替换模拟 string.format(str,ar1,arn)
|
|
|
+const stringFormat = (formatted, args) => {
|
|
|
+ for (let i = 0; i < args.length; i++) {
|
|
|
+ let regexp = new RegExp('\\{' + i + '\\}', 'gi');
|
|
|
+ formatted = formatted.replace(regexp, args[i]);
|
|
|
+ }
|
|
|
+ return formatted;
|
|
|
+};
|
|
|
|
|
|
onMounted(async () => {
|
|
|
init_topic();
|
|
|
- nextTick(() => {
|
|
|
- });
|
|
|
+ nextTick(() => {});
|
|
|
});
|
|
|
|
|
|
// topic & QoS for MQTT subscribing 订阅主题(多个)
|
|
|
const subscription = ref({
|
|
|
- topic: `$(connection.subTopics.value)`,
|
|
|
- qos: 0 as MQTT.QoS,
|
|
|
+ topic: `$(connection.subTopics.value)`,
|
|
|
+ qos: 0 as MQTT.QoS,
|
|
|
});
|
|
|
|
|
|
// topic, QoS & payload for publishing message 发布主题
|
|
|
const publish = ref({
|
|
|
- topic: `${connection.pubTopic}`,
|
|
|
- qos: 0 as MQTT.QoS,
|
|
|
- retain:false,//保留否
|
|
|
- payload: "55 AA AA AA AA 91 CF",//'{ "msg": "Hello, I am browser." }',
|
|
|
+ topic: `${connection.pubTopic}`,
|
|
|
+ qos: 0 as MQTT.QoS,
|
|
|
+ retain: false, //保留否
|
|
|
+ payload: '55 AA AA AA AA 91 CF', //'{ "msg": "Hello, I am browser." }',
|
|
|
});
|
|
|
|
|
|
-
|
|
|
-
|
|
|
const initData = () => {
|
|
|
- client.value = {
|
|
|
- connected: false,
|
|
|
- } as MQTT.MqttClient;
|
|
|
- retryTimes.value = 0;
|
|
|
- btnLoadingType.value = "";
|
|
|
- subscribedSuccess.value = false;
|
|
|
+ client.value = {
|
|
|
+ connected: false,
|
|
|
+ } as MQTT.MqttClient;
|
|
|
+ retryTimes.value = 0;
|
|
|
+ btnLoadingType.value = '';
|
|
|
+ subscribedSuccess.value = false;
|
|
|
};
|
|
|
|
|
|
const handleOnReConnect = () => {
|
|
|
- retryTimes.value++;
|
|
|
- connection.clientId="emqx_vue3_" + Math.random().toString(16).substring(2, 8);
|
|
|
- console.log(retryTimes.value,"重试次数");
|
|
|
- if (retryTimes.value > 5) {
|
|
|
- try {
|
|
|
- client.value.end();//重连超过5次断开
|
|
|
- initData();
|
|
|
- console.log("connection maxReconnectTimes limit, stop retry");
|
|
|
- appmessage(now.toLocaleString()+'|超出重连接次数,停止重试'+retryTimes.value)
|
|
|
- } catch (error) {
|
|
|
- console.log("handleOnReConnect catch error:", error);
|
|
|
- }
|
|
|
- }
|
|
|
+ retryTimes.value++;
|
|
|
+ connection.clientId = 'emqx_vue3_' + Math.random().toString(16).substring(2, 8);
|
|
|
+ console.log(retryTimes.value, '重试次数');
|
|
|
+ if (retryTimes.value > 5) {
|
|
|
+ try {
|
|
|
+ client.value.end(); //重连超过5次断开
|
|
|
+ initData();
|
|
|
+ console.log('connection maxReconnectTimes limit, stop retry');
|
|
|
+ appmessage(now.toLocaleString() + '|超出重连接次数,停止重试' + retryTimes.value);
|
|
|
+ } catch (error) {
|
|
|
+ console.log('handleOnReConnect catch error:', error);
|
|
|
+ }
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- /**
|
|
|
- * if protocol is "ws", connectUrl = "ws://broker.emqx.io:8083/mqtt"
|
|
|
- * if protocol is "wss", connectUrl = "wss://broker.emqx.io:8084/mqtt"
|
|
|
- *
|
|
|
- * /mqtt: MQTT-WebSocket uniformly uses /path as the connection path,
|
|
|
- * which should be specified when connecting, and the path used on EMQX is /mqtt.
|
|
|
- *
|
|
|
- * for more details about "mqtt.connect" method & options,
|
|
|
- * please refer to https://github.com/mqttjs/MQTT.js#mqttconnecturl-options
|
|
|
- */
|
|
|
+/**
|
|
|
+ * if protocol is "ws", connectUrl = "ws://broker.emqx.io:8083/mqtt"
|
|
|
+ * if protocol is "wss", connectUrl = "wss://broker.emqx.io:8084/mqtt"
|
|
|
+ *
|
|
|
+ * /mqtt: MQTT-WebSocket uniformly uses /path as the connection path,
|
|
|
+ * which should be specified when connecting, and the path used on EMQX is /mqtt.
|
|
|
+ *
|
|
|
+ * for more details about "mqtt.connect" method & options,
|
|
|
+ * please refer to https://github.com/mqttjs/MQTT.js#mqttconnecturl-options
|
|
|
+ */
|
|
|
// create MQTT connection 创建连接
|
|
|
const createConnection = () => {
|
|
|
- try {
|
|
|
- btnLoadingType.value = "connect";
|
|
|
- const { protocol, host, port, ...options } = connection;
|
|
|
- const connectUrl = `${protocol}://${host}:${port}/mqtt`;//组成新的连接字符串
|
|
|
- console.log(connectUrl,"连接地址");
|
|
|
- client.value = MQTT.connect(connectUrl, options);
|
|
|
- if (client.value.on) {
|
|
|
- // https://github.com/mqttjs/MQTT.js#event-connect
|
|
|
- client.value.on("connect", () => {//v5.x reconnect
|
|
|
- btnLoadingType.value = "";
|
|
|
- connSuccess.value=true;//client.value.connected;
|
|
|
- console.log("connection successful",client.value.connected);
|
|
|
- appmessage(now.toLocaleString()+'|连接服务成功');
|
|
|
- });
|
|
|
-
|
|
|
- // https://github.com/mqttjs/MQTT.js#event-reconnect 重连回调
|
|
|
- client.value.on("reconnect", handleOnReConnect);
|
|
|
- // https://github.com/mqttjs/MQTT.js#event-error
|
|
|
- client.value.on("error", (error) => {
|
|
|
- console.log("connection error:", error);
|
|
|
- appmessage(now.toLocaleString()+'|发生错误:'+error);
|
|
|
- });
|
|
|
-
|
|
|
- // https://github.com/mqttjs/MQTT.js#event-message 接收消息,处理方法单独定义
|
|
|
- client.value.on("message", (topic: string, message) => {
|
|
|
- //处理方法
|
|
|
- recvnum.value++;//接收次数累计
|
|
|
- doAction(topic,message);//处理
|
|
|
- receivedMessages.value = receivedMessages.value.concat(//拼接字符串输出
|
|
|
- now.toLocaleString()+" "+`${topic}\r\n`+message.toString()+'\r\n'
|
|
|
- );
|
|
|
- // console.log(now.toLocaleString()+`收到消息: ${message} from topic: ${topic}`);
|
|
|
- //滚动此方法可行
|
|
|
- nextTick(() => {
|
|
|
- setTimeout(() => {
|
|
|
- syncBottom();//滚动到底部
|
|
|
- }, 50)
|
|
|
- })
|
|
|
- });
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- btnLoadingType.value = "";
|
|
|
- console.log("mqtt.connect error:", error);
|
|
|
- }
|
|
|
+ try {
|
|
|
+ btnLoadingType.value = 'connect';
|
|
|
+ const { protocol, host, port, ...options } = connection;
|
|
|
+ const connectUrl = `${protocol}://${host}:${port}/mqtt`; //组成新的连接字符串
|
|
|
+ console.log(connectUrl, '连接地址');
|
|
|
+ client.value = MQTT.connect(connectUrl, options);
|
|
|
+ if (client.value.on) {
|
|
|
+ // https://github.com/mqttjs/MQTT.js#event-connect
|
|
|
+ client.value.on('connect', () => {
|
|
|
+ //v5.x reconnect
|
|
|
+ btnLoadingType.value = '';
|
|
|
+ connSuccess.value = true; //client.value.connected;
|
|
|
+ console.log('connection successful', client.value.connected);
|
|
|
+ appmessage(now.toLocaleString() + '|连接服务成功');
|
|
|
+ });
|
|
|
+
|
|
|
+ // https://github.com/mqttjs/MQTT.js#event-reconnect 重连回调
|
|
|
+ client.value.on('reconnect', handleOnReConnect);
|
|
|
+ // https://github.com/mqttjs/MQTT.js#event-error
|
|
|
+ client.value.on('error', (error) => {
|
|
|
+ console.log('connection error:', error);
|
|
|
+ appmessage(now.toLocaleString() + '|发生错误:' + error);
|
|
|
+ });
|
|
|
+
|
|
|
+ // https://github.com/mqttjs/MQTT.js#event-message 接收消息,处理方法单独定义
|
|
|
+ client.value.on('message', (topic: string, message) => {
|
|
|
+ //处理方法
|
|
|
+ recvnum.value++; //接收次数累计
|
|
|
+ doAction(topic, message); //处理
|
|
|
+ receivedMessages.value = receivedMessages.value.concat(
|
|
|
+ //拼接字符串输出
|
|
|
+ now.toLocaleString() + ' ' + `${topic}\r\n` + message.toString() + '\r\n'
|
|
|
+ );
|
|
|
+ // console.log(now.toLocaleString()+`收到消息: ${message} from topic: ${topic}`);
|
|
|
+ //滚动此方法可行
|
|
|
+ nextTick(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ syncBottom(); //滚动到底部
|
|
|
+ }, 50);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ btnLoadingType.value = '';
|
|
|
+ console.log('mqtt.connect error:', error);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
// 处理事件
|
|
|
-const doAction=(t,msg)=>{
|
|
|
- let res =JSON.parse(msg.toString());//必须规范的json格式否则出错,双引号不能是单引号;;;后不安全但强大 eval('(' + message.toString() + ')'); //JSON.parse(message.toString());//json对象
|
|
|
-
|
|
|
- //消息不能带''否则错误
|
|
|
- let regp=res.regpacket;//接收的注册包
|
|
|
- let regs=connection.repacket;//订阅的注册包
|
|
|
- let isOK=regp==regs?true:false;//是不是本设备的消息
|
|
|
- if(!isOK||regp==null){
|
|
|
- return;//不是丢弃
|
|
|
+const doAction = (t, msg) => {
|
|
|
+ let res = JSON.parse(msg.toString()); //必须规范的json格式否则出错,双引号不能是单引号;;;后不安全但强大 eval('(' + message.toString() + ')'); //JSON.parse(message.toString());//json对象
|
|
|
+
|
|
|
+ // 消息不能带''否则错误
|
|
|
+ let regp = res.regpacket; // 接收的注册包
|
|
|
+ let regs = connection.repacket; // 订阅的注册包
|
|
|
+ let isOK = regp == regs ? true : false; // 是不是本设备的消息
|
|
|
+ if (!isOK || regp == null) {
|
|
|
+ return; // 不是丢弃
|
|
|
}
|
|
|
-
|
|
|
- if(t==connection.dhtTopic)
|
|
|
- {//温湿度
|
|
|
- let rp=res.regpacket;
|
|
|
- let wd=res.temperature;
|
|
|
- let sd=res.humidity;
|
|
|
- let sj=res.time;
|
|
|
- let sc=res.runsec;
|
|
|
- if(rp!=connection.dhtRegpack)
|
|
|
- {//来自订阅的温湿度包
|
|
|
+
|
|
|
+ if (t == connection.dhtTopic) {
|
|
|
+ // 温湿度
|
|
|
+ let rp = res.regpacket;
|
|
|
+ let wd = res.temperature;
|
|
|
+ let sd = res.humidity;
|
|
|
+ let sj = res.time;
|
|
|
+ let sc = res.runsec;
|
|
|
+ if (rp != connection.dhtRegpack) {
|
|
|
+ // 来自订阅的温湿度包
|
|
|
return;
|
|
|
}
|
|
|
- if(rp!=null)
|
|
|
- {
|
|
|
- dht_wd.value=wd;//实际应用时替换此3个变量即可
|
|
|
- dht_sd.value=sd;
|
|
|
- dht_tm.value="更新时间:"+sj;
|
|
|
- runSeconds.value=sc;
|
|
|
- dht_wsd.value="温度:"+dht_wd.value+"℃,湿度:"+dht_sd.value+"%";
|
|
|
+ if (rp != null) {
|
|
|
+ dht_wd.value = wd; // 实际应用时替换此3个变量即可
|
|
|
+ dht_sd.value = sd;
|
|
|
+ dht_tm.value = '更新时间:' + sj;
|
|
|
+ runSeconds.value = sc;
|
|
|
+ dht_wsd.value = '温度:' + dht_wd.value + '℃,湿度:' + dht_sd.value + '%';
|
|
|
//state.option.title.text="实时温湿度变化趋势图(运行"+parseInt(sc)+"秒)";
|
|
|
//updatechart(false);//实时数据(这种方法是实时推送,如果用 定时器 是定时显示的)updatewsd_time(false)
|
|
|
}
|
|
|
}
|
|
|
- if(t==connection.willTopic)
|
|
|
- {//遗嘱
|
|
|
- if(res.redata=='offline')
|
|
|
- {
|
|
|
- connection.onlineStatus=false;
|
|
|
- }else{
|
|
|
- connection.onlineStatus=true;
|
|
|
+ if (t == connection.willTopic) {
|
|
|
+ // 遗嘱
|
|
|
+ if (res.redata == 'offline') {
|
|
|
+ connection.onlineStatus = false;
|
|
|
+ } else {
|
|
|
+ connection.onlineStatus = true;
|
|
|
}
|
|
|
}
|
|
|
- if(t==connection.subTopic)
|
|
|
- {
|
|
|
- let rp0=res.regpacket;
|
|
|
- if(rp0!=undefined){
|
|
|
- if(rp0==regs)
|
|
|
- {
|
|
|
- op(res.redata);//该设备执行指令其他放弃
|
|
|
- }
|
|
|
+ if (t == connection.subTopic) {
|
|
|
+ let rp0 = res.regpacket;
|
|
|
+ if (rp0 != undefined) {
|
|
|
+ if (rp0 == regs) {
|
|
|
+ op(res.redata); // 该设备执行指令其他放弃
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
-//处理开关状态(自定义的指令,需要修改为您自己的指令)
|
|
|
-const op=(cmd)=>{
|
|
|
- if(cmd=="55 AA AA AA AA 82 01 01"){
|
|
|
- connection.ch1_Status=true;
|
|
|
+// 处理开关状态(自定义的指令,需要修改为您自己的指令)
|
|
|
+const op = (cmd: any) => {
|
|
|
+ if (cmd == '55 AA AA AA AA 82 01 01') {
|
|
|
+ connection.ch1_Status = true;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 82 01 00"){
|
|
|
- connection.ch1_Status=false;
|
|
|
+ if (cmd == '55 AA AA AA AA 82 01 00') {
|
|
|
+ connection.ch1_Status = false;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 82 02 01"){
|
|
|
- connection.ch2_Status=true;
|
|
|
+ if (cmd == '55 AA AA AA AA 82 02 01') {
|
|
|
+ connection.ch2_Status = true;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 82 02 00"){
|
|
|
- connection.ch2_Status=false;
|
|
|
+ if (cmd == '55 AA AA AA AA 82 02 00') {
|
|
|
+ connection.ch2_Status = false;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 82 03 01"){
|
|
|
- connection.ch3_Status=true;
|
|
|
+ if (cmd == '55 AA AA AA AA 82 03 01') {
|
|
|
+ connection.ch3_Status = true;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 82 03 00"){
|
|
|
- connection.ch3_Status=false;
|
|
|
+ if (cmd == '55 AA AA AA AA 82 03 00') {
|
|
|
+ connection.ch3_Status = false;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 82 04 01"){
|
|
|
- connection.ch4_Status=true;
|
|
|
+ if (cmd == '55 AA AA AA AA 82 04 01') {
|
|
|
+ connection.ch4_Status = true;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 82 04 00"){
|
|
|
- connection.ch4_Status=false;
|
|
|
+ if (cmd == '55 AA AA AA AA 82 04 00') {
|
|
|
+ connection.ch4_Status = false;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 82 A4 01"){
|
|
|
- connection.ch1_Status=true;
|
|
|
- connection.ch2_Status=true;
|
|
|
- connection.ch3_Status=true;
|
|
|
- connection.ch4_Status=true;
|
|
|
+ if (cmd == '55 AA AA AA AA 82 A4 01') {
|
|
|
+ connection.ch1_Status = true;
|
|
|
+ connection.ch2_Status = true;
|
|
|
+ connection.ch3_Status = true;
|
|
|
+ connection.ch4_Status = true;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 82 A4 00"){
|
|
|
- connection.ch1_Status=false;
|
|
|
- connection.ch2_Status=false;
|
|
|
- connection.ch3_Status=false;
|
|
|
- connection.ch4_Status=false;
|
|
|
+ if (cmd == '55 AA AA AA AA 82 A4 00') {
|
|
|
+ connection.ch1_Status = false;
|
|
|
+ connection.ch2_Status = false;
|
|
|
+ connection.ch3_Status = false;
|
|
|
+ connection.ch4_Status = false;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 84 AC 01"){
|
|
|
- connection.isAC=true;
|
|
|
+ if (cmd == '55 AA AA AA AA 84 AC 01') {
|
|
|
+ connection.isAC = true;
|
|
|
}
|
|
|
- if(cmd=="55 AA AA AA AA 84 AC 00"){
|
|
|
- connection.isAC=false;
|
|
|
+ if (cmd == '55 AA AA AA AA 84 AC 00') {
|
|
|
+ connection.isAC = false;
|
|
|
}
|
|
|
- if (connection.ch1_Status && connection.ch2_Status && connection.ch3_Status && connection.ch4_Status) {
|
|
|
- connection.all_Status = true;
|
|
|
- }
|
|
|
- if (!connection.ch1_Status && !connection.ch2_Status && !connection.ch3_Status && !connection.ch4_Status) {
|
|
|
- connection.all_Status = false;
|
|
|
- }
|
|
|
- if (cmd == "55 AA AA AA AA 84 AC 01") {
|
|
|
- connection.isAC = true;
|
|
|
- }
|
|
|
- if (cmd == "55 AA AA AA AA 84 AC 00") {
|
|
|
- connection.isAC = false;
|
|
|
- }
|
|
|
-}
|
|
|
+ if (connection.ch1_Status && connection.ch2_Status && connection.ch3_Status && connection.ch4_Status) {
|
|
|
+ connection.all_Status = true;
|
|
|
+ }
|
|
|
+ if (!connection.ch1_Status && !connection.ch2_Status && !connection.ch3_Status && !connection.ch4_Status) {
|
|
|
+ connection.all_Status = false;
|
|
|
+ }
|
|
|
+ if (cmd == '55 AA AA AA AA 84 AC 01') {
|
|
|
+ connection.isAC = true;
|
|
|
+ }
|
|
|
+ if (cmd == '55 AA AA AA AA 84 AC 00') {
|
|
|
+ connection.isAC = false;
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
// 自动同步滚动(建议延时执行)textarea:any=null
|
|
|
-const syncBottom=()=>{
|
|
|
- const textarea = document.getElementById('recv');
|
|
|
- if(textarea)
|
|
|
- {
|
|
|
- textarea.scrollTop = textarea.scrollHeight-30;
|
|
|
- }
|
|
|
-}
|
|
|
+const syncBottom = () => {
|
|
|
+ const textarea = document.getElementById('recv');
|
|
|
+ if (textarea) {
|
|
|
+ textarea.scrollTop = textarea.scrollHeight - 30;
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
// subscribe topic 开始订阅
|
|
|
// https://github.com/mqttjs/MQTT.js#mqttclientsubscribetopictopic-arraytopic-object-options-callback
|
|
|
const doSubscribe = () => {
|
|
|
- btnLoadingType.value = "subscribe";
|
|
|
- const { topic, qos } = subscription.value;
|
|
|
- console.log(connection.subTopics,"订阅主题");
|
|
|
- client.value.subscribe(connection.subTopics,{ qos },
|
|
|
- (error: Error, granted: mqtt.ISubscriptionGrant[]) => {
|
|
|
- btnLoadingType.value = "";
|
|
|
- if (error) {
|
|
|
- console.log("subscribe error:", error);
|
|
|
- return;
|
|
|
- }
|
|
|
- subscribedSuccess.value = true;//订阅成功
|
|
|
- //连接成功,发布首个问询指令
|
|
|
- switchLight("55 AA AA AA AA 91 CF");//发送首页问询指令
|
|
|
- console.log("订阅成功subscribe successfully:", granted);
|
|
|
- }
|
|
|
- );
|
|
|
+ btnLoadingType.value = 'subscribe';
|
|
|
+ const { topic, qos } = subscription.value;
|
|
|
+ console.log(connection.subTopics, '订阅主题');
|
|
|
+ client.value.subscribe(connection.subTopics, { qos }, (error: Error, granted: mqtt.ISubscriptionGrant[]) => {
|
|
|
+ btnLoadingType.value = '';
|
|
|
+ if (error) {
|
|
|
+ console.log('subscribe error:', error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ subscribedSuccess.value = true; //订阅成功
|
|
|
+ // 连接成功,发布首个问询指令
|
|
|
+ switchLight('55 AA AA AA AA 91 CF'); //发送首页问询指令
|
|
|
+ console.log('订阅成功subscribe successfully:', granted);
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
// unsubscribe topic 取消订阅
|
|
|
// https://github.com/mqttjs/MQTT.js#mqttclientunsubscribetopictopic-array-options-callback
|
|
|
const doUnSubscribe = () => {
|
|
|
- btnLoadingType.value = "unsubscribe";
|
|
|
- const { topic, qos } = subscription.value;
|
|
|
- client.value.unsubscribe(connection.subTopics, { qos }, (error) => {
|
|
|
- btnLoadingType.value = "";
|
|
|
- subscribedSuccess.value = false;
|
|
|
- if (error) {
|
|
|
- console.log("unsubscribe error:", error);
|
|
|
- return;
|
|
|
- }
|
|
|
- console.log(`unsubscribed topic: ${topic}`);
|
|
|
- });
|
|
|
+ btnLoadingType.value = 'unsubscribe';
|
|
|
+ const { topic, qos } = subscription.value;
|
|
|
+ client.value.unsubscribe(connection.subTopics, { qos }, (error) => {
|
|
|
+ btnLoadingType.value = '';
|
|
|
+ subscribedSuccess.value = false;
|
|
|
+ if (error) {
|
|
|
+ console.log('unsubscribe error:', error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ console.log(`unsubscribed topic: ${topic}`);
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
// publish message发布消息
|
|
|
// https://github.com/mqttjs/MQTT.js#mqttclientpublishtopic-message-options-callback
|
|
|
-const doPublish = (b,t) => {
|
|
|
- //btnLoadingType.value = "publish";
|
|
|
- const { topic, qos, payload,retain } = publish.value;
|
|
|
- //console.log(t+b,"发布内容")
|
|
|
- let paybody=stringFormat(connection.pubPayload,[connection.mqttToken,b??publish.value.payload,"",connection.clientId]);//标准格式payload
|
|
|
- client.value.publish(t??connection.pubTopic, paybody, { qos }, (error) => {
|
|
|
- nextTick(() => {//测试延时
|
|
|
- setTimeout(() => {
|
|
|
- btnLoadingType.value = "";
|
|
|
- }, 50)
|
|
|
- })
|
|
|
- if (error) {
|
|
|
- appmessage(now.toLocaleString()+'|发布消息错误.'+error);
|
|
|
- console.log("publish error:", error);
|
|
|
- return;
|
|
|
- }
|
|
|
- });
|
|
|
+const doPublish = (b, t) => {
|
|
|
+ //btnLoadingType.value = "publish";
|
|
|
+ const { topic, qos, payload, retain } = publish.value;
|
|
|
+ //console.log(t+b,"发布内容")
|
|
|
+ let paybody = stringFormat(connection.pubPayload, [connection.mqttToken, b ?? publish.value.payload, '', connection.clientId]); //标准格式payload
|
|
|
+ client.value.publish(t ?? connection.pubTopic, paybody, { qos }, (error) => {
|
|
|
+ nextTick(() => {
|
|
|
+ // 测试延时
|
|
|
+ setTimeout(() => {
|
|
|
+ btnLoadingType.value = '';
|
|
|
+ }, 50);
|
|
|
+ });
|
|
|
+ if (error) {
|
|
|
+ appmessage(now.toLocaleString() + '|发布消息错误.' + error);
|
|
|
+ console.log('publish error:', error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
// 消息追加消息框
|
|
|
-const appmessage=(msg)=>{
|
|
|
- receivedMessages.value = receivedMessages.value.concat(//拼接字符串输出
|
|
|
- msg+'\r\n'
|
|
|
+const appmessage = (msg) => {
|
|
|
+ receivedMessages.value = receivedMessages.value.concat(
|
|
|
+ // 拼接字符串输出
|
|
|
+ msg + '\r\n'
|
|
|
);
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
// 开关
|
|
|
-const switchLight=(cmd)=>{
|
|
|
- if(!client.value.connected){
|
|
|
- appmessage("尚未连接到服务!");
|
|
|
+const switchLight = (cmd) => {
|
|
|
+ if (!client.value.connected) {
|
|
|
+ appmessage('尚未连接到服务!');
|
|
|
return;
|
|
|
}
|
|
|
- let paybody=stringFormat(connection.pubPayload,[connection.mqttToken,cmd??publish.value.payload,"",connection.clientId]);
|
|
|
- const { topic, qos, payload,retain } = publish.value;
|
|
|
+ let paybody = stringFormat(connection.pubPayload, [connection.mqttToken, cmd ?? publish.value.payload, '', connection.clientId]);
|
|
|
+ const { topic, qos, payload, retain } = publish.value;
|
|
|
//console.log(t+b,"发布内容")
|
|
|
- client.value.publish(connection.pubTopic, paybody, { qos }, retain,(error) => {
|
|
|
- btnLoadingType.value = "";
|
|
|
- if (error) {
|
|
|
- console.log("publish error:", error);
|
|
|
- return;
|
|
|
- }
|
|
|
+ client.value.publish(connection.pubTopic, paybody, { qos }, retain, (error) => {
|
|
|
+ btnLoadingType.value = '';
|
|
|
+ if (error) {
|
|
|
+ console.log('publish error:', error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
});
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
// disconnect 端口连接
|
|
|
// https://github.com/mqttjs/MQTT.js#mqttclientendforce-options-callback
|
|
|
const destroyConnection = () => {
|
|
|
- if (client.value.connected) {
|
|
|
- btnLoadingType.value = "disconnect";
|
|
|
- try {
|
|
|
- client.value.end(false, () => {
|
|
|
- initData();
|
|
|
- connSuccess.value=false;
|
|
|
- //console.log("断开成功 disconnected successfully");
|
|
|
- appmessage(now.toLocaleString()+'|连接已断开.');
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- btnLoadingType.value = "";
|
|
|
- console.log("断开错误 disconnect error:", error);
|
|
|
- }
|
|
|
- }
|
|
|
+ if (client.value.connected) {
|
|
|
+ btnLoadingType.value = 'disconnect';
|
|
|
+ try {
|
|
|
+ client.value.end(false, () => {
|
|
|
+ initData();
|
|
|
+ connSuccess.value = false;
|
|
|
+ //console.log("断开成功 disconnected successfully");
|
|
|
+ appmessage(now.toLocaleString() + '|连接已断开.');
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ btnLoadingType.value = '';
|
|
|
+ console.log('断开错误 disconnect error:', error);
|
|
|
+ }
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
// 端口随协议而改变
|
|
|
const handleProtocolChange = (value: string) => {
|
|
|
- connection.port = value === "wss" ? 8084 : 8083;
|
|
|
+ connection.port = value === 'wss' ? 8084 : 8083;
|
|
|
};
|
|
|
|
|
|
// 清空消息框
|
|
|
const clsmsg = () => {
|
|
|
- receivedMessages.value="";
|
|
|
+ receivedMessages.value = '';
|
|
|
};
|
|
|
-
|
|
|
-
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.mqtt-box {
|
|
|
- max-width: 100%;
|
|
|
- padding:4px;
|
|
|
- margin: 10px auto 0 auto;
|
|
|
+ max-width: 100%;
|
|
|
+ padding: 4px;
|
|
|
+ margin: 10px auto 0 auto;
|
|
|
}
|
|
|
|
|
|
-.header{
|
|
|
- font-size:24px;
|
|
|
- font-weight:bold;
|
|
|
- margin:-12px auto 8px auto;
|
|
|
+.header {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+ margin: -12px auto 8px auto;
|
|
|
}
|
|
|
|
|
|
h1 {
|
|
|
- font-size: 16px;
|
|
|
- margin-top: 10px auto 20px auto;
|
|
|
- padding:5px 0px 5px 0;
|
|
|
+ font-size: 16px;
|
|
|
+ margin-top: 10px auto 20px auto;
|
|
|
+ padding: 5px 0px 5px 0;
|
|
|
}
|
|
|
|
|
|
-.el-col{
|
|
|
- padding:4px;
|
|
|
+.el-col {
|
|
|
+ padding: 4px;
|
|
|
}
|
|
|
|
|
|
-.el-input{font-size: 13px;}
|
|
|
+.el-input {
|
|
|
+ font-size: 13px;
|
|
|
+}
|
|
|
.el-card {
|
|
|
- margin-bottom: 12px;
|
|
|
+ margin-bottom: 12px;
|
|
|
}
|
|
|
.el-card__body {
|
|
|
- padding: 24px;
|
|
|
+ padding: 24px;
|
|
|
}
|
|
|
|
|
|
.el-select {
|
|
|
- width: 100%;
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
|
|
|
.text-right {
|
|
|
- text-align: right;
|
|
|
+ text-align: right;
|
|
|
}
|
|
|
|
|
|
.sub-btn {
|
|
|
- margin-top: 20px;width:160px;
|
|
|
+ margin-top: 20px;
|
|
|
+ width: 160px;
|
|
|
}
|
|
|
|
|
|
-.hidden{display: none;}
|
|
|
-.w80{width: 80px;}
|
|
|
-.w100{width: 100px;}
|
|
|
-.log{font-size: 14px;color:#fff;background-color: black;}
|
|
|
-.center{text-align: center;}
|
|
|
-#ch1,#ch2,#ch3,#ch4,#ch5{width: 120px;}
|
|
|
-.el-tag{padding:auto 4px; margin: 5px;min-width: 60px;}
|
|
|
+.hidden {
|
|
|
+ display: none;
|
|
|
+}
|
|
|
+.w80 {
|
|
|
+ width: 80px;
|
|
|
+}
|
|
|
+.w100 {
|
|
|
+ width: 100px;
|
|
|
+}
|
|
|
+.log {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #fff;
|
|
|
+ background-color: black;
|
|
|
+}
|
|
|
+.center {
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+#ch1,
|
|
|
+#ch2,
|
|
|
+#ch3,
|
|
|
+#ch4,
|
|
|
+#ch5 {
|
|
|
+ width: 120px;
|
|
|
+}
|
|
|
+.el-tag {
|
|
|
+ padding: auto 4px;
|
|
|
+ margin: 5px;
|
|
|
+ min-width: 60px;
|
|
|
+}
|
|
|
</style>
|