diff --git a/src/components/layout/Breadcrumb.vue b/src/components/layout/Breadcrumb.vue index 639ffa3..2e653c0 100644 --- a/src/components/layout/Breadcrumb.vue +++ b/src/components/layout/Breadcrumb.vue @@ -37,13 +37,18 @@ const iconMap = { 'tags': 'List', 'statistics': 'PieChart', 'visits': 'DataAnalysis', - 'performance': 'DataAnalysis' + 'performance': 'DataAnalysis', + 'servers': 'Setting', + 'server': 'Setting', + 'vm': 'Setting', + 'container': 'Setting' } // 生成面包屑数据 const breadcrumbs = computed(() => { // 当前路由的完整路径 const currentPath = route.path + const currentQuery = route.query // 按照路径层级生成面包屑 const pathSegments = currentPath.split('/').filter(segment => segment !== '') @@ -56,7 +61,90 @@ const breadcrumbs = computed(() => { icon: 'HomeFilled' }) - // 构建剩余的面包屑 + // 特殊处理服务器相关页面 + if (currentPath.includes('/servers/')) { + // 处理服务器详情页面 + if (currentPath === '/servers/server') { + // 构建完整的路径,包含查询参数,用于面包屑导航 + const fullPath = currentQuery.server_id ? + `${currentPath}?server_id=${currentQuery.server_id}&type=${currentQuery.type || ''}` : + currentPath + + result.push({ + path: fullPath, + title: '服务器详情', + icon: 'Setting' + }) + } else if (currentPath === '/servers/vm') { + const fullPath = currentQuery.server_id ? + `${currentPath}?server_id=${currentQuery.server_id}&type=${currentQuery.type || ''}` : + currentPath + + result.push({ + path: fullPath, + title: '虚拟机详情', + icon: 'Setting' + }) + } else if (currentPath === '/servers/container') { + const fullPath = currentQuery.server_id ? + `${currentPath}?server_id=${currentQuery.server_id}&type=${currentQuery.type || ''}` : + currentPath + + result.push({ + path: fullPath, + title: '容器详情', + icon: 'Setting' + }) + } else if (currentPath === '/servers/container/console') { + // 添加容器详情面包屑 + const containerPath = currentQuery.server_id ? + `/servers/container?server_id=${currentQuery.server_id}&type=${currentQuery.type || ''}` : + '/servers/container' + + result.push({ + path: containerPath, + title: '容器详情', + icon: 'Setting' + }) + + // 添加当前页面面包屑 + const consolePath = currentQuery.server_id ? + `${currentPath}?server_id=${currentQuery.server_id}&type=${currentQuery.type || ''}` : + currentPath + + result.push({ + path: consolePath, + title: '终端容器', + icon: 'Setting' + }) + } else if (currentPath === '/servers/container/files') { + // 添加容器详情面包屑 + const containerPath = currentQuery.server_id ? + `/servers/container?server_id=${currentQuery.server_id}&type=${currentQuery.type || ''}` : + '/servers/container' + + result.push({ + path: containerPath, + title: '容器详情', + icon: 'Setting' + }) + + // 添加当前页面面包屑 + const filesPath = currentQuery.server_id ? + `${currentPath}?server_id=${currentQuery.server_id}&type=${currentQuery.type || ''}` : + currentPath + + result.push({ + path: filesPath, + title: '容器文件管理', + icon: 'Setting' + }) + } + + return result + } + + // 构建剩余的面包屑(非服务器页面的常规处理) let currentPathBuilder = '' for (let i = 0; i < pathSegments.length; i++) { @@ -73,6 +161,7 @@ const breadcrumbs = computed(() => { title: matchedRoute.meta.title, icon: segmentIcon }) + } else if (segment) { // 如果没有匹配的路由,但有路径段,也添加到面包屑 result.push({ diff --git a/src/config/menus.js b/src/config/menus.js index 9ed7679..0085fb3 100644 --- a/src/config/menus.js +++ b/src/config/menus.js @@ -45,5 +45,20 @@ export const menus = [ // { path: '/system/operation-log', title: '操作日志' }, { path: '/system/domain-whitelist', title: '域名白名单' } ] + },{ + path: '/audit', + title: '站点审计', + icon: 'Monitor', + children: [ + { path: '/audit/all', title: '所有站点' }, + { path: '/audit/violation', title: '违规站点' } + ] + },{ + path:'/setting', + title:'全局设置管理', + icon:'Setting', + children:[ + {path:'/setting/global',title:'全局设置'} + ] } ] \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index f33fd74..5ff23a4 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -41,6 +41,7 @@ const routes = [ }, component: () => import('../views/ticket/TicketChat.vue'), }, + // ACS管理路由 { path: 'acs', @@ -139,22 +140,23 @@ const routes = [ title: '系统管理', icon: 'Setting' }, - redirect: '/system/users', + redirect: '/system/domain-whitelist', children: [ - { - path: 'users', - name: 'Users', - component: () => import('../views/system/Users.vue'), - meta: { - title: '用户管理' - } - }, - { - path: 'operation-log', - name: 'OperationLog', - component: OperationLog, - meta: { title: '操作日志' } - }, + // 注释掉的用户管理和操作日志路由,与菜单配置保持一致 + // { + // path: 'users', + // name: 'Users', + // component: () => import('../views/system/Users.vue'), + // meta: { + // title: '用户管理' + // } + // }, + // { + // path: 'operation-log', + // name: 'OperationLog', + // component: OperationLog, + // meta: { title: '操作日志' } + // }, { path: 'domain-whitelist', name: 'DomainWhitelist', @@ -163,6 +165,54 @@ const routes = [ } ] }, + // 站点审计路由 + { + path: 'audit', + name: 'Audit', + meta: { + title: '站点审计', + icon: 'Monitor' + }, + redirect: '/audit/all', + children: [ + { + path: 'all', + name: 'AuditAll', + component: () => import('../views/audit/AllSites.vue'), + meta: { + title: '所有站点' + } + }, + { + path: 'violation', + name: 'AuditViolation', + component: () => import('../views/audit/ViolationSites.vue'), + meta: { + title: '违规站点' + } + } + ] + }, + // 全局设置管理路由 + { + path: 'setting', + name: 'Setting', + meta: { + title: '全局设置管理', + icon: 'Setting' + }, + redirect: '/setting/global', + children: [ + { + path: 'global', + name: 'GlobalSetting', + component: () => import('../views/setting/GlobalSetting.vue'), + meta: { + title: '全局设置' + } + } + ] + }, // 个人中心路由 { path: 'profile', diff --git a/src/utils/acs/audit.js b/src/utils/acs/audit.js index dc84395..7c83abb 100644 --- a/src/utils/acs/audit.js +++ b/src/utils/acs/audit.js @@ -3,13 +3,13 @@ import {http2} from "@/utils/request.js"; /**获取所有站点 */ export const getSiteList = (data) => { - return http2.get(`/v1/admin/audit/list?page=${data.page}&count=${data.count}&key=${data.key}`) + return http2.get(`/v1/admin/audit/list?page=${data.page}&server_id=${data.server_id}&user_id=${data.user_id}&count=${data.count}&key=${data.key}`) } /**手动触发站点审计 */ export const auditSite = () => { return http2.get(`/v1/admin/audit/start`) } -/**删除违规网页审计 */ +/**删除违规网页审计 传入参数: web_key 站点名*/ export const delAudit = (data) => { return http2.post(`/v1/admin/audit/delete`,data,{ headers: { diff --git a/src/utils/acs/server.js b/src/utils/acs/server.js index faa4603..dfa217c 100644 --- a/src/utils/acs/server.js +++ b/src/utils/acs/server.js @@ -410,6 +410,10 @@ export const getRealDisk = data => { export const getTraffic = data => { return http2.get(`/v1/admin/server/get_server_bandwidth?server_id=${data}`); }; +/**获取服务器总流量信息 */ +export const getTotalTraffic = data => { + return http2.get(`/v1/admin/server/get_server_total_bandwidth?server_id=${data}`); +}; /**获取版本更新 */ export const getVersion = () => { return http2.get(`/v1/admin/version`); diff --git a/src/utils/request.js b/src/utils/request.js index d4bf37b..5634548 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -72,7 +72,7 @@ class Request { // break // } // } - return error.response.data + return error.response } ) } diff --git a/src/views/acs/images/ContainerImages.vue b/src/views/acs/images/ContainerImages.vue index 46f0ed9..91c995c 100644 --- a/src/views/acs/images/ContainerImages.vue +++ b/src/views/acs/images/ContainerImages.vue @@ -181,7 +181,7 @@ @@ -195,12 +195,18 @@ - + + + + + + +
@@ -391,7 +397,7 @@ import { ElMessage, ElMessageBox } from 'element-plus' import { getServer } from '@/utils/acs/server' import { getMirrorList, uploadMirror, editMirror, delMirror, - syncMirror, getUserMirrorList, pullMirror + syncMirror, getUserMirrorList, pullMirror, getImageTypeList } from '@/utils/acs/mirror' import { uploadFile, getFileList } from '@/utils/acs/message' // import { message } from '@/utils/acs/message' @@ -410,6 +416,7 @@ const resetSearch = () => { searchForm.name = '' handleSearch() } +const options = ref([]) // 表格数据 const loading = ref(false) @@ -710,6 +717,20 @@ const addnet = (data) => { const delProt = (index) => { prot_data.value.splice(index, 1) } +// 获取镜像分类列表 +const fetchCategoryList = async (serverId) => { + try { + const response = await getImageTypeList(serverId) + if (response.data.code === 200) { + options.value = response.data.data || [] + } else { + ElMessage.error('获取镜像分类失败:' + response.data.message) + } + } catch (error) { + console.error('获取镜像分类出错:', error) + ElMessage.error('获取镜像分类列表失败') + } +} // 编辑镜像 const handleEdit = async (data) => { @@ -722,6 +743,8 @@ const handleEdit = async (data) => { id: item.plan_id, } }) + await fetchCategoryList(data.server_id) + } else { planlist.value = [] } @@ -904,6 +927,7 @@ const uploadImage = async () => { return acc }, {}) form.env = JSON.stringify(env) + form.class_id = form.image_class_id if (editOr.value == true) { let res = await editMirror(form) diff --git a/src/views/acs/images/VmImages.vue b/src/views/acs/images/VmImages.vue index fa585f9..e6bb9b4 100644 --- a/src/views/acs/images/VmImages.vue +++ b/src/views/acs/images/VmImages.vue @@ -143,13 +143,13 @@ - + - + diff --git a/src/views/acs/nodes/VmDetail.vue b/src/views/acs/nodes/VmDetail.vue index d36b3ae..441984c 100644 --- a/src/views/acs/nodes/VmDetail.vue +++ b/src/views/acs/nodes/VmDetail.vue @@ -273,46 +273,18 @@
- + -
-
-
- - - -
-
-
-
- - - - -
-
-
- - - -
+
@@ -804,7 +776,8 @@ import { openInstance, pauseInstance, unpauseInstance, - deleteInstance + deleteInstance, + getInstanceStatus } from '@/utils/acs/server'; import { Mirrorinfo, @@ -867,14 +840,12 @@ const monitorDateRange = ref([ new Date(new Date().getTime() - 24 * 60 * 60 * 1000), // 默认24小时前 new Date() // 当前时间 ]); -const cpuChart = ref(null); -const memoryChart = ref(null); -const diskChart = ref(null); -const networkChart = ref(null); -let cpuChartInstance = null; -let memoryChartInstance = null; -let diskChartInstance = null; -let networkChartInstance = null; +const realTimeChart = ref(null); +let realTimeChartInstance = null; + +// 最新的监控数据 +const latestCpuUsage = ref(0); +const latestMemoryUsage = ref(0); // 日期选择器快捷选项 const dateRangeShortcuts = [ @@ -1156,6 +1127,13 @@ const fetchDataVolumesList = async () => { volumesLoading.value = false; } }; +const fetchInstanceStatus = async () => { + const res = await getInstanceStatus(route.query.instance_id); + console.log("获取虚拟机状态",res) + if (res && res.data && res.data.data.data.state === 200) { + vmInfo.value.state = res.data.data; + } +}; // 初始化数据 onMounted(() => { @@ -1173,6 +1151,7 @@ onMounted(() => { fetchNetworkRulesList(); fetchSnapshotsList(); fetchDataVolumesList(); + fetchInstanceStatus(); // 延迟初始化图表,确保DOM已经渲染 setTimeout(() => { @@ -1738,148 +1717,152 @@ const getStatusText = (state) => { // 初始化图表 const initCharts = () => { - if (!cpuChart.value || !memoryChart.value || !diskChart.value || !networkChart.value) return; + if (!realTimeChart.value) return; - // 初始化CPU图表 - cpuChartInstance = echarts.init(cpuChart.value); - const cpuOption = { - tooltip: { - formatter: '{a}
{b} : {c}%' - }, - series: [ - { - name: 'CPU', - type: 'gauge', - detail: { formatter: '{value}%' }, - data: [{ value: 0, name: '使用率' }], - axisLine: { - lineStyle: { - width: 30, - color: [ - [0.3, '#67C23A'], - [0.7, '#E6A23C'], - [1, '#F56C6C'] - ] - } - } + // 初始化实时监控图表 + realTimeChartInstance = echarts.init(realTimeChart.value); + const realTimeOption = { + title: { + text: '实时监控数据', + left: 'center', + textStyle: { + fontSize: 16, + color: '#303133' } - ] - }; - cpuChartInstance.setOption(cpuOption); - - // 初始化内存图表 - memoryChartInstance = echarts.init(memoryChart.value); - const memoryOption = { - tooltip: { - formatter: '{a}
{b} : {c}%' }, - series: [ - { - name: '内存', - type: 'gauge', - detail: { formatter: '{value}%' }, - data: [{ value: 0, name: '使用率' }], - axisLine: { - lineStyle: { - width: 30, - color: [ - [0.3, '#67C23A'], - [0.7, '#E6A23C'], - [1, '#F56C6C'] - ] - } - } - } - ] - }; - memoryChartInstance.setOption(memoryOption); - - // 初始化磁盘图表 - diskChartInstance = echarts.init(diskChart.value); - const diskOption = { tooltip: { - trigger: 'item', - formatter: '{a}
{b} : {c} ({d}%)' + trigger: 'axis', + formatter: function (params) { + let result = params[0].axisValueLabel + '
'; + params.forEach(param => { + const unit = param.seriesName === 'CPU使用率' ? '%' : 'MB'; + result += `${param.marker}${param.seriesName}: ${param.value}${unit}
`; + }); + return result; + } }, legend: { - orient: 'vertical', - left: 'left', - data: ['已使用', '可用'] + data: ['CPU使用率', '内存使用量'], + top: '10%' }, - series: [ - { - name: '磁盘空间', - type: 'pie', - radius: '55%', - center: ['50%', '60%'], - data: [ - { value: 0, name: '已使用' }, - { value: 100, name: '可用' } - ], - emphasis: { - itemStyle: { - shadowBlur: 10, - shadowOffsetX: 0, - shadowColor: 'rgba(0, 0, 0, 0.5)' - } - } - } - ] - }; - diskChartInstance.setOption(diskOption); - - // 初始化网络流量图表 - networkChartInstance = echarts.init(networkChart.value); - const networkOption = { - tooltip: { - trigger: 'axis' - }, - legend: { - data: ['上传', '下载'] + grid: { + left: '3%', + right: '4%', + bottom: '3%', + containLabel: true }, xAxis: { type: 'category', boundaryGap: false, - data: Array(10).fill('').map((_, i) => `${i}`) + data: [] }, - yAxis: { - type: 'value', - axisLabel: { - formatter: '{value} MB/s' - } - }, - series: [ + yAxis: [ { - name: '上传', - type: 'line', - data: Array(10).fill(0), - areaStyle: {} + type: 'value', + name: 'CPU使用率(%)', + position: 'left', + axisLabel: { + formatter: '{value}%' + } }, { - name: '下载', + type: 'value', + name: '内存使用量(MB)', + position: 'right', + axisLabel: { + formatter: '{value}MB' + } + } + ], + series: [ + { + name: 'CPU使用率', type: 'line', - data: Array(10).fill(0), - areaStyle: {} + yAxisIndex: 0, + data: [], + smooth: true, + lineStyle: { + color: '#409EFF' + }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [{ + offset: 0, color: 'rgba(64, 158, 255, 0.3)' + }, { + offset: 1, color: 'rgba(64, 158, 255, 0.1)' + }] + } + } + }, + { + name: '内存使用量', + type: 'line', + yAxisIndex: 1, + data: [], + smooth: true, + lineStyle: { + color: '#67C23A' + }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [{ + offset: 0, color: 'rgba(103, 194, 58, 0.3)' + }, { + offset: 1, color: 'rgba(103, 194, 58, 0.1)' + }] + } + } } ] }; - networkChartInstance.setOption(networkOption); + realTimeChartInstance.setOption(realTimeOption); }; + +const formatDateTime = (date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + }; + + + + + // 获取监控数据 const fetchMonitorData = async () => { if (!monitorDateRange.value || !monitorDateRange.value[0] || !monitorDateRange.value[1]) return; monitorLoading.value = true; try { - const startTime = Math.floor(monitorDateRange.value[0].getTime() / 1000); - const endTime = Math.floor(monitorDateRange.value[1].getTime() / 1000); + // 将日期转换为 YYYY-MM-DD HH:MM:ss 格式 + + + const startTime = formatDateTime(monitorDateRange.value[0]); + const endTime = formatDateTime(monitorDateRange.value[1]); + + console.log('监控数据时间范围:', startTime, 'to', endTime); const res = await getVirtualLog({ id: route.query.instance_id, start_time: startTime, end_time: endTime }); + console.log("获取监控数据",res) if (res && res.data && res.data.code === 200) { const monitorData = res.data.data; @@ -1899,78 +1882,45 @@ const fetchMonitorData = async () => { // 更新图表数据 const updateCharts = (data) => { - if (!data) return; + if (!data || !Array.isArray(data) || data.length === 0) return; - // 更新CPU图表 - if (cpuChartInstance && data.cpu) { - const cpuUsage = data.cpu.current || 0; - cpuChartInstance.setOption({ - series: [ - { - data: [{ value: parseFloat(cpuUsage).toFixed(2), name: '使用率' }] - } - ] - }); + if (!realTimeChartInstance) return; + + // 处理时间轴数据 + const timeLabels = data.map(item => { + const date = new Date(item.time); + return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`; + }); + + // 处理CPU数据 + const cpuData = data.map(item => parseFloat(item.cpu_usage || 0).toFixed(2)); + + // 处理内存数据 + const memoryData = data.map(item => parseInt(item.memory_usage || 0)); + + // 更新最新数据显示 + if (data.length > 0) { + const latestData = data[data.length - 1]; + latestCpuUsage.value = parseFloat(latestData.cpu_usage || 0).toFixed(2); + latestMemoryUsage.value = parseInt(latestData.memory_usage || 0); } - // 更新内存图表 - if (memoryChartInstance && data.memory) { - const memoryUsage = data.memory.usage_percent || 0; - memoryChartInstance.setOption({ - series: [ - { - data: [{ value: parseFloat(memoryUsage).toFixed(2), name: '使用率' }] - } - ] - }); - } - - // 更新磁盘图表 - if (diskChartInstance && data.disk) { - const used = data.disk.used || 0; - const available = data.disk.available || 100; - const total = used + available; - const usedPercent = (used / total) * 100; - const availablePercent = 100 - usedPercent; - - diskChartInstance.setOption({ - series: [ - { - data: [ - { value: usedPercent.toFixed(2), name: '已使用' }, - { value: availablePercent.toFixed(2), name: '可用' } - ] - } - ] - }); - } - - // 更新网络流量图表 - if (networkChartInstance && data.network) { - const timestamps = data.network.timestamps || []; - const upload = data.network.upload || []; - const download = data.network.download || []; - - // 格式化时间戳 - const formattedTimes = timestamps.map(ts => { - const date = new Date(ts * 1000); - return `${date.getHours()}:${date.getMinutes()}`; - }); - - networkChartInstance.setOption({ - xAxis: { - data: formattedTimes + // 更新图表 + realTimeChartInstance.setOption({ + xAxis: { + data: timeLabels + }, + series: [ + { + name: 'CPU使用率', + data: cpuData }, - series: [ - { - data: upload - }, - { - data: download - } - ] - }); - } + { + name: '内存使用量', + data: memoryData + } + ] + }); }; // 处理日期范围变化 @@ -2276,10 +2226,7 @@ const submitEditVolume = async () => { // 调整图表大小 const resizeCharts = () => { - cpuChartInstance && cpuChartInstance.resize(); - memoryChartInstance && memoryChartInstance.resize(); - diskChartInstance && diskChartInstance.resize(); - networkChartInstance && networkChartInstance.resize(); + realTimeChartInstance && realTimeChartInstance.resize(); }; // 监听窗口大小变化 @@ -2291,10 +2238,7 @@ onBeforeUnmount(() => { window.removeEventListener('resize', resizeCharts); // 销毁图表实例 - cpuChartInstance && cpuChartInstance.dispose(); - memoryChartInstance && memoryChartInstance.dispose(); - diskChartInstance && diskChartInstance.dispose(); - networkChartInstance && networkChartInstance.dispose(); + realTimeChartInstance && realTimeChartInstance.dispose(); }); // 获取服务器列表 @@ -2556,6 +2500,21 @@ const fetchServersList = async () => { align-items: center; } +.monitor-stats { + display: flex; + gap: 20px; + align-items: center; +} + +.stat-item { + padding: 4px 12px; + background-color: #f0f2f5; + border-radius: 4px; + font-size: 13px; + font-weight: 500; + color: #606266; +} + .chart { height: 300px; } diff --git a/src/views/acs/nodes/server.vue b/src/views/acs/nodes/server.vue index d87c122..2ad550e 100644 --- a/src/views/acs/nodes/server.vue +++ b/src/views/acs/nodes/server.vue @@ -3,6 +3,14 @@