From be664d7c011a473002c1b413bac8303f7905d160 Mon Sep 17 00:00:00 2001 From: dongyukun <1208714201@qq.com> Date: 星期四, 29 五月 2025 14:26:36 +0800 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- src/views/infra/monitor/components/MonitorDisk.vue | 410 +++++++++++++++++++++++++++++++++++----------------------- 1 files changed, 246 insertions(+), 164 deletions(-) diff --git a/src/views/infra/monitor/components/MonitorDisk.vue b/src/views/infra/monitor/components/MonitorDisk.vue index ffec629..3a0c954 100644 --- a/src/views/infra/monitor/components/MonitorDisk.vue +++ b/src/views/infra/monitor/components/MonitorDisk.vue @@ -8,41 +8,25 @@ :inline="true" label-width="68px" > - <!-- <el-form-item label="主机名称" prop="hostName">--> - <!-- <el-input--> - <!-- v-model="queryParams.hostName"--> - <!-- placeholder="请输入主机名称"--> - <!-- clearable--> - <!-- @keyup.enter="handleQuery"--> - <!-- class="!w-240px"--> - <!-- />--> - <!-- </el-form-item>--> - <el-form-item label="服务器IP" prop="hostIp"> - <el-input - v-model="queryParams.hostIp" - placeholder="请输入服务器IP" - clearable - @keyup.enter="handleQuery" - class="!w-120px" - /> + <el-form-item label="主机名称" prop="hostName"> + <el-select v-model="queryParams.hostName" clearable placeholder="请选择" class="!w-180px" @change="getDataList"> + <el-option + v-for="(host, index) in hosts" + :key="index" + :label="host" + :value="host" + /> + </el-select> </el-form-item> - <!-- <el-form-item label="盘符" prop="disk">--> - <!-- <el-input--> - <!-- v-model="queryParams.disk"--> - <!-- placeholder="请输入盘符"--> - <!-- clearable--> - <!-- @keyup.enter="handleQuery"--> - <!-- class="!w-240px"--> - <!-- />--> - <!-- </el-form-item>--> - <el-form-item label="磁盘名" prop="diskName"> - <el-input - v-model="queryParams.diskName" - placeholder="请输入磁盘名" - clearable - @keyup.enter="handleQuery" - class="!w-120px" - /> + <el-form-item label="服务器IP" prop="hostIp"> + <el-select v-model="queryParams.hostIp" clearable placeholder="请选择" class="!w-160px" @change="getDataList"> + <el-option + v-for="(ip, index) in ips" + :key="index" + :label="ip" + :value="ip" + /> + </el-select> </el-form-item> <el-form-item label="创建时间" prop="createTime"> <el-date-picker @@ -109,9 +93,8 @@ <ContentWrap v-if="showType == 'chart'"> <!-- 磁盘使用率折线图 --> - <el-skeleton :loading="echartsLoading" animated> - <Echart :height="320" :options="diskChartOptions"/> - </el-skeleton> + <div id="chartArea"></div> + <!-- 磁盘使用率饼图 --> <h3 style="margin-top: 20px; margin-bottom: 10px">主机磁盘使用率</h3> <div v-for="host in hostList" :key="host.name" class="host"> @@ -131,19 +114,6 @@ </el-skeleton> </div> </div> -<!-- <div v-for="(host, hostIndex) in hostList" :key="hostIndex">--> -<!-- <div style="margin-top: 10px">--> -<!-- <el-skeleton :loading="echartsLoading" animated>--> -<!-- {{ hostIndex }} 主机名: {{ host.name }} --> -<!-- 服务器IP:{{ host.ip }}--> -<!-- <div v-for="(disk, diskIndex) in host.disks" :key="diskIndex">--> -<!-- <h3>{{ disk.name }}</h3>--> -<!-- <div :ref="el => chartRefs[hostIndex][diskIndex] = el"--> -<!-- :style="{ width: '300px', height: '300px' }"></div>--> -<!-- </div>--> -<!-- </el-skeleton>--> -<!-- </div>--> -<!-- </div>--> </ContentWrap> <!-- 列表 --> @@ -217,6 +187,8 @@ const loading = ref(true) // 列表的加载中 const list = ref<MonitorDiskVO[]>([]) // 列表的数据 +const hosts = ref<String[]>([]) // 主机列表 +const ips = ref<String[]>([]) // ip列表 const total = ref(0) // 列表的总页数 const queryParams = reactive({ pageNo: 1, @@ -236,26 +208,197 @@ const echartsLoading = ref(true) // 图表加载中 const showType = ref() //展示类型(chart-图例,data-数据) -const hostList = ref([ - { - name: 'Thinkpad-E14', - ip: '172.16.216.133', - disks: [ - {disk: '磁盘C', used: 70, total: 200}, - {disk: '磁盘D', used: 40, total: 60} - ] - }, - { - name: 'Thinkpad-E16', - ip: '172.16.216.133', - disks: [ - {disk: '磁盘C', used: 80, total: 500}, - {disk: '磁盘D', used: 20, total: 500} - ] - } -]); +const hostList = ref([]); -const chartRefs = ref([]); + +// 颜色集合(10个区分度较好的颜色) +const colors = [ + '#5470C6', '#91CC75', '#FAC858', '#EE6666', '#73C0DE', + '#3BA272', '#FC8452', '#9A60B4', '#EA7CCC', '#19DCDC' +] +const chartRefs = ref<HTMLElement[]>([]) +// 图表实例和容器管理 +const chartInstances = ref<{ instance: echarts.ECharts; container: HTMLDivElement }[]>([]) + +// 清理所有图表资源 +const cleanCharts = () => { + // 1. 销毁所有图表实例 + chartInstances.value.forEach(({ instance }) => { + instance.dispose() + }) + + // 2. 移除所有容器元素 + chartInstances.value.forEach(({ container }) => { + container.remove() + }) + + // 3. 清空实例记录 + chartInstances.value = [] +} + + +// 处理接口数据 +const processServerData = (apiData: any[]) => { + return apiData.flatMap(serverObj => { + return Object.entries(serverObj).map(([serverName, dataPoints]) => ({ + serverName, + data: (dataPoints as any[]).map(item => ({ + createTime: item.createTime, + ...Object.fromEntries( + Object.entries(item) + .filter(([key]) => key !== 'createTime') + .map(([key, value]) => [key.replace(/[()/]/g, '_'), value]) // 清理特殊字符 + ) + })).sort((a, b) => a.createTime - b.createTime) // 按时间排序 + })) + }) +} + +// 初始化图表 +const initCharts = async (apiData: any[]) => { + cleanCharts() + await nextTick() + + const servers = processServerData(apiData) + const chartArea = document.querySelector('#chartArea') + + servers.forEach((server, index) => { + // 创建容器 + const container = document.createElement('div') + container.style.width = '100%' + container.style.height = '300px' + container.classList.add('chart-container') + chartArea?.appendChild(container) + + // 初始化图表实例 + const chart = echarts.init(container) + const dimensions = Object.keys(server.data[0]).filter(k => k !== 'createTime') + + // 图表配置 + const option: echarts.EChartsOption = { + title: { + text: `${server.serverName} 磁盘使用率趋势`, + left: 'center', + textStyle: { + fontSize: 16, + fontWeight: 'bold' + } + }, + tooltip: { + trigger: 'axis', + valueFormatter: (value) => `${value}%`, + axisPointer: { + type: 'cross', + label: { + backgroundColor: '#6a7985' + } + } + }, + legend: { + type: 'scroll', + top: 30, + pageIconColor: '#2c3e50', + pageTextStyle: { + color: '#666' + } + }, + grid: { + top: 80, + left: 50, + right: 30, + bottom: 30, + containLabel: true + }, + xAxis: { + type: 'time', + axisLabel: { + formatter: (value: number) => { + const date = new Date(value) + return `${date.getMonth() + 1}/${date.getDate()}` + }, + interval: 0, + length: 6, + rotate: 45, + fontSize: 12 + }, + min: (value) => value.min - 86400000, + max: (value) => value.max + 86400000 + }, + yAxis: { + type: 'value', + min: 0, + max: 100, + axisLabel: { + formatter: '{value}%', + fontSize: 12 + }, + splitLine: { + show: true, + lineStyle: { + type: 'dashed' + } + } + }, + toolbox: { + feature: { + dataZoom: { + type: 'inside', + filterMode: 'none', // 禁用数据过滤 + yAxisIndex: false, + title: { zoom: '缩放', back: '还原' } + }, + brush: { + type: ['lineX', 'clear'], + title: { lineX: '选择', clear: '清除' } + }, + saveAsImage: { + name: '磁盘使用率图表', + title: '保存', + pixelRatio: 2 + } + } + }, + color: colors, + series: dimensions.map((dim, dimIndex) => ({ + name: dim.replace(/_/g, ' '), // 还原清理的特殊字符 + type: 'line', + // 关键配置项 + progressive: 0, // 禁用分片渲染 + large: false, // 禁用大数据模式 + showAllSymbol: true, // 显示所有数据点 + sampling: 'none', // 禁用采样 + // 数据维度声明 + dimensions: ['createTime', 'value'], + smooth: true, + symbol: 'none', + areaStyle: { + opacity: 0.3, + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: colors[dimIndex % colors.length] }, + { offset: 1, color: 'rgba(255,255,255,0)' } + ]) + }, + lineStyle: { + width: 2, + color: colors[dimIndex % colors.length] + }, + data: server.data.map(d => [d.createTime, d[dim]]) + })), + dataZoom: [{ + type: 'inside', + filterMode: 'none' // 禁用数据过滤 + // type: 'inside', + // start: 0, + // end: 100, + // minValueSpan: 86400000 * 1 // 最小缩放范围为1天 + }] + } + + chart.setOption(option) + chartInstances.value.push({ instance: chart, container }) + }) +} + /** 查询列表 */ const getList = async () => { @@ -268,6 +411,19 @@ loading.value = false } } + +/** 查询所有主机名 */ +const getAllHost = async () => { + const data = await MonitorDiskApi.getAllHost() + hosts.value = data +} + +/** 查询所有ip */ +const getAllIp = async () => { + const data = await MonitorDiskApi.getAllIp() + ips.value = data +} + /** 搜索按钮操作 */ const handleQuery = () => { @@ -321,98 +477,26 @@ } } -/** 堆叠面积图配置 */ -const diskChartOptions = reactive<EChartsOption>({ - title: { - text: '磁盘空间折线图' - }, - dataset: { - dimensions: [], - source: [] - }, - grid: { - left: 30, - right: 20, - bottom: 10, - top: 70, - containLabel: true - }, - legend: { - top: 0 - }, - series: [ - { - name: 'disk', type: 'line', - emphasis: { - focus: 'series' - }, smooth: false - } - ], - toolbox: { - feature: { - // 数据区域缩放 - dataZoom: { - yAxisIndex: false // Y轴不缩放 - }, - brush: { - type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮 - }, - saveAsImage: {show: true, name: '物理内存日志图片'} // 保存为图片 - } - }, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'cross', - label: { - backgroundColor: '#6a7985' - } - }, - padding: [5, 10] - }, - xAxis: { - type: 'category', - boundaryGap: false, - axisTick: { - show: false - } - }, - yAxis: { - name: "单位(百分比)", - nameTextStyle: { - color: "#aaa", - nameLocation: "start", - }, - }, -}) as EChartsOption - /** 查询统计数据列表 */ const getMonitorDiskDataList = async () => { const list = await MonitorDiskApi.getMonitorDiskList(queryParams) - if (list != null && list != undefined && list.length > 0) { - diskChartOptions.dataset['dimensions'] = Object.keys(list[0]) - diskChartOptions.series = diskChartOptions.dataset['dimensions'].map(item => ({ - name: item.name, - type: 'line', - emphasis: { - focus: 'series' - }, - smooth: false - })); - diskChartOptions.series.splice(0, 1) - for (let item of list) { - item.createTime = formatTime(item.createTime, 'yyyy-MM-dd HH:mm:ss') - } - } - // 更新 Echarts 数据 - diskChartOptions.dataset['source'] = list echartsLoading.value = false + await initCharts(list) } const usedDiskInstance = async () => { const list = await MonitorDiskApi.getMonitorDiskInfo(queryParams) hostList.value = list - // 仪表盘详情,用于显示数据。 +} + +/** 封装接口调用 */ +const getDataList = async () => { + if(showType.value == 'data') { + await getList() + } else { + await getMonitorDiskDataList() + await usedDiskInstance() + } } /** 切换展示方式 */ @@ -430,19 +514,16 @@ /** 初始化 **/ onMounted(() => { showType.value = 'data'; - const currentDate = new Date(); - const previousDate = new Date(currentDate); - previousDate.setDate(currentDate.getDate() - 1); - queryParams.createTime[0] = formatDate(previousDate, 'YYYY-MM-DD HH:mm:ss'); - queryParams.createTime[1] = formatDate(currentDate, 'YYYY-MM-DD HH:mm:ss'); + getAllHost(); + getAllIp(); + getDataList() intervalId = setInterval(() => { - if (showType.value == 'data') { - getList() - } else { - getMonitorDiskDataList() - usedDiskInstance() - } - }, 30000); + getDataList() + }, 300000); +}) + +onBeforeUnmount(() => { + cleanCharts() }) onUnmounted(() => { @@ -484,4 +565,5 @@ font-size: 0.9em; color: #666; } + </style> -- Gitblit v1.9.3