merge(frontend): sync main styles with auth flow
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
@@ -189,11 +189,11 @@ tr.tr-hover:hover td {
|
||||
.pg-btn.active {
|
||||
background: var(--accent-dim);
|
||||
border-color: var(--accent-dim);
|
||||
color: #CFFCE9;
|
||||
color: var(--active-text);
|
||||
}
|
||||
|
||||
.pg-btn:hover {
|
||||
border-color: #3A4356;
|
||||
border-color: var(--hover-border);
|
||||
color: var(--text-hi);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<header class="topbar">
|
||||
<div class="brand">
|
||||
<div class="mark">xi</div>
|
||||
<img src="/logo-qiniu.png" alt="logo" class="mark-img" />
|
||||
xinfra <span class="sub">统一基础设施平台</span>
|
||||
</div>
|
||||
<span class="env-pill">● 生产环境</span>
|
||||
@@ -11,6 +11,14 @@
|
||||
搜索集群 / 节点 / 服务实例
|
||||
<kbd>⌘K</kbd>
|
||||
</div>
|
||||
<div class="theme-switch" @click="toggleTheme" :title="theme === 'light' ? '切换到深色模式' : '切换到浅色模式'">
|
||||
<el-icon v-if="theme === 'light'" :size="18">
|
||||
<Moon />
|
||||
</el-icon>
|
||||
<el-icon v-else :size="18">
|
||||
<Sunny />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="identity">
|
||||
<div class="avatar">{{ userInitial }}</div>
|
||||
<div>
|
||||
@@ -25,7 +33,9 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { Moon, Sunny } from '@element-plus/icons-vue'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const { logout } = useAuth()
|
||||
@@ -34,6 +44,8 @@ const userInitial = computed(() => {
|
||||
return user.value?.display_name?.charAt(0) || 'U'
|
||||
})
|
||||
|
||||
const { theme, toggleTheme } = useTheme()
|
||||
|
||||
const handleLogout = () => {
|
||||
logout()
|
||||
}
|
||||
@@ -56,27 +68,20 @@ const handleLogout = () => {
|
||||
gap: 9px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.3px;
|
||||
font-size: 15px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.brand .mark {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 5px;
|
||||
background: linear-gradient(135deg, var(--accent), #1C8F69);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #08140F;
|
||||
.brand .mark-img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.brand .sub {
|
||||
color: var(--text-dim);
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
font-size: 15px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
@@ -86,10 +91,10 @@ const handleLogout = () => {
|
||||
|
||||
.env-pill {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
font-size: 14px;
|
||||
color: var(--accent);
|
||||
border: 1px solid var(--accent-dim);
|
||||
background: #0F261D;
|
||||
background: var(--accent-dim);
|
||||
padding: 3px 9px;
|
||||
border-radius: 20px;
|
||||
letter-spacing: 0.4px;
|
||||
@@ -104,50 +109,52 @@ const handleLogout = () => {
|
||||
border-radius: 6px;
|
||||
padding: 6px 10px;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
width: 240px;
|
||||
font-size: 15px;
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.search-box kbd {
|
||||
margin-left: auto;
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
background: #262C38;
|
||||
font-size: 12px;
|
||||
background: var(--bg-panel);
|
||||
padding: 1px 5px;
|
||||
border-radius: 3px;
|
||||
color: var(--text-dim);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.identity {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12.5px;
|
||||
font-size: 15px;
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.identity .avatar {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #2A3142;
|
||||
background: var(--bg-panel-2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
font-size: 14px;
|
||||
color: var(--text-hi);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.identity .ldap-tag {
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.logout-button {
|
||||
color: var(--text-dim);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
@@ -155,4 +162,36 @@ const handleLogout = () => {
|
||||
color: var(--text-hi);
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
.theme-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
color: var(--text-mid);
|
||||
background: var(--bg-panel-2);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.theme-switch:hover {
|
||||
background: var(--bg-panel);
|
||||
color: var(--text-hi);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.theme-switch:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.theme-switch .el-icon {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.theme-switch:hover .el-icon {
|
||||
transform: rotate(15deg);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
font-size: 10.5px;
|
||||
font-size: 13px;
|
||||
color: var(--text-dim);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
@@ -97,10 +97,10 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 10px;
|
||||
padding: 10px 10px;
|
||||
border-radius: 6px;
|
||||
color: var(--text-mid);
|
||||
font-size: 13px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
margin-bottom: 2px;
|
||||
@@ -111,7 +111,7 @@
|
||||
.nav-item .ic {
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
font-size: 16px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
@@ -121,31 +121,33 @@
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: #13261F;
|
||||
background: var(--accent-dim);
|
||||
color: var(--accent);
|
||||
border-color: var(--accent-dim);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.nav-item .badge {
|
||||
margin-left: auto;
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
background: #262C38;
|
||||
font-size: 12px;
|
||||
background: var(--bg-panel-2);
|
||||
color: var(--text-dim);
|
||||
padding: 1px 6px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.nav-item.active .badge {
|
||||
background: var(--accent-dim);
|
||||
color: #CFFCE9;
|
||||
background: var(--accent);
|
||||
color: #FFFFFF;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.sidebar-foot {
|
||||
margin-top: auto;
|
||||
padding: 10px;
|
||||
border-top: 1px solid var(--line-soft);
|
||||
font-size: 11px;
|
||||
font-size: 14px;
|
||||
color: var(--text-dim);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@@ -27,26 +27,26 @@ const props = defineProps<{
|
||||
|
||||
const bgColor = computed(() => {
|
||||
const colors: Record<string, string> = {
|
||||
W: '#1C2A3A',
|
||||
DM: '#1C3A2E',
|
||||
CC: '#3A1C28',
|
||||
AP: '#1C2A3A',
|
||||
QP: '#241B0A',
|
||||
GF: '#1B202B',
|
||||
W: 'var(--logo-w-bg)',
|
||||
DM: 'var(--logo-dm-bg)',
|
||||
CC: 'var(--logo-cc-bg)',
|
||||
AP: 'var(--logo-ap-bg)',
|
||||
QP: 'var(--logo-qp-bg)',
|
||||
GF: 'var(--logo-gf-bg)',
|
||||
}
|
||||
return colors[props.system.icon] || '#262C38'
|
||||
return colors[props.system.icon] || 'var(--bg-panel-2)'
|
||||
})
|
||||
|
||||
const iconColor = computed(() => {
|
||||
const colors: Record<string, string> = {
|
||||
W: '#7FB8FF',
|
||||
DM: '#7FFFC2',
|
||||
CC: '#FF8E9C',
|
||||
AP: '#8EC8FF',
|
||||
QP: '#FFC97A',
|
||||
GF: '#F2B544',
|
||||
W: 'var(--tag-blue-text)',
|
||||
DM: 'var(--tag-green-text)',
|
||||
CC: 'var(--err)',
|
||||
AP: 'var(--tag-blue-text)',
|
||||
QP: 'var(--tag-amber-text)',
|
||||
GF: 'var(--warn)',
|
||||
}
|
||||
return colors[props.system.icon] || '#9AA3B5'
|
||||
return colors[props.system.icon] || 'var(--text-mid)'
|
||||
})
|
||||
|
||||
const description = computed(() => {
|
||||
@@ -85,7 +85,7 @@ const handleClick = async () => {
|
||||
}
|
||||
|
||||
.ext-card:hover {
|
||||
border-color: #3A4356;
|
||||
border-color: var(--hover-border);
|
||||
}
|
||||
|
||||
.ext-top {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
export type Theme = 'light' | 'dark'
|
||||
|
||||
const THEME_KEY = 'xinfra-theme'
|
||||
|
||||
// 默认浅色模式
|
||||
const theme = ref<Theme>('light')
|
||||
|
||||
export function useTheme() {
|
||||
// 初始化主题
|
||||
const initTheme = () => {
|
||||
const savedTheme = localStorage.getItem(THEME_KEY) as Theme | null
|
||||
if (savedTheme && ['light', 'dark'].includes(savedTheme)) {
|
||||
theme.value = savedTheme
|
||||
} else {
|
||||
// 默认浅色模式
|
||||
theme.value = 'light'
|
||||
}
|
||||
applyTheme(theme.value)
|
||||
}
|
||||
|
||||
// 应用主题
|
||||
const applyTheme = (newTheme: Theme) => {
|
||||
document.documentElement.setAttribute('data-theme', newTheme)
|
||||
document.documentElement.classList.toggle('dark', newTheme === 'dark')
|
||||
}
|
||||
|
||||
// 切换主题
|
||||
const toggleTheme = () => {
|
||||
theme.value = theme.value === 'light' ? 'dark' : 'light'
|
||||
localStorage.setItem(THEME_KEY, theme.value)
|
||||
applyTheme(theme.value)
|
||||
}
|
||||
|
||||
// 设置主题
|
||||
const setTheme = (newTheme: Theme) => {
|
||||
theme.value = newTheme
|
||||
localStorage.setItem(THEME_KEY, theme.value)
|
||||
applyTheme(theme.value)
|
||||
}
|
||||
|
||||
// 监听系统主题变化(可选)
|
||||
const watchSystemTheme = () => {
|
||||
if (window.matchMedia) {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
mediaQuery.addEventListener('change', (e) => {
|
||||
// 只有在用户没有手动设置过主题时才跟随系统
|
||||
if (!localStorage.getItem(THEME_KEY)) {
|
||||
theme.value = e.matches ? 'dark' : 'light'
|
||||
applyTheme(theme.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initTheme()
|
||||
watchSystemTheme()
|
||||
})
|
||||
|
||||
return {
|
||||
theme,
|
||||
toggleTheme,
|
||||
setTheme,
|
||||
initTheme
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ import AppMain from '@/components/Layout/AppMain.vue'
|
||||
<style scoped>
|
||||
.shell {
|
||||
display: grid;
|
||||
grid-template-columns: 226px 1fr;
|
||||
grid-template-rows: 56px 1fr;
|
||||
grid-template-columns: 282px 1fr;
|
||||
grid-template-rows: 70px 1fr;
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,7 +10,23 @@ html, body {
|
||||
background: var(--bg-deep);
|
||||
color: var(--text-hi);
|
||||
font-family: var(--sans);
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
/* 主题切换过渡效果 */
|
||||
*, *::before, *::after {
|
||||
transition: background-color 0.2s ease,
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
/* 排除不需要过渡的元素 */
|
||||
.no-transition,
|
||||
.no-transition *,
|
||||
.no-transition *::before,
|
||||
.no-transition *::after {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
@@ -20,10 +36,14 @@ html, body {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #2A3140;
|
||||
background: var(--bar-bg);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--text-dim);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
@@ -54,7 +74,7 @@ button {
|
||||
|
||||
.dot.ok {
|
||||
background: var(--accent);
|
||||
box-shadow: 0 0 6px #3FE0A599;
|
||||
box-shadow: 0 0 6px var(--accent-glow);
|
||||
}
|
||||
|
||||
.dot.warn {
|
||||
@@ -109,7 +129,7 @@ button {
|
||||
.bar-wrap {
|
||||
width: 90px;
|
||||
height: 5px;
|
||||
background: #262C38;
|
||||
background: var(--bar-bg);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
@@ -130,3 +150,498 @@ button {
|
||||
.bar-fill.err {
|
||||
background: var(--err);
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
.card {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
/* ========== 页面标题区 ========== */
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
/* ========== 面板 ========== */
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg);
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
.panel-head h3 {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-head .meta {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.panel-body.padded {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* ========== 统计卡片行 ========== */
|
||||
.stat-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 14px 16px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-card .label {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.stat-card .value {
|
||||
font-family: var(--mono);
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stat-card .value.accent-pill {
|
||||
color: var(--accent);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.stat-card .value.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.stat-card .value.err {
|
||||
color: var(--err);
|
||||
}
|
||||
|
||||
.stat-card .value.accent {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.stat-card .delta {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.stat-card .delta.up {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.stat-card .delta.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.stat-card .delta.err {
|
||||
color: var(--err);
|
||||
}
|
||||
|
||||
/* ========== 双栏布局 ========== */
|
||||
.cols-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 1fr;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.cols-2-equal {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
/* ========== 表格 ========== */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
color: var(--text-dim);
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
padding: 9px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr.tr-hover:hover td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
tr.tr-hover.active td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
.strong {
|
||||
color: var(--text-hi);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text-xs {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.text-dim {
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* ========== 标签 ========== */
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-family: var(--mono);
|
||||
font-size: 10.5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--line);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.tag.zone-a {
|
||||
color: var(--tag-blue-text);
|
||||
border-color: var(--tag-blue-border);
|
||||
background: var(--tag-blue-bg);
|
||||
}
|
||||
|
||||
.tag.zone-b {
|
||||
color: var(--tag-purple-text);
|
||||
border-color: var(--tag-purple-border);
|
||||
background: var(--tag-purple-bg);
|
||||
}
|
||||
|
||||
.tag.zone-c {
|
||||
color: var(--tag-amber-text);
|
||||
border-color: var(--tag-amber-border);
|
||||
background: var(--tag-amber-bg);
|
||||
}
|
||||
|
||||
/* ========== 状态文本 ========== */
|
||||
.status-text {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-text.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.status-text.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.status-text.err {
|
||||
color: var(--err);
|
||||
}
|
||||
|
||||
.status-text.idle {
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
/* ========== 进度条 ========== */
|
||||
.bar-wrap {
|
||||
width: 90px;
|
||||
height: 5px;
|
||||
background: var(--bar-bg);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.bar-fill {
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.bar-fill.warn {
|
||||
background: var(--warn);
|
||||
}
|
||||
|
||||
.bar-fill.err {
|
||||
background: var(--err);
|
||||
}
|
||||
|
||||
/* ========== 分页 ========== */
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 16px;
|
||||
border-top: 1px solid var(--line-soft);
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.pg-btns {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pg-btn {
|
||||
min-width: 26px;
|
||||
height: 26px;
|
||||
padding: 0 6px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-mid);
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
background: var(--bg-panel-2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pg-btn.active {
|
||||
background: var(--accent-dim);
|
||||
border-color: var(--accent-dim);
|
||||
color: var(--active-text);
|
||||
}
|
||||
|
||||
.pg-btn:hover {
|
||||
border-color: var(--hover-border);
|
||||
color: var(--text-hi);
|
||||
}
|
||||
|
||||
.pg-btn.disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pg-sep {
|
||||
color: var(--text-dim);
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
/* ========== 日志流 ========== */
|
||||
.log-stream {
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.task-log-line {
|
||||
font-family: var(--mono);
|
||||
font-size: 11.5px;
|
||||
padding: 2px 0;
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.task-log-line .t {
|
||||
color: var(--text-dim);
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.task-log-line.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.task-log-line.tag-ok {
|
||||
color: var(--tag-blue-text);
|
||||
}
|
||||
|
||||
/* ========== 工具栏 ========== */
|
||||
.las-toolbar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 14px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ========== 子系统/扩展卡片网格 ========== */
|
||||
.ext-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.svc-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
/* ========== Apollo / 外部服务卡片 ========== */
|
||||
.ext-card {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 18px;
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.ext-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.ext-logo {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: var(--radius-lg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--mono);
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ext-logo.ap-logo {
|
||||
background: var(--logo-ap-bg);
|
||||
color: var(--tag-blue-text);
|
||||
}
|
||||
|
||||
.ext-sub {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
/* ========== 环境状态标签 ========== */
|
||||
.env-pill {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
color: var(--accent);
|
||||
border: 1px solid var(--accent-dim);
|
||||
background: var(--accent-dim);
|
||||
padding: 3px 9px;
|
||||
border-radius: 20px;
|
||||
letter-spacing: 0.4px;
|
||||
}
|
||||
|
||||
/* ========== SSO 行 ========== */
|
||||
.sso-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.sso-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
/* ========== 响应式断点 ========== */
|
||||
|
||||
/* 平板 */
|
||||
@media (max-width: 1024px) {
|
||||
.stat-row {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.cols-2,
|
||||
.cols-2-equal {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动端 */
|
||||
@media (max-width: 768px) {
|
||||
.page-head {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.cols-2,
|
||||
.cols-2-equal {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.las-toolbar {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.ext-grid,
|
||||
.svc-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.panel-body table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,102 @@
|
||||
/* 浅色模式(默认) */
|
||||
:root {
|
||||
/* 背景色 */
|
||||
--bg-deep: #F5F7FA;
|
||||
--bg-panel: #FFFFFF;
|
||||
--bg-panel-2: #F0F2F5;
|
||||
|
||||
/* 边框色 */
|
||||
--line: #E4E7ED;
|
||||
--line-soft: #EBEEF5;
|
||||
|
||||
/* 文字色 */
|
||||
--text-hi: #1D2129;
|
||||
--text-mid: #4E5969;
|
||||
--text-dim: #86909C;
|
||||
|
||||
/* 强调色 */
|
||||
--accent: #00B42A;
|
||||
--accent-dim: #E8FFEA;
|
||||
|
||||
/* 状态色 */
|
||||
--warn: #FF7D00;
|
||||
--err: #F53F3F;
|
||||
--info: #165DFF;
|
||||
|
||||
/* 圆角 */
|
||||
--radius: 6px;
|
||||
--radius-sm: 4px;
|
||||
--radius-lg: 8px;
|
||||
--radius-xl: 12px;
|
||||
|
||||
/* 字体 */
|
||||
--mono: 'IBM Plex Mono', monospace;
|
||||
--sans: 'IBM Plex Sans', sans-serif;
|
||||
|
||||
/* 阴影 */
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
|
||||
/* 渐变终点色 */
|
||||
--accent-end: #1C8F69;
|
||||
|
||||
/* 标签/徽章颜色 - 蓝色系 */
|
||||
--tag-blue-text: #165DFF;
|
||||
--tag-blue-bg: #E8F3FF;
|
||||
--tag-blue-border: #BEDAFF;
|
||||
|
||||
/* 标签/徽章颜色 - 紫色系 */
|
||||
--tag-purple-text: #722ED1;
|
||||
--tag-purple-bg: #F5E8FF;
|
||||
--tag-purple-border: #D3ADF7;
|
||||
|
||||
/* 标签/徽章颜色 - 琥珀色系 */
|
||||
--tag-amber-text: #D46B08;
|
||||
--tag-amber-bg: #FFF7E6;
|
||||
--tag-amber-border: #FFD591;
|
||||
|
||||
/* 标签/徽章颜色 - 绿色系 */
|
||||
--tag-green-text: #00B42A;
|
||||
--tag-green-bg: #E8FFEA;
|
||||
--tag-green-border: #9FD9A8;
|
||||
|
||||
/* 节点单元格颜色 */
|
||||
--node-empty-bg: #F0F2F5;
|
||||
--node-a-bg: #E8FFEA;
|
||||
--node-a-border: #9FD9A8;
|
||||
--node-b-bg: #F5E8FF;
|
||||
--node-b-border: #D3ADF7;
|
||||
--node-c-bg: #FFF7E6;
|
||||
--node-c-border: #FFD591;
|
||||
|
||||
/* 进度条背景 */
|
||||
--bar-bg: #E4E7ED;
|
||||
|
||||
/* 悬停边框 */
|
||||
--hover-border: #C9CDD4;
|
||||
|
||||
/* 激活状态文字 */
|
||||
--active-text: #00873E;
|
||||
|
||||
/* 主题切换按钮 */
|
||||
--theme-btn-bg: #F0F2F5;
|
||||
--theme-btn-hover: #E4E7ED;
|
||||
|
||||
/* 子系统卡片logo背景 */
|
||||
--logo-w-bg: #E8F3FF;
|
||||
--logo-dm-bg: #E8FFEA;
|
||||
--logo-cc-bg: #FFF0F0;
|
||||
--logo-ap-bg: #E8F3FF;
|
||||
--logo-qp-bg: #FFF7E6;
|
||||
--logo-gf-bg: #F5F7FA;
|
||||
|
||||
/* 发光/阴影效果 */
|
||||
--accent-glow: rgba(0, 180, 42, 0.3);
|
||||
}
|
||||
|
||||
/* 深色模式 */
|
||||
:root.dark {
|
||||
/* 背景色 */
|
||||
--bg-deep: #0E1117;
|
||||
--bg-panel: #151922;
|
||||
@@ -24,8 +122,72 @@
|
||||
|
||||
/* 圆角 */
|
||||
--radius: 6px;
|
||||
--radius-sm: 4px;
|
||||
--radius-lg: 8px;
|
||||
--radius-xl: 12px;
|
||||
|
||||
/* 字体 */
|
||||
--mono: 'IBM Plex Mono', monospace;
|
||||
--sans: 'IBM Plex Sans', sans-serif;
|
||||
|
||||
/* 阴影 */
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.3);
|
||||
|
||||
/* 渐变终点色 */
|
||||
--accent-end: #1F6B52;
|
||||
|
||||
/* 标签/徽章颜色 - 蓝色系 */
|
||||
--tag-blue-text: #8EC8FF;
|
||||
--tag-blue-bg: #0E1C2C;
|
||||
--tag-blue-border: #1C3A5C;
|
||||
|
||||
/* 标签/徽章颜色 - 紫色系 */
|
||||
--tag-purple-text: #C9A6FF;
|
||||
--tag-purple-bg: #1A1430;
|
||||
--tag-purple-border: #3A2C5C;
|
||||
|
||||
/* 标签/徽章颜色 - 琥珀色系 */
|
||||
--tag-amber-text: #FFC97A;
|
||||
--tag-amber-bg: #241B0A;
|
||||
--tag-amber-border: #5C4A1C;
|
||||
|
||||
/* 标签/徽章颜色 - 绿色系 */
|
||||
--tag-green-text: #7FFFC2;
|
||||
--tag-green-bg: #0F261D;
|
||||
--tag-green-border: var(--accent-dim);
|
||||
|
||||
/* 节点单元格颜色 */
|
||||
--node-empty-bg: #1E2532;
|
||||
--node-a-bg: #13261F;
|
||||
--node-a-border: var(--accent-dim);
|
||||
--node-b-bg: #1A1430;
|
||||
--node-b-border: #3A2C5C;
|
||||
--node-c-bg: #241B0A;
|
||||
--node-c-border: #5C4A1C;
|
||||
|
||||
/* 进度条背景 */
|
||||
--bar-bg: #262C38;
|
||||
|
||||
/* 悬停边框 */
|
||||
--hover-border: #3A4356;
|
||||
|
||||
/* 激活状态文字 */
|
||||
--active-text: #CFFCE9;
|
||||
|
||||
/* 主题切换按钮 */
|
||||
--theme-btn-bg: #1B202B;
|
||||
--theme-btn-hover: #262C38;
|
||||
|
||||
/* 子系统卡片logo背景 */
|
||||
--logo-w-bg: #1C2A3A;
|
||||
--logo-dm-bg: #1C3A2E;
|
||||
--logo-cc-bg: #3A1C28;
|
||||
--logo-ap-bg: #1C2A3A;
|
||||
--logo-qp-bg: #241B0A;
|
||||
--logo-gf-bg: #1B202B;
|
||||
|
||||
/* 发光/阴影效果 */
|
||||
--accent-glow: rgba(63, 224, 165, 0.3);
|
||||
}
|
||||
|
||||
@@ -52,23 +52,5 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
/* 所有样式已在 global.css 中定义 */
|
||||
</style>
|
||||
|
||||
@@ -53,23 +53,5 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
/* 所有样式已在 global.css 中定义 */
|
||||
</style>
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
<h2>{{ title }}</h2>
|
||||
<p>{{ subtitle }}</p>
|
||||
</div>
|
||||
<el-button type="primary" size="large" style="width: 100%" @click="redirectToSSO('', '/')">{{ buttonText }}</el-button>
|
||||
<el-button type="primary" size="large" class="login-btn" @click="redirectToSSO('', '/')">
|
||||
{{ buttonText }}
|
||||
</el-button>
|
||||
<div class="login-footer">
|
||||
<p>统一 LDAP 账号,同账号同密码</p>
|
||||
</div>
|
||||
@@ -48,10 +50,14 @@ onMounted(() => {
|
||||
width: 400px;
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
border-radius: var(--radius-xl);
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 32px;
|
||||
@@ -70,15 +76,15 @@ onMounted(() => {
|
||||
.login-header .brand .mark {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, var(--accent), #1C8F69);
|
||||
border-radius: var(--radius-lg);
|
||||
background: linear-gradient(135deg, var(--accent), var(--accent-end));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--mono);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #08140F;
|
||||
color: var(--bg-deep);
|
||||
}
|
||||
|
||||
.login-header h2 {
|
||||
@@ -117,7 +123,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
:deep(.el-input__wrapper:hover) {
|
||||
border-color: #3A4356;
|
||||
border-color: var(--hover-border);
|
||||
}
|
||||
|
||||
:deep(.el-input__wrapper.is-focus) {
|
||||
@@ -131,11 +137,11 @@ onMounted(() => {
|
||||
:deep(.el-button--primary) {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: #06150F;
|
||||
color: var(--bg-deep);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.el-button--primary:hover) {
|
||||
background: #55E8B3;
|
||||
opacity: 0.9;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<td class="strong mono">{{ node.name }}</td>
|
||||
<td class="mono">{{ node.ip }}</td>
|
||||
<td><span class="tag biz" :style="{ color: node.labelColor, borderColor: node.labelBorder }">{{ node.label }}</span></td>
|
||||
<td class="mono" style="font-size: 11px">{{ node.taint }}</td>
|
||||
<td class="mono text-xs">{{ node.taint }}</td>
|
||||
<td class="mono">{{ node.spec }}</td>
|
||||
<td><span class="bar-wrap"><span class="bar-fill" :class="node.cpuClass" :style="{ width: node.cpu + '%' }"></span></span>{{ node.cpu }}%</td>
|
||||
<td :class="['status-text', node.statusClass]">● {{ node.status }}</td>
|
||||
@@ -86,170 +86,17 @@ const clusters = ref([
|
||||
])
|
||||
|
||||
const nodes = ref([
|
||||
{ name: 'bj-node-014', ip: '10.21.4.14', label: 'business-line=kodo', labelColor: '#8EC8FF', labelBorder: '#1C3A5C', taint: 'kodo:NoSchedule', spec: '64C/256G', cpu: 58, cpuClass: '', status: 'Ready', statusClass: 'ok' },
|
||||
{ name: 'bj-node-015', ip: '10.21.4.15', label: 'business-line=kodo', labelColor: '#8EC8FF', labelBorder: '#1C3A5C', taint: 'kodo:NoSchedule', spec: '64C/256G', cpu: 62, cpuClass: '', status: 'Ready', statusClass: 'ok' },
|
||||
{ name: 'bj-node-031', ip: '10.21.4.31', label: 'business-line=las', labelColor: '#C9A6FF', labelBorder: '#3A2C5C', taint: 'las:NoSchedule', spec: '32C/128G', cpu: 88, cpuClass: 'warn', status: '资源告警', statusClass: 'warn' },
|
||||
{ name: 'bj-node-014', ip: '10.21.4.14', label: 'business-line=kodo', labelColor: 'var(--tag-blue-text)', labelBorder: 'var(--tag-blue-border)', taint: 'kodo:NoSchedule', spec: '64C/256G', cpu: 58, cpuClass: '', status: 'Ready', statusClass: 'ok' },
|
||||
{ name: 'bj-node-015', ip: '10.21.4.15', label: 'business-line=kodo', labelColor: 'var(--tag-blue-text)', labelBorder: 'var(--tag-blue-border)', taint: 'kodo:NoSchedule', spec: '64C/256G', cpu: 62, cpuClass: '', status: 'Ready', statusClass: 'ok' },
|
||||
{ name: 'bj-node-031', ip: '10.21.4.31', label: 'business-line=las', labelColor: 'var(--tag-purple-text)', labelBorder: 'var(--tag-purple-border)', taint: 'las:NoSchedule', spec: '32C/128G', cpu: 88, cpuClass: 'warn', status: '资源告警', statusClass: 'warn' },
|
||||
{ name: 'bj-node-048', ip: '10.21.4.48', label: '未分配', labelColor: 'var(--text-dim)', labelBorder: 'var(--line)', taint: '—', spec: '32C/128G', cpu: 4, cpuClass: '', status: 'Ready · 空闲', statusClass: 'ok' },
|
||||
])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
.panel-head h3 {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-head .meta {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
color: var(--text-dim);
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
padding: 9px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr.tr-hover:hover td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
.strong {
|
||||
color: var(--text-hi);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-family: var(--mono);
|
||||
font-size: 10.5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--line);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.tag.zone-a {
|
||||
color: #8EC8FF;
|
||||
border-color: #1C3A5C;
|
||||
background: #0E1C2C;
|
||||
}
|
||||
|
||||
.tag.zone-b {
|
||||
color: #C9A6FF;
|
||||
border-color: #3A2C5C;
|
||||
background: #1A1430;
|
||||
}
|
||||
|
||||
.tag.zone-c {
|
||||
color: #FFC97A;
|
||||
border-color: #5C4A1C;
|
||||
background: #241B0A;
|
||||
}
|
||||
/* 公共样式已在 global.css 中定义 */
|
||||
|
||||
.tag.biz {
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.bar-wrap {
|
||||
width: 90px;
|
||||
height: 5px;
|
||||
background: #262C38;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.bar-fill {
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.bar-fill.warn {
|
||||
background: var(--warn);
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-text.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.status-text.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">同步状态</div>
|
||||
<div class="value" style="color: var(--accent); font-size: 16px">● 正常</div>
|
||||
<div class="value accent-pill">● 正常</div>
|
||||
<div class="delta">最近发布 09:12:30</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -41,10 +41,10 @@
|
||||
<h3>统一入口</h3>
|
||||
<span class="meta">Apollo Portal · LDAP 单点登录</span>
|
||||
</div>
|
||||
<div class="panel-body" style="padding: 16px">
|
||||
<div class="panel-body padded">
|
||||
<div class="ext-card">
|
||||
<div class="ext-top">
|
||||
<div class="ext-logo" style="background: #1C2A3A; color: #8EC8FF">AP</div>
|
||||
<div class="ext-logo ap-logo">AP</div>
|
||||
<div>
|
||||
<h4>Apollo Portal</h4>
|
||||
<div class="ext-sub">apollo.xinfra.internal</div>
|
||||
@@ -81,7 +81,7 @@
|
||||
<tr v-for="cluster in clusters" :key="cluster.name" class="tr-hover">
|
||||
<td><span class="tag" :class="cluster.zoneClass">{{ cluster.name }}</span></td>
|
||||
<td class="mono">cluster={{ cluster.cluster }}</td>
|
||||
<td class="mono" style="font-size: 11px">{{ cluster.address }}</td>
|
||||
<td class="mono text-xs">{{ cluster.address }}</td>
|
||||
<td class="mono">{{ cluster.type }}</td>
|
||||
<td class="mono">{{ cluster.biz }}</td>
|
||||
<td class="mono">{{ cluster.items }}</td>
|
||||
@@ -109,241 +109,17 @@ const clusters = ref([
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.env-pill {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
color: var(--accent);
|
||||
border: 1px solid var(--accent-dim);
|
||||
background: #0F261D;
|
||||
padding: 3px 9px;
|
||||
border-radius: 20px;
|
||||
letter-spacing: 0.4px;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.stat-card .label {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.stat-card .value {
|
||||
font-family: var(--mono);
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stat-card .delta {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
.panel-head h3 {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-head .meta {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.ext-card {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 18px;
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.ext-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.ext-logo {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--mono);
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
}
|
||||
/* 公共样式已在 global.css 中定义 */
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ext-sub {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: var(--text-mid);
|
||||
line-height: 1.6;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.sso-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.sso-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
color: var(--text-dim);
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
padding: 9px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr.tr-hover:hover td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-family: var(--mono);
|
||||
font-size: 10.5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--line);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.tag.zone-a {
|
||||
color: #8EC8FF;
|
||||
border-color: #1C3A5C;
|
||||
background: #0E1C2C;
|
||||
}
|
||||
|
||||
.tag.zone-b {
|
||||
color: #C9A6FF;
|
||||
border-color: #3A2C5C;
|
||||
background: #1A1430;
|
||||
}
|
||||
|
||||
.tag.zone-c {
|
||||
color: #FFC97A;
|
||||
border-color: #5C4A1C;
|
||||
background: #241B0A;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-text.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.status-text.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.status-text.idle {
|
||||
color: var(--text-dim);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">进行中任务</div>
|
||||
<div class="value" style="color: var(--warn)">2</div>
|
||||
<div class="value warn">2</div>
|
||||
<div class="delta warn">1 个待关注</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -46,7 +46,7 @@
|
||||
<div class="topo-cluster">
|
||||
<div class="tname">
|
||||
<span class="tag zone-a">IDC-华北机房</span>
|
||||
<span class="mono" style="color: var(--text-dim)">46节点</span>
|
||||
<span class="mono text-dim">46节点</span>
|
||||
</div>
|
||||
<div class="node-grid">
|
||||
<div v-for="i in 24" :key="i" :class="['node-cell', i <= 18 ? 'a' : '']"></div>
|
||||
@@ -55,7 +55,7 @@
|
||||
<div class="topo-cluster">
|
||||
<div class="tname">
|
||||
<span class="tag zone-b">IDC-华东机房</span>
|
||||
<span class="mono" style="color: var(--text-dim)">52节点</span>
|
||||
<span class="mono text-dim">52节点</span>
|
||||
</div>
|
||||
<div class="node-grid">
|
||||
<div v-for="i in 24" :key="i" :class="['node-cell', i <= 20 ? 'b' : '']"></div>
|
||||
@@ -64,7 +64,7 @@
|
||||
<div class="topo-cluster">
|
||||
<div class="tname">
|
||||
<span class="tag zone-c">阿里云-华南</span>
|
||||
<span class="mono" style="color: var(--text-dim)">30节点</span>
|
||||
<span class="mono text-dim">30节点</span>
|
||||
</div>
|
||||
<div class="node-grid">
|
||||
<div v-for="i in 18" :key="i" :class="['node-cell', i <= 12 ? 'c' : '']"></div>
|
||||
@@ -127,107 +127,7 @@ const refresh = () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 14px 16px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-card .label {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.stat-card .value {
|
||||
font-family: var(--mono);
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stat-card .delta {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.stat-card .delta.up {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.stat-card .delta.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.cols-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 1fr;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
.panel-head h3 {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-head .meta {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
/* 公共样式(.page-head, .stat-row, .cols-2, .panel, table, .tag, .status-text)已在 global.css 中定义 */
|
||||
|
||||
.topo {
|
||||
padding: 18px 16px;
|
||||
@@ -238,7 +138,7 @@ const refresh = () => {
|
||||
.topo-cluster {
|
||||
flex: 1;
|
||||
border: 1px dashed var(--line);
|
||||
border-radius: 8px;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
@@ -260,23 +160,23 @@ const refresh = () => {
|
||||
.node-cell {
|
||||
aspect-ratio: 1;
|
||||
border-radius: 3px;
|
||||
background: #1E2532;
|
||||
background: var(--node-empty-bg);
|
||||
border: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
.node-cell.a {
|
||||
background: #13261F;
|
||||
border-color: var(--accent-dim);
|
||||
background: var(--node-a-bg);
|
||||
border-color: var(--node-a-border);
|
||||
}
|
||||
|
||||
.node-cell.b {
|
||||
background: #1A1430;
|
||||
border-color: #3A2C5C;
|
||||
background: var(--node-b-bg);
|
||||
border-color: var(--node-b-border);
|
||||
}
|
||||
|
||||
.node-cell.c {
|
||||
background: #241B0A;
|
||||
border-color: #5C4A1C;
|
||||
background: var(--node-c-bg);
|
||||
border-color: var(--node-c-border);
|
||||
}
|
||||
|
||||
.node-cell.empty {
|
||||
@@ -298,92 +198,4 @@ const refresh = () => {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
color: var(--text-dim);
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
padding: 9px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr.tr-hover:hover td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
td.strong {
|
||||
color: var(--text-hi);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-family: var(--mono);
|
||||
font-size: 10.5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--line);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.tag.zone-a {
|
||||
color: #8EC8FF;
|
||||
border-color: #1C3A5C;
|
||||
background: #0E1C2C;
|
||||
}
|
||||
|
||||
.tag.zone-b {
|
||||
color: #C9A6FF;
|
||||
border-color: #3A2C5C;
|
||||
background: #1A1430;
|
||||
}
|
||||
|
||||
.tag.zone-c {
|
||||
color: #FFC97A;
|
||||
border-color: #5C4A1C;
|
||||
background: #241B0A;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-text.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.status-text.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.status-text.err {
|
||||
color: var(--err);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cols-2">
|
||||
<div class="cols-2-equal">
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h3>VictoriaMetrics · 指标概览</h3>
|
||||
@@ -94,123 +94,5 @@ const logs = ref([
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.cols-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
.panel-head h3 {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-head .meta {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.log-stream {
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr.tr-hover:hover td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
.strong {
|
||||
color: var(--text-hi);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-text.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.status-text.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.task-log-line {
|
||||
font-family: var(--mono);
|
||||
font-size: 11.5px;
|
||||
padding: 2px 0;
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.task-log-line .t {
|
||||
color: var(--text-dim);
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.task-log-line.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
/* .page-head, .cols-2-equal, .panel, table, .status-text, .log-stream, .task-log-line 已在 global.css 中定义 */
|
||||
</style>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">最近一次同步</div>
|
||||
<div class="value" style="color: var(--accent); font-size: 16px">● 正常</div>
|
||||
<div class="value accent-pill">● 正常</div>
|
||||
<div class="delta">07:39:10 · 新增 3 条</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -58,7 +58,7 @@
|
||||
<el-option label="maas" value="maas" />
|
||||
<el-option label="未分配" value="unassigned" />
|
||||
</el-select>
|
||||
<el-input placeholder="搜索 hostname / asset_number / IP" style="flex: 1" />
|
||||
<el-input placeholder="搜索 hostname / asset_number / IP" class="search-input" />
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
@@ -84,11 +84,11 @@
|
||||
<tbody>
|
||||
<tr v-for="item in resources" :key="item.hostname" class="tr-hover">
|
||||
<td class="strong mono">{{ item.hostname }}</td>
|
||||
<td class="mono" style="font-size: 11px">{{ item.asset_number }}</td>
|
||||
<td class="mono text-xs">{{ item.asset_number }}</td>
|
||||
<td><span class="tag" :class="item.type === '物理机' ? '' : 'vm'">{{ item.type }}</span></td>
|
||||
<td><span class="tag zone-a">{{ item.location }}</span></td>
|
||||
<td class="mono">{{ item.ip }}</td>
|
||||
<td class="mono" style="font-size: 11px">{{ item.spec }}</td>
|
||||
<td class="mono text-xs">{{ item.spec }}</td>
|
||||
<td class="mono">{{ item.business_line }}</td>
|
||||
<td><span class="tag src-cmdb">{{ item.source }}</span></td>
|
||||
<td class="status-text ok">● {{ item.status }}</td>
|
||||
@@ -127,234 +127,17 @@ const resources = ref([
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.stat-card .label {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.stat-card .value {
|
||||
font-family: var(--mono);
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stat-card .delta {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.stat-card .delta.up {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.stat-card .delta.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.las-toolbar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 14px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
.panel-head h3 {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-head .meta {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
color: var(--text-dim);
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
padding: 9px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr.tr-hover:hover td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
.strong {
|
||||
color: var(--text-hi);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-family: var(--mono);
|
||||
font-size: 10.5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--line);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
/* 公共样式已在 global.css 中定义 */
|
||||
|
||||
.tag.vm {
|
||||
color: #8EC8FF;
|
||||
border-color: #1C3A5C;
|
||||
background: #0E1C2C;
|
||||
}
|
||||
|
||||
.tag.zone-a {
|
||||
color: #8EC8FF;
|
||||
border-color: #1C3A5C;
|
||||
background: #0E1C2C;
|
||||
color: var(--tag-blue-text);
|
||||
border-color: var(--tag-blue-border);
|
||||
background: var(--tag-blue-bg);
|
||||
}
|
||||
|
||||
.tag.src-cmdb {
|
||||
color: #7FFFC2;
|
||||
border-color: var(--accent-dim);
|
||||
background: #0F261D;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-text.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 16px;
|
||||
border-top: 1px solid var(--line-soft);
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.pg-btns {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pg-btn {
|
||||
min-width: 26px;
|
||||
height: 26px;
|
||||
padding: 0 6px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 5px;
|
||||
color: var(--text-mid);
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
background: var(--bg-panel-2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pg-btn.active {
|
||||
background: var(--accent-dim);
|
||||
border-color: var(--accent-dim);
|
||||
color: #CFFCE9;
|
||||
}
|
||||
|
||||
.pg-btn:hover {
|
||||
border-color: #3A4356;
|
||||
color: var(--text-hi);
|
||||
}
|
||||
|
||||
.pg-btn.disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pg-sep {
|
||||
color: var(--text-dim);
|
||||
padding: 0 2px;
|
||||
color: var(--tag-green-text);
|
||||
border-color: var(--tag-green-border);
|
||||
background: var(--tag-green-bg);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -26,17 +26,17 @@
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">P0 / Disaster 告警</div>
|
||||
<div class="value" style="color: var(--err)">1</div>
|
||||
<div class="delta" style="color: var(--err)">来自 VictoriaMetrics</div>
|
||||
<div class="value err">1</div>
|
||||
<div class="delta err">来自 VictoriaMetrics</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">P1 / High 告警</div>
|
||||
<div class="value" style="color: var(--warn)">5</div>
|
||||
<div class="value warn">5</div>
|
||||
<div class="delta warn">来自 Zabbix · 4 条 / VM · 1 条</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cols-2">
|
||||
<div class="cols-2-equal">
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h3>物理机状态 · 按机房</h3>
|
||||
@@ -131,184 +131,5 @@ const refresh = () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.stat-card .label {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.stat-card .value {
|
||||
font-family: var(--mono);
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stat-card .delta {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.stat-card .delta.up {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.stat-card .delta.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.cols-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
.panel-head h3 {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-head .meta {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
color: var(--text-dim);
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
padding: 9px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr.tr-hover:hover td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
.strong {
|
||||
color: var(--text-hi);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.bar-wrap {
|
||||
width: 90px;
|
||||
height: 5px;
|
||||
background: #262C38;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.bar-fill {
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.bar-fill.warn {
|
||||
background: var(--warn);
|
||||
}
|
||||
|
||||
.bar-fill.err {
|
||||
background: var(--err);
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-text.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.status-text.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.status-text.err {
|
||||
color: var(--err);
|
||||
}
|
||||
/* 所有样式已在 global.css 中定义 */
|
||||
</style>
|
||||
|
||||
@@ -30,11 +30,11 @@ import { ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const services = ref([
|
||||
{ name: 'MySQL', icon: 'My', bgColor: '#1C2E4A', iconColor: '#7FB8FF', description: '一主多从,自动注册进 open-cdm 数据源,支持 LDAP 账号统一审核 SQL 上线。', playbook: 'roles/mysql-deploy', version: 'v8.0.36' },
|
||||
{ name: 'Redis', icon: 'Rd', bgColor: '#3A1C28', iconColor: '#FF8E9C', description: '支持 Standalone / Sentinel / Cluster,部署完成后自动注册进 CacheCloud。', playbook: 'roles/redis-deploy', version: 'v7.2' },
|
||||
{ name: 'openresty', icon: 'Or', bgColor: '#1C3A2E', iconColor: '#7FFFC2', description: '容器化部署在 K8s 内,作为业务边缘网关,支持灰度路由配置。', playbook: 'roles/openresty-deploy', version: 'v1.25' },
|
||||
{ name: 'dpvs', icon: 'Dp', bgColor: '#3A2E1C', iconColor: '#FFC97A', description: '四层负载均衡,直接管理宿主机网络命名空间,不纳入容器编排。', playbook: 'roles/dpvs-deploy', version: 'v1.9' },
|
||||
{ name: 'PgSQL', icon: 'Pg', bgColor: '#2E1C3A', iconColor: '#C9A6FF', description: '流复制主从架构,规划中 · 第二期接入 open-cdm 统一审核体系。', playbook: 'roles/pgsql-deploy', version: '规划中' },
|
||||
{ name: 'MySQL', icon: 'My', bgColor: 'var(--tag-blue-bg)', iconColor: 'var(--tag-blue-text)', description: '一主多从,自动注册进 open-cdm 数据源,支持 LDAP 账号统一审核 SQL 上线。', playbook: 'roles/mysql-deploy', version: 'v8.0.36' },
|
||||
{ name: 'Redis', icon: 'Rd', bgColor: 'var(--logo-cc-bg)', iconColor: 'var(--err)', description: '支持 Standalone / Sentinel / Cluster,部署完成后自动注册进 CacheCloud。', playbook: 'roles/redis-deploy', version: 'v7.2' },
|
||||
{ name: 'openresty', icon: 'Or', bgColor: 'var(--logo-dm-bg)', iconColor: 'var(--tag-green-text)', description: '容器化部署在 K8s 内,作为业务边缘网关,支持灰度路由配置。', playbook: 'roles/openresty-deploy', version: 'v1.25' },
|
||||
{ name: 'dpvs', icon: 'Dp', bgColor: 'var(--logo-qp-bg)', iconColor: 'var(--tag-amber-text)', description: '四层负载均衡,直接管理宿主机网络命名空间,不纳入容器编排。', playbook: 'roles/dpvs-deploy', version: 'v1.9' },
|
||||
{ name: 'PgSQL', icon: 'Pg', bgColor: 'var(--tag-purple-bg)', iconColor: 'var(--tag-purple-text)', description: '流复制主从架构,规划中 · 第二期接入 open-cdm 统一审核体系。', playbook: 'roles/pgsql-deploy', version: '规划中' },
|
||||
])
|
||||
|
||||
const handleDeploy = (service: any) => {
|
||||
@@ -43,42 +43,19 @@ const handleDeploy = (service: any) => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.svc-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
/* .page-head, .svc-grid 已在 global.css 中定义 */
|
||||
|
||||
.svc-card {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 16px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
|
||||
.svc-card:hover {
|
||||
border-color: #3A4356;
|
||||
border-color: var(--hover-border);
|
||||
}
|
||||
|
||||
.svc-top {
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">健康实例占比</div>
|
||||
<div class="value" style="color: var(--accent)">98.6%</div>
|
||||
<div class="value accent">98.6%</div>
|
||||
<div class="delta warn">12 个实例 critical</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">最近同步</div>
|
||||
<div class="value" style="color: var(--accent); font-size: 16px">● 正常</div>
|
||||
<div class="value accent-pill">● 正常</div>
|
||||
<div class="delta">07:41:05 · 间隔 30s</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -61,7 +61,7 @@
|
||||
<el-option label="部分异常" value="partial" />
|
||||
<el-option label="全部下线" value="offline" />
|
||||
</el-select>
|
||||
<el-input placeholder="搜索服务名 / IP" style="flex: 1" />
|
||||
<el-input placeholder="搜索服务名 / IP" class="search-input" />
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
@@ -126,234 +126,5 @@ const services = ref([
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.stat-card .label {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.stat-card .value {
|
||||
font-family: var(--mono);
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stat-card .delta {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.stat-card .delta.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.las-toolbar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 14px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
.panel-head h3 {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-head .meta {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
color: var(--text-dim);
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
padding: 9px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr.tr-hover:hover td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
.strong {
|
||||
color: var(--text-hi);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-family: var(--mono);
|
||||
font-size: 10.5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--line);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.tag.zone-a {
|
||||
color: #8EC8FF;
|
||||
border-color: #1C3A5C;
|
||||
background: #0E1C2C;
|
||||
}
|
||||
|
||||
.tag.zone-b {
|
||||
color: #C9A6FF;
|
||||
border-color: #3A2C5C;
|
||||
background: #1A1430;
|
||||
}
|
||||
|
||||
.tag.zone-c {
|
||||
color: #FFC97A;
|
||||
border-color: #5C4A1C;
|
||||
background: #241B0A;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-text.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.status-text.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 16px;
|
||||
border-top: 1px solid var(--line-soft);
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.pg-btns {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pg-btn {
|
||||
min-width: 26px;
|
||||
height: 26px;
|
||||
padding: 0 6px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 5px;
|
||||
color: var(--text-mid);
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
background: var(--bg-panel-2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pg-btn.active {
|
||||
background: var(--accent-dim);
|
||||
border-color: var(--accent-dim);
|
||||
color: #CFFCE9;
|
||||
}
|
||||
|
||||
.pg-btn:hover {
|
||||
border-color: #3A4356;
|
||||
color: var(--text-hi);
|
||||
}
|
||||
|
||||
.pg-btn.disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pg-sep {
|
||||
color: var(--text-dim);
|
||||
padding: 0 2px;
|
||||
}
|
||||
/* 所有样式已在 global.css 中定义 */
|
||||
</style>
|
||||
|
||||
@@ -44,29 +44,5 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.ext-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
/* .page-head, .ext-grid 已在 global.css 中定义 */
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cols-2">
|
||||
<div class="cols-2-equal">
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h3>任务列表</h3>
|
||||
@@ -18,7 +18,7 @@
|
||||
<tr v-for="task in tasks" :key="task.id" class="tr-hover" :class="{ active: task.id === selectedTask }" @click="selectedTask = task.id">
|
||||
<td :class="['status-text', task.statusClass]">● {{ task.status }}</td>
|
||||
<td class="strong">{{ task.name }}</td>
|
||||
<td class="mono" style="font-size: 11px; color: var(--text-dim)">{{ task.playbook }}</td>
|
||||
<td class="mono text-xs text-dim">{{ task.playbook }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -83,138 +83,13 @@ const logs = ref([
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 19px;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.cols-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
|
||||
.panel-head h3 {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-head .meta {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
/* 公共样式已在 global.css 中定义 */
|
||||
|
||||
.log-stream {
|
||||
padding: 14px 16px;
|
||||
max-height: 340px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr.tr-hover:hover td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
tr.tr-hover.active td {
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
.strong {
|
||||
color: var(--text-hi);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-text.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.status-text.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.status-text.err {
|
||||
color: var(--err);
|
||||
}
|
||||
|
||||
.task-log-line {
|
||||
font-family: var(--mono);
|
||||
font-size: 11.5px;
|
||||
padding: 2px 0;
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
.task-log-line .t {
|
||||
color: var(--text-dim);
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.task-log-line.ok {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.task-log-line.tag-ok {
|
||||
color: #7FB8FF;
|
||||
}
|
||||
|
||||
.blink {
|
||||
animation: blink 1s step-start infinite;
|
||||
color: var(--accent);
|
||||
@@ -223,57 +98,4 @@ tr.tr-hover.active td {
|
||||
@keyframes blink {
|
||||
50% { opacity: 0; }
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 16px;
|
||||
border-top: 1px solid var(--line-soft);
|
||||
font-size: 11.5px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.pg-btns {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pg-btn {
|
||||
min-width: 26px;
|
||||
height: 26px;
|
||||
padding: 0 6px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 5px;
|
||||
color: var(--text-mid);
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
background: var(--bg-panel-2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pg-btn.active {
|
||||
background: var(--accent-dim);
|
||||
border-color: var(--accent-dim);
|
||||
color: #CFFCE9;
|
||||
}
|
||||
|
||||
.pg-btn:hover {
|
||||
border-color: #3A4356;
|
||||
color: var(--text-hi);
|
||||
}
|
||||
|
||||
.pg-btn.disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pg-sep {
|
||||
color: var(--text-dim);
|
||||
padding: 0 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user