/** * ============================================ * 🎨 企业级通用组件样式库 * ============================================ * 参考:Ant Design、Material Design、iOS HIG */ @import './design-tokens.wxss'; /* ==================== 按钮组件 ==================== */ .btn { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); padding: var(--space-4) var(--space-8); border-radius: var(--radius-xl); font-size: var(--font-size-base); font-weight: var(--font-weight-medium); line-height: var(--line-height-tight); transition: var(--transition-base); border: none; outline: none; overflow: hidden; user-select: none; -webkit-tap-highlight-color: transparent; } .btn::before { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.3); transform: translate(-50%, -50%); transition: width var(--duration-moderate) var(--ease-out), height var(--duration-moderate) var(--ease-out); } .btn:active::before { width: 200%; height: 200%; } /* 主要按钮 */ .btn-primary { background: var(--gradient-purple-dream); color: var(--text-inverse); box-shadow: var(--shadow-primary); } .btn-primary:active { transform: translateY(2rpx); box-shadow: var(--shadow-sm); } /* 次要按钮 */ .btn-secondary { background: var(--bg-secondary); color: var(--text-primary); box-shadow: var(--shadow-sm); } /* 幽灵按钮 */ .btn-ghost { background: transparent; border: 2rpx solid var(--border-base); color: var(--text-primary); } /* 文本按钮 */ .btn-text { background: transparent; color: var(--brand-primary); padding: var(--space-2) var(--space-4); } /* 危险按钮 */ .btn-danger { background: linear-gradient(135deg, #FF6B6B 0%, #FF4757 100%); color: var(--text-inverse); box-shadow: var(--shadow-error); } /* 按钮尺寸 */ .btn-large { padding: var(--space-6) var(--space-12); font-size: var(--font-size-lg); border-radius: var(--radius-2xl); } .btn-small { padding: var(--space-2) var(--space-6); font-size: var(--font-size-sm); border-radius: var(--radius-md); } .btn-mini { padding: var(--space-1) var(--space-4); font-size: var(--font-size-xs); border-radius: var(--radius-sm); } /* 块级按钮 */ .btn-block { width: 100%; } /* 禁用状态 */ .btn-disabled { opacity: 0.4; pointer-events: none; } /* 加载状态 */ .btn-loading { pointer-events: none; } .btn-loading::after { content: ''; display: inline-block; width: 28rpx; height: 28rpx; margin-left: var(--space-2); border: 3rpx solid currentColor; border-right-color: transparent; border-radius: 50%; animation: btn-spin 0.6s linear infinite; } @keyframes btn-spin { to { transform: rotate(360deg); } } /* ==================== 卡片组件 ==================== */ .card { background: var(--bg-primary); border-radius: var(--radius-2xl); box-shadow: var(--shadow-sm); overflow: hidden; transition: var(--transition-base); } .card-hover { cursor: pointer; } .card-hover:active { transform: scale(0.98); box-shadow: var(--shadow-md); } /* 卡片头部 */ .card-header { padding: var(--space-6); border-bottom: 1rpx solid var(--divider); } .card-title { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); color: var(--text-primary); } .card-subtitle { margin-top: var(--space-1); font-size: var(--font-size-sm); color: var(--text-tertiary); } /* 卡片内容 */ .card-body { padding: var(--space-6); } /* 卡片底部 */ .card-footer { padding: var(--space-6); border-top: 1rpx solid var(--divider); background: var(--bg-secondary); } /* 渐变卡片 */ .card-gradient { background: var(--gradient-purple-dream); color: var(--text-inverse); box-shadow: var(--shadow-primary); } /* 毛玻璃卡片 */ .card-glass { background: var(--glass-white); backdrop-filter: blur(var(--blur-lg)); border: 1rpx solid var(--glass-border); box-shadow: var(--glass-shadow); } /* 边框卡片 */ .card-bordered { border: 1rpx solid var(--border-base); box-shadow: none; } /* 无阴影卡片 */ .card-flat { box-shadow: none; } /* ==================== 头像组件 ==================== */ .avatar { display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-full); overflow: hidden; background: var(--bg-tertiary); color: var(--text-tertiary); font-weight: var(--font-weight-medium); } .avatar-sm { width: 48rpx; height: 48rpx; font-size: var(--font-size-xs); } .avatar-base { width: 64rpx; height: 64rpx; font-size: var(--font-size-sm); } .avatar-lg { width: 80rpx; height: 80rpx; font-size: var(--font-size-base); } .avatar-xl { width: 120rpx; height: 120rpx; font-size: var(--font-size-lg); } .avatar-2xl { width: 160rpx; height: 160rpx; font-size: var(--font-size-xl); } .avatar-img { width: 100%; height: 100%; object-fit: cover; } /* 头像组 */ .avatar-group { display: inline-flex; align-items: center; } .avatar-group .avatar { margin-left: calc(var(--space-2) * -1); border: 3rpx solid var(--bg-primary); } .avatar-group .avatar:first-child { margin-left: 0; } /* ==================== 徽标组件 ==================== */ .badge { position: relative; display: inline-flex; } .badge-dot { position: absolute; top: 0; right: 0; width: 16rpx; height: 16rpx; border-radius: 50%; background: var(--color-error); border: 3rpx solid var(--bg-primary); } .badge-count { position: absolute; top: -8rpx; right: -8rpx; min-width: 32rpx; height: 32rpx; padding: 0 8rpx; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-full); background: var(--color-error); color: var(--text-inverse); font-size: var(--font-size-xs); font-weight: var(--font-weight-medium); line-height: 1; white-space: nowrap; border: 2rpx solid var(--bg-primary); box-shadow: var(--shadow-sm); } /* 标签徽标 */ .tag { display: inline-flex; align-items: center; gap: var(--space-1); padding: var(--space-1) var(--space-3); border-radius: var(--radius-base); font-size: var(--font-size-xs); font-weight: var(--font-weight-medium); line-height: var(--line-height-tight); white-space: nowrap; } .tag-primary { background: rgba(102, 126, 234, 0.1); color: var(--brand-primary); } .tag-success { background: var(--color-success-bg); color: var(--color-success); } .tag-warning { background: var(--color-warning-bg); color: var(--color-warning); } .tag-error { background: var(--color-error-bg); color: var(--color-error); } .tag-info { background: var(--color-info-bg); color: var(--color-info); } /* ==================== 分割线组件 ==================== */ .divider { height: 1rpx; background: var(--divider); border: none; margin: var(--space-6) 0; } .divider-vertical { width: 1rpx; height: auto; margin: 0 var(--space-4); display: inline-block; } .divider-text { position: relative; text-align: center; margin: var(--space-6) 0; } .divider-text::before, .divider-text::after { content: ''; position: absolute; top: 50%; width: calc(50% - 60rpx); height: 1rpx; background: var(--divider); } .divider-text::before { left: 0; } .divider-text::after { right: 0; } .divider-text-content { position: relative; padding: 0 var(--space-4); color: var(--text-tertiary); font-size: var(--font-size-sm); background: var(--bg-primary); } /* ==================== 空状态组件 ==================== */ .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-20) var(--space-6); text-align: center; } .empty-icon { font-size: 120rpx; margin-bottom: var(--space-6); opacity: 0.5; } .empty-title { font-size: var(--font-size-lg); font-weight: var(--font-weight-medium); color: var(--text-secondary); margin-bottom: var(--space-2); } .empty-description { font-size: var(--font-size-sm); color: var(--text-tertiary); line-height: var(--line-height-relaxed); max-width: 400rpx; } .empty-action { margin-top: var(--space-8); } /* ==================== 骨架屏组件 ==================== */ .skeleton { background: linear-gradient(90deg, var(--bg-tertiary) 25%, var(--bg-secondary) 50%, var(--bg-tertiary) 75% ); background-size: 200% 100%; animation: skeleton-loading 1.5s ease-in-out infinite; border-radius: var(--radius-base); } @keyframes skeleton-loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .skeleton-text { height: 28rpx; margin-bottom: var(--space-3); } .skeleton-title { height: 40rpx; width: 60%; margin-bottom: var(--space-4); } .skeleton-avatar { width: 80rpx; height: 80rpx; border-radius: var(--radius-full); } .skeleton-image { width: 100%; height: 400rpx; border-radius: var(--radius-xl); } .skeleton-button { height: 72rpx; width: 160rpx; border-radius: var(--radius-xl); } /* ==================== 加载组件 ==================== */ .loading-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-12); } .loading-spinner { width: 64rpx; height: 64rpx; border: 6rpx solid var(--border-light); border-top-color: var(--brand-primary); border-radius: 50%; animation: spinner-rotate 0.8s linear infinite; } @keyframes spinner-rotate { to { transform: rotate(360deg); } } .loading-dots { display: flex; gap: var(--space-3); } .loading-dot { width: 16rpx; height: 16rpx; border-radius: 50%; background: var(--brand-primary); animation: dot-bounce 1.4s ease-in-out infinite; } .loading-dot:nth-child(1) { animation-delay: -0.32s; } .loading-dot:nth-child(2) { animation-delay: -0.16s; } @keyframes dot-bounce { 0%, 80%, 100% { transform: scale(0); opacity: 0.5; } 40% { transform: scale(1); opacity: 1; } } .loading-text { margin-top: var(--space-4); font-size: var(--font-size-sm); color: var(--text-tertiary); } /* ==================== 遮罩组件 ==================== */ .overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: var(--bg-mask); z-index: var(--z-index-modal-backdrop); animation: overlay-fadein var(--duration-normal) var(--ease-out); } @keyframes overlay-fadein { from { opacity: 0; } to { opacity: 1; } } /* ==================== 模态框组件 ==================== */ .modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; display: flex; align-items: center; justify-content: center; padding: var(--space-8); z-index: var(--z-index-modal); animation: modal-fadein var(--duration-normal) var(--ease-out); } @keyframes modal-fadein { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } } .modal-content { width: 100%; max-width: 600rpx; background: var(--bg-primary); border-radius: var(--radius-2xl); box-shadow: var(--shadow-2xl); overflow: hidden; animation: modal-slideup var(--duration-moderate) var(--ease-out); } @keyframes modal-slideup { from { opacity: 0; transform: translateY(40rpx); } to { opacity: 1; transform: translateY(0); } } .modal-header { position: relative; padding: var(--space-8); border-bottom: 1rpx solid var(--divider); } .modal-title { font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold); color: var(--text-primary); text-align: center; } .modal-close { position: absolute; top: var(--space-6); right: var(--space-6); width: 56rpx; height: 56rpx; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-full); background: var(--bg-hover); color: var(--text-tertiary); font-size: var(--font-size-lg); transition: var(--transition-fast); } .modal-close:active { background: var(--bg-active); transform: scale(0.9); } .modal-body { padding: var(--space-8); max-height: 800rpx; overflow-y: auto; } .modal-footer { padding: var(--space-6) var(--space-8); border-top: 1rpx solid var(--divider); display: flex; gap: var(--space-4); } .modal-footer .btn { flex: 1; } /* ==================== Toast 组件 ==================== */ .toast { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: var(--space-6) var(--space-8); background: rgba(0, 0, 0, 0.8); backdrop-filter: blur(var(--blur-md)); color: var(--text-inverse); border-radius: var(--radius-xl); font-size: var(--font-size-base); text-align: center; z-index: var(--z-index-notification); animation: toast-show var(--duration-normal) var(--ease-out); max-width: 500rpx; box-shadow: var(--shadow-lg); } @keyframes toast-show { from { opacity: 0; transform: translate(-50%, -50%) scale(0.8); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } } .toast-icon { font-size: 64rpx; margin-bottom: var(--space-3); } /* ==================== 输入框组件 ==================== */ .input-group { margin-bottom: var(--space-6); } .input-label { display: block; margin-bottom: var(--space-2); font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); color: var(--text-secondary); } .input-field { width: 100%; padding: var(--space-4) var(--space-6); border: 2rpx solid var(--border-base); border-radius: var(--radius-xl); font-size: var(--font-size-base); color: var(--text-primary); background: var(--bg-primary); transition: var(--transition-fast); } .input-field:focus { border-color: var(--brand-primary); box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1); } .input-field::placeholder { color: var(--text-quaternary); } .input-error { border-color: var(--color-error); } .input-error:focus { box-shadow: 0 0 0 6rpx var(--color-error-bg); } .input-helper { margin-top: var(--space-2); font-size: var(--font-size-xs); color: var(--text-tertiary); } .input-error-text { color: var(--color-error); }