前端开发服务器管理的详情页面 、前端开发服务器管理的PPPoE配置

This commit is contained in:
康冉冉
2025-12-31 18:07:20 +08:00
parent f98f7b39a5
commit 9765cf824d
12 changed files with 765 additions and 43 deletions
+2 -1
View File
@@ -42,7 +42,8 @@
"vue-cropper": "0.5.5", "vue-cropper": "0.5.5",
"vue-router": "3.4.9", "vue-router": "3.4.9",
"vuedraggable": "2.24.3", "vuedraggable": "2.24.3",
"vuex": "3.6.0" "vuex": "3.6.0",
"xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "4.4.6", "@vue/cli-plugin-babel": "4.4.6",
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.1 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1763452689974" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11937" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M695.98076 0.061839c18.636877 0 34.373736 13.988319 36.548749 32.497254l0.255884 4.307379v69.941597h147.303828c18.679524 0 34.373736 13.988319 36.548749 32.539902l0.298531 4.307379V290.91653h69.856303a36.804633 36.804633 0 0 1 4.307379 73.39603l-4.264732 0.255884h-69.941597v110.456547h69.941597a36.804633 36.804633 0 0 1 4.264732 73.353382l-4.264732 0.298532h-69.941597v110.456547h69.941597a36.804633 36.804633 0 0 1 4.264732 73.353382l-4.264732 0.298531h-69.941597v147.261181a36.804633 36.804633 0 0 1-32.497254 36.548749l-4.307379 0.255884h-147.303828v69.941597a36.804633 36.804633 0 0 1-73.39603 4.350026l-0.255883-4.307379v-69.941597h-110.456547v69.941597a36.804633 36.804633 0 0 1-73.39603 4.264732l-0.298532-4.264732v-69.941597h-110.456547v69.941597a36.804633 36.804633 0 0 1-73.396029 4.264732l-0.255884-4.264732v-69.941597H143.570083a36.804633 36.804633 0 0 1-36.548749-32.539902l-0.298531-4.307378V732.742718H36.8665a36.804633 36.804633 0 0 1-4.307379-73.39603l4.264732-0.255884h69.941597v-110.456547H36.781205a36.804633 36.804633 0 0 1-4.264731-73.353382l4.264731-0.298532h69.941598v-110.456547H36.781205a36.804633 36.804633 0 0 1-4.264731-73.353382l4.264731-0.298531h69.941598V143.65535c0-18.679524 13.945672-34.373736 32.497254-36.54875l4.307379-0.255884h147.303827V36.909119A36.804633 36.804633 0 0 1 364.269941 32.516446l0.255883 4.307379v69.941597h110.456547V36.909119a36.804633 36.804633 0 0 1 73.39603-4.264731l0.298532 4.264731v69.941597h110.456547V36.909119c0-20.300122 16.504511-36.804633 36.84728-36.804633z m147.303828 180.398144H180.374716v662.739282h662.909872V180.459983z m-147.303828 110.456547c18.636877 0 34.373736 13.988319 36.548749 32.539902l0.255884 4.264731v368.216922a36.804633 36.804633 0 0 1-32.497254 36.548749l-4.307379 0.298531H327.678544a36.804633 36.804633 0 0 1-36.548749-32.539901l-0.255884-4.264732V327.721163c0-18.679524 13.988319-34.373736 32.497254-36.591396l4.307379-0.255884h368.302216z m-36.84728 73.651914H364.525824v294.565008h294.607656V364.568444z" fill="#1296db" p-id="11938"></path></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1763452455440" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7878" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M453.95 64.79c68.02 0 133.9 13.35 195.96 39.6a502.426 502.426 0 0 1 159.97 107.92 502 502 0 0 1 107.96 160.01 501.21 501.21 0 0 1 39.84 196.13c0 12.38-4.99 24.46-13.66 33.13a46.333 46.333 0 0 1-33.03 13.69l-59.28-0.1c2.72 6.61 3.44 13.9 2.06 21.09a400.69 400.69 0 0 1-136.41 230.53 396.749 396.749 0 0 1-255.2 92.47c-53.84 0-106.06-10.56-155.3-31.41a397.983 397.983 0 0 1-126.81-85.67C105.13 767.21 63.09 665.51 63.2 559.51a399.019 399.019 0 0 1 89.82-252.66 399.818 399.818 0 0 1 224.51-138.06c10.66-2.3 21.43 0.48 29.72 6.61l-0.03-63.82c0.01-25.8 20.96-46.79 46.73-46.79z m-46.72 170.16c-4.2 3.07-8.99 5.22-14.07 6.29-149.48 32.81-255.97 165.23-255.96 318.27a323.767 323.767 0 0 0 95.19 230.26 322.39 322.39 0 0 0 229.78 95.4c75.99 0.09 149.58-26.59 207.86-75.34a326.65 326.65 0 0 0 111.09-187.8c0.48-2.41 1.2-4.82 2.13-7.02l-329.41-0.55c-25.76-0.13-46.58-21.03-46.61-46.79V234.95z m69.39-108.99v420.58l419.85 0.72c-12.11-105-59.28-211.44-134.62-286.78-75.34-75.37-180.31-122.61-285.2-134.51h-0.03z m0 0" p-id="7879" fill="#1296db"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1763452772113" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13072" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M922.688 810.624h-149.312a37.376 37.376 0 0 1-37.312-37.376L736 736h-49.792v74.688H611.584V736h-49.792v74.688H462.208V736h-49.728v74.688H337.792V736H288l-0.256 37.248a37.312 37.312 0 0 1-37.312 37.376h-149.12A37.312 37.312 0 0 1 64 773.248V250.752c0-20.672 16.704-37.376 37.312-37.376h821.312a37.312 37.312 0 0 1 37.376 37.376v522.496a37.312 37.312 0 0 1-37.312 37.376z m-37.376-177.344h-39.168a37.376 37.376 0 0 1 0-74.752h39.168V288.128H138.688v270.4h37.952a37.376 37.376 0 0 1 0 74.752h-37.952v102.528H213.12l0.256-49.6c0-20.672 4.288-24.896 24.896-24.896h547.584c20.608 0 24.896 4.224 24.896 24.896v49.6h74.624V633.28zM736 337.792h74.688V512H736V337.792z m-174.208 0h74.688V512H561.792V337.792z m-174.208 0h74.688V512H387.584V337.792z m-174.272 0H288V512l-73.472-0.128-1.216-174.08z" p-id="13073" fill="#1296db"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+2 -2
View File
@@ -115,7 +115,7 @@
<template v-else> <template v-else>
<!-- <el-tooltip v-if="item && item.content" :content="item.content" placement="top">--> <!-- <el-tooltip v-if="item && item.content" :content="item.content" placement="top">-->
<template v-if="item && item.content"> <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> <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="item && item.hasPermi ? checkPermi([item.hasPermi]) : true">{{item.content}}</el-button>
</template> </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-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>--> <!-- </el-tooltip>-->
@@ -299,7 +299,7 @@
} }
return true; return true;
} else { } else {
if (!checkPermi([buttonConfig.hasPermi])) { if (buttonConfig && buttonConfig.hasPermi && !checkPermi([buttonConfig.hasPermi])) {
return false; return false;
} }
if (buttonConfig.showName) { if (buttonConfig.showName) {
+16 -1
View File
@@ -364,7 +364,7 @@ export const dynamicRoutes = [
path: '/resource/serverRegister/edit', path: '/resource/serverRegister/edit',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['resource:serverRegister:add', 'resource:serverRegister:edit', 'resource:serverRegister:details'], permissions: ['resource:serverRegister:add', 'resource:serverRegister:edit'],
children: [ children: [
{ {
path: ':id?', path: ':id?',
@@ -374,6 +374,21 @@ export const dynamicRoutes = [
} }
] ]
}, },
// 服务器管理详情
{
path: '/resource/serverRegister/details',
component: Layout,
hidden: true,
permissions: ['resource:serverRegister:details'],
children: [
{
path: ':id?',
component: () => import('@/views/resource/serverRegister/details'),
name: 'serverRegisterDetails',
meta: { title: '服务器管理详情', activeMenu: '/resource/serverRegister' }
}
]
},
{ {
path: '/resource/serverRegister/monitorChart', path: '/resource/serverRegister/monitorChart',
component: Layout, component: Layout,
+168 -37
View File
@@ -1,47 +1,53 @@
<template> <template>
<div style="padding: 0 30px;"> <div style="padding: 0 30px;">
<Form ref="formRef" :formList="formList" :ruleFormData="ruleForm" :config="{labelWidth: '140px'}" @fnClick="callback"> <el-button type="primary" style="position: absolute;right: 20px;z-index: 9999;" @click="formSubmit()">{{readonly ? '编辑' : '保存'}}</el-button>
<Form ref="formRef" :formList="formList" :ruleFormData="ruleForm" :config="{labelWidth: '140px', buttonGroup: []}" @fnClick="callback">
<template #tempPoeConfigRules="{ field, model, formModel }"> <template #tempPoeConfigRules="{ field, model, formModel }">
<el-button type="primary" size="mini">导入</el-button> <div v-if="!readonly">
<el-button type="info" size="mini">模板下载</el-button> <el-upload
class="disInlineBlock mr20"
action=""
:auto-upload="false"
:on-change="handleFileUpload"
accept=".xls, .xlsx, .numbers"
:show-file-list="false">
<el-button size="mini" type="primary">导入</el-button>
</el-upload>
<!-- <el-button type="primary" size="mini" >导入</el-button>-->
<el-button type="info" size="mini" @click="downloadTemp">模板下载</el-button>
<div style="margin-left: -120px"> <div style="margin-left: -120px">
<div v-for="(item,index) of contentList" class="w100"> <TableList id="tabHeadCenter" ref="tabRef" :columns="columns" :config="config" :queryParams="{total: 0}" :tableList="roleList" @fnClick="callback">
<span class="disInlineBlock" style="width: 20px;">{{index + 1}}.</span> <template #tempEditModel="{ row, valueKey, column }">
<span class="disInlineBlock" style="width: calc((100% - 45px)/4)">VLANID<el-input-number v-model="item.oneNum" style="width: calc(100% - 68px)" :controls="false" size="small" placeholder="请输入数字" /></span> <el-input-number v-if="column.type === 'number'" v-model="row[valueKey]" class="w100" :controls="false" :min="column.min || undefined" :max="column.max || undefined" size="small" :placeholder="column.placeholder || ''" />
<span class="disInlineBlock" style="width: calc((100% - 45px)/4)"><span class="disInlineBlock">IPv4地址</span><el-input v-model="item.ipv4Addr" style="width: calc(100% - 75px)" class="disInlineBlock" size="small" placeholder="如1.1.1.1" /></span> <el-input v-else v-model="row[valueKey]" class="w100" size="small" :placeholder="column.placeholder" />
<span class="disInlineBlock" style="width: calc((100% - 45px)/4)">IPv4掩码位数<el-input-number v-model="item.ipv4Num" style="width: calc(100% - 103px)" :controls="false" :min="1" :max="32" size="small" placeholder="请输入1-32" /></span> </template>
<span class="disInlineBlock" style="width: calc((100% - 45px)/4)"><span class="disInlineBlock">IPv4网关</span><el-input v-model="item.ipv4Graw" style="width: calc(100% - 75px)" class="disInlineBlock" size="small" placeholder="如1.1.1.1" /></span> </TableList>
<el-button style="width: 25px;padding: 9px 0;" type="primary" size="small" :icon="index === 0 ? 'el-icon-plus' : 'el-icon-minus'" @click="handleChange(index)"></el-button>
</div> </div>
</div> </div>
<div v-else style="margin-left: -120px">
<TableList ref="tabRef" :columns="columns" :config="{colHiddenCheck: true, colTopHiddenIcon: true}" :queryParams="{total: 0}" :tableList="roleList"></TableList>
</div>
</template> </template>
</Form> </Form>
</div> </div>
</template> </template>
<script> <script>
import * as XLSX from 'xlsx';
import Form from '@/components/form/index.vue'; import Form from '@/components/form/index.vue';
import TableList from '@/components/table/index.vue';
export default { export default {
name: "PPPoEConfig", name: "PPPoEConfig",
components: {Form}, components: {Form, TableList},
props: { props: {
open: { modelVal: {
type: String, type: Array,
default: '' default: () => []
},
dialogRowData: {
type: Object,
default: () => {}
} }
}, },
watch: { watch: {
open: { modelVal: {
handler(val) { handler(val) {
if (val === 'type_true') {
} else if (val === 'type_false') {
// 清空form
this.$refs['formRef'].$refs.ruleForm.resetFields();
}
}, },
deep: true, deep: true,
immediate: true immediate: true
@@ -51,38 +57,69 @@
return { return {
ruleForm: {}, ruleForm: {},
formList: [], formList: [],
contentList: [{oneNum: '',ipv4Addr: '', ipv4Num: '', ipv4Graw: ''}] readonly: true,
contentList: [{oneNum: '',ipv4Addr: '', ipv4Num: '', ipv4Graw: ''}],
roleList: [],
columns: {},
config: {
colHiddenCheck: true, colTopHiddenIcon: true,
tableButton: {
line: [
{content: '新增', fnCode: 'add', type: 'text'},
{content: '删除', fnCode: 'delete', type: 'text', showName: 'showNum', showVal: '1'},
]
}
},
} }
}, },
created() { created() {
this.fnFormList(); this.fnFormList(this.readonly);
}, },
methods: { methods: {
// formList集合 // formList集合
fnFormList() { fnFormList(readonly) {
this.formList = [{ this.formList = [{
config: {title: ' ', labelWidth: '140px', colSpan: 'disBlock'}, config: {title: '', labelWidth: '140px', readonly: readonly},
controls: { controls: {
id: {label: 'ID', hidden: true}, id: {label: 'ID', hidden: true},
businessName: {label: '启用PPPoE', span: 12, type: 'radio', options: [], required: true}, businessName: {label: '启用PPPoE', span: readonly ? 12 : 24, type: 'radio', options: [], required: true},
deployDevice: {label: 'PPPoE配置方式', span: 12, type: 'select', options: [], required: true}, deployDevice: {label: 'PPPoE配置方式', span: readonly ? 10 : 24, type: 'select', options: [], required: true},
deploy: {label: 'PPPoE网卡', span: 12, type: 'select', options: [], required: true}, deploy: {label: 'PPPoE网卡', span: readonly ? 12 : 24, type: 'select', options: [], required: true},
deploed: {label: 'PPPoE配置规则', span: 24, slotName: 'tempPoeConfigRules'}, deploed: {label: 'PPPoE配置规则', span: 24, slotName: 'tempPoeConfigRules'},
} }
}]; }];
this.columns = {
id: { label: `ID`,width: '50'},
serialNumber: { label: `序号`, width: '50',visible: true},
vlanId: { label: `VLANID`, minWidth: '100',visible: true, type: 'number', placeholder: '请输入数字', slotName: readonly ? null : 'tempEditModel'},
ipv4Address: { label: `IPv4地址`, minWidth: '100',visible: true, placeholder: '如1.1.1.1', slotName: readonly ? null : 'tempEditModel'},
ipv4MaskBits: { label: `IPv4掩码位数`, minWidth: '100',visible: true, type: 'number', min: 1, max: 32, placeholder: '请输入1-32', slotName: readonly ? null : 'tempEditModel'},
ipv4Gateway:{ label: `IPv4网关`,minWidth: '100',visible: true, placeholder: '如1.1.1.1', slotName: readonly ? null : 'tempEditModel'},
bandwidthResult: { label: `虚拟网卡上报带宽(Mbps)`, minWidth: '120', visible: true, placeholder: '请输入', slotName: readonly ? null : 'tempEditModel'},
macAddress: { label: `MAC地址`, minWidth: '120', visible: readonly},
status: { label: `连通状态`, minWidth: '100', visible: readonly},
};
}, },
// 按钮事件 formSubmit() {
handleChange(indexNum) { this.ruleForm = {name: '12'};
if (indexNum === 0) { if (!this.readonly) {
this.contentList.push({oneNum: '',ipv4Addr: '', ipv4Num: '', ipv4Graw: ''}); console.log('submit=====',this.roleList,this.ruleForm);
} else { } else {
this.contentList.splice(indexNum, 1); this.roleList = this.roleList.length > 0 ? this.roleList : [{serialNumber: '1', vlanId: undefined, ipv4Address: undefined, ipv4MaskBits: undefined, ipv4Gateway: undefined, bandwidthResult: undefined}];
} }
this.readonly = !this.readonly;
this.fnFormList(this.readonly);
}, },
// 监听事件 // 监听事件
callback(result, dataVal, formVal) { callback(result, dataVal, formVal) {
if (result && result.fnCode) { if (result && result.fnCode) {
switch (result.fnCode) { switch (result.fnCode) {
case 'add':
this.roleList.push({showNum: '1', serialNumber: this.roleList.length + 1, vlanId: undefined, ipv4Address: undefined, ipv4MaskBits: undefined, ipv4Gateway: undefined, bandwidthResult: undefined});
break;
case 'delete':
console.log('dataVal===',dataVal);
this.roleList.splice(dataVal.serialNumber - 1, 1);
case 'submit': case 'submit':
console.log('contentList===',this.contentList, 'dataVal===',dataVal); console.log('contentList===',this.contentList, 'dataVal===',dataVal);
// this.$emit("dialogResult", {open: false}); // this.$emit("dialogResult", {open: false});
@@ -94,10 +131,104 @@
} }
} }
}, },
downloadTemp() {
this.download('http://172.16.15.51:9300/statics/csvfile/macvlan_vlan.csv', {
}, `template_${new Date().getTime()}.xlsx`)
},
handleFileUpload(file) {
console.log('file====',file);
// 如果是使用 Element UI 的 el-upload 组件,文件对象是 file.raw
const actualFile = file.raw || file; // 兼容两种获取方式[1,3](@ref)
if (!actualFile) {
return;
}
const fileReader = new FileReader();
fileReader.onload = (e) => {
try {
// 1. 获取读取结果,并将其转换为 Uint8Array
const data = new Uint8Array(e.target.result);
// 2. 使用 xlsx 解析数据
const workbook = XLSX.read(data, { type: 'array' });
// 3. 获取第一个工作表(Sheet)的名称
const firstSheetName = workbook.SheetNames[0];
// 4. 根据工作表名称获取对应的工作表对象
const worksheet = workbook.Sheets[firstSheetName];
// 5. 将工作表数据转换为 JSON 格式
// header: 1 表示按数组格式解析,包含表头;默认按对象格式(第一行为键名)
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
// 或者使用对象格式(更常用,第一行作为对象的键)[3,6](@ref)
// const jsonData = XLSX.utils.sheet_to_json(worksheet);
// 6. 将解析后的数据存储在组件的 data 中
// this.excelData = jsonData;
console.log('解析到的Excel数据:', jsonData);
// 可以在这里调用后续处理方法,如数据校验、发送到后端等
this.processExcelData(jsonData);
} catch (error) {
console.error('解析Excel文件时出错:', error);
this.$message.error('文件解析失败,请检查文件格式是否正确。');
}
};
fileReader.onerror = (error) => {
console.error('读取文件失败:', error);
this.$message.error('读取文件失败,请重试。');
};
// 开始以 ArrayBuffer 格式读取文件
fileReader.readAsArrayBuffer(actualFile);
},
processExcelData(data) {
// 在这里对解析出的数据进行进一步处理
// 例如:数据清洗、验证、转换格式等[2](@ref)
if (data.length > 0) {
let oldDataList = this.oldTableList();
let arrList = [];
data.map((item,index) => {
if (item && item.length > 0) {
let arr = ['vlanId', 'ipv4Address', 'ipv4MaskBits', 'ipv4Gateway', 'bandwidthResult'];
let obj = {serialNumber: oldDataList.length + index + 1, showNum: '1'};
arr.forEach((val, valIndex) => {
obj[val] = item[valIndex];
// console.log('val===',val, valIndex);
// arrList.push(obj);
});
// console.log('item===',item, 'obj===', obj);
}
});
// console.log('arrList===',arrList);
this.roleList = oldDataList.concat(arrList);
} else {
this.$message.warning('导入的文件中没有数据。');
}
},
oldTableList() {
let contentList = [];
this.roleList.forEach(item => {
let indexNum = 0;
Object.keys(item).some(val => {
if (val !== 'serialNumber' && val !== 'showNum' && item[val]) {
indexNum = 1;
return;
}
});
if (indexNum === 1) {
contentList.push(item);
}
});
return contentList;
},
} }
} }
</script> </script>
<style scoped> <style scoped>
::v-deep #tabHeadCenter .el-table .el-table__header-wrapper th, .el-table .el-table__fixed-header-wrapper th {
text-align: center!important;
}
</style> </style>
@@ -0,0 +1,220 @@
<template>
<div class="app-container">
<!-- header box -->
<div>
<div v-for="item of headerList" class="headerBox">
<div class="boxChild w100 h100">
<div class="disInlineBlock w80" style="padding-left: 10px;">
<div>{{item.name}}</div>
<div class="mt20" style="font-size: 2rem;">{{item.num}}</div>
</div>
<div class="disInlineBlock w20 " style="vertical-align: top;">
<img width="50" :src="item.imgUrl" alt="">
</div>
</div>
</div>
</div>
<!-- form -->
<div class="mt20">
<Form :formList="formList" :ruleFormData="ruleForm" :config=" {labelWidth: '92px', buttonGroup: []}" @fnClick="callback">
<!-- SN -->
<template #tempClient="{field, model, formModel}">
{{model}}
<div class="disInlineBlock" style="margin: 0 20px;">
<dict-tag class="disInlineBlock ml10 mr10" :options="dict.type.rm_register_status" :value="formModel.registrationStatus"/>
<dict-tag class="disInlineBlock" :options="dict.type.rm_register_online_state" :value="formModel.onlineStatus"/>
</div>
<el-tag class="disInlineBlock dynamic-tag ml5" v-for="(tag, tagIndex) in formModel.remark" :closable="false" :disable-transitions="false">
{{ tag }}
</el-tag>
</template>
<!-- ip -->
<template #tempIp1public="{field, model, formModel}">
{{model}}
<div class="disInlineBlock" style="color: #0052d9;">
<span class="disInlineBlock" style="margin: 0 20px;">{{formModel.ip1Province}}</span>
<span>{{formModel.ip1Isp}}</span>
</div>
</template>
<!-- 不同label宽度 -->
<template #dynamicLabelWidth="{field, model, formModel}">
<div class="disInlineBlock" style="padding-right: 12px;color: #C0C4CC;font-weight: 700;" :style="field.inlineSty">{{field.label}}</div>
{{model}}
</template>
<!-- 最新版本号 -->
<template #tempMaxVersion="{ field, model, formModel }">
{{model}}
<div v-if="formModel['maxAgentVersion']" class="disInlineBlock ml20">
最新版本({{formModel.maxAgentVersion}})
<el-button class="ml20" style="background-color: #169bd5;border-color: #169bd5;" type="primary" size="mini" @click="upMaxVersion(formModel)">升级到最新</el-button>
</div>
</template>
</Form>
</div>
<!-- tab___echarts/form/table -->
<div class="mt20">
<el-tabs v-model="activeName" type="border-card" @tab-click="handleClick">
<el-tab-pane v-for="item of tabList" :label="item.name" :name="item.name">
<!-- 网卡流量控制系统监控挂载文件系统监控硬盘设备监控 -->
<div v-if="activeName === '网卡流量监控' || activeName === '系统监控' || activeName === '挂载文件系统监控' || activeName === '硬盘设备监控'">
<serRegisTabsChart :activeName="activeName" :chartModelList="[]"></serRegisTabsChart>
</div>
<!-- 网卡信息FRP信息收益查看虚拟网卡信息 -->
<div v-if="activeName === '网卡信息' || activeName === 'FRP信息' || activeName === '收益查看' || activeName === '虚拟网卡信息'">
<serRegisFormOrTable :activeName="activeName" :modelVal="modelVal"></serRegisFormOrTable>
</div>
<!-- PPPoE配置 -->
<div v-if="activeName === 'PPPoE配置'">
<PPPoEConfig :modelVal="modelVal"></PPPoEConfig>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import Form from '@/components/form/index.vue';
import cpuUseImg from '@/assets/images/cpuUse.svg';
import bandWImg from '@/assets/images/bandwidth.svg';
import cpuImg from '@/assets/images/cpu.svg';
import memoryImg from '@/assets/images/memory.svg';
import {getHandle} from "@/api/disRevenue/resource";
import serRegisFormOrTable from './formOrTable';
import serRegisTabsChart from './tabsChart';
import PPPoEConfig from "./PPPoEConfig";
export default {
name: "server_register_details",
components: {PPPoEConfig, Form, serRegisFormOrTable, serRegisTabsChart},
dicts: ['rm_register_status','rm_register_online_state'],
data() {
return {
ruleForm: {},
formList: [],
modelVal: [],
paramsData: {},
headerList: [
{name: 'CPU使用率(%)', num: '30', imgUrl: cpuUseImg},
{name: '带宽', num: '10000Mbps', imgUrl: bandWImg},
{name: 'CPU', num: '32核', imgUrl: cpuImg},
{name: '内存', num: '32G', imgUrl: memoryImg},
],
activeName: '网卡流量监控',
tabList: [
{name: '网卡流量监控'}, {name: '系统监控'}, {name: '挂载文件系统监控'}, {name: '硬盘设备监控'}, {name: '网卡信息'},
{name: '虚拟网卡信息'}, {name: '收益查看'}, {name: 'FRP信息'}, {name: 'PPPoE配置'}
],
echartList: {}
}
},
created() {
this.paramsData = this.$route && this.$route.query;
if (this.paramsData && this.paramsData.id) {
this.getFormDataList(this.paramsData.id);
}
if (this.paramsData && this.paramsData.target) {
this.activeName = this.paramsData.target;
}
this.fnFormList();
},
methods: {
fnFormList(objVal) {
this.formList = [{
config: {title: '',readonly: true},
controls: {
id: {label: 'ID',hidden: true},
clientId: {label: 'SN', span: 24, slotName: 'tempClient'},
// registrationStatus: {label: '', span: 3, type: 'select', options: this.dict.type.rm_register_status},
// onlineStatus: {label: '', span: 3, type: 'select', options: this.dict.type.rm_register_online_state},
// remark: {label: '', span: 14, type: 'dynamicTags'},
ip1PublicIp: {label: 'IP', span: 24, slotName: 'tempIp1public'},
}
}, {
config: {title: '标识信息',readonly: true},
controls: {
id: {label: 'ID',hidden: true},
businessName: {label: '业务名称', span: 8, type: 'input'},
logicalNodeId: {label: '逻辑节点标识', span: 4, inlineSty: 'margin-left: -18px;', labelWidthNull: true, slotName: 'dynamicLabelWidth'},
hardwareSn: {label: '设备SN', span: 12, type: 'input'},
machineCode: {label: '金山machineCode', span: 24, labelWidthNull: true, slotName: 'dynamicLabelWidth'},
}
},{
config: {title: '时间信息',readonly: true},
controls: {
id: {label: 'ID',hidden: true},
createTime: {label: '注册时间', span: 8, type: 'input'},
onboardTime: {label: '上机时间', span: 8, type: 'input'},
}
},{
config: {title: '其他',readonly: true},
controls: {
id: {label: 'ID',hidden: true},
agentVersion: {label: 'agent版本', span: 8, slotName: 'tempMaxVersion'},
heartbeatInterval: {label: '心跳间隔(s)', span: 6, type: 'input'},
heartbeatCount: {label: '心跳次数', span: 6, type: 'input'},
}
}];
},
// 获取详情
getFormDataList(id) {
let params = id;
getHandle(params).then(val => {
if (val && val.data) {
val.data['remark'] = val.data && val.data['remark'] ? val.data['remark'].split(',') : null;
this.ruleForm = val && val.data;
}
}).catch(() => {
this.$modal.msgError("操作失败")
});
},
// 更新到最新版本
upMaxVersion(model) {
let content = '<p style="font-size: 1rem;font-weight: 600;">升级警告</p>' +
'<p style="height: 0px;margin-bottom: 50px;">未找到最新版本xxx的下载地址,请到【AGENT更新】中添加一条版本xxx的更新策略,才可自动进行升级更新';
this.$modal.confirm(content, {submitTitle: '去添加'}).then(() => {
this.$router.push("/resource/agentUpdate");
}).catch(() => {});
},
// tab栏点击事件
handleClick(tab, event) {
if (this.activeName === '虚拟网卡信息') {
this.modelVal = [
{title: 'eth0.24', formModel: {interfaceName: '112'}},
{title: 'eth0.25', formModel: {interfaceName: '334'}},
];
} else if (this.activeName === '网卡信息') {
this.modelVal = [this.ruleForm];
}
},
// 监听事件
callback(result, dataVal, formVal) {
if (result && result.fnCode) {
switch (result.fnCode) {
case 'cancel':
// if (this.paramsData && this.paramsData.type === 'alarmLog') {
// // this.$router.push("/resource/alarmLog");
// history.go(-1);
// } else {
// this.$router.push("/resource/serverRegister");
// }
break;
default:
}
}
},
}
}
</script>
<style scoped>
.headerBox {
display: inline-block;
width: 25%;
height: 120px;
padding: 0 10px;
}
.boxChild {
border: 1px solid #ddd;
padding: 15px;
}
</style>
@@ -0,0 +1,185 @@
<template>
<div class="w100 h100">
<TableList v-if="activeName === '收益查看'" class="w100 h100" :columns="columns" :config="config" :queryParams="queryParams" :tableList="modelVal" @fnClick="callback">
<template #tempDay="{ valueKey, row, column }">
<span v-if="row && row[valueKey] || row[valueKey] === 0">{{row[valueKey]}}/{{row.createTime}}</span>
</template>
<template #tempMonth="{ valueKey, row, column }">
<span v-if="row && row[valueKey] || row[valueKey] === 0">{{row[valueKey]}}/{{row.createTime.slice(0, 7)}}</span>
</template>
</TableList>
<div v-else-if="activeName === '虚拟网卡信息'">
<el-collapse v-model="activeShowList">
<template v-for="(val, index) of modelVal">
<el-collapse-item :title="`【${val && val.title || ''}】${val && val.serverIp || ''}`" :name="index">
<div class="mt10 w100">
<div class="w100 plr-20" style="font-size: 14px">
<div v-for="(item,key,index) of collFormList || []" :key="`${key}-${index}`" class="disInlineBlock p10" style="width: calc(100% / 3);">
<div class="disInlineBlock" style="width: 120px;color: #C0C4CC;">{{item}}</div>
<div style="width: calc(100% - 120px);vertical-align: top;" class="disInlineBlock">{{val && val.formModel[key] || '-'}}</div>
</div>
</div>
</div>
</el-collapse-item>
</template>
</el-collapse>
</div>
<Form v-else :formList="formList" :ruleFormData="modelVal[0]" :config=" {labelWidth: '140px', buttonGroup: []}" @fnClick="callback"></Form>
</div>
</template>
<script>
import Form from '@/components/form/index.vue';
import TableList from '@/components/table/index.vue';
export default {
name: "serRegisFormOrTable",
components: {Form, TableList},
dicts: ['frpc_connection_status','frpc_status'],
props: {
activeName: {
type: String,
default: ''
},
modelVal: {
type: Array,
default: () => []
}
},
watch: {
activeName: {
handler(val) {
if (val && (val !== '收益查看' && val !== '虚拟网卡信息')) {
this.formListData(val);
}
},
deep: true,
immediate: true
},
},
data() {
return {
activeShowList: [0,1,2,3,4,5,6,7,8,9],
formList: [],
// 列显隐信息
columns: {
id: {label: `ID`, width: '50'},
clientId: {label: `ClientID`, minWidth: '280', visible: true},
businessName: {label: `业务名称`, minWidth: '150', visible: true},
bandwidth95Daily: {label: `95带宽值/日(Mbit)`, minWidth: '150', slotName: 'tempDay',visible: true},
bandwidth95Monthly: {label: `95带宽值/月(Mbit)`, minWidth: '150', slotName: 'tempMonth',visible: true},
avgMonthlyBandwidth95: {label: `月均日95值(Mbit)`, minWidth: '150',slotName: 'tempDay',visible: true},
},
config: {
colHiddenCheck: true, colTopHiddenIcon: true,
tableButton: {
line: [
{content: '相关数据', fnCode: 'edit', type: 'text', icon: 'el-icon-tickets', hasPermi: 'earnManage:server:relevantData'},
]
}
},
queryParams: {
pageNum: 1,
pageSize: 8,
total: 0,
},
collFormList: {
interfaceName: '接口名称', interfaceType: '接口类型', macAddress: 'MAC地址',publicIp: '公网地址',
isp: '运营商', province: '省', city: '市', ipv4Address: 'IPv4地址', ipv4Gateway: 'IPv4网关', ipv6Address: 'IPv6全球单播地址'
},
}
},
created() {
},
methods: {
// 网卡信息
formListData(val){
if (val === '网卡信息') {
this.formList = [{
config: {title: '', readonly: true},
controls: {
id: {label: 'ID',hidden: true},
ip1Isp: {label: 'IP1-运营商', span: 8, type: 'input'},
ip1Province: {label: 'IP1-省', span: 8, type: 'input'},
ip1City: {label: 'IP1-市', span: 8, type: 'input'},
ip1PublicIp: {label: 'IP1-业务公网', span: 8, type: 'input'},
ip1InterfaceName: {label: 'IP1-接口名称', span: 8, type: 'input'},
ip1MacAddress: {label: 'IP1-mac地址', span: 8, type: 'input'},
ip1InterfaceType: {label: 'IP1-接口类型', span: 8, type: 'input'},
ip1Ipv4Address: {label: 'IP1-IPv4地址', span: 8, type: 'input'},
ip1Gateway: {label: 'IP1-网关', span: 8, type: 'input'},
ip2Isp: {label: 'IP2-运营商', span: 8, type: 'input'},
ip2Province: {label: 'IP2-省', span: 8, type: 'input'},
ip2City: {label: 'IP2-市', span: 8, type: 'input'},
ip2PublicIp: {label: 'IP2-业务公网', span: 8, type: 'input'},
ip2InterfaceName: {label: 'IP2-接口名称', span: 8, type: 'input'},
ip2MacAddress: {label: 'IP2-mac地址', span: 8, type: 'input'},
ip2InterfaceType: {label: 'IP2-接口类型', span: 8, type: 'input'},
ip2Ipv4Address: {label: 'IP2-IPv4地址', span: 8, type: 'input'},
ip2Gateway: {label: 'IP2-网关', span: 8, type: 'input'},
ip3Isp: {label: 'IP3-运营商', span: 8, type: 'input'},
ip3Province: {label: 'IP3-省', span: 8, type: 'input'},
ip3City: {label: 'IP3-市', span: 8, type: 'input'},
ip3PublicIp: {label: 'IP3-业务公网', span: 8, type: 'input'},
ip3InterfaceName: {label: 'IP3-接口名称', span: 8, type: 'input'},
ip3MacAddress: {label: 'IP3-mac地址', span: 8, type: 'input'},
ip3InterfaceType: {label: 'IP3-接口类型', span: 8, type: 'input'},
ip3Ipv4Address: {label: 'IP3-IPv4地址', span: 8, type: 'input'},
ip3Gateway: {label: 'IP3-网关', span: 8, type: 'input'},
mgmtIsp: {label: '管理网-运营商', span: 8, type: 'input'},
mgmtProvince: {label: '管理网-省', span: 8, type: 'input'},
mgmtCity: {label: '管理网-市', span: 8, type: 'input'},
mgmtPublicIp: {label: '管理网-业务公网', span: 8, type: 'input'},
mgmtInterfaceName: {label: '管理网-接口名称', span: 8, type: 'input'},
mgmtMacAddress: {label: '管理网-mac地址', span: 8, type: 'input'},
mgmtInterfaceType: {label: '管理网-接口类型', span: 8, type: 'input'},
mgmtIpv4Address: {label: '管理网-IPv4地址', span: 8, type: 'input'},
mgmtGateway: {label: '管理网-网关', span: 8, type: 'input'},
}
}];
} else if (val === 'FRP信息') {
this.formList = [{
config: {title: '', readonly: true},
controls: {
id: {label: 'ID',hidden: true},
frpcConnectionStatus: { label: `FRP连接地址生成状态`,span: 8, type: 'select', options: this.dict.type.frpc_connection_status},
frpcCreateTime: { label: `链接生成时间`,span: 8, type: 'datetime'},
frpcStatus: { label: `FRPC服务状态`,span: 8, options: this.dict.type.frpc_status},
frpcServerAddr: { label: `FRPC_serverAddr`, span: 8, type: 'input'},
frpcServerPort: { label: `FRPC_serverPort`,span: 8, type: 'input'},
frpcName: { label: `FRPC_name`,span: 8, type: 'input'},
frpcType: { label: `FRPC_tcp`,span: 8, type: 'input'},
frpcLocalIp: { label: `FRPC_localIP`,span: 8, type: 'input'},
frpcLocalPort: { label: `FRPC_localPort`,span: 8, type: 'input'},
}
}];
}
},
callback(result, rowData, selectChange, selectList) {
if (result && result.fnCode) {
switch (result.fnCode) {
// case 'calculate':
// let fnType = recalBandWidth;
// if (rowData && rowData.bandwidthType === '4') {
// fnType = avgDetailMsg;
// }
// fnType(rowData.id).then(res => {
// this.getList();
// this.$modal.msgSuccess("操作成功!");
// }).catch(() => {});
// break;
default:
}
}
},
}
}
</script>
<style scoped>
::v-deep .el-collapse-item__header {
background-color: #d4e3fc!important;
/*color: #fff!important;*/
padding-left: 20px;
font-size: 1rem;
}
</style>
+8 -1
View File
@@ -311,6 +311,7 @@
{content: '取消闪烁', fnCode: 'cancelFlash', type: 'text', showName: 'alarmFlag', showVal: 'true', icon: 'el-icon-magic-stick', hasPermi: 'resource:serverRegister:cancelFlash'}, {content: '取消闪烁', fnCode: 'cancelFlash', type: 'text', showName: 'alarmFlag', showVal: 'true', icon: 'el-icon-magic-stick', hasPermi: 'resource:serverRegister:cancelFlash'},
{content: '生成FRP连接地址', fnCode: 'createFrpAddr', type: 'text', showName: 'frpcConnectionStatus', showVal: ['0', null], icon: 'el-icon-link', hasPermi: 'resource:serverRegister:createFrpAddr'}, {content: '生成FRP连接地址', fnCode: 'createFrpAddr', type: 'text', showName: 'frpcConnectionStatus', showVal: ['0', null], icon: 'el-icon-link', hasPermi: 'resource:serverRegister:createFrpAddr'},
{content: '显示FRP连接地址', fnCode: 'showFrpAddr', type: 'text', showName: 'frpcConnectionStatus', showVal: ['1','2'], icon: 'el-icon-link', hasPermi: 'resource:serverRegister:showFrpAddr'}, {content: '显示FRP连接地址', fnCode: 'showFrpAddr', type: 'text', showName: 'frpcConnectionStatus', showVal: ['1','2'], icon: 'el-icon-link', hasPermi: 'resource:serverRegister:showFrpAddr'},
{content: 'PPPoE配置', fnCode: 'poeConfig', type: 'text', icon: 'el-icon-data-analysis', hasPermi: 'resource:serverRegister:poeConfig'},
] ]
} }
// {content: '注册', fnCode: 'enroll', showName: 'registrationStatus', showVal: '0', type: 'text', icon: 'el-icon-circle-check', hasPermi: 'resource:register:enroll'}, // {content: '注册', fnCode: 'enroll', showName: 'registrationStatus', showVal: '0', type: 'text', icon: 'el-icon-circle-check', hasPermi: 'resource:register:enroll'},
@@ -549,10 +550,16 @@
break; break;
case 'details': case 'details':
this.$router.push({ this.$router.push({
path:'/resource/serverRegister/edit', path:'/resource/serverRegister/details',
query: {id: rowData.id, readonly: true} query: {id: rowData.id, readonly: true}
}); });
break; break;
case 'poeConfig':
this.$router.push({
path:'/resource/serverRegister/details',
query: {id: rowData.id, readonly: true, target: 'PPPoE配置'}
});
break;
case 'echartView': case 'echartView':
this.$router.push({ this.$router.push({
path:'/resource/serverRegister/monitorChart', path:'/resource/serverRegister/monitorChart',
@@ -0,0 +1,159 @@
<template>
<div class="w100 h100">
<div class="w100" style="height: 40px;">
<el-date-picker
v-model="timeList"
:style="{width: '360px!important', marginRight: '10px'}"
type="datetimerange"
:disabled="!timeShow"
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)"/>
<el-checkbox v-model="timeShow">选择时间</el-checkbox>
<!-- 不同条时间件切换 -->
<el-radio-group v-model="timeModel" size="medium" class="ml20" :disabled="timeShow" @input="radioTimeChange">
<el-radio-button v-for="item of radioTimeList" :label="item.value">{{item.name}}</el-radio-button>
</el-radio-group>
<!-- 单双列切换 -->
<el-radio-group v-model="layoutModel" size="medium" class="ml20" style="float: right;" @input="radioLayouteChange">
<el-radio-button v-for="item of radioLayoutList" :label="item.value">{{item.name}}</el-radio-button>
</el-radio-group>
</div>
<template v-if="Object.keys(echartList).length > 0">
<div v-for="(item, key, index) of echartList" class="disInlineBlock mt20 mb20 plr-10" :style="`width: calc(100% / ${layoutModel}); height:200px;`">
<EchartsLine v-if="item && item.dataVal" :key="`chart-${key || ''}-${index}`" :lineData="item && item.dataVal || {}"
:title="item && item.title || '图表数据'" class="w100 h100" style="border: 1.5px solid #cfcfcf;"
:chartData="(valData, unit) => chartDataEvent(valData, item.fnEvent,val.title, unit)"></EchartsLine>
</div>
</template>
<div v-else class="w100 h100 flex-center disInlineBlock mt20 mb20 plr-10" :style="`width: calc(100% / ${layoutModel}); height:200px;background: #f5f7fa;`">
<div class="loading-placeholder">
<i class="el-icon-loading"></i>
<span>加载中...</span>
</div>
</div>
</div>
</template>
<script>
import EchartsLine from "@/components/echartsList/line.vue";
export default {
name: "serRegisTabsChart",
components: {EchartsLine},
props: {
activeName: {
type: String,
default: ''
},
chartModelList: {
type: Array,
default: () => []
}
},
watch: {
activeName: {
handler(val) {
this.$nextTick(() => {
let chartData = 1;
if (val && (val === '网卡流量监控' || val === '硬盘设备监控')) {
chartData = 2;
}
this.echartList = {};
this.chartListData(chartData);
});
},
deep: true,
immediate: true
},
},
data() {
return {
timeList: [],
timeShow: false,
radioTimeList: [{name: '近6小时', value: '6'}, {name: '近24小时', value: '24'}, {name: '昨天', value: '-1'}, {name: '今天', value: '1'}],
timeModel: '6',
radioLayoutList: [{name: '单列显示', value: '1'}, {name: '双列显示', value: '2'}],
layoutModel: '1',
echartList: {},
echartData: {
title: ' ',
dataVal: {
// titleVal: {textAlign: 'center'},
yAxisName: ' ',
gridTop: '30%',
legend: {top: '12%', left: '10%'},
hiddenTime: true,
lineXData: ['a','b','c','d','e'],
// unitSelList: [{label: 'Kb', value: 'Kb'},{label: 'Mb', value: 'Mb'}, {label: 'Gb', value: 'Gb'}],
dataList: [{
name: '出战流量',
data: [1,2,3,4,5],
},{
name: '入站流量',
data: [6,7,8,9,10]
}]
}
},
}
},
created() {
},
methods: {
// 时间变化时
dateChange(val){
// this.currTimeList = {startTime: val[0], endTime: val[1]};
// this.getNetTraffic(this.paramsData.clientIdList);
},
// 时间按钮切换
radioTimeChange(val) {
this.timeModel = val;
},
// 单双列按钮切换
radioLayouteChange(val) {
this.layoutModel = val;
this.$nextTick(() => {
const resizeEvent = new Event('resize');
// 派发这个事件,这会触发所有已绑定的 window resize 监听器
window.dispatchEvent(resizeEvent);
});
},
chartListData(charNum) {
setTimeout(() => {
let echartsModel = JSON.parse(JSON.stringify(this.echartData));
if (charNum === 1) {
delete echartsModel['dataVal'].dataList;
echartsModel['dataVal'].data = [1,3,5,7,9];
}
this.echartList = {one: echartsModel, tow: echartsModel};
},200);
},
chartDataEvent(valData, funcName, tabName, unit) {
this.$emit("chartFnEvent", valData, funcName, tabName, unit);
// // 检查函数是否存在,避免报错
// if (typeof this[funcName] === 'function') {
// // 调用实际函数,并传递参数(如选中的值、当前项)
// // this[funcName]({startTime: valData[0], endTime: valData[1]});
// } else {
// console.warn(`函数 ${funcName} 未定义`);
// }
}
}
}
</script>
<style scoped>
::v-deep .el-radio-button__orig-radio:checked + .el-radio-button__inner {
color: #1890ff!important;
background-color: unset!important;
}
.loading-placeholder {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-size: 1.1rem;
}
</style>