html,
body {
width: 100%;
height: 100%;
overflow: hidden;
}
#app {
width: 100%;
height: 100%;
}
/* transition fade-slide - 已移至 animations.css */
/* 自定义滚动条样式 - 优化版 */
.cus-scroll {
overflow: auto;
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
}
.cus-scroll-x {
overflow-x: auto;
&::-webkit-scrollbar {
width: 0;
height: 8px;
}
}
.cus-scroll-y {
overflow-y: auto;
&::-webkit-scrollbar {
width: 8px;
height: 0;
}
}
.cus-scroll,
.cus-scroll-x,
.cus-scroll-y {
&::-webkit-scrollbar-track {
background: transparent;
border-radius: var(--radius-sm);
}
&::-webkit-scrollbar-thumb {
background: var(--gray-300);
border-radius: var(--radius-sm);
transition: background var(--transition-fast);
}
&::-webkit-scrollbar-thumb:hover {
background: var(--gray-400);
}
&::-webkit-scrollbar-corner {
background: transparent;
}
}
/* Naive Popover 会 Teleport 到 body,下列样式放全局,避免侧栏折叠菜单弹层圆角被 scoped 样式漏掉。 */
body .n-popover__content.forge-side-menu-popover-content,
body .n-popover:has(.forge-side-menu-flyout) .n-popover__content {
padding: 0 !important;
border-radius: 8px !important;
background: var(--side-menu-bg-color) !important;
overflow: hidden !important;
}
body .n-popover:has(.forge-side-menu-flyout) {
border-radius: 8px !important;
background: var(--side-menu-bg-color) !important;
overflow: hidden !important;
}
/* DataTable 分页分隔线放在整行 wrapper 上,避免只跟随分页内容宽度。 */
.n-data-table__pagination {
width: 100%;
margin: 0 !important;
padding: 12px 16px;
border-top: 1px solid var(--border-light);
}
.n-data-table__pagination > .n-pagination {
width: 100%;
padding: 0;
border-top: 0 !important;
}
/* 暗色模式滚动条 */
.dark .cus-scroll,
.dark .cus-scroll-x,
.dark .cus-scroll-y {
&::-webkit-scrollbar-thumb {
background: var(--gray-600);
}
&::-webkit-scrollbar-thumb:hover {
background: var(--gray-500);
}
}
/* 切换主题的动画效果 */
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-old(root),
.dark::view-transition-new(root) {
z-index: 9999;
}
/* ═══════════════════════════════════════
* 通用工具类 - Utility Classes
* ═══════════════════════════════════════ */
/* 玻璃态效果 */
.glass {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.dark .glass {
background: rgba(30, 41, 59, 0.9);
border-color: rgba(255, 255, 255, 0.1);
}
/* 卡片增强效果 */
.card-enhanced {
background: var(--bg-primary);
border-radius: var(--radius-card);
box-shadow: var(--shadow-card);
transition: all var(--transition);
}
.card-enhanced:hover {
box-shadow: var(--shadow-card-hover);
transform: translateY(-2px);
}
/* 按钮增强效果 */
.btn-enhanced {
position: relative;
overflow: hidden;
}
.btn-enhanced::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(
circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
rgba(255, 255, 255, 0.1) 0%,
transparent 80%
);
opacity: 0;
transition: opacity var(--transition);
pointer-events: none;
}
.btn-enhanced:hover::after {
opacity: 1;
}
/* 输入框增强效果 */
.input-enhanced {
transition: all var(--transition);
border-color: var(--border-default);
}
.input-enhanced:hover {
border-color: var(--border-strong);
}
.input-enhanced:focus {
border-color: var(--primary-500);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
/* 文字截断 */
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.text-ellipsis-2 {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.text-ellipsis-3 {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
/* 加载遮罩 */
.loading-overlay {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
backdrop-filter: blur(2px);
}
.dark .loading-overlay {
background: rgba(15, 23, 42, 0.8);
}
/* 悬浮提示 */
.tooltip-wrapper {
position: relative;
}
.tooltip {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%) translateY(-8px);
padding: var(--space-2) var(--space-3);
background: var(--gray-900);
color: white;
border-radius: var(--radius-sm);
font-size: var(--font-size-xs);
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: all var(--transition-fast);
z-index: var(--z-tooltip);
}
.tooltip-wrapper:hover .tooltip {
opacity: 1;
transform: translateX(-50%) translateY(-4px);
}
/* 响应式容器 */
.container {
width: 100%;
max-width: var(--content-max-width);
margin: 0 auto;
padding: 0 var(--content-padding);
}
/* Grid系统 */
.grid {
display: grid;
gap: var(--space-4);
}
.grid-2 {
grid-template-columns: repeat(2, 1fr);
}
.grid-3 {
grid-template-columns: repeat(3, 1fr);
}
.grid-4 {
grid-template-columns: repeat(4, 1fr);
}
@media (max-width: 768px) {
.grid-2,
.grid-3,
.grid-4 {
grid-template-columns: 1fr;
}
}
/* 分隔线 */
.divider {
height: 1px;
background: var(--border-default);
margin: var(--space-4) 0;
}
.divider-vertical {
width: 1px;
background: var(--border-default);
margin: 0 var(--space-4);
}
/* 徽章 */
.badge {
display: inline-flex;
align-items: center;
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-full);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
line-height: 1;
}
.badge-primary {
background: var(--primary-100);
color: var(--primary-700);
}
.badge-success {
background: var(--success-100);
color: var(--success-700);
}
.badge-warning {
background: var(--warning-100);
color: var(--warning-700);
}
.badge-error {
background: var(--error-100);
color: var(--error-700);
}
/* 暗色模式适配 */
.dark .badge-primary {
background: var(--primary-900);
color: var(--primary-100);
}
.dark .badge-success {
background: var(--success-900);
color: var(--success-100);
}
.dark .badge-warning {
background: var(--warning-900);
color: var(--warning-100);
}
.dark .badge-error {
background: var(--error-900);
color: var(--error-100);
}
::view-transition-new(root),
.dark::view-transition-old(root) {
z-index: 9999;
}
/* ═══════════════════════════════════════
* 页面级布局工具类 - Page Layout
* ═══════════════════════════════════════ */
/* 页面容器 */
.page-wrapper {
display: flex;
flex-direction: column;
height: 100%;
background: var(--bg-page, var(--gray-100));
overflow: hidden;
}
/* 页面头部(带标题 + 操作按钮) */
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 20px;
background: var(--bg-primary);
border-bottom: 1px solid var(--border-light);
flex-shrink: 0;
}
.page-header-left {
display: flex;
align-items: center;
gap: var(--space-3);
}
.page-header-title {
font-size: var(--font-size-base);
font-weight: var(--font-weight-semibold);
color: var(--text-primary);
line-height: 1.4;
}
.page-header-desc {
font-size: var(--font-size-sm);
color: var(--text-tertiary);
margin-top: 2px;
}
.page-header-actions {
display: flex;
align-items: center;
gap: var(--space-2);
}
/* 卡片容器 */
.page-card {
background: var(--bg-primary);
border-radius: var(--radius-card);
border: 1px solid var(--border-light);
box-shadow: var(--shadow-xs);
overflow: hidden;
}
.page-card + .page-card {
margin-top: var(--space-4);
}
/* 内容滚动区 */
.page-content {
flex: 1;
overflow-y: auto;
padding: var(--space-4);
}
/* ═══════════════════════════════════════
* 状态标签 - Status Tags
* ═══════════════════════════════════════ */
.status-tag {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 10px;
border-radius: var(--radius-full);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
line-height: 1.6;
white-space: nowrap;
}
.status-tag::before {
content: '';
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
opacity: 0.85;
}
.status-tag.success {
background: var(--success-50);
color: var(--success-700);
border: 1px solid var(--success-200);
}
.status-tag.warning {
background: var(--warning-50);
color: var(--warning-700);
border: 1px solid var(--warning-200);
}
.status-tag.error,
.status-tag.danger {
background: var(--error-50);
color: var(--error-700);
border: 1px solid var(--error-200);
}
.status-tag.info {
background: var(--info-50);
color: var(--info-700);
border: 1px solid var(--info-200);
}
.status-tag.default {
background: var(--gray-100);
color: var(--text-secondary);
border: 1px solid var(--border-default);
}
.dark .status-tag.success {
background: rgba(21, 128, 61, 0.15);
color: #4ade80;
border-color: rgba(21, 128, 61, 0.3);
}
.dark .status-tag.warning {
background: rgba(180, 83, 9, 0.15);
color: #fcd34d;
border-color: rgba(180, 83, 9, 0.3);
}
.dark .status-tag.error,
.dark .status-tag.danger {
background: rgba(185, 28, 28, 0.15);
color: #f87171;
border-color: rgba(185, 28, 28, 0.3);
}
.dark .status-tag.info {
background: rgba(3, 105, 161, 0.15);
color: #38bdf8;
border-color: rgba(3, 105, 161, 0.3);
}
.dark .status-tag.default {
background: rgba(255, 255, 255, 0.06);
color: var(--text-secondary);
border-color: var(--border-default);
}
/* ═══════════════════════════════════════
* 表格操作列 - Table Action
* ═══════════════════════════════════════ */
/* 操作列容器 */
.table-actions {
display: flex;
align-items: center;
gap: 4px;
flex-wrap: nowrap;
}
/* 操作文字链接 */
.table-action-link {
display: inline-flex;
align-items: center;
gap: 3px;
font-size: var(--font-size-sm);
color: var(--primary-600);
cursor: pointer;
padding: 2px 6px;
border-radius: var(--radius-sm);
transition: all var(--transition-fast);
text-decoration: none;
line-height: 1.5;
white-space: nowrap;
border: 1px solid transparent;
}
.table-action-link:hover {
color: var(--primary-700);
background: var(--primary-50);
border-color: var(--primary-200);
}
.table-action-link.danger {
color: var(--error-600);
}
.table-action-link.danger:hover {
color: var(--error-700);
background: var(--error-50);
border-color: var(--error-200);
}
.table-action-link.warning {
color: var(--warning-600);
}
.table-action-link.warning:hover {
color: var(--warning-700);
background: var(--warning-50);
border-color: var(--warning-200);
}
.table-action-link[disabled] {
opacity: 0.45;
cursor: not-allowed;
pointer-events: none;
}
/* 操作列分隔符 */
.table-action-divider {
display: inline-block;
width: 1px;
height: 12px;
margin: 0 2px;
/*vertical-align: middle;*/
}
/* ═══════════════════════════════════════
* 表单区域 - Form Section
* ═══════════════════════════════════════ */
/* 表单分组标题 */
.form-section-title {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-semibold);
color: var(--text-secondary);
padding: var(--space-3) 0 var(--space-2) 0;
border-bottom: 1px solid var(--border-light);
margin-bottom: var(--space-4);
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* 必填标记统一覆盖 */
.required-mark::after {
content: ' *';
color: var(--error-500);
}
/* ═══════════════════════════════════════
* 空状态 - Empty State
* ═══════════════════════════════════════ */
.empty-state-wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
color: var(--text-tertiary);
user-select: none;
}
.empty-state-icon {
font-size: 56px;
margin-bottom: 12px;
opacity: 0.4;
}
.empty-state-title {
font-size: var(--font-size-base);
font-weight: var(--font-weight-medium);
color: var(--text-secondary);
margin-bottom: 6px;
}
.empty-state-desc {
font-size: var(--font-size-sm);
color: var(--text-tertiary);
text-align: center;
max-width: 300px;
line-height: 1.6;
}
/* ═══════════════════════════════════════
* 描述列表 - Description List
* ═══════════════════════════════════════ */
.desc-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 0;
border: 1px solid var(--border-light);
border-radius: var(--radius-lg);
overflow: hidden;
}
.desc-item {
display: flex;
border-bottom: 1px solid var(--border-light);
border-right: 1px solid var(--border-light);
}
.desc-item:nth-child(2n) {
border-right: none;
}
.desc-label {
flex-shrink: 0;
width: 110px;
padding: 10px 14px;
background: var(--bg-secondary);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--text-secondary);
display: flex;
align-items: center;
}
.desc-value {
flex: 1;
padding: 10px 14px;
font-size: var(--font-size-sm);
color: var(--text-primary);
display: flex;
align-items: center;
word-break: break-all;
}
/* ═══════════════════════════════════════
* 通用Flex布局辅助类
* ═══════════════════════════════════════ */
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.flex-between {
display: flex;
align-items: center;
justify-content: space-between;
}
.flex-start {
display: flex;
align-items: center;
justify-content: flex-start;
}
.gap-1 {
gap: var(--space-1);
}
.gap-2 {
gap: var(--space-2);
}
.gap-3 {
gap: var(--space-3);
}
.gap-4 {
gap: var(--space-4);
}
/* ═══════════════════════════════════════
* 全局 n-tree 精品化样式 - Premium Tree
* ═══════════════════════════════════════ */
.n-tree {
background: transparent;
}
.n-tree-node {
padding: 1px 0;
}
.n-tree-node-content {
position: relative;
min-height: 36px;
padding: 4px 10px 4px 15px;
border-radius: 8px;
transition:
background-color 0.18s ease,
color 0.18s ease;
}
.n-tree-node-content:hover {
background-color: color-mix(in srgb, var(--primary-color, #165dff) 6%, transparent);
}
/* 修复选中态: 干掉 n-tree-node 的背景,统一由 content 承载 */
.n-tree-node--selected,
.n-tree-node--selected:hover {
background-color: transparent !important;
}
.n-tree-node--selected > .n-tree-node-content {
background: color-mix(in srgb, var(--primary-color, #165dff) 8%, transparent);
color: #0f172a;
font-weight: 600;
}
/* 展开/折叠图标 */
.n-tree-node-switcher {
width: 20px;
height: 20px;
color: #94a3b8;
transition:
transform 0.2s ease,
color 0.2s ease;
}
.n-tree-node-switcher:hover {
color: var(--primary-color, #165dff);
}
/* 缩进线 */
.n-tree-node-indent {
width: 18px;
}
/* 树容器内滚动条 */
.n-tree .n-tree-wrapper::-webkit-scrollbar {
width: 4px;
}
.n-tree .n-tree-wrapper::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 2px;
}
/* 暗色模式 */
.dark .n-tree-node-content:hover {
background-color: color-mix(in srgb, var(--primary-color, #165dff) 10%, transparent);
}
.dark .n-tree-node--selected > .n-tree-node-content {
background: color-mix(in srgb, var(--primary-color, #165dff) 16%, transparent);
}
.dark .n-tree-node-switcher {
color: #64748b;
}
.dark .n-tree-node-switcher:hover {
color: var(--primary-color, #4080ff);
}
/* ═══════════════════════════════════════
* 表格行渐进入场动画 — Staggered Row Reveal
* ═══════════════════════════════════════ */
@keyframes table-row-enter {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.n-data-table-tr {
animation: table-row-enter 0.28s cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
.n-data-table-tr:nth-child(1) {
animation-delay: 0.01s;
}
.n-data-table-tr:nth-child(2) {
animation-delay: 0.03s;
}
.n-data-table-tr:nth-child(3) {
animation-delay: 0.05s;
}
.n-data-table-tr:nth-child(4) {
animation-delay: 0.07s;
}
.n-data-table-tr:nth-child(5) {
animation-delay: 0.09s;
}
.n-data-table-tr:nth-child(6) {
animation-delay: 0.1s;
}
.n-data-table-tr:nth-child(7) {
animation-delay: 0.12s;
}
.n-data-table-tr:nth-child(8) {
animation-delay: 0.13s;
}
.n-data-table-tr:nth-child(9) {
animation-delay: 0.15s;
}
.n-data-table-tr:nth-child(10) {
animation-delay: 0.16s;
}
.n-data-table-tr:nth-child(11) {
animation-delay: 0.17s;
}
.n-data-table-tr:nth-child(12) {
animation-delay: 0.18s;
}
.n-data-table-tr:nth-child(13) {
animation-delay: 0.19s;
}
.n-data-table-tr:nth-child(14) {
animation-delay: 0.2s;
}
.n-data-table-tr:nth-child(15) {
animation-delay: 0.21s;
}
.n-data-table-tr:nth-child(16) {
animation-delay: 0.22s;
}
.n-data-table-tr:nth-child(17) {
animation-delay: 0.23s;
}
.n-data-table-tr:nth-child(18) {
animation-delay: 0.24s;
}
.n-data-table-tr:nth-child(19) {
animation-delay: 0.25s;
}
.n-data-table-tr:nth-child(20) {
animation-delay: 0.26s;
}