服务器管理添加流量图形展示功能、权限控制试调测

1、服务器管理添加流量图形展示功能
2、agent更新模块表单修改更新方式为可编辑
3、权限控制试调测
4、优化并修改bug
This commit is contained in:
康冉冉
2025-12-08 19:21:54 +08:00
parent 17d61dbe43
commit 4c634ab06a
9 changed files with 268 additions and 51 deletions
+8
View File
@@ -736,6 +736,14 @@ export function getRegistList(query) {
data: query
})
}
// 通过clientId查询流量图
export function getNetTraffic(query) {
return request({
url: '/rocketmq/traffic/netBatchTrafficEcharts',
method: 'post',
data: query
})
}
export function bindBusByClient(query) {
return request({
url: 'system/registration/bindBusinessByClientIds',
+21 -4
View File
@@ -4,7 +4,8 @@
<el-row :gutter="10" style="margin: 0 0 8px 0;">
<template v-if="config && config.tableButton && config.tableButton.top">
<el-col :span="1.5" v-for="item of config.tableButton.top">
<el-button :type="item.type" plain size="mini" :icon="item.icon" @click="handleClick(item,{})" :hasPermi="[item.hasPermi]">{{item.content}}</el-button>
<!-- <el-button :type="item.type" plain size="mini" :icon="item.icon" @click="handleClick(item,{})" :hasPermi="[item.hasPermi]">{{item.content}}</el-button>-->
<el-button :type="item.type" plain size="mini" :icon="item.icon" @click="handleClick(item,{})" v-if="checkPermi([item.hasPermi])">{{item.content}}</el-button>
</el-col>
</template>
<right-toolbar v-if="!(config && config.colTopHiddenIcon)" :modelIdent="this.modelIdent" :showSearch.sync="showSearch" @queryTable="renderList" @columnsChange="columnsChange" :columns="columns"></right-toolbar>
@@ -76,13 +77,17 @@
<el-dropdown size="mini" trigger="click" @command="(command) => handleClick({fnCode: command}, scope.row)">
<el-button size="mini" type="text" icon="el-icon-d-arrow-right"><span style="margin-left: -5px;">{{item && item.title ? item.title : '更多'}}</span></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="dropBut of item.more" :command="dropBut.fnCode" :icon="dropBut.icon" :hasPermi="[dropBut.hasPermi]">{{dropBut.content}}</el-dropdown-item>
<!-- <el-dropdown-item v-for="dropBut of item.more" :command="dropBut.fnCode" :icon="dropBut.icon" :hasPermi="[dropBut.hasPermi]">{{dropBut.content}}</el-dropdown-item>-->
<el-dropdown-item v-for="dropBut of item.more" :command="dropBut.fnCode" :icon="dropBut.icon" v-if="checkPermi([dropBut.hasPermi])">{{dropBut.content}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<template v-else>
<!-- <el-tooltip v-if="item && item.content" :content="item.content" placement="top">-->
<el-button v-if="item && item.content" size="mini" link :type="item.type" :icon="item.icon" v-show="item && item.showName ? item.showVal.includes(scope.row[item.showName]) : true" @click="handleClick(item, scope.row)" :hasPermi="[item.hasPermi]">{{item.content}}</el-button>
<template v-if="item && item.content">
<el-button size="mini" link :type="item.type" :icon="item.icon" v-show="item && item.showName ? item.showVal.includes(scope.row[item.showName]) : true" @click="handleClick(item, scope.row)" v-if="checkPermi([item.hasPermi])">{{item.content}}</el-button>
</template>
<!-- <el-button v-if="item && item.content" size="mini" link :type="item.type" :icon="item.icon" v-show="item && item.showName ? item.showVal.includes(scope.row[item.showName]) : true" @click="handleClick(item, scope.row)" :hasPermi="[item.hasPermi]">{{item.content}}</el-button>-->
<!-- </el-tooltip>-->
</template>
</template>
@@ -106,7 +111,8 @@
</template>
<script setup>
export default {
import { checkPermi } from "@/utils/permission";
export default {
name: 'table-list',
props: {
modelIdent: {
@@ -157,6 +163,16 @@
handler(val) {
this.$emit('value-change', val)
}
},
queryParams: {
handler(val) {
// 重置后,重新渲染组件,起到排序图标重置功能
if (val && val.resetVal) {
this.tableKey++;
}
},
deep: true,
immediate: true
}
},
created() {
@@ -165,6 +181,7 @@
}
},
methods: {
checkPermi,
// 1. 格式化单元格内容:将\n替换为<br>,支持HTML渲染
formatCellContent(content) {
if (!content) return '';
+28 -28
View File
@@ -1,28 +1,28 @@
/**
* v-hasPermi 操作权限处理
* Copyright (c) 2019 ruoyi
*/
import store from '@/store'
export default {
inserted(el, binding, vnode) {
const { value } = binding
const all_permission = "*:*:*"
const permissions = store.getters && store.getters.permissions
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
const hasPermissions = permissions.some(permission => {
return all_permission === permission || permissionFlag.includes(permission)
})
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置操作权限标签值`)
}
}
}
/**
* v-hasPermi 操作权限处理
* Copyright (c) 2019 ruoyi
*/
import store from '@/store'
export default {
inserted(el, binding, vnode) {
const { value } = binding
const all_permission = "*:*:*"
const permissions = store.getters && store.getters.permissions
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
const hasPermissions = permissions.some(permission => {
return all_permission === permission || permissionFlag.includes(permission)
})
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置操作权限标签值`)
}
}
}
+14 -7
View File
@@ -6,6 +6,7 @@ Vue.use(Router)
/* Layout */
import Layout from '@/layout'
import DialogView from '@/views/earnManage/server/dialogView.vue'
import TrafficChart from '@/views/resource/serverRegister/trafficChart.vue'
import Welcome from '@/views/welcome.vue'
/**
@@ -88,6 +89,12 @@ export const constantRoutes = [
component: DialogView,
hidden: true,
name: 'DialogView'
},
{
path: '/traffic-chart',
component: TrafficChart,
hidden: true,
name: 'TrafficChart'
}
]
@@ -181,7 +188,7 @@ export const dynamicRoutes = [
path: '/earnManage/busValueCount/edit',
component: Layout,
hidden: true,
permissions: ['earnManage:busValueCount:edit'],
permissions: ['earnManage:busValueCount:add'],
children: [
{
path: 'index/:id?',
@@ -195,7 +202,7 @@ export const dynamicRoutes = [
path: '/earnManage/busValueCount/list',
component: Layout,
hidden: true,
permissions: ['earnManage:busValueCount:list'],
permissions: ['earnManage:busValueCount:relevantData'],
children: [
{
path: 'index/:id?',
@@ -209,7 +216,7 @@ export const dynamicRoutes = [
path: '/earnManage/busValueCount/chart',
component: Layout,
hidden: true,
permissions: ['earnManage:busValueCount:chart'],
permissions: ['earnManage:busValueCount:graphicAnalysis'],
children: [
{
path: 'index/:id?',
@@ -224,7 +231,7 @@ export const dynamicRoutes = [
path: '/earnManage/server/list',
component: Layout,
hidden: true,
permissions: ['earnManage:server:list'],
permissions: ['earnManage:server:relevantData'],
children: [
{
path: 'index/:serverId?',
@@ -238,7 +245,7 @@ export const dynamicRoutes = [
path: '/earnManage/server/edit',
component: Layout,
hidden: true,
permissions: ['earnManage:server:edit'],
permissions: ['rocketmq:traffic'],
children: [
{
path: 'index/:serverId?',
@@ -371,7 +378,7 @@ export const dynamicRoutes = [
path: '/resource/serverRegister/monitorChart',
component: Layout,
hidden: true,
permissions: ['resource:serverRegister:echartView'],
permissions: ['rocketmq:traffic'],
children: [
{
path: ':id?',
@@ -414,7 +421,7 @@ export const dynamicRoutes = [
path: '/resource/switchRegister/monitorChart',
component: Layout,
hidden: true,
permissions: ['resource:switchRegister:echartView'],
permissions: ['resource:switchRegister:graphicAnalysis'],
children: [
{
path: ':id?',
@@ -84,13 +84,13 @@
config: {
tableButton: {
top: [
{content: '新增', fnCode: 'add', type: 'primary', icon: 'el-icon-plus', hasPermi: 'resource:monitorStategy:add'},
{content: '导出', fnCode: 'export', type: 'warning', icon: 'el-icon-download', hasPermi: 'earnManage:server:export'},
{content: '新增', fnCode: 'add', type: 'primary', icon: 'el-icon-plus', hasPermi: 'earnManage:busValueCount:add'},
{content: '导出', fnCode: 'export', type: 'warning', icon: 'el-icon-download', hasPermi: 'earnManage:busValueCount:export'},
],
line: [
{content: '相关数据', fnCode: 'edit', type: 'text', icon: 'el-icon-tickets', hasPermi: 'earnManage:server:relevantData'},
{content: '重新计算', fnCode: 'calculate', type: 'text', icon: 'el-icon-brush', hasPermi: 'earnManage:server:calculate'},
{content: '图形查看', fnCode: 'echartView', type: 'text', icon: 'el-icon-data-analysis', hasPermi: 'earnManage:server:graphicAnalysis'},
{content: '相关数据', fnCode: 'edit', type: 'text', icon: 'el-icon-tickets', hasPermi: 'earnManage:busValueCount:relevantData'},
{content: '重新计算', fnCode: 'calculate', type: 'text', icon: 'el-icon-brush', hasPermi: 'earnManage:busValueCount:calculate'},
{content: '图形查看', fnCode: 'echartView', type: 'text', icon: 'el-icon-data-analysis', hasPermi: 'earnManage:busValueCount:graphicAnalysis'},
{},{}
]
}
+1 -1
View File
@@ -197,7 +197,7 @@
tableButton: {
top: [
{content: '导出', fnCode: 'export', type: 'warning', icon: 'el-icon-download', hasPermi: 'earnManage:server:export'},
{content: '图形分析', fnCode: 'echartView', type: 'success', icon: 'el-icon-data-analysis', hasPermi: 'earnManage:server:graphicAnalysis'},
{content: '图形分析', fnCode: 'echartView', type: 'success', icon: 'el-icon-data-analysis', hasPermi: 'rocketmq:traffic'},
// {content: '生成月均日95值', fnCode: 'createData', type: 'success', icon: 'el-icon-data-analysis', hasPermi: 'earnManage:server:add'},
],
line: [
@@ -50,8 +50,8 @@
managePublicIp: {label: '管理网-公网IP', span: 18, type: 'input', disabled: true, hidden: this.paramsData && this.paramsData.readonly ? false : true},
status: {label: '状态', span: 18, type: 'select', options: this.dict.type.rm_register_online_state, disabled: true, hidden: this.paramsData && this.paramsData.readonly ? false : true},
agentVersion: {label: 'AGENT版本', span: 18, type: 'input', disabled: true, hidden: this.paramsData && this.paramsData.readonly ? false : true},
method: {label: '更新方式', span: 18, type: 'select', options: this.dict.type.policy_method, eventName: 'change', required: true, disabled: this.paramsData && this.paramsData.id ? true : false,},
scheduledUpdateTime: {label: '定时执行时间', span: 18, type: 'datetime', required: true, hidden: objVal && objVal.method === '1' ? false: true, disabled: this.paramsData && this.paramsData.id ? true : false,},
method: {label: '更新方式', span: 18, type: 'select', options: this.dict.type.policy_method, eventName: 'change', required: true},
scheduledUpdateTime: {label: '定时执行时间', span: 18, type: 'datetime', required: true, hidden: objVal && objVal.method === '1' ? false: true},
// fileUrlType: {label: '文件格式', span: 18, type: 'radio', required: true,options: [{label: '外网HTTP(S)',value: '1'}], warningTitle: '注意:当文件大小超过100M时,请选择【外网HTTP(S)】地址格式'},
fileUrl: {label: '文件地址', span: 18, type: 'input', required: true, placeholder: '请输入外网地址', warningTitle: '如:http://www.tr.com/server-1.1.1.jar'},
fileMd5: {label: '文件MD5', span: 18, type: 'input', required: true},
+44 -4
View File
@@ -108,7 +108,9 @@
<!-- 标签 -->
<template #tempRemark="{ row, column }">
<template v-if="row.remark">
<el-tag v-for="(item, index) of row.remark.split(',')" :style="{marginLeft: index !== 0 ? '5px' : null}">{{item}}</el-tag>
<el-tag v-for="(item, index) of row.remark.split(',')" :style="{marginLeft: index !== 0 ? '5px' : null}">
{{item}}<span v-if="index+1 !== row.remark.split(',').length" style="color: transparent;">,</span>
</el-tag>
</template>
</template>
<template #tempHandle="{ row, column }">
@@ -204,7 +206,7 @@
stratOpen: false,
roleList: [],
busNameList: [],
loading: true,
loading: false,
showSearch: true,
ids: [],
single: true,
@@ -276,11 +278,12 @@
{content: '添加金山machineCode', fnCode: 'add', type: 'primary', icon: 'el-icon-plus', hasPermi: 'resource:serverRegister:add'},
{content: '修改业务名称', fnCode: 'edit', type: 'success', icon: 'el-icon-edit', hasPermi: 'resource:serverRegister:edit'},
{content: '批量增加标签', fnCode: 'tagRemark', type: 'primary', icon: 'el-icon-plus', hasPermi: 'resource:serverRegister:tagRemark'},
{content: '流量图形展示', fnCode: 'trafficChart', type: 'primary', icon: 'el-icon-data-analysis', hasPermi: 'resource:serverRegister:trafficChart'},
{content: '导出', fnCode: 'export', type: 'warning', icon: 'el-icon-download', hasPermi: 'resource:serverRegister:export'},
],
line: [
{content: '选择业务IP', fnCode: 'pubilcNet', type: 'text', icon: 'el-icon-thumb', showName: 'multiPublicIpStatus', showVal: '0', hasPermi: 'resource:serverRegister:pubilcNet'},
{content: '图形监控', fnCode: 'echartView', type: 'text', icon: 'el-icon-data-analysis', hasPermi: 'resource:serverRegister:graphicAnalysis'},
{content: '图形监控', fnCode: 'echartView', type: 'text', icon: 'el-icon-data-analysis', hasPermi: 'rocketmq:traffic'},
{content: '详情', fnCode: 'details', type: 'text', icon: 'el-icon-view', hasPermi: 'resource:serverRegister:details'},
{title: '更多', more: [
{content: '下发业务', fnCode: 'issueBusiness', type: 'text', icon: 'el-icon-circle-check', hasPermi: 'resource:serverRegister:issueBusiness'},
@@ -380,6 +383,7 @@
this.$modal.loading();
listHandle(this.addDateRange(params)).then(response => {
this.roleList = response.rows;
delete this.queryParams.resetVal;
this.queryParams.total = response.total;
this.$modal.closeLoading();
}).catch(() => {
@@ -396,7 +400,7 @@
/** 重置按钮操作 */
resetQuery() {
this.$refs['queryRef'].resetFields();
this.queryParams = {pageNum: 1, pageSize: 10,total: 0};
this.queryParams = {pageNum: 1, pageSize: 10,total: 0, resetVal: true};
// this.resetForm("queryRef");
this.handleQuery();
},
@@ -436,6 +440,21 @@
this.loading = false;
});
},
/** 图形分析 */
graphicTraffic(list) {
// 生成唯一 key(避免数据冲突)
const storageKey = `temp_data_${Date.now()}`;
// 1. 将数据存入 localStorage(需序列化)
localStorage.setItem(storageKey, JSON.stringify({clientIdList: list}));
// 1. 用 Vue Router 解析目标路由的完整 URL
const routeLocation = this.$router.resolve({
name: 'TrafficChart',
query: { storageKey }
});
// 2. 打开新窗口(routeLocation.href 是完整路径)
window.open(routeLocation.href, '_blank');
// _blank:新标签页;_self:当前窗口(默认)
},
callback(result, rowData, selectChange, selectList) {
this.dialogRowData = {};
if (result && result.fnCode) {
@@ -467,6 +486,27 @@
query: {clientId: rowData.clientId}
});
break;
case 'trafficChart':
if (selectList && selectList.length <= 0) {
this.$modal.msgWarning("请选择数据!");
return;
}
let clientIdData = {};
selectList.forEach(item => {
let interFaceList = [];
if (item && item.ip1InterfaceName) {
interFaceList.push(item.ip1InterfaceName);
}
if (item && item.ip2InterfaceName) {
interFaceList.push(item.ip2InterfaceName);
}
if (item && item.ip3InterfaceName) {
interFaceList.push(item.ip3InterfaceName);
}
clientIdData[item.clientId] = interFaceList;
});
this.graphicTraffic(clientIdData);
break;
case 'mtrChartVew':
this.mtrChartOpen = true;
this.dialogRowData = rowData;
@@ -0,0 +1,145 @@
<template>
<div style="height: calc(100vh)!important; padding: 10px;">
<div class="w100 h100">
<div class="w100" style="text-align: right;">
<el-date-picker
v-model="timeList"
:style="{width: '360px!important', marginRight: '10px'}"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
range-separator=""
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
@change="(val) => dateChange(val)"/>
</div>
<template v-for="(item,key, index) of echartListData">
<EchartsLine :lineData="item.dataVal" :title="item.title" class="w100 mt20 mb20" style="border: 1.5px solid #878787;height:200px;" :chartData="(valData, unit) => chartDataEvent(item, unit, key)"></EchartsLine>
</template>
</div>
</div>
</template>
<script>
import {getNetTraffic, trafficEcharts} from "@/api/disRevenue/resource"
import EchartsLine from "@/components/echartsList/line.vue";
export default {
name: "trafficChart",
components: {EchartsLine},
data() {
return {
timeList: [],
paramsData: {},
currTimeList: {},
echartListData: {},
echartData: {
title: 'GE1/0/1的丢包数',
dataVal: {
titleVal: {textAlign: 'center'},
yAxisName: ' ',
gridTop: '30%',
legend: {top: '12%', left: '10%'},
hiddenTime: true,
lineXData: [],
unitSelList: [{label: 'Kb', value: 'Kb'},{label: 'Mb', value: 'Mb'}, {label: 'Gb', value: 'Gb'}],
dataList: [{
name: '出战流量',
data: [],
},{
name: '入站流量',
data: []
}]
}
},
}
},
created() {
const now = new Date();
// 获取当前时间
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
// 正确计算前一天(处理跨月情况)
const prevDate = new Date(now);
prevDate.setDate(now.getDate() - 1);
const prevYear = prevDate.getFullYear();
const prevMonth = String(prevDate.getMonth() + 1).padStart(2, '0');
const prevDay = String(prevDate.getDate()).padStart(2, '0');
// 构建时间字符串
const timeString = `${hours}:${minutes}:${seconds}`;
const startData = `${prevYear}-${prevMonth}-${prevDay} ${timeString}`;
const endData = `${year}-${month}-${day} ${timeString}`;
this.currTimeList = {startTime: startData, endTime: endData};
this.timeList = [startData, endData];
this.initData();
},
methods: {
// 时间变化时
dateChange(val){
this.currTimeList = {startTime: val[0], endTime: val[1]};
this.getNetTraffic(this.paramsData.clientIdList);
},
initData(){
this.storageKey = this.$route.query && this.$route.query['storageKey'];
if (this.storageKey) {
this.paramsData = JSON.parse(localStorage.getItem(this.storageKey));
this.getNetTraffic(this.paramsData.clientIdList);
}
},
// 总流量图查询
getNetTraffic(val) {
getNetTraffic(Object.assign({},{clientInterfaces: val}, this.currTimeList)).then(res => {
if (res && res.data && res.data.length > 0) {
res.data.forEach(item => {
let netEcharts = JSON.parse(JSON.stringify(this.echartData));
netEcharts.title = `${item && item.clientId}${item && item.interfaceName}`;
netEcharts.interfaceName = item && item.interfaceName;
netEcharts.dataVal.yAxisName = item && item.unit ? '单位' + item.unit : ' ';
netEcharts.dataVal.lineXData = item && item.xData.length > 0 ? item.xData : [];
netEcharts.dataVal['unitModel'] = item && item.unit || '';
netEcharts.dataVal.dataList[0].data = item && item.yData['netOutSpeedData'] || [];
netEcharts.dataVal.dataList[1].data = item && item.yData['netInSpeedData'] || [];
this.$set(this.echartListData, item.clientId, netEcharts);
});
}
});
},
// 单位查询
getTrafficChartData(clientIdTitle, titleName, unitData) {
return trafficEcharts(Object.assign(unitData || {}, {name : titleName,clientId: clientIdTitle}, this.currTimeList)).then(res => {
if (res && res.data) {
let netEcharts = JSON.parse(JSON.stringify(this.echartData));
netEcharts.title = `${clientIdTitle}${titleName}`;
netEcharts.interfaceName = titleName;
netEcharts.dataVal.yAxisName = res.data && res.data.unit ? '单位' + res.data.unit : ' ';
netEcharts.dataVal.lineXData = res.data && res.data.xData.length > 0 ? res.data.xData : [];
netEcharts.dataVal['unitModel'] = res.data && res.data.unit || '';
netEcharts.dataVal.dataList[0].data = res.data && res.data.yData['netOutSpeedData'] || [];
netEcharts.dataVal.dataList[1].data = res.data && res.data.yData['netInSpeedData'] || [];
this.$set(this.echartListData, clientIdTitle, netEcharts);
}
return true;
}).catch(() => {
return true;
});
},
async chartDataEvent(valData, unit, clientIdTitle) {
if (unit) {
await Promise.all([
this.$modal.loading(),
this.getTrafficChartData(clientIdTitle, valData.interfaceName,{unit: unit}),
]);
this.$modal.closeLoading();
}
},
}
}
</script>
<style scoped>
</style>