|
|
@@ -1,268 +1,73 @@
|
|
|
<template>
|
|
|
- <div class="dashboard-wrapper">
|
|
|
- <el-row :gutter="20" class="stat-row">
|
|
|
- <el-col :span="24">
|
|
|
- <el-card shadow="never" class="role-card">
|
|
|
- <div class="role-container">
|
|
|
- <span class="role-label"><i class="el-icon-medal"></i> 当前权限角色:</span>
|
|
|
- <el-tag
|
|
|
- v-for="(item, index) in roles"
|
|
|
- :key="index"
|
|
|
- effect="dark"
|
|
|
- size="medium"
|
|
|
- class="role-tag"
|
|
|
- @click="goToRole(item)"
|
|
|
- >
|
|
|
- {{ item }}
|
|
|
- </el-tag>
|
|
|
- </div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <el-row :gutter="20" class="process-row">
|
|
|
- <el-col :span="24">
|
|
|
- <el-card shadow="hover">
|
|
|
- <div slot="header" class="card-header">
|
|
|
- <span><i class="el-icon-refresh"></i> CI/CD 流水线</span>
|
|
|
- </div>
|
|
|
- <div class="steps-wrapper">
|
|
|
- <el-steps :active="1" finish-status="success" align-center>
|
|
|
- <el-step title="更新代码" icon="el-icon-edit-outline" />
|
|
|
- <el-step title="编译代码" icon="el-icon-set-up" />
|
|
|
- <el-step title="应用打包" icon="el-icon-box" />
|
|
|
- <el-step title="推送应用" icon="el-icon-upload" />
|
|
|
- <el-step title="拉取应用" icon="el-icon-download" />
|
|
|
- <el-step title="部署应用" icon="el-icon-monitor" />
|
|
|
- </el-steps>
|
|
|
- </div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <el-row :gutter="20">
|
|
|
- <el-col :md="12" :sm="24">
|
|
|
- <el-card shadow="hover" class="data-card">
|
|
|
- <div slot="header" class="card-header">
|
|
|
- <span><i class="el-icon-cpu"></i> 机器节点状态</span>
|
|
|
- </div>
|
|
|
- <el-table :data="machineStatList" border stripe size="small">
|
|
|
- <el-table-column prop="env" label="运行环境">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-tag size="mini" effect="plain">{{ scope.row.env }}</el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="total" label="资产总数" align="center" />
|
|
|
- <el-table-column label="健康状况" align="center">
|
|
|
- <template slot-scope="scope">
|
|
|
- <div class="status-cell">
|
|
|
- <span class="status-dot online"></span>
|
|
|
- <span class="status-count">{{ scope.row.onlineCount }}</span>
|
|
|
- <span class="status-divider">/</span>
|
|
|
- <span class="status-dot offline"></span>
|
|
|
- <span class="status-count">{{ scope.row.offlineCount }}</span>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
-
|
|
|
- <el-col :md="12" :sm="24">
|
|
|
- <el-card shadow="hover" class="data-card">
|
|
|
- <div slot="header" class="card-header">
|
|
|
- <span><i class="el-icon-info"></i> 系统核心信息</span>
|
|
|
- </div>
|
|
|
- <el-descriptions v-if="sysInfo !== null" :column="1" border size="small">
|
|
|
- <el-descriptions-item label="应用版本">
|
|
|
- <el-link
|
|
|
- type="primary"
|
|
|
- target="_blank"
|
|
|
- :href="`https://git.reghao.cn/reghao/devops/commit/${sysInfo.commitId}`"
|
|
|
- icon="el-icon-link"
|
|
|
- >
|
|
|
- {{ sysInfo.commitId.substring(0, 8) }}
|
|
|
- </el-link>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="机器地址">
|
|
|
- <code>{{ sysInfo.ipv4 }}</code>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="操作系统">
|
|
|
- <i class="el-icon-monitor"></i> {{ sysInfo.osInfo }}
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="JVM 环境">
|
|
|
- <el-tooltip effect="dark" :content="sysInfo.jvmInfo" placement="top">
|
|
|
- <span class="text-truncate">{{ sysInfo.jvmInfo }}</span>
|
|
|
- </el-tooltip>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="启动/PID">
|
|
|
- <el-tag size="mini" type="success">{{ sysInfo.startAt }}</el-tag>
|
|
|
- <el-tag size="mini" type="warning" style="margin-left:5px">PID: {{ sysInfo.pid }}</el-tag>
|
|
|
- </el-descriptions-item>
|
|
|
- </el-descriptions>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
+ <div class="report-container">
|
|
|
+ <el-card class="header-card">
|
|
|
+ <h1>📊 容器资源同比巡检报告</h1>
|
|
|
+ <p>实线: 今日 | 虚线: 昨日</p>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <div v-for="item in instances" :key="item.name" class="node-card">
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <div slot="header" class="node-title">
|
|
|
+ <span>节点实例: {{ item.name }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <chart-item
|
|
|
+ title="CPU 同比 (%)"
|
|
|
+ :time-labels="timeLabels"
|
|
|
+ :data-group="item.cpu"
|
|
|
+ unit="%"
|
|
|
+ />
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <chart-item
|
|
|
+ title="内存同比 (MB)"
|
|
|
+ :time-labels="timeLabels"
|
|
|
+ :data-group="item.mem"
|
|
|
+ unit="MB"
|
|
|
+ />
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+import ChartItem from '@/components/ChartItem.vue'
|
|
|
import { getDashboard } from '@/api/devops'
|
|
|
-import { getAuthedUser } from '@/utils/auth'
|
|
|
|
|
|
export default {
|
|
|
- name: 'Dashboard',
|
|
|
+ components: { ChartItem },
|
|
|
data() {
|
|
|
return {
|
|
|
- roles: [],
|
|
|
- machineStatList: [],
|
|
|
- sysInfo: null
|
|
|
+ timeLabels: [], // 后端返回
|
|
|
+ instances: [] // 后端返回
|
|
|
}
|
|
|
},
|
|
|
- created() {
|
|
|
- const { roles } = getAuthedUser()
|
|
|
- this.roles = roles
|
|
|
- document.title = 'Dashboard'
|
|
|
- this.getData()
|
|
|
+ mounted() {
|
|
|
+ this.fetchData()
|
|
|
},
|
|
|
methods: {
|
|
|
- getData() {
|
|
|
- this.getDevopsDashboard()
|
|
|
- },
|
|
|
- getDevopsDashboard() {
|
|
|
+ fetchData() {
|
|
|
getDashboard().then(resp => {
|
|
|
if (resp.code === 0) {
|
|
|
- this.sysInfo = resp.data.sysInfo
|
|
|
- this.machineStatList = resp.data.machineStatList
|
|
|
+ this.timeLabels = resp.data.timeLabels
|
|
|
+ this.instances = resp.data.instances
|
|
|
} else {
|
|
|
this.$message.error(resp.msg)
|
|
|
}
|
|
|
}).catch(error => {
|
|
|
this.$message.error(error.message)
|
|
|
})
|
|
|
- },
|
|
|
- goToRole(data) {
|
|
|
- this.$message.info('当前操作角色: ' + data)
|
|
|
- },
|
|
|
- // 其余逻辑保持不变...
|
|
|
- goToDisk() { const path = '/disk'; this.handleRoute(path) },
|
|
|
- goToVod() { const path = '/vod'; this.handleRoute(path) },
|
|
|
- goToBlog() { const path = '/blog'; this.handleRoute(path) },
|
|
|
- handleRoute(path) {
|
|
|
- if (this.$route.path === path) { this.$router.go(0); return }
|
|
|
- this.$router.push(path)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.dashboard-wrapper {
|
|
|
- padding: 10px;
|
|
|
-}
|
|
|
-
|
|
|
-.stat-row {
|
|
|
- margin-bottom: 20px;
|
|
|
-}
|
|
|
-
|
|
|
-.role-card {
|
|
|
- background: #fcfcfc;
|
|
|
- border-left: 4px solid #409EFF;
|
|
|
-}
|
|
|
-
|
|
|
-.role-container {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
-}
|
|
|
-
|
|
|
-.role-label {
|
|
|
- font-weight: bold;
|
|
|
- color: #606266;
|
|
|
- margin-right: 15px;
|
|
|
-}
|
|
|
-
|
|
|
-.role-tag {
|
|
|
- margin-right: 10px;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.3s;
|
|
|
-}
|
|
|
-
|
|
|
-.role-tag:hover {
|
|
|
- opacity: 0.8;
|
|
|
- transform: translateY(-1px);
|
|
|
-}
|
|
|
-
|
|
|
-.card-header {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- font-weight: bold;
|
|
|
-}
|
|
|
-
|
|
|
-.process-row {
|
|
|
- margin-bottom: 20px;
|
|
|
-}
|
|
|
-
|
|
|
-.steps-wrapper {
|
|
|
- padding: 20px 0;
|
|
|
-}
|
|
|
-
|
|
|
-.data-card {
|
|
|
- min-height: 380px;
|
|
|
-}
|
|
|
-
|
|
|
-/* 状态点样式 */
|
|
|
-.status-cell {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
-}
|
|
|
-
|
|
|
-.status-dot {
|
|
|
- width: 8px;
|
|
|
- height: 8px;
|
|
|
- border-radius: 50%;
|
|
|
- margin: 0 5px;
|
|
|
-}
|
|
|
-
|
|
|
-.online {
|
|
|
- background-color: #67C23A;
|
|
|
- box-shadow: 0 0 5px #67C23A;
|
|
|
-}
|
|
|
-
|
|
|
-.offline {
|
|
|
- background-color: #F56C6C;
|
|
|
- box-shadow: 0 0 5px #F56C6C;
|
|
|
-}
|
|
|
-
|
|
|
-.status-count {
|
|
|
- font-weight: bold;
|
|
|
-}
|
|
|
-
|
|
|
-.status-divider {
|
|
|
- margin: 0 8px;
|
|
|
- color: #DCDFE6;
|
|
|
-}
|
|
|
-
|
|
|
-.text-truncate {
|
|
|
- display: inline-block;
|
|
|
- max-width: 250px;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- white-space: nowrap;
|
|
|
- vertical-align: middle;
|
|
|
-}
|
|
|
-
|
|
|
-code {
|
|
|
- background-color: #f5f7fa;
|
|
|
- padding: 2px 4px;
|
|
|
- border-radius: 4px;
|
|
|
- color: #e6a23c;
|
|
|
- font-family: monospace;
|
|
|
-}
|
|
|
-
|
|
|
-::v-deep .el-descriptions-item__label {
|
|
|
- width: 120px;
|
|
|
- background-color: #fafafa !important;
|
|
|
-}
|
|
|
+.report-container { padding: 20px; background: #f4f7f9; }
|
|
|
+.node-card { margin-bottom: 25px; }
|
|
|
+.node-title { font-weight: bold; color: #1890ff; }
|
|
|
</style>
|