diff --git a/internal/handlers/setup.go b/internal/handlers/setup.go index c4b6c40..ec4990c 100644 --- a/internal/handlers/setup.go +++ b/internal/handlers/setup.go @@ -166,6 +166,41 @@ func (h *SetupHandler) enableUserSystem(adminConfig AdminConfig) error { }) } +func (h *SetupHandler) isSystemInitialized() (bool, error) { + return isSystemInitialized(h.manager, h.daoManager) +} + +func isSystemInitialized(manager *config.ConfigManager, daoManager *repository.RepositoryManager) (bool, error) { + if daoManager != nil && daoManager.User != nil { + count, err := daoManager.User.CountAdminUsers() + if err != nil { + return false, err + } + return count > 0, nil + } + + if manager == nil { + return false, nil + } + + db := manager.GetDB() + if db == nil { + return false, nil + } + + repo := repository.NewRepositoryManager(db) + if repo.User == nil { + return false, nil + } + + count, err := repo.User.CountAdminUsers() + if err != nil { + return false, err + } + + return count > 0, nil +} + // contains 检查字符串是否包含子字符串 func contains(s, substr string) bool { for i := 0; i <= len(s)-len(substr); i++ { @@ -197,6 +232,17 @@ func InitializeNoDB(manager *config.ConfigManager) gin.HandlerFunc { return } defer atomic.StoreInt32(&initInProgress, 0) + + initialized, err := isSystemInitialized(manager, nil) + if err != nil { + logrus.WithError(err).Error("[InitializeNoDB] 检查系统初始化状态失败") + common.InternalServerErrorResponse(c, "检查系统初始化状态失败") + return + } + if initialized { + common.ForbiddenResponse(c, "系统已初始化,禁止重复初始化") + return + } // 解析 JSON(仅接受嵌套结构),不再兼容 legacy 扁平字段 var req SetupRequest if !utils.BindJSONWithValidation(c, &req) { @@ -431,6 +477,17 @@ func (h *SetupHandler) Initialize(c *gin.Context) { return } + initialized, err := h.isSystemInitialized() + if err != nil { + logrus.WithError(err).Error("[SetupHandler.Initialize] 检查系统初始化状态失败") + common.InternalServerErrorResponse(c, "检查系统初始化状态失败") + return + } + if initialized { + common.ForbiddenResponse(c, "系统已初始化,禁止重复初始化") + return + } + var req SetupRequest if !utils.BindJSONWithValidation(c, &req) { return diff --git a/internal/middleware/setup_guard.go b/internal/middleware/setup_guard.go new file mode 100644 index 0000000..2da08fd --- /dev/null +++ b/internal/middleware/setup_guard.go @@ -0,0 +1,109 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" +) + +// SetupGuardConfig controls how the setup guard middleware behaves. +type SetupGuardConfig struct { + // IsInitialized returns the current initialization status. + IsInitialized func() (bool, error) + // SetupPath denotes the setup entry path, defaults to /setup. + SetupPath string + // RedirectPath denotes the path to redirect to once initialized, defaults to /. + RedirectPath string + // AllowPaths lists exact paths that should remain accessible before initialization. + AllowPaths []string + // AllowPrefixes lists path prefixes that should remain accessible before initialization. + AllowPrefixes []string +} + +// SetupGuard ensures only setup resources are accessible before initialization +// and blocks setup routes after initialization is complete. +func SetupGuard(cfg SetupGuardConfig) gin.HandlerFunc { + setupPath := cfg.SetupPath + if setupPath == "" { + setupPath = "/setup" + } + redirectPath := cfg.RedirectPath + if redirectPath == "" { + redirectPath = "/" + } + + allowPaths := map[string]struct{}{ + setupPath: {}, + setupPath + "/": {}, + } + + for _, p := range cfg.AllowPaths { + allowPaths[p] = struct{}{} + } + + allowPrefixes := []string{setupPath + "/"} + allowPrefixes = append(allowPrefixes, cfg.AllowPrefixes...) + + return func(c *gin.Context) { + initialized := false + if cfg.IsInitialized != nil { + var err error + initialized, err = cfg.IsInitialized() + if err != nil { + logrus.WithError(err).Warn("setup guard: failed to determine initialization state") + // Fail closed on error so users can still reach setup for recovery. + initialized = false + } + } + + path := c.Request.URL.Path + + if initialized { + if path == setupPath || strings.HasPrefix(path, setupPath+"/") { + switch c.Request.Method { + case http.MethodGet, http.MethodHead: + c.Redirect(http.StatusFound, redirectPath) + case http.MethodOptions: + c.Status(http.StatusNoContent) + default: + c.AbortWithStatusJSON(http.StatusForbidden, gin.H{ + "code": http.StatusForbidden, + "message": "系统已初始化,禁止重新初始化", + }) + } + c.Abort() + return + } + + c.Next() + return + } + + if _, ok := allowPaths[path]; ok { + c.Next() + return + } + for _, prefix := range allowPrefixes { + if strings.HasPrefix(path, prefix) { + c.Next() + return + } + } + + switch c.Request.Method { + case http.MethodGet, http.MethodHead: + c.Redirect(http.StatusFound, setupPath) + case http.MethodOptions: + // Allow CORS preflight to complete without redirect loops. + c.Status(http.StatusNoContent) + default: + c.AbortWithStatusJSON(http.StatusForbidden, gin.H{ + "code": http.StatusForbidden, + "message": "系统未初始化,请访问 /setup 完成初始化", + }) + } + c.Abort() + } +} diff --git a/internal/routes/setup.go b/internal/routes/setup.go index 6898e34..92ebfe4 100644 --- a/internal/routes/setup.go +++ b/internal/routes/setup.go @@ -90,6 +90,71 @@ func CreateAndSetupRouter( router.Use(middleware.CORS()) router.Use(middleware.RateLimit(manager)) + var cachedInitialized atomic.Bool + var cachedRepo atomic.Pointer[repository.RepositoryManager] + + guardConfig := middleware.SetupGuardConfig{ + SetupPath: "/setup", + RedirectPath: "/", + AllowPaths: []string{ + "/setup/initialize", + "/check-init", + "/user/system-info", + "/health", + }, + AllowPrefixes: []string{ + "/assets/", + "/css/", + "/js/", + "/components/", + }, + } + + guardConfig.IsInitialized = func() (bool, error) { + if cachedInitialized.Load() { + return true, nil + } + + if daoManager != nil && daoManager.User != nil { + count, err := daoManager.User.CountAdminUsers() + if err != nil { + return false, err + } + if count > 0 { + cachedInitialized.Store(true) + return true, nil + } + return false, nil + } + + db := manager.GetDB() + if db == nil { + return false, nil + } + + repo := cachedRepo.Load() + if repo == nil || repo.DB() != db { + repo = repository.NewRepositoryManager(db) + cachedRepo.Store(repo) + } + if repo.User == nil { + return false, nil + } + + count, err := repo.User.CountAdminUsers() + if err != nil { + return false, err + } + + if count > 0 { + cachedInitialized.Store(true) + return true, nil + } + return false, nil + } + + router.Use(middleware.SetupGuard(guardConfig)) + // 如果 daoManager 为 nil,表示尚未初始化数据库,只注册基础和初始化相关的路由 if daoManager == nil { // 基础路由(不传 userHandler) diff --git a/themes/2025/admin/css/admin-modern.css b/themes/2025/admin/css/admin-modern.css index 2e66ebe..9ded24b 100644 --- a/themes/2025/admin/css/admin-modern.css +++ b/themes/2025/admin/css/admin-modern.css @@ -60,6 +60,20 @@ --admin-status-dot-online: #34d399; --admin-status-dot-warning: #facc15; --admin-status-dot-offline: #f87171; + --admin-radius-lg: 20px; + --admin-radius-md: 16px; + --admin-radius-sm: 12px; + --admin-input-bg: rgba(255, 255, 255, 0.9); + --admin-input-border: rgba(148, 163, 184, 0.25); + --admin-input-focus: rgba(99, 102, 241, 0.28); + --admin-table-header-bg: rgba(99, 102, 241, 0.08); + --admin-table-row-hover: rgba(99, 102, 241, 0.05); + --admin-chip-bg: rgba(79, 70, 229, 0.12); + --admin-chip-text: #4338ca; + --admin-warning-bg: rgba(250, 204, 21, 0.14); + --admin-warning-text: #b45309; + --admin-info-bg: rgba(59, 130, 246, 0.14); + --admin-info-text: #1d4ed8; } .admin-theme-dark { @@ -124,6 +138,260 @@ --admin-status-dot-online: #34d399; --admin-status-dot-warning: #facc15; --admin-status-dot-offline: #f87171; + --admin-radius-lg: 18px; + --admin-radius-md: 14px; + --admin-radius-sm: 10px; + --admin-input-bg: rgba(15, 23, 42, 0.72); + --admin-input-border: rgba(99, 102, 241, 0.32); + --admin-input-focus: rgba(129, 140, 248, 0.45); + --admin-table-header-bg: rgba(79, 70, 229, 0.24); + --admin-table-row-hover: rgba(79, 70, 229, 0.14); + --admin-chip-bg: rgba(129, 140, 248, 0.24); + --admin-chip-text: #e0e7ff; + --admin-warning-bg: rgba(250, 204, 21, 0.18); + --admin-warning-text: #facc15; + --admin-info-bg: rgba(59, 130, 246, 0.24); + --admin-info-text: #93c5fd; +} + +/* -------------------------------------------------------------------------- */ +/* Shared Admin Components */ +/* -------------------------------------------------------------------------- */ + +.admin-card, +.admin-surface-card, +.admin-panel, +.admin-shell-card { + position: relative; + background: var(--admin-panel-bg); + border: 1px solid var(--admin-panel-border); + border-radius: var(--admin-radius-lg); + box-shadow: var(--admin-panel-shadow); + padding: 24px; + backdrop-filter: blur(14px); + transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; +} + +.admin-card:hover, +.admin-surface-card:hover, +.admin-panel:hover, +.admin-shell-card:hover { + transform: translateY(-4px); + box-shadow: var(--admin-shadow-soft); + border-color: rgba(79, 70, 229, 0.22); +} + +.admin-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + flex-wrap: wrap; + padding: 22px; + border-radius: var(--admin-radius-lg); + background: rgba(255, 255, 255, 0.78); + border: 1px solid var(--admin-border); + box-shadow: var(--admin-shadow-card); +} + +.admin-toolbar .toolbar-actions { + display: flex; + gap: 12px; + align-items: center; + flex-wrap: wrap; +} + +.admin-input, +.admin-toolbar input[type="text"], +.admin-toolbar input[type="search"], +.admin-toolbar select, +.admin-modal input, +.admin-modal select { + width: 100%; + padding: 12px 16px; + border-radius: var(--admin-radius-sm); + border: 1px solid var(--admin-input-border); + background: var(--admin-input-bg); + color: var(--admin-text); + font-size: 14px; + transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease; +} + +.admin-input:focus, +.admin-toolbar input[type="text"]:focus, +.admin-toolbar input[type="search"]:focus, +.admin-toolbar select:focus, +.admin-modal input:focus, +.admin-modal select:focus { + outline: none; + border-color: var(--admin-accent-strong); + box-shadow: 0 0 0 3px var(--admin-input-focus); + background: rgba(255, 255, 255, 0.95); +} + +.admin-chip, +.admin-pill, +.admin-tag { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 14px; + border-radius: 999px; + font-size: 12px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + background: var(--admin-chip-bg); + color: var(--admin-chip-text); +} + +.admin-chip.icon i, +.admin-pill.icon i, +.admin-tag.icon i { + font-size: 12px; +} + +.admin-chip.warning { + background: var(--admin-warning-bg); + color: var(--admin-warning-text); +} + +.admin-chip.info { + background: var(--admin-info-bg); + color: var(--admin-info-text); +} + +.admin-button, +.admin-toolbar .btn, +.admin-chip-action { + display: inline-flex; + align-items: center; + gap: 8px; + border-radius: var(--admin-radius-sm); + border: none; + padding: 12px 18px; + font-size: 14px; + font-weight: 600; + background: linear-gradient(135deg, var(--admin-primary-btn-start) 0%, var(--admin-primary-btn-end) 100%); + color: #fff; + box-shadow: var(--admin-primary-btn-shadow); + cursor: pointer; + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.admin-button:hover, +.admin-toolbar .btn:hover, +.admin-chip-action:hover { + transform: translateY(-2px); + box-shadow: 0 16px 28px rgba(99, 102, 241, 0.3); +} + +.admin-button.ghost, +.admin-toolbar .btn.ghost { + background: var(--admin-ghost-bg); + color: var(--admin-accent); + box-shadow: none; +} + +.admin-button.ghost:hover, +.admin-toolbar .btn.ghost:hover { + background: var(--admin-ghost-bg-hover); +} + +.admin-table { + width: 100%; + border-collapse: collapse; + background: var(--admin-panel-bg); + border-radius: var(--admin-radius-lg); + overflow: hidden; + border: 1px solid var(--admin-panel-border); + box-shadow: var(--admin-shadow-card); +} + +.admin-table thead { + background: var(--admin-table-header-bg); + color: var(--admin-heading); + text-transform: uppercase; + letter-spacing: 0.04em; + font-size: 12px; +} + +.admin-table th, +.admin-table td { + padding: 16px 20px; + text-align: left; + border-bottom: 1px solid rgba(148, 163, 184, 0.18); + font-size: 14px; +} + +.admin-table tbody tr:hover { + background: var(--admin-table-row-hover); +} + +.admin-table tbody tr:last-child td { + border-bottom: none; +} + +.admin-muted-note { + color: var(--admin-muted); + font-size: 13px; +} + +.admin-modal { + background: var(--admin-panel-bg); + border-radius: var(--admin-radius-lg); + border: 1px solid var(--admin-panel-border); + box-shadow: 0 40px 80px rgba(15, 23, 42, 0.28); + overflow: hidden; + backdrop-filter: blur(24px); +} + +.admin-modal-header { + padding: 24px 28px; + background: linear-gradient(145deg, var(--admin-nav-active-start) 0%, var(--admin-nav-active-end) 100%); + color: #fff; + display: flex; + justify-content: space-between; + align-items: center; +} + +.admin-modal-body { + padding: 24px 28px; + display: flex; + flex-direction: column; + gap: 20px; + background: rgba(255, 255, 255, 0.92); +} + +.admin-modal-footer { + padding: 20px 28px; + background: rgba(249, 250, 255, 0.85); + display: flex; + justify-content: flex-end; + gap: 12px; + border-top: 1px solid rgba(148, 163, 184, 0.18); +} + +.admin-form-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 20px; +} + +.admin-form-group label { + display: block; + font-size: 13px; + letter-spacing: 0.02em; + font-weight: 600; + margin-bottom: 8px; + color: var(--admin-muted); +} + +.admin-description { + font-size: 14px; + color: var(--admin-muted); + line-height: 1.7; + margin-bottom: 16px; } body.admin-modern-body { diff --git a/themes/2025/admin/css/files.css b/themes/2025/admin/css/files.css index 13a65f8..3866869 100644 --- a/themes/2025/admin/css/files.css +++ b/themes/2025/admin/css/files.css @@ -2,15 +2,16 @@ /* 文件管理工具栏 */ .files-toolbar { - background: white; - border-radius: 8px; - padding: 20px; - margin-bottom: 20px; + background: rgba(255, 255, 255, 0.86); + border-radius: var(--admin-radius-lg); + padding: 22px; + margin-bottom: 24px; display: flex; justify-content: space-between; align-items: center; gap: 20px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); + box-shadow: var(--admin-shadow-card); + border: 1px solid var(--admin-border); flex-wrap: wrap; } @@ -28,17 +29,19 @@ .files-search-input { flex: 1; - padding: 10px 16px; - border: 1px solid #ced4da; - border-radius: 6px; + padding: 12px 16px; + border: 1px solid var(--admin-input-border); + border-radius: var(--admin-radius-sm); font-size: 14px; - transition: border-color 0.2s ease; + transition: border-color 0.2s ease, box-shadow 0.2s ease; + background: var(--admin-input-bg); } .files-search-input:focus { outline: none; - border-color: #007bff; - box-shadow: 0 0 0 0.2rem rgba(0,123,255,0.25); + border-color: var(--admin-accent-strong); + box-shadow: 0 0 0 3px var(--admin-input-focus); + background: rgba(255, 255, 255, 0.98); } .files-actions { @@ -57,40 +60,41 @@ } .files-stat-card { - background: white; - border-radius: 8px; - padding: 16px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); + background: var(--admin-panel-bg); + border-radius: var(--admin-radius-lg); + padding: 20px; + box-shadow: var(--admin-panel-shadow); text-align: center; - border-left: 4px solid #007bff; + border-left: 4px solid var(--admin-accent-strong); + border: 1px solid var(--admin-panel-border); } .files-stat-value { font-size: 24px; font-weight: 700; - color: #333; + color: var(--admin-heading); margin-bottom: 4px; } .files-stat-label { font-size: 13px; - color: #6c757d; + color: var(--admin-muted); font-weight: 500; } /* 文件视图切换 */ .view-toggle { display: flex; - border: 1px solid #e9ecef; - border-radius: 6px; + border: 1px solid var(--admin-border); + border-radius: var(--admin-radius-sm); overflow: hidden; } .view-toggle-btn { padding: 8px 12px; border: none; - background: white; - color: #6c757d; + background: var(--admin-surface); + color: var(--admin-muted); cursor: pointer; transition: all 0.2s ease; display: flex; @@ -99,48 +103,53 @@ } .view-toggle-btn.active { - background: #007bff; - color: white; + background: linear-gradient(135deg, var(--admin-primary-btn-start), var(--admin-primary-btn-end)); + color: #fff; } .view-toggle-btn:hover:not(.active) { - background: #f8f9fa; + background: var(--admin-interactive-bg-hover); } /* 文件列表视图 */ .files-list-container { - background: white; - border-radius: 8px; + background: transparent; + border-radius: var(--admin-radius-lg); overflow: hidden; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); + box-shadow: none; margin-bottom: 20px; } .files-table { width: 100%; border-collapse: collapse; + background: var(--admin-panel-bg); + border: 1px solid var(--admin-panel-border); + border-radius: var(--admin-radius-lg); + overflow: hidden; + box-shadow: var(--admin-shadow-card); } .files-table th { - background: #f8f9fa; - padding: 12px 16px; + background: var(--admin-table-header-bg); + padding: 16px 20px; text-align: left; font-weight: 600; - color: #333; - border-bottom: 2px solid #e9ecef; - font-size: 14px; + color: var(--admin-heading); + border-bottom: 1px solid rgba(148, 163, 184, 0.22); + font-size: 13px; white-space: nowrap; } .files-table td { - padding: 12px 16px; - border-bottom: 1px solid #e9ecef; + padding: 16px 20px; + border-bottom: 1px solid rgba(148, 163, 184, 0.18); vertical-align: middle; font-size: 14px; } .files-table tbody tr:hover { - background: #f8f9fa; + background: var(--admin-table-row-hover); } .files-table tbody tr:last-child td { @@ -182,7 +191,7 @@ .file-name { font-weight: 600; - color: #333; + color: var(--admin-heading); display: block; margin-bottom: 2px; word-break: break-all; @@ -193,49 +202,50 @@ gap: 12px; align-items: center; flex-wrap: wrap; + color: var(--admin-muted); } .file-code { font-family: monospace; font-size: 12px; - color: #6c757d; - background: #f8f9fa; + color: var(--admin-subtle); + background: rgba(148, 163, 184, 0.16); padding: 2px 6px; - border-radius: 3px; + border-radius: var(--admin-radius-sm); } .file-size { font-size: 12px; - color: #6c757d; + color: var(--admin-muted); } .file-date { font-size: 12px; - color: #6c757d; + color: var(--admin-muted); } /* 文件状态标签 */ .file-status { padding: 2px 8px; - border-radius: 12px; + border-radius: 999px; font-size: 11px; font-weight: 500; text-transform: uppercase; } .file-status.public { - background: #d4edda; - color: #155724; + background: rgba(34, 197, 94, 0.18); + color: #047857; } .file-status.private { - background: #f8d7da; - color: #721c24; + background: rgba(239, 68, 68, 0.18); + color: #b91c1c; } .file-status.expired { - background: #fff3cd; - color: #856404; + background: rgba(245, 158, 11, 0.18); + color: #b45309; } /* 文件操作按钮 */ @@ -248,7 +258,7 @@ .file-action-btn { padding: 4px 8px; border: none; - border-radius: 4px; + border-radius: var(--admin-radius-sm); cursor: pointer; transition: all 0.2s ease; font-size: 12px; @@ -261,32 +271,32 @@ .file-action-btn:hover { transform: translateY(-1px); - box-shadow: 0 2px 4px rgba(0,0,0,0.15); + box-shadow: 0 6px 12px rgba(79, 70, 229, 0.16); } .btn-view { - background: #17a2b8; - color: white; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.85), rgba(29, 78, 216, 0.95)); + color: #eff6ff; } .btn-download { - background: #28a745; - color: white; + background: linear-gradient(135deg, rgba(34, 197, 94, 0.9), rgba(5, 150, 105, 0.96)); + color: #ecfdf5; } .btn-copy { - background: #6c757d; - color: white; + background: linear-gradient(135deg, rgba(148, 163, 184, 0.95), rgba(100, 116, 139, 0.92)); + color: #f8fafc; } .btn-edit { - background: #ffc107; - color: #212529; + background: linear-gradient(135deg, rgba(249, 115, 22, 0.92), rgba(217, 119, 6, 0.92)); + color: #fff7ed; } .btn-delete { - background: #dc3545; - color: white; + background: linear-gradient(135deg, rgba(239, 68, 68, 0.9), rgba(220, 38, 38, 0.96)); + color: #fee2e2; } /* 文件网格视图 */ @@ -298,22 +308,23 @@ } .file-card { - background: white; - border-radius: 8px; + background: var(--admin-panel-bg); + border-radius: var(--admin-radius-lg); overflow: hidden; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); + box-shadow: var(--admin-panel-shadow); transition: all 0.2s ease; cursor: pointer; + border: 1px solid var(--admin-panel-border); } .file-card:hover { transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0,0,0,0.15); + box-shadow: var(--admin-shadow-soft); } .file-card-preview { height: 120px; - background: #f8f9fa; + background: rgba(148, 163, 184, 0.12); display: flex; align-items: center; justify-content: center; @@ -344,7 +355,7 @@ .file-card-name { font-weight: 600; - color: #333; + color: var(--admin-heading); margin-bottom: 4px; display: -webkit-box; -webkit-line-clamp: 2; @@ -359,7 +370,7 @@ justify-content: space-between; align-items: center; font-size: 11px; - color: #6c757d; + color: var(--admin-muted); margin-bottom: 8px; } @@ -371,9 +382,9 @@ /* 批量操作 */ .bulk-actions { - background: #e9ecef; + background: rgba(99, 102, 241, 0.08); padding: 12px 16px; - border-radius: 6px; + border-radius: var(--admin-radius-lg); margin-bottom: 15px; display: none; align-items: center; @@ -387,7 +398,7 @@ .bulk-info { flex: 1; font-size: 14px; - color: #495057; + color: var(--admin-text); } .bulk-buttons { @@ -397,25 +408,25 @@ /* 文件上传区域 */ .upload-area { - border: 2px dashed #ced4da; - border-radius: 8px; + border: 2px dashed rgba(99, 102, 241, 0.25); + border-radius: var(--admin-radius-lg); padding: 40px 20px; text-align: center; - background: #f8f9fa; + background: rgba(99, 102, 241, 0.06); transition: all 0.2s ease; margin-bottom: 20px; } .upload-area.dragover { - border-color: #007bff; - background: #e6f3ff; + border-color: var(--admin-accent-strong); + background: rgba(99, 102, 241, 0.12); } .upload-icon { width: 48px; height: 48px; border-radius: 50%; - background: #007bff; + background: linear-gradient(135deg, var(--admin-primary-btn-start), var(--admin-primary-btn-end)); color: white; display: flex; align-items: center; @@ -426,13 +437,13 @@ .upload-text { font-size: 16px; - color: #495057; + color: var(--admin-heading); margin-bottom: 8px; } .upload-hint { font-size: 14px; - color: #6c757d; + color: var(--admin-muted); } /* 文件过滤器 */ @@ -446,36 +457,36 @@ .filter-label { font-size: 14px; - color: #495057; + color: var(--admin-text); font-weight: 500; } .filter-select { - padding: 6px 10px; - border: 1px solid #ced4da; - border-radius: 4px; + padding: 8px 12px; + border: 1px solid var(--admin-input-border); + border-radius: var(--admin-radius-sm); font-size: 13px; - background: white; + background: var(--admin-input-bg); } .filter-tag { padding: 4px 8px; - background: #e9ecef; - border-radius: 12px; + background: var(--admin-chip-bg); + border-radius: 999px; font-size: 12px; - color: #495057; + color: var(--admin-chip-text); cursor: pointer; transition: all 0.2s ease; } .filter-tag.active { - background: #007bff; - color: white; + background: linear-gradient(135deg, var(--admin-primary-btn-start), var(--admin-primary-btn-end)); + color: #fff; } .filter-tag:hover { - background: #007bff; - color: white; + background: linear-gradient(135deg, var(--admin-nav-active-start), var(--admin-nav-active-end)); + color: #fff; } /* 响应式设计 */ @@ -552,4 +563,4 @@ align-items: flex-start; gap: 4px; } -} \ No newline at end of file +} diff --git a/themes/2025/admin/css/pagination.css b/themes/2025/admin/css/pagination.css index 4116c9b..e7674e8 100644 --- a/themes/2025/admin/css/pagination.css +++ b/themes/2025/admin/css/pagination.css @@ -7,10 +7,11 @@ gap: 8px; flex-wrap: wrap; margin: 20px 0; - padding: 16px; - background: #f8f9fa; - border-radius: 8px; - border: 1px solid #e9ecef; + padding: 16px 20px; + background: rgba(255, 255, 255, 0.82); + border-radius: var(--admin-radius-lg); + border: 1px solid var(--admin-border); + box-shadow: var(--admin-shadow-card); } .pagination-numbers { @@ -30,10 +31,10 @@ margin: 0 2px; font-size: 14px; font-weight: 500; - color: #495057; - background: #fff; - border: 1px solid #dee2e6; - border-radius: 6px; + color: var(--admin-text); + background: var(--admin-surface); + border: 1px solid var(--admin-border); + border-radius: var(--admin-radius-sm); cursor: pointer; transition: all 0.2s ease; text-decoration: none; @@ -41,11 +42,11 @@ } .btn-page:hover { - color: #007bff; - background: #e7f3ff; - border-color: #007bff; + color: var(--admin-accent); + background: var(--admin-interactive-bg-hover); + border-color: rgba(99, 102, 241, 0.24); transform: translateY(-1px); - box-shadow: 0 2px 4px rgba(0, 123, 255, 0.2); + box-shadow: 0 6px 12px rgba(99, 102, 241, 0.16); } .btn-page:active { @@ -55,15 +56,14 @@ .btn-page.active { color: #fff; - background: #007bff; - border-color: #007bff; + background: linear-gradient(135deg, var(--admin-primary-btn-start), var(--admin-primary-btn-end)); + border-color: transparent; font-weight: 600; - box-shadow: 0 2px 4px rgba(0, 123, 255, 0.3); + box-shadow: 0 8px 18px rgba(99, 102, 241, 0.32); } .btn-page.active:hover { - background: #0056b3; - border-color: #0056b3; + background: linear-gradient(135deg, var(--admin-nav-active-start), var(--admin-nav-active-end)); } /* 特殊按钮样式 */ @@ -91,7 +91,7 @@ justify-content: center; min-width: 36px; height: 36px; - color: #6c757d; + color: var(--admin-muted); font-weight: bold; user-select: none; } @@ -103,11 +103,11 @@ gap: 8px; margin-left: 16px; padding-left: 16px; - border-left: 1px solid #dee2e6; + border-left: 1px solid var(--admin-border); } .pagination-jump span { - color: #495057; + color: var(--admin-muted); font-size: 14px; white-space: nowrap; } @@ -116,8 +116,8 @@ width: 60px; height: 32px; padding: 4px 8px; - border: 1px solid #ced4da; - border-radius: 4px; + border: 1px solid var(--admin-input-border); + border-radius: var(--admin-radius-sm); text-align: center; font-size: 14px; outline: none; @@ -125,15 +125,15 @@ } .pagination-jump input:focus { - border-color: #007bff; - box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); + border-color: var(--admin-accent-strong); + box-shadow: 0 0 0 3px var(--admin-input-focus); } .pagination-jump .btn { height: 32px; padding: 4px 12px; font-size: 13px; - border-radius: 4px; + border-radius: var(--admin-radius-sm); } /* 页面大小选择器样式 */ @@ -143,11 +143,11 @@ gap: 8px; margin-left: 16px; padding-left: 16px; - border-left: 1px solid #dee2e6; + border-left: 1px solid var(--admin-border); } .pagination-size span { - color: #495057; + color: var(--admin-muted); font-size: 14px; white-space: nowrap; } @@ -155,18 +155,18 @@ .pagination-size select { height: 32px; padding: 4px 8px; - border: 1px solid #ced4da; - border-radius: 4px; + border: 1px solid var(--admin-input-border); + border-radius: var(--admin-radius-sm); font-size: 14px; - background: #fff; + background: var(--admin-input-bg); outline: none; cursor: pointer; transition: border-color 0.2s ease; } .pagination-size select:focus { - border-color: #007bff; - box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); + border-color: var(--admin-accent-strong); + box-shadow: 0 0 0 3px var(--admin-input-focus); } /* 分页信息样式 */ @@ -175,15 +175,15 @@ align-items: center; margin-bottom: 12px; padding: 8px 16px; - background: #e9ecef; - border-radius: 6px; - color: #495057; + background: rgba(99, 102, 241, 0.08); + border-radius: var(--admin-radius-sm); + color: var(--admin-text); font-size: 14px; font-weight: 500; } .pagination-info span { - color: #007bff; + color: var(--admin-accent-strong); font-weight: 600; } @@ -191,9 +191,10 @@ .pagination-container { margin-top: 24px; padding: 20px; - background: #fff; - border-radius: 8px; - border: 1px solid #e9ecef; + background: rgba(255, 255, 255, 0.86); + border-radius: var(--admin-radius-lg); + border: 1px solid var(--admin-border); + box-shadow: var(--admin-shadow-card); } /* 响应式设计 */ @@ -214,7 +215,7 @@ margin-left: 0; padding-left: 0; border-left: none; - border-top: 1px solid #dee2e6; + border-top: 1px solid var(--admin-border); padding-top: 12px; } @@ -257,17 +258,17 @@ /* 禁用状态 */ .btn-page:disabled { - color: #6c757d; - background: #f8f9fa; - border-color: #dee2e6; + color: rgba(71, 85, 105, 0.65); + background: rgba(148, 163, 184, 0.16); + border-color: transparent; cursor: not-allowed; opacity: 0.6; } .btn-page:disabled:hover { - color: #6c757d; - background: #f8f9fa; - border-color: #dee2e6; + color: rgba(71, 85, 105, 0.65); + background: rgba(148, 163, 184, 0.16); + border-color: transparent; transform: none; box-shadow: none; } @@ -310,49 +311,4 @@ } } -/* 暗色主题支持 */ -@media (prefers-color-scheme: dark) { - .pagination-wrapper { - background: #343a40; - border-color: #495057; - } - - .btn-page { - color: #e9ecef; - background: #495057; - border-color: #6c757d; - } - - .btn-page:hover { - color: #007bff; - background: #343a40; - border-color: #007bff; - } - - .btn-page.active { - color: #fff; - background: #007bff; - border-color: #007bff; - } - - .pagination-info { - background: #495057; - color: #e9ecef; - } - - .pagination-jump input, - .pagination-size select { - background: #495057; - border-color: #6c757d; - color: #e9ecef; - } - - .pagination-ellipsis { - color: #adb5bd; - } - - .pagination-container { - background: #343a40; - border-color: #495057; - } -} \ No newline at end of file +/* 暗色主题支持通过 admin-theme-dark 变量自动生效 */ diff --git a/themes/2025/admin/css/users.css b/themes/2025/admin/css/users.css index 078c517..729057a 100644 --- a/themes/2025/admin/css/users.css +++ b/themes/2025/admin/css/users.css @@ -2,12 +2,14 @@ /* 用户模态框美化 */ #user-modal .modal-content { - max-width: 700px; - background: white; - border-radius: 16px; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15); + max-width: 720px; + background: var(--admin-panel-bg); + border-radius: var(--admin-radius-lg); + border: 1px solid var(--admin-panel-border); + box-shadow: var(--admin-panel-shadow); overflow: hidden; animation: modalSlideIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); + backdrop-filter: blur(24px); } @keyframes modalSlideIn { @@ -22,7 +24,7 @@ } #user-modal .modal-header { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + background: linear-gradient(135deg, var(--admin-nav-active-start) 0%, var(--admin-nav-active-end) 100%); padding: 24px 32px; border-bottom: none; position: relative; @@ -74,7 +76,7 @@ transition: all 0.3s ease; position: relative; z-index: 2; - background: rgba(255, 255, 255, 0.1); + background: rgba(255, 255, 255, 0.16); border-radius: 50%; width: 40px; height: 40px; @@ -91,7 +93,7 @@ #user-modal .modal-body { padding: 32px; - background: #fafbfc; + background: rgba(255, 255, 255, 0.9); } #user-modal .form-row { @@ -108,7 +110,7 @@ #user-modal .form-group label { font-weight: 600; - color: #374151; + color: var(--admin-muted); margin-bottom: 8px; display: flex; align-items: center; @@ -120,35 +122,35 @@ content: ''; width: 3px; height: 16px; - background: linear-gradient(135deg, #667eea, #764ba2); - border-radius: 2px; + background: linear-gradient(135deg, var(--admin-nav-active-start), var(--admin-nav-active-end)); + border-radius: 999px; } #user-modal .form-control { width: 100%; padding: 12px 16px; - border: 2px solid #e5e7eb; - border-radius: 12px; + border: 1px solid var(--admin-input-border); + border-radius: var(--admin-radius-sm); font-size: 14px; transition: all 0.3s ease; - background: white; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); + background: var(--admin-input-bg); + box-shadow: none; } #user-modal .form-control:focus { - border-color: #667eea; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); + border-color: var(--admin-accent-strong); + box-shadow: 0 0 0 3px var(--admin-input-focus); outline: none; - background: white; + background: rgba(255, 255, 255, 0.98); } #user-modal .form-control:hover { - border-color: #d1d5db; + border-color: rgba(99, 102, 241, 0.2); } #user-modal .form-text { font-size: 12px; - color: #6b7280; + color: var(--admin-subtle); margin-top: 6px; display: flex; align-items: center; @@ -157,7 +159,7 @@ #user-modal .form-text::before { content: 'ℹ'; - color: #9ca3af; + color: var(--admin-subtle); font-style: normal; font-weight: bold; } @@ -167,31 +169,31 @@ align-items: center; gap: 12px; padding: 16px; - background: white; - border: 2px solid #e5e7eb; - border-radius: 12px; + background: var(--admin-surface); + border: 1px solid var(--admin-border); + border-radius: var(--admin-radius-md); cursor: pointer; transition: all 0.3s ease; font-weight: 500; - color: #374151; + color: var(--admin-text); } #user-modal .checkbox-label:hover { - border-color: #667eea; - background: #f8faff; + border-color: rgba(99, 102, 241, 0.28); + background: rgba(99, 102, 241, 0.08); } #user-modal .checkbox-label input[type="checkbox"] { width: 20px; height: 20px; - accent-color: #667eea; + accent-color: var(--admin-accent-strong); cursor: pointer; } #user-modal .modal-footer { padding: 24px 32px; - background: white; - border-top: 1px solid #e5e7eb; + background: rgba(249, 250, 255, 0.92); + border-top: 1px solid rgba(148, 163, 184, 0.18); display: flex; justify-content: flex-end; gap: 12px; @@ -199,7 +201,7 @@ #user-modal .modal-footer .btn { padding: 12px 24px; - border-radius: 8px; + border-radius: var(--admin-radius-sm); font-weight: 600; font-size: 14px; border: none; @@ -213,26 +215,25 @@ } #user-modal .modal-footer .btn-secondary { - background: #f3f4f6; - color: #374151; - border: 2px solid #e5e7eb; + background: var(--admin-ghost-bg); + color: var(--admin-accent); + border: 1px solid transparent; } #user-modal .modal-footer .btn-secondary:hover { - background: #e5e7eb; - border-color: #d1d5db; + background: var(--admin-ghost-bg-hover); } #user-modal .modal-footer .btn-primary { - background: linear-gradient(135deg, #667eea, #764ba2); - color: white; - border: 2px solid transparent; - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); + background: linear-gradient(135deg, var(--admin-primary-btn-start), var(--admin-primary-btn-end)); + color: #fff; + border: 1px solid transparent; + box-shadow: var(--admin-primary-btn-shadow); } #user-modal .modal-footer .btn-primary:hover { transform: translateY(-2px); - box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5); + box-shadow: 0 16px 28px rgba(99, 102, 241, 0.32); } #user-modal .modal-footer .btn-primary:active { @@ -241,13 +242,13 @@ /* 表单错误样式增强 */ #user-modal .form-control.error { - border-color: #ef4444 !important; - box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1) !important; - background: #fef2f2; + border-color: rgba(239, 68, 68, 0.52) !important; + box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.18) !important; + background: rgba(254, 242, 242, 0.92); } #user-modal .form-error { - color: #ef4444; + color: var(--admin-danger); font-size: 12px; margin-top: 6px; display: flex; @@ -258,13 +259,13 @@ #user-modal .form-error::before { content: '⚠'; - color: #ef4444; + color: currentColor; } #user-modal .form-control.success { - border-color: #10b981; - box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1); - background: #f0fdf4; + border-color: rgba(34, 197, 94, 0.55); + box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18); + background: rgba(240, 253, 244, 0.94); } /* 特殊字段样式 */ @@ -278,7 +279,7 @@ } #user-modal input[type="number"].form-control { - background-image: url('data:image/svg+xml,'); + background-image: url('data:image/svg+xml,'); background-repeat: no-repeat; background-position: right 12px center; background-size: 16px; @@ -314,14 +315,26 @@ } } -/* 表单错误样式 */ +/* 表单状态增强 */ +.form-control:focus { + border-color: var(--admin-accent-strong); + box-shadow: 0 0 0 3px var(--admin-input-focus); + outline: none; +} + .form-control.error { - border-color: #dc3545 !important; - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important; + border-color: rgba(239, 68, 68, 0.52) !important; + box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.18) !important; + background: rgba(254, 242, 242, 0.94); +} + +.form-control.success { + border-color: rgba(34, 197, 94, 0.55); + box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18); } .form-error { - color: #dc3545; + color: var(--admin-danger); font-size: 12px; margin-top: 4px; display: block; @@ -332,17 +345,6 @@ margin-bottom: 1rem; } -.form-control:focus { - border-color: #007bff; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - outline: 0; -} - -.form-control.success { - border-color: #28a745; - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - /* 用户统计卡片 */ .stats-row { display: grid; @@ -352,12 +354,12 @@ } .stat-card { - background: white; - border-radius: 8px; - padding: 20px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); + background: var(--admin-panel-bg); + border-radius: var(--admin-radius-lg); + padding: 22px; + box-shadow: var(--admin-panel-shadow); text-align: center; - transition: all 0.3s ease; + transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease; min-height: 120px; display: flex; flex-direction: column; @@ -365,6 +367,7 @@ justify-content: center; position: relative; overflow: hidden; + border: 1px solid var(--admin-panel-border); } .stat-card::before { @@ -373,13 +376,14 @@ top: 0; left: 0; right: 0; - height: 3px; - background: linear-gradient(135deg, #007bff, #0056b3); + height: 4px; + background: linear-gradient(135deg, var(--admin-nav-active-start), var(--admin-nav-active-end)); } .stat-card:hover { - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0,0,0,0.15); + transform: translateY(-4px); + box-shadow: var(--admin-shadow-soft); + border-color: rgba(79, 70, 229, 0.22); } .stat-icon { @@ -392,50 +396,52 @@ margin-bottom: 12px; font-size: 24px; color: white; + box-shadow: 0 12px 24px rgba(79, 70, 229, 0.24); } .stat-icon.users { - background: linear-gradient(135deg, #007bff, #0056b3); + background: linear-gradient(135deg, var(--admin-nav-active-start), var(--admin-nav-active-end)); } .stat-icon.admins { - background: linear-gradient(135deg, #28a745, #1e7e34); + background: linear-gradient(135deg, rgba(34, 197, 94, 0.8), rgba(5, 150, 105, 0.95)); } .stat-icon.active { - background: linear-gradient(135deg, #17a2b8, #138496); + background: linear-gradient(135deg, rgba(59, 130, 246, 0.85), rgba(29, 78, 216, 0.95)); } .stat-icon.uploads { - background: linear-gradient(135deg, #ffc107, #e0a800); + background: linear-gradient(135deg, rgba(249, 115, 22, 0.9), rgba(217, 119, 6, 0.95)); } .stat-value { font-size: 28px; font-weight: 700; - color: #333; + color: var(--admin-heading); margin-bottom: 4px; line-height: 1; } .stat-label { font-size: 14px; - color: #6c757d; + color: var(--admin-muted); font-weight: 500; margin: 0; } /* 工具栏样式 */ .user-toolbar { - background: white; - border-radius: 8px; + background: rgba(255, 255, 255, 0.86); + border-radius: var(--admin-radius-lg); padding: 24px; - margin-bottom: 20px; + margin-bottom: 24px; display: flex; justify-content: space-between; align-items: center; gap: 24px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); + box-shadow: var(--admin-shadow-card); + border: 1px solid var(--admin-border); flex-wrap: wrap; } @@ -453,17 +459,19 @@ .search-input { flex: 1; - padding: 10px 16px; - border: 1px solid #ced4da; - border-radius: 6px; + padding: 12px 16px; + border: 1px solid var(--admin-input-border); + border-radius: var(--admin-radius-sm); font-size: 14px; - transition: border-color 0.2s ease; + transition: border-color 0.2s ease, box-shadow 0.2s ease; + background: var(--admin-input-bg); } .search-input:focus { outline: none; - border-color: #007bff; - box-shadow: 0 0 0 0.2rem rgba(0,123,255,0.25); + border-color: var(--admin-accent-strong); + box-shadow: 0 0 0 3px var(--admin-input-focus); + background: rgba(255, 255, 255, 0.98); } .action-section { @@ -475,52 +483,54 @@ /* 用户表格样式 */ .users-table-container { - background: white; - border-radius: 12px; + background: transparent; + border-radius: var(--admin-radius-lg); overflow: hidden; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); margin-bottom: 20px; - border: 1px solid #e5e7eb; } .users-table { width: 100%; border-collapse: collapse; margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; + font-family: inherit; + background: var(--admin-panel-bg); + border: 1px solid var(--admin-panel-border); + border-radius: var(--admin-radius-lg); + overflow: hidden; + box-shadow: var(--admin-shadow-card); } .users-table th { - background: linear-gradient(135deg, #f8faff 0%, #f1f5f9 100%); + background: var(--admin-table-header-bg); padding: 18px 24px; text-align: left; font-weight: 600; - color: #1e293b; - border-bottom: 2px solid #e2e8f0; + color: var(--admin-heading); + border-bottom: 1px solid rgba(148, 163, 184, 0.22); position: sticky; top: 0; z-index: 10; - font-size: 14px; white-space: nowrap; text-transform: uppercase; - letter-spacing: 0.5px; + letter-spacing: 0.08em; font-size: 12px; } .users-table th:first-child { - border-top-left-radius: 12px; + border-top-left-radius: var(--admin-radius-lg); } .users-table th:last-child { - border-top-right-radius: 12px; + border-top-right-radius: var(--admin-radius-lg); } .users-table td { - padding: 16px 24px; - border-bottom: 1px solid #f1f5f9; + padding: 18px 24px; + border-bottom: 1px solid rgba(148, 163, 184, 0.18); vertical-align: middle; font-size: 14px; - color: #374151; + color: var(--admin-text); transition: background-color 0.2s ease; } @@ -530,13 +540,13 @@ } .users-table tbody tr:hover { - background: linear-gradient(135deg, #f8faff 0%, #f0f9ff 100%); + background: var(--admin-table-row-hover); transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); + box-shadow: 0 4px 12px rgba(79, 70, 229, 0.12); } .users-table tbody tr:hover td { - border-color: #e0e7ff; + border-color: rgba(99, 102, 241, 0.2); } .users-table tbody tr:last-child td { @@ -546,7 +556,7 @@ .loading-cell { text-align: center; padding: 60px 20px; - background: linear-gradient(135deg, #f8faff 0%, #f0f9ff 100%); + background: linear-gradient(135deg, rgba(99, 102, 241, 0.08), rgba(129, 140, 248, 0.12)); } .loading-spinner { @@ -554,12 +564,12 @@ flex-direction: column; align-items: center; gap: 16px; - color: #6b7280; + color: var(--admin-muted); } .loading-spinner i { font-size: 32px; - color: #667eea; + color: var(--admin-accent-strong); animation: spin 1s linear infinite; } @@ -578,7 +588,7 @@ width: 44px; height: 44px; border-radius: 50%; - background: linear-gradient(135deg, #667eea, #764ba2); + background: linear-gradient(135deg, var(--admin-nav-active-start), var(--admin-nav-active-end)); display: flex; align-items: center; justify-content: center; @@ -586,7 +596,7 @@ font-weight: 700; font-size: 16px; flex-shrink: 0; - box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); + box-shadow: 0 12px 24px rgba(79, 70, 229, 0.24); position: relative; } @@ -597,8 +607,8 @@ right: -2px; width: 12px; height: 12px; - background: #10b981; - border: 2px solid white; + background: var(--admin-status-dot-online); + border: 2px solid var(--admin-panel-bg); border-radius: 50%; opacity: 0; transition: opacity 0.3s ease; @@ -617,16 +627,16 @@ .user-name { font-weight: 600; - color: #1e293b; + color: var(--admin-heading); font-size: 15px; line-height: 1.2; } .user-id { font-size: 12px; - color: #64748b; + color: var(--admin-subtle); font-family: 'SF Mono', 'Monaco', 'Consolas', monospace; - background: #f1f5f9; + background: rgba(148, 163, 184, 0.18); padding: 2px 6px; border-radius: 4px; display: inline-block; @@ -639,7 +649,7 @@ align-items: center; gap: 6px; padding: 6px 14px; - border-radius: 20px; + border-radius: 999px; font-size: 12px; font-weight: 600; min-width: 80px; @@ -649,6 +659,9 @@ letter-spacing: 0.5px; position: relative; overflow: hidden; + background: var(--admin-chip-bg); + color: var(--admin-chip-text); + border: 1px solid transparent; } .status-badge::before { @@ -667,9 +680,9 @@ } .status-active { - background: linear-gradient(135deg, #10b981, #059669); - color: white; - box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3); + background: linear-gradient(135deg, rgba(34, 197, 94, 0.88), rgba(5, 150, 105, 0.96)); + color: #ecfdf5; + box-shadow: 0 2px 10px rgba(16, 185, 129, 0.28); } .status-active::after { @@ -679,9 +692,9 @@ } .status-inactive { - background: linear-gradient(135deg, #ef4444, #dc2626); - color: white; - box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3); + background: linear-gradient(135deg, rgba(239, 68, 68, 0.9), rgba(220, 38, 38, 0.96)); + color: #fee2e2; + box-shadow: 0 2px 10px rgba(239, 68, 68, 0.28); } .status-inactive::after { @@ -706,7 +719,7 @@ padding: 8px 10px; font-size: 12px; border: none; - border-radius: 8px; + border-radius: var(--admin-radius-sm); cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); display: flex; @@ -753,28 +766,28 @@ /* 编辑按钮 */ .btn-small:nth-child(1) { - background: linear-gradient(135deg, #3b82f6, #2563eb); - color: white; - box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3); + background: linear-gradient(135deg, var(--admin-nav-active-start), var(--admin-nav-active-end)); + color: #fff; + box-shadow: 0 8px 16px rgba(99, 102, 241, 0.28); } /* 删除按钮 */ .btn-small:nth-child(2) { - background: linear-gradient(135deg, #ef4444, #dc2626); - color: white; - box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3); + background: linear-gradient(135deg, rgba(239, 68, 68, 0.9), rgba(220, 38, 38, 0.96)); + color: #fee2e2; + box-shadow: 0 8px 16px rgba(239, 68, 68, 0.28); } /* 激活/禁用按钮 */ .btn-small:nth-child(3) { - background: linear-gradient(135deg, #10b981, #059669); - color: white; - box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3); + background: linear-gradient(135deg, rgba(34, 197, 94, 0.9), rgba(5, 150, 105, 0.96)); + color: #ecfdf5; + box-shadow: 0 8px 16px rgba(16, 185, 129, 0.28); } .btn-small:nth-child(3).inactive { - background: linear-gradient(135deg, #f59e0b, #d97706); - box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3); + background: linear-gradient(135deg, rgba(245, 158, 11, 0.88), rgba(217, 119, 6, 0.94)); + box-shadow: 0 8px 16px rgba(217, 119, 6, 0.26); } /* 分页样式 */ @@ -783,16 +796,17 @@ justify-content: space-between; align-items: center; padding: 20px 24px; - background: white; - border-radius: 8px; + background: rgba(255, 255, 255, 0.82); + border-radius: var(--admin-radius-lg); margin-top: 20px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); + box-shadow: var(--admin-shadow-card); + border: 1px solid var(--admin-border); flex-wrap: wrap; gap: 16px; } .pagination-info { - color: #6c757d; + color: var(--admin-muted); font-size: 14px; font-weight: 500; } @@ -806,10 +820,10 @@ .btn-page { padding: 8px 12px; margin: 0 2px; - border: 1px solid #dee2e6; - background: white; - color: #495057; - border-radius: 6px; + border: 1px solid var(--admin-border); + background: var(--admin-surface); + color: var(--admin-text); + border-radius: var(--admin-radius-sm); cursor: pointer; transition: all 0.2s ease; font-size: 14px; @@ -822,22 +836,22 @@ } .btn-page:hover { - background: #e9ecef; - border-color: #adb5bd; + background: var(--admin-interactive-bg-hover); + border-color: rgba(99, 102, 241, 0.24); transform: translateY(-1px); } .btn-page.active { - background: #007bff; - color: white; - border-color: #007bff; - box-shadow: 0 2px 4px rgba(0,123,255,0.25); + background: linear-gradient(135deg, var(--admin-primary-btn-start), var(--admin-primary-btn-end)); + color: #fff; + border-color: transparent; + box-shadow: 0 6px 16px rgba(99, 102, 241, 0.32); } .btn-page:disabled { - background: #f8f9fa; - color: #6c757d; - border-color: #e9ecef; + background: rgba(148, 163, 184, 0.16); + color: rgba(71, 85, 105, 0.65); + border-color: transparent; cursor: not-allowed; transform: none; } @@ -847,14 +861,14 @@ width: 18px; height: 18px; cursor: pointer; - accent-color: #007bff; + accent-color: var(--admin-accent-strong); } .select-all-checkbox { width: 18px; height: 18px; cursor: pointer; - accent-color: #007bff; + accent-color: var(--admin-accent-strong); } /* 用户详情模态框 */ @@ -868,14 +882,14 @@ gap: 16px; margin-bottom: 20px; padding-bottom: 16px; - border-bottom: 1px solid #e9ecef; + border-bottom: 1px solid var(--admin-border); } .user-detail-avatar { width: 60px; height: 60px; border-radius: 50%; - background: linear-gradient(135deg, #007bff, #0056b3); + background: linear-gradient(135deg, var(--admin-nav-active-start), var(--admin-nav-active-end)); display: flex; align-items: center; justify-content: center; @@ -886,12 +900,12 @@ .user-detail-info h3 { margin: 0 0 4px 0; - color: #333; + color: var(--admin-heading); } .user-detail-info p { margin: 0; - color: #6c757d; + color: var(--admin-muted); font-size: 14px; } @@ -901,7 +915,7 @@ .user-detail-section h4 { margin: 0 0 12px 0; - color: #495057; + color: var(--admin-heading); font-size: 16px; font-weight: 600; } @@ -916,18 +930,18 @@ display: flex; justify-content: space-between; padding: 8px 12px; - background: #f8f9fa; - border-radius: 4px; + background: rgba(148, 163, 184, 0.12); + border-radius: var(--admin-radius-sm); font-size: 14px; } .user-detail-label { font-weight: 500; - color: #495057; + color: var(--admin-heading); } .user-detail-value { - color: #6c757d; + color: var(--admin-muted); font-family: monospace; } @@ -1053,7 +1067,7 @@ } .user-filters label { font-weight: 500; - color: #495057; + color: var(--admin-muted); margin-right: 6px; } @@ -1075,11 +1089,12 @@ align-items: center; } .search-input { - padding: 8px 12px; - border: 1px solid #e9ecef; - border-radius: 6px; + padding: 12px 16px; + border: 1px solid var(--admin-input-border); + border-radius: var(--admin-radius-sm); min-width: 280px; - box-shadow: inset 0 1px 2px rgba(0,0,0,0.03); + box-shadow: none; + background: var(--admin-input-bg); } .action-section { @@ -1088,18 +1103,18 @@ align-items: center; } .btn { - padding: 8px 12px; - border-radius: 6px; + padding: 10px 16px; + border-radius: var(--admin-radius-sm); cursor: pointer; border: none; - background: #f5f7fa; - color: #333; + background: var(--admin-ghost-bg); + color: var(--admin-accent); font-weight: 500; } .btn:hover { opacity: 0.95; } .btn-secondary { - background: #ffffff; - border: 1px solid #e9ecef; + background: var(--admin-surface); + border: 1px solid var(--admin-border); } /* 简单的下拉菜单样式(批量操作) */ @@ -1108,17 +1123,17 @@ position: absolute; right: 0; top: 100%; - background: #ffffff; - border: 1px solid #e9ecef; - box-shadow: 0 6px 12px rgba(0,0,0,0.08); + background: var(--admin-surface); + border: 1px solid var(--admin-border); + box-shadow: 0 18px 32px rgba(15, 23, 42, 0.12); display: none; min-width: 180px; - border-radius: 6px; + border-radius: var(--admin-radius-sm); z-index: 1000; overflow: hidden; } - .dropdown-menu a { display: block; padding: 8px 12px; color: #333; text-decoration: none; } - .dropdown-menu a:hover { background: #f8f9fa; } + .dropdown-menu a { display: block; padding: 10px 14px; color: var(--admin-text); text-decoration: none; } + .dropdown-menu a:hover { background: var(--admin-interactive-bg-hover); } .dropdown.open .dropdown-menu { display: block; } } -/* End: 强制桌面端布局 */ \ No newline at end of file +/* End: 强制桌面端布局 */