This commit is contained in:
kerwincui
2024-03-17 14:59:23 +08:00
parent 3d44f4674c
commit 5539c1b6af
999 changed files with 115642 additions and 10757 deletions

View File

@@ -0,0 +1,324 @@
<template>
<div style="padding: 6px">
<el-card v-show="showSearch" style="margin-bottom: 6px">
<el-form @submit.native.prevent :model="queryParams" ref="queryForm" :inline="true" label-width="68px" style="margin-bottom: -20px">
<el-form-item label="客户端" prop="clientId">
<el-input v-model="queryParams.clientId" placeholder="请输入客户端ID" clearable size="small" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item prop="isClient">
<el-checkbox v-model="queryParams.isClient" true-label="1" false-label="0">只看设备端</el-checkbox>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-tabs type="border-card" v-model="serverType" @tab-click="handleClick" style="flex: 1; height: 800px; margin-bottom: 5px">
<el-tab-pane label="MQTT客户端" name="MQTT">
<el-table v-loading="loading" :data="clientList">
<el-table-column label="客户端ID" align="left" header-align="center" prop="clientId">
<template slot-scope="scope">
<el-link :underline="false" type="primary" @click.native="handleOpen(scope.row)">{{ scope.row.clientId }}</el-link>
</template>
</el-table-column>
<el-table-column label="类型" align="center" prop="type">
<template slot-scope="scope">
<el-tag type="danger" v-if="scope.row.clientId.indexOf('server') == 0">服务端</el-tag>
<el-tag type="success" v-else-if="scope.row.clientId.indexOf('web') == 0">Web端</el-tag>
<el-tag type="warning" v-else-if="scope.row.clientId.indexOf('phone') == 0">移动端</el-tag>
<el-tag type="info" v-else-if="scope.row.clientId.indexOf('test') == 0">测试端</el-tag>
<el-tag type="primary" v-else>设备端</el-tag>
</template>
</el-table-column>
<el-table-column label="连接状态" align="center" prop="connected">
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.connected">已连接</el-tag>
<el-tag type="info" v-else>已断开</el-tag>
</template>
</el-table-column>
<el-table-column label="心跳(秒)" align="center" prop="keepAlive" width="100" />
<el-table-column label="账号" align="center" prop="username" width="100px" />
<el-table-column label="当前订阅数量" align="center" prop="topicCount" width="100" />
<el-table-column label="连接时间" align="center" prop="connected_at" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="clickClientOut(scope.row)">踢出</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- MQTT客户端详细 -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
<el-tabs v-model="activeName" tab-position="top" style="padding: 10px">
<el-tab-pane name="subscribe">
<span slot="label">订阅列表</span>
<el-row :gutter="10" class="mb8"></el-row>
<el-table :data="subscribeList" size="mini">
<el-table-column label="主题" align="center" prop="topicName" />
<el-table-column label="QoS" align="center" prop="qos" />
</el-table>
</el-tab-pane>
</el-tabs>
</el-dialog>
</el-tab-pane>
<el-tab-pane label="TCP客户端" name="TCP">
<el-table v-loading="loading" :data="clientList">
<el-table-column label="客户端ID" align="left" header-align="center" prop="clientId"></el-table-column>
<el-table-column label="类型" align="center" prop="type">
<template slot-scope="scope">
<el-tag type="danger" v-if="scope.row.clientId.indexOf('server') == 0">服务端</el-tag>
<el-tag type="success" v-else-if="scope.row.clientId.indexOf('web') == 0">Web端</el-tag>
<el-tag type="warning" v-else-if="scope.row.clientId.indexOf('phone') == 0">移动端</el-tag>
<el-tag type="info" v-else-if="scope.row.clientId.indexOf('test') == 0">测试端</el-tag>
<el-tag type="primary" v-else>设备端</el-tag>
</template>
</el-table-column>
<el-table-column label="连接状态" align="center" prop="connected">
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.connected">已连接</el-tag>
<el-tag type="info" v-else>已断开</el-tag>
</template>
</el-table-column>
<el-table-column label="心跳(秒)" align="center" prop="keepAlive" width="100" />
<el-table-column label="连接时间" align="center" prop="connected_at" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="clickClientOut(scope.row)">踢出</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- MQTT客户端详细 -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
<el-tabs v-model="activeName" tab-position="top" style="padding: 10px">
<el-tab-pane name="subscribe">
<span slot="label">订阅列表</span>
<el-row :gutter="10" class="mb8"></el-row>
<el-table :data="subscribeList" size="mini">
<el-table-column label="主题" align="center" prop="topicName" />
<el-table-column label="QoS" align="center" prop="qos" />
</el-table>
</el-tab-pane>
</el-tabs>
</el-dialog>
</el-tab-pane>
</el-tabs>
<!-- 添加或修改订阅对话框 -->
<el-dialog title="添加订阅" :visible.sync="subscribeOpen" width="800px" append-to-body>
<el-form ref="subscribeForm" :model="subscribeForm" :rules="rules" label-width="60px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="主题" prop="topic">
<el-input v-model="subscribeForm.topic" placeholder="请输入主题" />
</el-form-item>
<el-form-item label="Qos" prop="qos">
<el-select v-model="subscribeForm.qos" placeholder="请选择消息类型">
<el-option key="0" label="0" value="0"></el-option>
<el-option key="1" label="1" value="1"></el-option>
<el-option key="2" label="2" value="2"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancelSubscribe"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { clientOut, listNettyMqttClient } from '@/api/iot/netty';
export default {
name: 'Category',
data() {
return {
// 非单个禁用
single: true,
// 遮罩层
loading: true,
loadSubscribeing: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 产品分类表格数据
clientList: [],
// 弹出层标题
title: '',
// 是否显示弹出层
open: false,
// 是否显示添加订阅弹出层
subscribeOpen: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
clientId: null,
isClient: 0,
serverCode: 'MQTT',
},
// 表单参数
form: {},
// 选中选项卡
activeName: 'subscribe',
//订阅列表数据
subscribeList: [],
//订阅数据
subscribe: {
topic: '',
clientId: '',
},
//添加订阅表单参数
subscribeForm: {
qos: '0',
},
//客户端ID
clientId: '',
//服务协议类型
serverType: 'MQTT',
// 表单校验
rules: {
topic: [
{
required: true,
message: '主题不能为空',
trigger: 'blur',
},
],
},
};
},
created() {
this.getList();
},
methods: {
/** 查询客户端列表 */
getList() {
this.loading = true;
listNettyMqttClient(this.queryParams).then((response) => {
this.clientList = response.data;
this.total = response.total;
this.loading = false;
});
},
/*将客户端踢出*/
clickClientOut(row) {
const params = { clientId: row.clientId };
clientOut(params).then((res) => {
//刷新列表
this.getList();
});
},
/*tabs切换*/
handleClick() {
this.queryParams.serverCode = this.serverType;
this.getList();
},
/** 查询客户端订阅列表 */
getSubscribeList(clientId) {
this.clientId = clientId;
this.loadSubscribeing = true;
getSubscriptionsByClientId(clientId).then((res) => {
this.subscribeList = res.data.data;
this.loadSubscribeing = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm');
this.handleQuery();
},
/** 断开客户端连接 */
handleDelete(row) {
const clientId = row.clientId;
this.$modal
.confirm('是否确认删除MQTT客户端编号为"' + clientId + '"的数据项?')
.then(function () {
return eliminateClient(clientId);
})
.then(() => {
this.getList();
this.$modal.msgSuccess('删除成功');
})
.catch(() => {});
},
//取消主题订阅
handleUnsubscribe(row) {
const clientId = row.clientId;
const topic = row.topic;
this.$modal
.confirm('是否确认取消订阅主题为"' + topic + '"的数据项?')
.then(function () {
const param = {};
param.topic = topic;
param.clientId = clientId;
return unsubscribe(param);
})
.then(() => {
this.getSubscribeList(clientId);
this.$modal.msgSuccess('取消订阅成功');
})
.catch(() => {});
},
//查看客户端详情
handleOpen(row) {
this.open = true;
this.title = '详情';
this.subscribeList = row.topics;
console.log(this.subscribeList);
},
//添加订阅
handleAdd() {
this.subscribeOpen = true;
},
//提交添加订阅表单
submitForm() {
this.subscribeForm.clientId = this.clientId;
console.log(this.subscribeForm);
this.$refs['subscribeForm'].validate((valid) => {
if (valid) {
addSubscribe(this.subscribeForm).then((response) => {
this.$modal.msgSuccess('新增订阅成功');
this.subscribeOpen = false;
this.getSubscribeList(this.clientId);
});
}
});
},
cancelSubscribe() {
this.subscribeOpen = false;
this.resetForm('subscribeForm');
//刷新列表
this.getSubscribeList(this.clientId);
},
},
};
</script>

View File

@@ -0,0 +1,346 @@
<template>
<div style="padding: 6px">
<el-card style="margin-bottom: 6px">
<el-row :gutter="120">
<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
<h3 style="font-weight: bold">Mqtt 统计指标</h3>
<el-row :gutter="20" class="panel-group">
<el-col :span="24" class="card-panel-col" style="margin-bottom: 17px">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-orange">
<svg-icon icon-class="guide" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div>
<div class="card-panel-text">发送消息</div>
<count-to :start-val="0" :end-val="this.static['send_total']" :duration="3000" class="card-panel-num" />
</div>
</div>
</div>
</el-col>
<el-col :span="24" class="card-panel-col" style="margin-bottom: 18px">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-green">
<svg-icon icon-class="receiver" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div>
<div class="card-panel-text">接收消息</div>
<count-to :start-val="0" :end-val="this.static['receive_total']" :duration="3000" class="card-panel-num" />
</div>
</div>
</div>
</el-col>
<el-col :span="24" class="card-panel-col" style="margin-bottom: 17px">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-orange">
<svg-icon icon-class="authenticate" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">认证次数</div>
<count-to :start-val="0" :end-val="this.static['auth_total']" :duration="1000" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :span="24" class="card-panel-col" style="margin-bottom: 18px">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-green">
<svg-icon icon-class="connect" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">连接次数</div>
<count-to :start-val="0" :end-val="this.static['connect_total']" :duration="1000" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :span="24" class="card-panel-col" style="margin-bottom: 17px">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-orange">
<svg-icon icon-class="subscribe1" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">订阅次数</div>
<count-to :start-val="0" :end-val="this.static['subscribe_total']" :duration="2000" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :span="24" class="card-panel-col" style="margin-bottom: 17px">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-green">
<svg-icon icon-class="message" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div>
<div class="card-panel-text">今日接收</div>
<count-to :start-val="0" :end-val="this.static['today_received']" :duration="3000" class="card-panel-num" />
</div>
</div>
</div>
</el-col>
<el-col :span="24" class="card-panel-col" style="margin-bottom: 17px">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-orange">
<svg-icon icon-class="subscribe1" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">今日发送</div>
<count-to :start-val="0" :end-val="this.static['today_send']" :duration="2000" class="card-panel-num" />
</div>
</div>
</el-col>
</el-row>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="15" :xl="15">
<div style="padding: 30px 0 85px">
<div ref="pieTotal" style="height: 230px"></div>
</div>
<div ref="statsChart" style="height: 275px; margin: 20px 0 40px 0"></div>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script>
import { getNettyMqttStats, statisticNettyMqtt } from '@/api/iot/netty';
import CountTo from 'vue-count-to';
export default {
name: 'Mqtt',
components: {
CountTo,
},
data() {
return {
// mqtt状态数据
stats: {},
// mqtt统计信息
static: {},
};
},
created() {
this.getMqttStats();
this.statisticMqtt();
},
methods: {
/** 查询mqtt统计*/
statisticMqtt() {
statisticNettyMqtt().then((response) => {
this.static = response.data;
this.totalMqtt();
});
},
/** 查询mqtt状态数据*/
getMqttStats() {
getNettyMqttStats().then((response) => {
this.stats = response.data;
this.drawStats();
});
},
// 绘制mqtt饼图
totalMqtt() {
// 基于准备好的dom初始化echarts实例
let myChart = this.$echarts.init(this.$refs.pieTotal);
var option;
option = {
title: {
text: 'Mqtt消息',
left: 'left',
textStyle: {
fontSize: 16,
},
},
tooltip: {
trigger: 'item',
},
legend: {
orient: 'vertical',
left: 'right',
},
color: ['#E6A23C', '#F56C6C', '#DDD'],
series: [
{
name: 'Mqtt消息 %',
type: 'pie',
radius: '55%',
label: {
show: true,
},
labelLine: {
normal: {
position: 'inner',
show: false,
},
},
data: [
{
value: this.static['send_total'],
name: '发送消息总数',
},
{
value: this.static['receive_total'],
name: '接收消息总数',
},
],
},
],
};
option && myChart.setOption(option);
},
/** 绘制mqtt状态统计 */
drawStats() {
// 基于准备好的dom初始化echarts实例
let myChart = this.$echarts.init(this.$refs.statsChart);
var option;
option = {
title: {
text: 'Mqtt 状态数据',
textStyle: {
fontSize: 18,
color: '#000',
fontWeight: 800,
},
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
legend: {},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01],
},
yAxis: {
type: 'category',
axisLabel: {
fontSize: 14,
},
data: ['连接数量', '会话数量', '订阅数量', '路由数量', '保留消息'],
},
series: [
{
name: '当前数量',
type: 'bar',
data: [this.stats['connection_count'], this.stats['session_count'], this.stats['subscription_count'], this.stats['retain_count'], this.stats['retain_count']],
itemStyle: {
color: '#67C23A',
},
},
{
name: '累计总数',
type: 'bar',
data: [this.stats['connection_total'], this.stats['session_total'], this.stats['subscription_total'], this.stats['retain_total'], this.stats['retain_total']],
itemStyle: {
color: '#409EFF',
},
},
],
};
option && myChart.setOption(option);
},
},
};
</script>
<style lang="scss" scoped>
.panel-group {
.card-panel-col {
margin-bottom: 10px;
}
.card-panel {
height: 68px;
cursor: pointer;
position: relative;
overflow: hidden;
color: #666;
border: 1px solid #eee;
border-radius: 5px;
//box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.08);
background-color: #fff;
&:hover {
.card-panel-icon-wrapper {
color: #fff;
}
.icon-blue {
background: #36a3f7;
}
.icon-green {
background: #34bfa3;
}
.icon-red {
background: #f56c6c;
}
.icon-orange {
background: #e6a23c;
}
}
.icon-blue {
color: #36a3f7;
}
.icon-green {
color: #34bfa3;
}
.icon-red {
color: #f56c6c;
}
.icon-orange {
color: #e6a23c;
}
.card-panel-icon-wrapper {
float: left;
margin: 10px;
padding: 10px;
transition: all 0.38s ease-out;
border-radius: 6px;
}
.card-panel-icon {
float: left;
font-size: 30px;
}
.card-panel-description {
float: right;
font-weight: bold;
margin: 15px;
margin-left: 0px;
.card-panel-text {
line-height: 14px;
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
margin-bottom: 12px;
text-align: right;
}
.card-panel-num {
font-size: 18px;
}
}
}
}
</style>