diff --git a/static/app.css b/static/app.css index 2167d28..ed60b00 100644 --- a/static/app.css +++ b/static/app.css @@ -768,6 +768,10 @@ body.auth-page { linear-gradient(180deg, color-mix(in oklab, var(--panel-strong) 95%, var(--accent-soft)) 0%, var(--panel) 100%); } +.summary-strip .status-band-list { + box-shadow: none; +} + .overview-calm-strip { display: grid; gap: 10px; @@ -932,6 +936,10 @@ body.auth-page { box-shadow: inset 0 1px 0 color-mix(in oklab, var(--panel-strong) 72%, transparent); } +.status-band-item-major { + min-width: 220px; +} + .status-band-item::before { content: ""; position: absolute; @@ -984,6 +992,13 @@ body.auth-page { text-transform: uppercase; } +.status-band-meta { + color: var(--muted); + font-size: 10px; + font-weight: 600; + line-height: 1.35; +} + .summary-card small { display: block; color: var(--muted); @@ -1151,14 +1166,12 @@ body.auth-page { --group-color: var(--accent); display: grid; gap: 10px; - padding: 10px; + padding: 10px 0 0; border: 1px solid color-mix(in oklab, var(--line) 92%, var(--panel-strong)); border-radius: 16px; background: linear-gradient(180deg, color-mix(in oklab, var(--panel-muted) 96%, var(--panel-strong)) 0%, color-mix(in oklab, var(--panel-strong) 96%, var(--panel-muted)) 100%); - box-shadow: - var(--shadow-soft), - inset 0 1px 0 color-mix(in oklab, var(--panel-strong) 76%, transparent); + box-shadow: inset 0 1px 0 color-mix(in oklab, var(--panel-strong) 76%, transparent); } .matrix-stack::before { @@ -1175,7 +1188,7 @@ body.auth-page { justify-content: space-between; gap: 10px; align-items: center; - padding: 0 2px; + padding: 0 12px; } .matrix-stack-headline { @@ -1264,6 +1277,12 @@ body.auth-page { background: color-mix(in oklab, var(--panel-strong) 96%, var(--panel-muted)); } +.matrix-stack .matrix-scroll { + border: 0; + border-radius: 0; + background: transparent; +} + .matrix-row { cursor: pointer; } @@ -1710,6 +1729,126 @@ body.auth-page { gap: 8px; } +.host-row-list-overview { + gap: 7px; +} + +.host-strip-overview { + border-radius: 12px; + background: linear-gradient(180deg, color-mix(in oklab, var(--panel-strong) 98%, var(--panel-muted)) 0%, color-mix(in oklab, var(--panel-muted) 90%, var(--panel-strong)) 100%); + box-shadow: none; +} + +.host-strip-overview:hover { + transform: none; + box-shadow: none; +} + +.host-strip-quiet { + border-color: color-mix(in oklab, var(--line) 88%, var(--panel-strong)); +} + +.host-strip-main-overview { + grid-template-columns: minmax(156px, 196px) minmax(260px, 1fr) minmax(220px, 0.9fr) auto; + gap: 12px; +} + +.host-metric-band { + display: flex; + gap: 10px; + align-items: center; + flex-wrap: wrap; + min-width: 0; +} + +.host-metric-band-fallback { + justify-content: flex-start; +} + +.host-metric-inline { + display: inline-flex; + align-items: baseline; + gap: 6px; + min-height: 0; + padding: 0; + border: 0; + background: transparent; + color: var(--muted); +} + +.host-metric-inline small, +.host-metric-inline strong { + display: inline-block; +} + +.host-metric-inline small { + color: inherit; + font-size: 9px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.host-metric-inline strong { + font-size: 11px; + line-height: 1.2; +} + +.host-metric-inline.tone-ok strong { + color: color-mix(in oklab, var(--ok) 62%, var(--text)); +} + +.host-metric-inline.tone-warn strong { + color: color-mix(in oklab, var(--warn) 76%, var(--text)); +} + +.host-metric-inline.tone-bad strong { + color: color-mix(in oklab, var(--bad) 82%, var(--text)); +} + +.host-service-summary { + display: grid; + gap: 4px; + min-width: 0; +} + +.host-service-summary small, +.host-service-summary strong { + display: block; +} + +.host-service-summary small { + color: var(--muted); + font-size: 9px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.host-service-summary-text { + overflow: hidden; + font-size: 11px; + line-height: 1.35; + text-overflow: ellipsis; + white-space: nowrap; +} + +.host-service-summary.tone-bad .host-service-summary-text { + color: color-mix(in oklab, var(--bad) 82%, var(--text)); +} + +.host-service-summary.tone-warn .host-service-summary-text { + color: color-mix(in oklab, var(--warn) 72%, var(--text)); +} + +.host-service-summary.tone-neutral .host-service-summary-text { + color: color-mix(in oklab, var(--muted) 76%, var(--text)); +} + +.host-inline-state-overview { + justify-content: flex-end; +} + .quiet-host-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); @@ -2787,6 +2926,35 @@ body.auth-page { z-index: 12; } +.release-task-head { + display: flex; + justify-content: space-between; + gap: 14px; + align-items: flex-start; + padding-bottom: 10px; + border-bottom: 1px solid var(--line); +} + +.release-task-context { + display: grid; + gap: 3px; + min-width: 0; +} + +.release-task-context strong { + color: color-mix(in oklab, var(--text) 88%, var(--accent)); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.release-task-context span { + color: var(--muted); + font-size: 10px; + letter-spacing: 0.04em; +} + .release-action-bar { display: flex; justify-content: space-between; @@ -2801,71 +2969,56 @@ body.auth-page { gap: 8px; flex-wrap: wrap; align-items: center; -} - -.release-status-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); - gap: 8px; margin-top: 10px; } -.release-status-item { - position: relative; - min-height: 0; - padding: 8px 10px; - border: 1px solid var(--line); - border-radius: 10px; - background: var(--panel-strong); - box-shadow: none; +.release-meta-strip { + margin-top: 10px; + align-items: stretch; } -.release-status-item::before { - content: ""; - position: absolute; - left: 0; - top: 8px; - bottom: 8px; - width: 3px; - border-radius: 999px; - background: color-mix(in oklab, currentColor 46%, transparent); - opacity: 0.82; +.release-inline-meta { + display: inline-grid; + gap: 3px; + min-width: 120px; + white-space: normal; } -.release-status-item.tone-ok { - border-color: color-mix(in oklab, var(--ok) 26%, var(--line)); - background: color-mix(in oklab, var(--ok-soft) 42%, var(--panel-strong)); -} - -.release-status-item.tone-warn { - border-color: color-mix(in oklab, var(--warn) 26%, var(--line)); - background: color-mix(in oklab, var(--warn-soft) 44%, var(--panel-strong)); -} - -.release-status-item.tone-bad { - border-color: color-mix(in oklab, var(--bad) 26%, var(--line)); - background: color-mix(in oklab, var(--bad-soft) 42%, var(--panel-strong)); -} - -.release-status-item small, -.release-status-item strong { +.release-inline-meta small, +.release-inline-meta strong { display: block; } -.release-status-item small { +.release-inline-meta small { color: var(--muted); - font-size: 10px; - text-transform: uppercase; + font-size: 9px; + font-weight: 700; letter-spacing: 0.08em; + text-transform: uppercase; } -.release-status-item strong { - margin-top: 6px; - font-size: 12.5px; - line-height: 1.45; +.release-inline-meta strong { + font-size: 11px; + line-height: 1.4; word-break: break-word; } +.release-inline-meta.tone-ok strong { + color: color-mix(in oklab, var(--ok) 72%, var(--text)); +} + +.release-inline-meta.tone-warn strong { + color: color-mix(in oklab, var(--warn) 74%, var(--text)); +} + +.release-inline-meta.tone-bad strong { + color: color-mix(in oklab, var(--bad) 82%, var(--text)); +} + +.release-check-strip { + margin-top: 8px; +} + .release-card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); @@ -2882,6 +3035,7 @@ body.auth-page { border: 1px solid var(--line); border-radius: 12px; background: var(--panel-strong); + cursor: pointer; transition: border-color 180ms ease, background 180ms ease, box-shadow 180ms ease; box-shadow: none; } @@ -2901,6 +3055,12 @@ body.auth-page { border-color: color-mix(in oklab, var(--accent) 28%, var(--line)); } +.release-service-card:focus-visible { + outline: 0; + border-color: color-mix(in oklab, var(--accent) 56%, var(--line-strong)); + box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent-soft) 84%, transparent); +} + .release-service-card.selected { border-color: color-mix(in oklab, var(--accent) 38%, var(--line)); background: color-mix(in oklab, var(--accent-soft) 26%, var(--panel-strong)); @@ -2975,7 +3135,7 @@ body.auth-page { .release-service-actions { display: grid; - gap: 8px; + gap: 6px; justify-items: end; align-self: center; } @@ -2984,17 +3144,39 @@ body.auth-page { align-self: flex-start; } +.release-service-action-row { + display: flex; + gap: 8px; + align-items: center; + justify-content: flex-end; + flex-wrap: wrap; +} + +.release-select-state { + display: inline-flex; + align-items: center; + min-height: 24px; + padding: 0 9px; + border: 1px solid color-mix(in oklab, var(--line) 90%, var(--panel-strong)); + border-radius: 999px; + background: color-mix(in oklab, var(--panel-muted) 94%, var(--panel-strong)); + color: var(--muted); + font-size: 10px; + font-weight: 700; + letter-spacing: 0.06em; +} + +.release-select-state.is-selected { + border-color: color-mix(in oklab, var(--accent) 34%, var(--line)); + background: color-mix(in oklab, var(--accent-soft) 42%, var(--panel-strong)); + color: var(--accent); +} + .release-service-note { color: var(--muted); margin: 0; } -.release-service-actions .inline-copy { - justify-content: flex-end; - gap: 8px; - flex-wrap: wrap; -} - .precheck-strip { display: flex; gap: 8px; @@ -4160,17 +4342,41 @@ body.auth-page { font-size: 12px; } +.update-field-toggle { + justify-content: flex-end; +} + .update-check { - justify-content: center; - padding: 12px 14px; + flex-direction: row; + align-items: center; + gap: 10px; + min-height: 44px; + padding: 0 12px; border: 1px solid var(--line); - border-radius: 14px; - background: var(--panel-muted); + border-radius: 12px; + background: color-mix(in oklab, var(--panel-muted) 96%, var(--panel-strong)); } .update-check input { width: 18px; height: 18px; + margin: 0; +} + +.update-check strong, +.update-check small { + display: block; +} + +.update-check strong { + font-size: 12px; + line-height: 1.2; +} + +.update-check small { + color: var(--muted); + font-size: 10px; + line-height: 1.2; } .operation-meta { @@ -4567,10 +4773,12 @@ body.auth-page { .taskbar-context, .taskbar-inline, .metadata-strip, + .release-meta-strip, .mongo-pager, .precheck-strip, .compact-workbench-head, .activity-item-side, + .release-task-head, .release-action-bar, .release-action-summary, .log-viewer-head, @@ -4599,6 +4807,7 @@ body.auth-page { } .release-action-bar, + .release-task-head, .log-viewer-head, .compact-workbench-head, .release-service-head, @@ -4611,7 +4820,7 @@ body.auth-page { justify-items: start; } - .release-service-actions .inline-copy { + .release-service-action-row { justify-content: flex-start; } diff --git a/static/app.js b/static/app.js index 562fa85..4b5848a 100644 --- a/static/app.js +++ b/static/app.js @@ -1568,76 +1568,62 @@ createApp({ updateAffectedServicesCount() { return this.updateOps.result?.services?.length || this.updateOps.selectedServices.length || 0; }, - summaryStripCards() { + overviewStatusSegments() { const serviceDownTotal = this.allServiceRows.filter((row) => row.downCount > 0).length; - const hostAlertTotal = this.allHostRows.filter((host) => host.hasAlert).length; + const instanceDownTotal = this.summary.down || 0; + const hostAlertTotal = this.overviewAlertHosts.length; + const hostMetricDown = this.summary.hostMetricDown || 0; + const hostTotal = this.summary.hostTotal || 0; const nacosUnavailable = !(this.payload.nacos && this.payload.nacos.ok); + const nacosUnhealthyTotal = this.summary.nacosUnhealthyInstanceTotal || 0; const mongoUnavailable = !(this.payload.mongo && this.payload.mongo.ok); - const tatOk = this.summary.hostMetricOk || 0; - const tatTotal = this.summary.hostTotal || 0; + const hasCriticalAlert = serviceDownTotal > 0 || instanceDownTotal > 0 || nacosUnavailable || mongoUnavailable; + const hasWarning = hostAlertTotal > 0 || hostMetricDown > 0 || nacosUnhealthyTotal > 0; return [ { - key: "service-down", - label: "服务", - value: `${serviceDownTotal}/${this.allServiceRows.length}`, - suffix: "异常", - tone: serviceDownTotal > 0 ? "bad" : "ok", - visible: serviceDownTotal > 0, + key: "overall", + label: "状态", + value: hasCriticalAlert ? "存在异常" : hasWarning ? "需要关注" : "当前无异常", + meta: hasCriticalAlert + ? `${serviceDownTotal} 服务 / ${instanceDownTotal} 实例` + : hasWarning + ? `${hostAlertTotal} 主机 / ${nacosUnhealthyTotal} Nacos` + : `${this.allServiceRows.length} 服务稳定`, + tone: hasCriticalAlert ? "bad" : hasWarning ? "warn" : "ok", }, { - key: "instance-down", - label: "实例", - value: `${this.summary.down || 0}/${this.summary.total || 0}`, - suffix: "异常", - tone: (this.summary.down || 0) > 0 ? "bad" : "ok", - visible: (this.summary.down || 0) > 0, - }, - { - key: "host-alert", - label: "主机", - value: `${hostAlertTotal}/${this.summary.hostTotal || 0}`, - suffix: "异常", - tone: hostAlertTotal > 0 ? "warn" : "ok", - visible: hostAlertTotal > 0, + key: "hosts", + label: "业务主机", + value: `${this.overviewHostRows.length} 台`, + meta: hostAlertTotal > 0 ? `${hostAlertTotal} 台异常` : `${this.overviewQuietServiceTotal} 服务在线`, + tone: hostAlertTotal > 0 ? (this.overviewAlertHosts.some((host) => host.downCount > 0) ? "bad" : "warn") : "ok", }, { key: "nacos", label: "Nacos", - value: nacosUnavailable ? "ERR" : String(this.summary.nacosUnhealthyInstanceTotal || 0), - suffix: nacosUnavailable ? "" : "异常", - tone: nacosUnavailable || (this.summary.nacosUnhealthyInstanceTotal || 0) > 0 ? "bad" : "ok", - visible: nacosUnavailable || (this.summary.nacosUnhealthyInstanceTotal || 0) > 0, + value: nacosUnavailable ? "连接异常" : nacosUnhealthyTotal > 0 ? `${nacosUnhealthyTotal} 异常` : "连接正常", + meta: `${this.payload.nacos?.summary?.serviceTotal || 0} 服务`, + tone: nacosUnavailable ? "bad" : nacosUnhealthyTotal > 0 ? "warn" : "ok", }, { key: "mongo", label: "Mongo", - value: mongoUnavailable ? "ERR" : String(this.summary.mongoDatabaseTotal || 0), - suffix: mongoUnavailable ? "" : "库", + value: mongoUnavailable ? "连接异常" : `${this.summary.mongoDatabaseTotal || 0} 库`, + meta: mongoUnavailable ? "deploy 直连失败" : "连接正常", tone: mongoUnavailable ? "bad" : "ok", - visible: mongoUnavailable, }, { key: "tat", label: "TAT", - value: `${tatOk}/${tatTotal}`, - suffix: "正常", - tone: (this.summary.hostMetricDown || 0) > 0 ? "warn" : "ok", - visible: (this.summary.hostMetricDown || 0) > 0, + value: `${this.summary.hostMetricOk || 0}/${hostTotal}`, + meta: hostMetricDown > 0 ? `${hostMetricDown} 台异常` : "采集正常", + tone: hostMetricDown > 0 ? "warn" : "ok", }, - ].filter((card) => card.visible); + ]; }, - overviewHealthyCards() { + overviewHealthyInfraCards() { const cards = []; - if (this.overviewQuietHosts.length > 0) { - cards.push({ - key: "hosts", - label: "业务主机", - value: `${this.overviewQuietHosts.length} 台`, - meta: "正常", - tone: "ok", - }); - } if (this.payload.nacos && this.payload.nacos.ok && (this.summary.nacosUnhealthyInstanceTotal || 0) === 0) { cards.push({ key: "nacos", @@ -1667,9 +1653,6 @@ createApp({ } return cards; }, - overviewHealthyInfraCards() { - return this.overviewHealthyCards.filter((card) => card.key !== "hosts"); - }, overviewHealthyHostGroups() { const groups = new Map(); this.overviewQuietHosts.forEach((host) => { diff --git a/static/index.html b/static/index.html index 8e00145..abecfed 100644 --- a/static/index.html +++ b/static/index.html @@ -300,34 +300,16 @@
-