feat: add glass morphism styles and variables for enhanced UI effects

- Introduced new mixins for glass morphism effects in `mixins.module.scss`
- Added corresponding variables for glass effects in `variables.module.scss`
- Created visual effects mixins for dynamic backgrounds and shadows in `visual-effects.module.scss`
- Updated Tailwind CSS configuration to include new content paths
This commit is contained in:
danial
2025-09-09 23:36:25 +08:00
parent 254b0a3b48
commit 8104165c7c
52 changed files with 3591 additions and 4113 deletions

View File

@@ -6,7 +6,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_async_db
from app.services.link_service import LinkService
from app.services.link_service import LinksService
from app.schemas.link import (
LinkCreate,
LinkListResponse,
@@ -18,15 +18,15 @@ logger = get_logger(__name__)
router = APIRouter()
def get_link_service(db: AsyncSession = Depends(get_async_db)) -> LinkService:
def get_link_service(db: AsyncSession = Depends(get_async_db)) -> LinksService:
"""获取链接服务实例"""
return LinkService(db)
return LinksService(db)
@router.post("/", response_model=LinkResponse)
async def create_link(
link_data: LinkCreate, link_service: LinkService = Depends(get_link_service)
link_data: LinkCreate, link_service: LinksService = Depends(get_link_service)
):
"""创建新链接"""
try:
@@ -50,7 +50,7 @@ async def get_links(
min_amount: float | None = Query(None, description="最小金额"),
max_amount: float | None = Query(None, description="最大金额"),
url_pattern: str | None = Query(None, description="URL模式"),
link_service: LinkService = Depends(get_link_service),
link_service: LinksService = Depends(get_link_service),
):
"""获取链接列表"""
try:
@@ -66,7 +66,7 @@ async def get_links(
raise HTTPException(status_code=500, detail="获取链接列表失败")
@router.get("/{link_id}", response_model=LinkResponse)
async def get_link(link_id: int, link_service: LinkService = Depends(get_link_service)):
async def get_link(link_id: int, link_service: LinksService = Depends(get_link_service)):
"""获取单个链接详情"""
try:
link = await link_service.get_link(str(link_id))
@@ -83,7 +83,7 @@ async def get_link(link_id: int, link_service: LinkService = Depends(get_link_se
@router.delete("/{link_id}")
async def delete_link(
link_id: str, link_service: LinkService = Depends(get_link_service)
link_id: str, link_service: LinksService = Depends(get_link_service)
):
"""删除链接"""
try:

View File

@@ -43,6 +43,7 @@ class RedisManager:
# 任务暂停状态键
self.PAUSE_STATE_KEY = "task:pause_state"
self.PAUSE_REASON_KEY = "task:pause_reason"
self.USER_DATA_ID_KEY = "user_data:id"
RedisManager._initialized = True
@@ -258,6 +259,26 @@ class RedisManager:
is_paused, _ = await self.get_task_pause_state()
return is_paused
# 保存user_data_id
async def save_user_data_id(self, user_data_id: str) -> bool:
"""保存user_data_id"""
try:
redis_client = await self.get_redis()
await redis_client.rpush(self.USER_DATA_ID_KEY, user_data_id)
return True
except Exception as e:
logger.error(f"保存user_data_id失败: {e}")
return False
async def get_user_data_id(self) -> str | None:
"""获取user_data_id"""
try:
redis_client = await self.get_redis()
return await redis_client.lpop(self.USER_DATA_ID_KEY)
except Exception as e:
logger.error(f"获取user_data_id失败: {e}")
return None
# 创建全局单例实例
redis_manager = RedisManager()

View File

@@ -465,8 +465,8 @@ class RedisStateManager:
class TaskStateManager:
"""任务状态管理器"""
def __init__(self, state_manager: RedisStateManager) -> None:
self.state_manager = state_manager
def __init__(self, _state_manager: RedisStateManager) -> None:
self.state_manager = _state_manager
async def set_task_state(
self,
@@ -488,7 +488,7 @@ class TaskStateManager:
error_message=error_message,
**kwargs,
)
return await self.state_manager.set_state(task_state)
return await self.state_manager.set_state(task_state, ttl=60 * 60 * 24 * 1)
async def get_task_state(self, task_id: str) -> TaskState | None:
"""获取任务状态"""

View File

@@ -30,7 +30,6 @@ class Orders(BaseModel):
__table_args__ = (
Index("ix_order_results_status", "status"),
Index("ix_order_results_user_data_id", "user_data_id"),
Index("ix_order_results_order_number", "order_number"),
)
status: Mapped[OrderResultStatus] = mapped_column(
@@ -39,9 +38,6 @@ class Orders(BaseModel):
nullable=False,
comment="订单状态",
)
order_number: Mapped[str | None] = mapped_column(
String(255), nullable=True, comment="订单号"
)
final_order_url: Mapped[str | None] = mapped_column(
Text, nullable=True, comment="最终订单URL"
)
@@ -53,21 +49,21 @@ class Orders(BaseModel):
user_data_id: Mapped[str] = mapped_column(
ForeignKey("user_data.id"), nullable=False, comment="用户数据ID"
)
links_id: Mapped[str | None] = mapped_column(
ForeignKey("links.id"), nullable=True, comment="链接ID"
links_id: Mapped[str] = mapped_column(
ForeignKey("links.id"), nullable=False, comment="链接ID"
)
# 关系映射
user_data: Mapped["UserData"] = relationship(
"UserData", back_populates="order_results"
"UserData", back_populates="orders"
)
links: Mapped["Links | None"] = relationship("Links", back_populates="orders")
links: Mapped["Links"] = relationship("Links", back_populates="orders")
gift_cards: Mapped["list[GiftCards]"] = relationship(
"GiftCards", back_populates="order_result", cascade="all, delete-orphan"
"GiftCards", back_populates="orders", cascade="all, delete-orphan"
)
def __repr__(self) -> str:
return f"<Orders(id={self.id}, status={self.status.value}, order_number={self.order_number})>"
return f"<Orders(id={self.id}, status={self.status.value}, order_number={self.id})>"
@property
def is_completed(self) -> bool:

View File

@@ -142,3 +142,14 @@ class OrderRepository(BaseRepository[Orders]):
return 0
return await self.update_by_filter(filters={"id": order_ids}, status=status)
async def get_all_user_ids(self) -> list[str]:
"""
获取所有用户ID列表
Returns:
所有用户ID列表
"""
query = select(Orders.user_data_id).distinct()
result = await self.db_session.execute(query)
return [row[0] for row in result.fetchall()]

View File

@@ -72,7 +72,6 @@ class UserDataUploadResponse(BaseModel):
"""用户数据上传响应模型"""
user_data: UserDataResponse
order_task_id: str | None = Field(None, description="创建的订单任务ID")
message: str = Field(..., description="响应消息")

View File

@@ -27,7 +27,7 @@ from app.core.log import get_logger
logger = get_logger(__name__)
class LinkService:
class LinksService:
"""链接服务类"""
POOL_POSITION_KEY = "link_pool:current_position"

View File

@@ -252,22 +252,10 @@ class OrderService:
query = (
select(Orders)
.where(Orders.status == OrderResultStatus.PENDING)
.where(Orders.links_id == None)
)
result = await self.db.execute(query)
return list(result.scalars().all())
async def get_best_link_for_order(self) -> Optional[Any]:
"""获取最适合关联到订单的链接使用LinkService的轮询池"""
from app.services.link_service import LinkService
link_service = LinkService(self.db)
pool_result = await link_service.get_next_link_from_pool()
if pool_result:
return pool_result.link
return None
async def associate_link_to_order(self, order: Orders, link_id: str) -> bool:
"""将链接关联到订单"""
try:
@@ -296,6 +284,20 @@ class OrderService:
logger.error(f"更新订单状态失败: {order_id}, error: {e}")
return False
async def create_order(self, user_data_id: str, links_id: str) -> str:
"""创建新订单"""
try:
order = await self.order_repo.create(
user_data_id=user_data_id,
links_id=links_id,
status=OrderResultStatus.PENDING
)
logger.info(f"成功创建订单: {order.id}, 用户数据ID: {user_data_id}")
return order.id
except Exception as e:
logger.error(f"创建订单失败: {e}")
raise
async def process_single_order(self, order_id: str) -> bool:
"""处理单个订单(关联链接并更新状态)"""

View File

@@ -5,8 +5,6 @@ Apple订单处理服务
"""
import asyncio
import time
import uuid
import traceback
from typing import Any
from datetime import datetime
@@ -14,7 +12,7 @@ from playwright.async_api import Page
from app.core.config import get_settings
from app.core.log import get_logger
from app.models.orders import Orders, OrderResultStatus
from app.models.orders import OrderResultStatus
from app.core.state_manager import task_state_manager
from app.repositories.order_repository import OrderRepository
from app.core.database import db_manager
@@ -104,7 +102,6 @@ class AppleOrderProcessor:
await self._load_order_info()
# 使用Playwright上下文管理器
playwright_manager = DistributedPlaywrightManager()
async with playwright_manager.get_order_context(
self.order_id
@@ -114,10 +111,11 @@ class AppleOrderProcessor:
# 设置页面超时
page.set_default_timeout(60000)
# 执行订单处理流程
result = await self._execute_order_flow(page)
# # 执行订单处理流程
# result = await self._execute_order_flow(page)
result = {}
if result["success"]:
if result.get("success"):
logger.info(f"{self.thread_prefix} 订单处理成功")
return result
else:
@@ -142,8 +140,7 @@ class AppleOrderProcessor:
"""加载订单信息"""
async with db_manager.get_async_session() as session:
order_repo = OrderRepository(session)
self.order = await order_repo.get_by_id(self.order_id)
self.order = await order_repo.get_order_with_full_details(self.order_id)
if not self.order:
raise ValueError(f"订单不存在: {self.order_id}")
@@ -472,16 +469,3 @@ class AppleOrderProcessor:
# 全局实例
playwright_manager = DistributedPlaywrightManager()
def create_apple_order_processor(order_id: str) -> AppleOrderProcessor:
"""
创建Apple订单处理器实例
Args:
order_id: 订单ID
Returns:
AppleOrderProcessor: 订单处理器实例
"""
return AppleOrderProcessor(order_id)

View File

@@ -7,6 +7,7 @@ from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from celery.result import AsyncResult
from app.core.redis_manager import redis_manager
from app.repositories.repository_factory import RepositoryFactory
from app.schemas.user_data import (
UserDataCreate,
@@ -15,7 +16,7 @@ from app.schemas.user_data import (
UserDataListResponse,
UserDataUploadResponse,
UserDataStatsResponse,
UserInfoResponse,
UserInfoResponse, UserDataBase,
)
from app.models.user_data import UserData
from app.models.orders import OrderResultStatus
@@ -71,31 +72,11 @@ class UserDataService:
)
logger.info(f"用户数据创建成功: {user.id}")
# 检查是否存在可用链接
links_count = await self.repo_factory.links.count()
order_task_id = None
message = "用户数据上传成功"
if links_count > 0:
# 触发创建订单任务
try:
create_order_task = get_create_order_task()
task_result = create_order_task.delay(user.id)
order_task_id = task_result.id
message = "用户数据上传成功,订单创建任务已启动"
logger.info(f"订单创建任务已启动: {order_task_id}")
except Exception as e:
logger.error(f"启动订单创建任务失败: {str(e)}")
message = "用户数据上传成功,但订单创建任务启动失败"
else:
message = "用户数据上传成功,但当前没有可用链接"
logger.warning("没有可用链接,跳过订单创建")
await redis_manager.save_user_data_id(user.id)
return UserDataUploadResponse(
user_data=self._convert_to_response(user),
order_task_id=order_task_id,
message=message,
message="创建成功",
)
async def get_user_data(self, user_id: str) -> UserDataResponse | None:

View File

@@ -7,7 +7,7 @@
import asyncio
import uuid
from typing import Any
from datetime import datetime, timedelta
from datetime import datetime
from celery import current_task
from celery.exceptions import Retry, WorkerLostError
@@ -21,7 +21,10 @@ from app.core.database import db_manager
from app.enums.task import OrderTaskStatus
from app.repositories.order_repository import OrderRepository
from app.models.orders import OrderResultStatus
from app.services.playwright_service import create_apple_order_processor
from app.services.link_service import LinksService
from app.services.playwright_service import create_apple_order_processor, \
AppleOrderProcessor
from app.services.user_data_service import UserDataService
logger = get_logger(__name__)
@@ -42,10 +45,10 @@ def process_apple_order(
dict[str, Any]: 处理结果
"""
task_id = getattr(current_task.request, "id", "unknown")
worker_id = worker_id or f"worker_{uuid.uuid4().hex[:8]}"
worker_id = order_id
logger.info(
f"开始处理Apple订单: order_id={order_id}, task_id={task_id}, worker_id={worker_id}"
f"开始处理Apple订单: task_id={task_id}, worker_id={worker_id}"
)
# 使用asyncio运行异步任务
@@ -54,7 +57,7 @@ def process_apple_order(
try:
result = loop.run_until_complete(
_process_apple_order_async(self, order_id, task_id, worker_id)
_process_apple_order_async(self, order_id, task_id)
)
return result
finally:
@@ -62,7 +65,7 @@ def process_apple_order(
async def _process_apple_order_async(
task_instance, order_id: str, task_id: str, worker_id: str
task_instance, order_id: str, task_id: str
) -> dict[str, Any]:
"""异步处理Apple订单"""
@@ -101,43 +104,26 @@ async def _process_apple_order_async(
await task_state_manager.set_task_state(
task_id=task_id,
status=OrderTaskStatus.RUNNING,
worker_id=worker_id,
order_id=order_id,
worker_id=order_id,
progress=0.0,
started_at=datetime.now().timestamp(),
)
# 验证订单状态
async with db_manager.get_async_session() as session:
order_repo = OrderRepository(session)
order = await order_repo.get_by_id(order_id)
if not order:
raise ValueError(f"订单不存在: {order_id}")
if order.status != OrderResultStatus.PENDING:
logger.warning(f"订单状态不是待处理: {order_id}, status={order.status}")
return {
"success": False,
"message": "订单状态不正确",
"order_id": order_id,
}
# 创建Apple订单处理器
processor = create_apple_order_processor(order_id)
processor = AppleOrderProcessor(order_id)
# 执行订单处理
result = await processor.process_order()
# 更新最终状态
if result["success"]:
if result.get("success"):
await task_state_manager.complete_task(task_id, result, success=True)
logger.info(f"Apple订单处理成功: {order_id}")
else:
await task_state_manager.fail_task(task_id, result.get("error", "未知错误"))
logger.error(f"Apple订单处理失败: {order_id}, error: {result.get('error')}")
return result
return {}
except Exception as e:
logger.error(f"处理Apple订单异常: {order_id}, error: {e}")
@@ -171,12 +157,11 @@ async def _process_apple_order_async(
@celery_app.task(name="app.tasks.crawler_tasks.batch_process_orders")
def batch_process_orders() -> dict[str, Any]:
def batch_process_orders():
"""
批量处理订单任务
Args:
order_ids: 订单ID列表如果为None则从数据库获取所有pending状态的订单
Returns:
dict[str, Any]: 批量处理结果
@@ -190,24 +175,16 @@ def batch_process_orders() -> dict[str, Any]:
loop.close()
async def _batch_process_orders_async() -> dict[str, Any]:
async def _batch_process_orders_async():
"""异步批量处理订单"""
# 检查任务是否暂停
if await redis_manager.is_task_paused():
is_paused, reason = await redis_manager.get_task_pause_state()
logger.info(f"任务已暂停,跳过批量处理: {reason}")
return {
"success": True,
"is_paused": True,
"pause_reason": reason,
"message": "任务已暂停",
"total": 0,
"success": 0,
"failed": 0,
"processing": 0,
"no_links_available": 0,
}
return None
# 获取数据库会话
async with db_manager.get_async_session() as session:
@@ -215,50 +192,30 @@ async def _batch_process_orders_async() -> dict[str, Any]:
from app.services.order_business_service import OrderService
order_service = OrderService(session)
user_service = UserDataService(session)
links_service = LinksService(session)
pending_orders = await order_service.get_pending_orders()
order_ids = [str(order.id) for order in pending_orders]
while True:
user_data_id = await redis_manager.get_user_data_id()
if not user_data_id:
return None
# 检查用户数据是否存在
user_info = await user_service.get_user_info(user_data_id)
if not user_info:
return None
# 检查是否可以获取到链接
link_info = await links_service.get_next_link_from_pool()
if not link_info:
return None
success_count = 0
failed_count = 0
no_links_count = 0
for order_id in order_ids:
# 再次检查暂停状态(处理长时间运行的任务)
if await redis_manager.is_task_paused():
logger.info(f"处理过程中任务暂停,停止后续处理")
break
# 关联订单
result = await order_service.process_single_order(order_id)
if result:
try:
# 开始创建订单
order_id = await order_service.create_order(user_data_id, link_info.link.id)
process_apple_order.delay(order_id)
success_count += 1
else:
# 检查是否因为没有可用链接而失败
order = await order_service.order_repo.get_by_id(order_id)
if order and not order.links_id:
no_links_count += 1
else:
failed_count += 1
except Exception as e:
logger.error(f"创建订单失败: {e}")
results = {
"success": True,
"is_paused": False,
"message": "批量处理完成",
"total": len(order_ids),
"success": success_count,
"failed": failed_count,
"processing": success_count, # 成功关联的订单都进入处理中
"no_links_available": no_links_count,
}
logger.info(
f"批量订单处理完成: 总数 {results['total']}, 成功 {results['success']}, "
f"失败 {results['failed']}, 处理中 {results['processing']}, "
f"无可用链接 {results['no_links_available']}"
)
return results
# @celery_app.task(name="app.tasks.crawler_tasks.recover_stalled_orders")

View File

@@ -25,11 +25,15 @@ RUN uv sync --frozen
# 生产阶段
FROM python:3.13-slim
# 接收构建参数
ARG SERVICE_TYPE=api
# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PATH="/app/.venv/bin:$PATH" \
PLAYWRIGHT_BROWSERS_PATH=/app/playwright-browsers
PLAYWRIGHT_BROWSERS_PATH=/app/playwright-browsers \
SERVICE_TYPE=$SERVICE_TYPE
# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
@@ -63,9 +67,11 @@ COPY .env.production .env
COPY test_gunicorn.py ./
COPY run.py ./
# 安装Playwright浏览器
RUN /app/.venv/bin/playwright install chromium
RUN /app/.venv/bin/playwright install-deps chromium
# 条件安装Playwright浏览器 - 仅在worker服务中安装
RUN if [ "$SERVICE_TYPE" = "worker" ]; then \
/app/.venv/bin/playwright install chromium && \
/app/.venv/bin/playwright install-deps chromium; \
fi
# 更改文件所有权
RUN chown -R appuser:appuser /app

View File

@@ -1,14 +1,11 @@
version: '3.8'
services:
# FastAPI API服务
api:
build:
context: ..
dockerfile: deploy/Dockerfile
container_name: apple-exchange-api
ports:
- "8000:8000"
args:
- SERVICE_TYPE=api
environment:
- SERVICE_TYPE=api
- ENVIRONMENT=production
@@ -20,30 +17,52 @@ services:
- WORKERS=4
- SCREENSHOT_DIR=/app/screenshots
- LOG_DIR=/app/logs
depends_on:
- db
- redis
- jaeger
volumes:
- ./logs:/app/logs
- ./data:/app/data
- ./screenshots:/app/screenshots
- logs:/app/logs
- data:/app/data
- screenshots:/app/screenshots
- shared_storage:/app/shared
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/v1/health/liveness"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
mode: replicated
replicas: 2
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
rollback_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
placement:
constraints:
- node.role == worker
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
# Celery Worker服务 (可扩展多个副本)
worker:
build:
context: ..
dockerfile: deploy/Dockerfile
args:
- SERVICE_TYPE=worker
environment:
- SERVICE_TYPE=worker
- ENVIRONMENT=production
@@ -57,33 +76,53 @@ services:
- SCREENSHOT_DIR=/app/screenshots
- LOG_DIR=/app/logs
- PLAYWRIGHT_BROWSERS_PATH=/app/playwright-browsers
depends_on:
- db
- redis
volumes:
- ./logs:/app/logs
- ./data:/app/data
- ./screenshots:/app/screenshots
- logs:/app/logs
- data:/app/data
- screenshots:/app/screenshots
- shared_storage:/app/shared
- playwright_browsers:/app/playwright-browsers
networks:
- app-network
restart: unless-stopped
deploy:
replicas: 2 # 默认启动2个worker副本
healthcheck:
test: ["CMD", "python", "-c", "from app.core.celery_app import get_celery_app; app = get_celery_app(); print('Worker healthy')"]
interval: 60s
timeout: 30s
retries: 3
start_period: 60s
deploy:
mode: replicated
replicas: 4
update_config:
parallelism: 2
delay: 10s
failure_action: rollback
rollback_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
placement:
constraints:
- node.role == worker
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '1.0'
memory: 1G
# Celery Beat调度服务
beat:
build:
context: ..
dockerfile: deploy/Dockerfile
container_name: apple-exchange-beat
args:
- SERVICE_TYPE=beat
environment:
- SERVICE_TYPE=beat
- ENVIRONMENT=production
@@ -91,22 +130,41 @@ services:
- REDIS_URL=redis://redis:6379/0
- CELERY_BROKER_URL=redis://redis:6379/0
- CELERY_RESULT_BACKEND=redis://redis:6379/1
depends_on:
- db
- redis
volumes:
- ./logs:/app/logs
- ./data:/app/data
- logs:/app/logs
- data:/app/data
networks:
- app-network
restart: unless-stopped
deploy:
mode: replicated
replicas: 1
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
placement:
constraints:
- node.role == manager
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.2'
memory: 256M
# Celery Flower监控服务
flower:
build:
context: ..
dockerfile: deploy/Dockerfile
container_name: apple-exchange-flower
args:
- SERVICE_TYPE=flower
ports:
- "5555:5555"
environment:
@@ -114,16 +172,34 @@ services:
- ENVIRONMENT=production
- REDIS_URL=redis://redis:6379/0
- CELERY_BROKER_URL=redis://redis:6379/0
depends_on:
- redis
networks:
- app-network
restart: unless-stopped
deploy:
mode: replicated
replicas: 1
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
placement:
constraints:
- node.role == manager
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.2'
memory: 256M
# PostgreSQL数据库
db:
image: postgres:15-alpine
container_name: apple-exchange-db
image: postgres:17-alpine
environment:
- POSTGRES_DB=apple_exchange
- POSTGRES_USER=postgres
@@ -135,67 +211,91 @@ services:
- "5432:5432"
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
deploy:
mode: replicated
replicas: 1
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
placement:
constraints:
- node.role == manager
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
# Redis缓存和消息代理
redis:
image: redis:7-alpine
container_name: apple-exchange-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
- ./redis.conf:/usr/local/etc/redis/redis.conf
command: redis-server /usr/local/etc/redis/redis.conf
ports:
- "6379:6379"
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
deploy:
mode: replicated
replicas: 1
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
placement:
constraints:
- node.role == manager
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.2'
memory: 256M
# Jaeger链路追踪
jaeger:
image: jaegertracing/all-in-one:latest
container_name: apple-exchange-jaeger
ports:
- "16686:16686" # Jaeger UI
- "14250:14250" # gRPC
environment:
- COLLECTOR_OTLP_ENABLED=true
networks:
- app-network
restart: unless-stopped
# Nginx反向代理
nginx:
image: nginx:alpine
container_name: apple-exchange-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- api
networks:
- app-network
restart: unless-stopped
volumes:
postgres_data:
driver: local
redis_data:
shared_storage: # 共享存储卷
playwright_browsers: # Playwright浏览器缓存
driver: local
shared_storage:
driver: local
playwright_browsers:
driver: local
logs:
driver: local
data:
driver: local
screenshots:
driver: local
networks:
app-network:
driver: bridge
driver: overlay
attachable: true

View File

@@ -6,6 +6,14 @@ SERVICE_TYPE=${SERVICE_TYPE:-api}
echo "Starting service type: $SERVICE_TYPE"
# 条件安装Playwright浏览器 - 仅在worker服务中安装
if [ "$SERVICE_TYPE" = "worker" ]; then
echo "Installing Playwright browsers for worker service..."
/app/.venv/bin/playwright install chromium
/app/.venv/bin/playwright install-deps chromium
echo "Playwright browsers installed successfully"
fi
case "$SERVICE_TYPE" in
"api")
echo "Starting FastAPI server with Gunicorn..."

View File

@@ -2,7 +2,11 @@
"permissions": {
"allow": [
"Bash(bun add:*)",
"Bash(bun run build:*)"
"Bash(bun run build:*)",
"Bash(del:*)",
"Bash(bun run dev)",
"Bash(timeout:*)",
"Bash(bun run lint)"
],
"deny": [],
"ask": []

13
frontend/.sassrc.json Normal file
View File

@@ -0,0 +1,13 @@
{
"quietDeps": true,
"silenceDeprecations": [
"import",
"global-builtin",
"mixed-decls",
"function-units",
"slash-div",
"moz-document",
"inconsistent-units"
],
"fatalDeprecations": []
}

View File

@@ -18,14 +18,12 @@
"@radix-ui/react-switch": "^1.2.5",
"@tanstack/react-query": "^5.85.5",
"@tanstack/react-table": "^8.21.3",
"@types/less": "^3.0.8",
"axios": "^1.11.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"es-toolkit": "^1.39.10",
"framer-motion": "^11.18.2",
"less": "^4.4.1",
"lodash-es": "^4.17.21",
"lucide-react": "^0.309.0",
"motion": "^12.23.12",
@@ -37,6 +35,7 @@
"react-hook-form": "^7.62.0",
"react-use-measure": "^2.1.7",
"recharts": "^2.15.4",
"sass": "^1.92.1",
"sonner": "^1.7.4",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
@@ -56,12 +55,12 @@
"eslint-config-next": "^15.4.6",
"eslint-config-prettier": "^10.1.8",
"inflected": "^2.1.0",
"less-loader": "^12.3.0",
"orval": "^7.11.2",
"pinyin": "^4.0.0",
"postcss": "^8.5.6",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.5.14",
"sass-loader": "^16.0.5",
"style-loader": "^4.0.0",
"tailwindcss": "^3.4.17",
"typescript": "^5.9.2",
@@ -305,6 +304,34 @@
"@orval/zod": ["@orval/zod@7.11.2", "https://registry.npmmirror.com/@orval/zod/-/zod-7.11.2.tgz", { "dependencies": { "@orval/core": "7.11.2", "lodash.uniq": "^4.5.0" } }, "sha512-4MzTg5Wms8/LlM3CbYu80dvCbP88bVlQjnYsBdFXuEv0K2GYkBCAhVOrmXCVrPXE89neV6ABkvWQeuKZQpkdxQ=="],
"@parcel/watcher": ["@parcel/watcher@2.5.1", "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="],
"@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="],
"@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="],
"@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="],
"@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="],
"@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="],
"@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="],
"@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="],
"@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="],
"@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="],
"@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="],
"@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="],
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@radix-ui/number": ["@radix-ui/number@1.1.1", "https://registry.npmmirror.com/@radix-ui/number/-/number-1.1.1.tgz", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
@@ -539,8 +566,6 @@
"@types/json5": ["@types/json5@0.0.29", "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
"@types/less": ["@types/less@3.0.8", "https://registry.npmmirror.com/@types/less/-/less-3.0.8.tgz", {}, "sha512-Gjm4+H9noDJgu5EdT3rUw5MhPBag46fiOy27BefvWkNL8mlZnKnCaVVVTLKj6RYXed9b62CPKnPav9govyQDzA=="],
"@types/node": ["@types/node@24.2.1", "https://registry.npmmirror.com/@types/node/-/node-24.2.1.tgz", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ=="],
"@types/prop-types": ["@types/prop-types@15.7.15", "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.15.tgz", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],
@@ -775,8 +800,6 @@
"concat-map": ["concat-map@0.0.1", "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"copy-anything": ["copy-anything@2.0.6", "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz", { "dependencies": { "is-what": "^3.14.1" } }, "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw=="],
"cross-spawn": ["cross-spawn@7.0.6", "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"css-loader": ["css-loader@7.1.2", "https://registry.npmmirror.com/css-loader/-/css-loader-7.1.2.tgz", { "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", "postcss-modules-extract-imports": "^3.1.0", "postcss-modules-local-by-default": "^4.0.5", "postcss-modules-scope": "^3.2.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", "semver": "^7.5.4" }, "peerDependencies": { "@rspack/core": "0.x || 1.x", "webpack": "^5.27.0" }, "optionalPeers": ["@rspack/core", "webpack"] }, "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA=="],
@@ -859,8 +882,6 @@
"entities": ["entities@4.5.0", "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"errno": ["errno@0.1.8", "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz", { "dependencies": { "prr": "~1.0.1" }, "bin": { "errno": "cli.js" } }, "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A=="],
"es-abstract": ["es-abstract@1.24.0", "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.0.tgz", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg=="],
"es-aggregate-error": ["es-aggregate-error@1.0.14", "https://registry.npmmirror.com/es-aggregate-error/-/es-aggregate-error-1.0.14.tgz", { "dependencies": { "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "globalthis": "^1.0.4", "has-property-descriptors": "^1.0.2", "set-function-name": "^2.0.2" } }, "sha512-3YxX6rVb07B5TV11AV5wsL7nQCHXNwoHPsQC8S4AmBiqYhyNCJ5BRKXkXyDJvs8QzXN20NgRtxe3dEEQD9NLHA=="],
@@ -1035,16 +1056,14 @@
"human-signals": ["human-signals@2.1.0", "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
"iconv-lite": ["iconv-lite@0.6.3", "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"icss-utils": ["icss-utils@5.1.0", "https://registry.npmmirror.com/icss-utils/-/icss-utils-5.1.0.tgz", { "peerDependencies": { "postcss": "^8.1.0" } }, "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA=="],
"ignore": ["ignore@5.3.2", "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"image-size": ["image-size@0.5.5", "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz", { "bin": { "image-size": "bin/image-size.js" } }, "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ=="],
"immer": ["immer@9.0.21", "https://registry.npmmirror.com/immer/-/immer-9.0.21.tgz", {}, "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA=="],
"immutable": ["immutable@5.1.3", "https://registry.npmmirror.com/immutable/-/immutable-5.1.3.tgz", {}, "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg=="],
"import-fresh": ["import-fresh@3.3.1", "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"imurmurhash": ["imurmurhash@0.1.4", "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
@@ -1115,8 +1134,6 @@
"is-weakset": ["is-weakset@2.0.4", "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
"is-what": ["is-what@3.14.1", "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz", {}, "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA=="],
"isarray": ["isarray@2.0.5", "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"isexe": ["isexe@2.0.0", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
@@ -1165,10 +1182,6 @@
"language-tags": ["language-tags@1.0.9", "https://registry.npmmirror.com/language-tags/-/language-tags-1.0.9.tgz", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
"less": ["less@4.4.1", "https://registry.npmmirror.com/less/-/less-4.4.1.tgz", { "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", "tslib": "^2.3.0" }, "optionalDependencies": { "errno": "^0.1.1", "graceful-fs": "^4.1.2", "image-size": "~0.5.0", "make-dir": "^2.1.0", "mime": "^1.4.1", "needle": "^3.1.0", "source-map": "~0.6.0" }, "bin": { "lessc": "bin/lessc" } }, "sha512-X9HKyiXPi0f/ed0XhgUlBeFfxrlDP3xR4M7768Zl+WXLUViuL9AOPPJP4nCV0tgRWvTYvpNmN0SFhZOQzy16PA=="],
"less-loader": ["less-loader@12.3.0", "https://registry.npmmirror.com/less-loader/-/less-loader-12.3.0.tgz", { "peerDependencies": { "@rspack/core": "0.x || 1.x", "less": "^3.5.0 || ^4.0.0", "webpack": "^5.0.0" }, "optionalPeers": ["@rspack/core", "webpack"] }, "sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw=="],
"leven": ["leven@3.1.0", "https://registry.npmmirror.com/leven/-/leven-3.1.0.tgz", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
"levn": ["levn@0.4.1", "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
@@ -1213,8 +1226,6 @@
"lunr": ["lunr@2.3.9", "https://registry.npmmirror.com/lunr/-/lunr-2.3.9.tgz", {}, "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="],
"make-dir": ["make-dir@2.1.0", "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", { "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" } }, "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA=="],
"markdown-it": ["markdown-it@14.1.0", "https://registry.npmmirror.com/markdown-it/-/markdown-it-14.1.0.tgz", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
@@ -1227,8 +1238,6 @@
"micromatch": ["micromatch@4.0.8", "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"mime": ["mime@1.6.0", "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
"mime-db": ["mime-db@1.52.0", "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
@@ -1257,8 +1266,6 @@
"natural-compare": ["natural-compare@1.4.0", "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"needle": ["needle@3.3.1", "https://registry.npmmirror.com/needle/-/needle-3.3.1.tgz", { "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" }, "bin": { "needle": "bin/needle" } }, "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q=="],
"neo-async": ["neo-async@2.6.2", "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="],
"next": ["next@15.4.6", "https://registry.npmmirror.com/next/-/next-15.4.6.tgz", { "dependencies": { "@next/env": "15.4.6", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.6", "@next/swc-darwin-x64": "15.4.6", "@next/swc-linux-arm64-gnu": "15.4.6", "@next/swc-linux-arm64-musl": "15.4.6", "@next/swc-linux-x64-gnu": "15.4.6", "@next/swc-linux-x64-musl": "15.4.6", "@next/swc-win32-arm64-msvc": "15.4.6", "@next/swc-win32-x64-msvc": "15.4.6", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-us++E/Q80/8+UekzB3SAGs71AlLDsadpFMXVNM/uQ0BMwsh9m3mr0UNQIfjKed8vpWXsASe+Qifrnu1oLIcKEQ=="],
@@ -1267,6 +1274,8 @@
"nimma": ["nimma@0.2.3", "https://registry.npmmirror.com/nimma/-/nimma-0.2.3.tgz", { "dependencies": { "@jsep-plugin/regex": "^1.0.1", "@jsep-plugin/ternary": "^1.0.2", "astring": "^1.8.1", "jsep": "^1.2.0" }, "optionalDependencies": { "jsonpath-plus": "^6.0.1 || ^10.1.0", "lodash.topath": "^4.5.2" } }, "sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA=="],
"node-addon-api": ["node-addon-api@7.1.1", "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
"node-fetch": ["node-fetch@2.7.0", "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"node-fetch-h2": ["node-fetch-h2@2.3.0", "https://registry.npmmirror.com/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", { "dependencies": { "http2-client": "^1.2.5" } }, "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg=="],
@@ -1329,8 +1338,6 @@
"parent-module": ["parent-module@1.0.1", "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"parse-node-version": ["parse-node-version@1.0.1", "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz", {}, "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA=="],
"path-exists": ["path-exists@4.0.0", "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-key": ["path-key@3.1.1", "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
@@ -1345,7 +1352,7 @@
"picomatch": ["picomatch@2.3.1", "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"pify": ["pify@4.0.1", "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="],
"pify": ["pify@2.3.0", "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
"pinyin": ["pinyin@4.0.0", "https://registry.npmmirror.com/pinyin/-/pinyin-4.0.0.tgz", { "dependencies": { "commander": "~1.1.1" }, "peerDependencies": { "@node-rs/jieba": "^1.6.0", "nodejieba": "^3.4.4", "segmentit": "^2.0.3" }, "optionalPeers": ["@node-rs/jieba", "nodejieba", "segmentit"], "bin": { "pinyin": "bin/pinyin" } }, "sha512-vHpV5K+vpp6XUUpZNGRDuHoN+1xcmieM3EWlH4QjSX2kkpG/gVOwpqwV9EOJ9x9c9UERFKeLml5XVSukE/PLgQ=="],
@@ -1387,8 +1394,6 @@
"proxy-from-env": ["proxy-from-env@1.1.0", "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
"prr": ["prr@1.0.1", "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz", {}, "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw=="],
"punycode": ["punycode@2.3.1", "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"punycode.js": ["punycode.js@2.3.1", "https://registry.npmmirror.com/punycode.js/-/punycode.js-2.3.1.tgz", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="],
@@ -1459,9 +1464,9 @@
"safe-stable-stringify": ["safe-stable-stringify@1.1.1", "https://registry.npmmirror.com/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", {}, "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw=="],
"safer-buffer": ["safer-buffer@2.1.2", "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"sass": ["sass@1.92.1", "https://registry.npmmirror.com/sass/-/sass-1.92.1.tgz", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ=="],
"sax": ["sax@1.4.1", "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz", {}, "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="],
"sass-loader": ["sass-loader@16.0.5", "https://registry.npmmirror.com/sass-loader/-/sass-loader-16.0.5.tgz", { "dependencies": { "neo-async": "^2.6.2" }, "peerDependencies": { "@rspack/core": "0.x || 1.x", "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "sass": "^1.3.0", "sass-embedded": "*", "webpack": "^5.0.0" }, "optionalPeers": ["@rspack/core", "node-sass", "sass", "sass-embedded", "webpack"] }, "sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw=="],
"scheduler": ["scheduler@0.23.2", "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
@@ -1705,6 +1710,8 @@
"@orval/mock/openapi3-ts": ["openapi3-ts@4.4.0", "https://registry.npmmirror.com/openapi3-ts/-/openapi3-ts-4.4.0.tgz", { "dependencies": { "yaml": "^2.5.0" } }, "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw=="],
"@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
"@stoplight/better-ajv-errors/ajv": ["ajv@8.17.1", "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
"@stoplight/json-ref-readers/tslib": ["tslib@1.14.1", "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
@@ -1753,8 +1760,6 @@
"jest-worker/supports-color": ["supports-color@8.1.1", "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
"make-dir/semver": ["semver@5.7.2", "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
"motion/framer-motion": ["framer-motion@12.23.12", "https://registry.npmmirror.com/framer-motion/-/framer-motion-12.23.12.tgz", { "dependencies": { "motion-dom": "^12.23.12", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg=="],
"next/postcss": ["postcss@8.4.31", "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
@@ -1773,8 +1778,6 @@
"prop-types/react-is": ["react-is@16.13.1", "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
"read-cache/pify": ["pify@2.3.0", "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
"schema-utils/ajv": ["ajv@8.17.1", "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
"string-width/emoji-regex": ["emoji-regex@8.0.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],

View File

@@ -28,15 +28,24 @@ const nextConfig = {
// 压缩配置
compress: true,
// 支持Less编译
// 支持Sass编译
webpack: (config, { isServer }) => {
// 添加Less支持
// 添加Sass支持
config.module.rules.push({
test: /\.less$/,
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'less-loader',
{
loader: 'sass-loader',
options: {
sassOptions: {
quietDeps: true,
silenceDeprecations: ['import'],
fatalDeprecations: []
}
}
},
],
});
@@ -53,8 +62,8 @@ const nextConfig = {
// Turbopack配置
turbopack: {
rules: {
"*.less": {
loaders: ["less-loader"],
"*.scss": {
loaders: ["sass-loader"],
as: "*.css",
},
},

View File

@@ -1,4 +1,4 @@
module.exports = {
const ovalCOnfig = {
// 爬虫监控系统 API 配置
petstore: {
prettier: true,
@@ -65,3 +65,5 @@ module.exports = {
},
},
};
export default ovalCOnfig;

View File

@@ -2,6 +2,8 @@
"name": "crawler-monitor-frontend",
"version": "0.1.0",
"private": true,
"description": "Crawler Monitor Frontend",
"type": "module",
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
@@ -27,14 +29,12 @@
"@radix-ui/react-switch": "^1.2.5",
"@tanstack/react-query": "^5.85.5",
"@tanstack/react-table": "^8.21.3",
"@types/less": "^3.0.8",
"axios": "^1.11.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"es-toolkit": "^1.39.10",
"framer-motion": "^11.18.2",
"less": "^4.4.1",
"lodash-es": "^4.17.21",
"lucide-react": "^0.309.0",
"motion": "^12.23.12",
@@ -46,6 +46,7 @@
"react-hook-form": "^7.62.0",
"react-use-measure": "^2.1.7",
"recharts": "^2.15.4",
"sass": "^1.92.1",
"sonner": "^1.7.4",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
@@ -65,12 +66,12 @@
"eslint-config-next": "^15.4.6",
"eslint-config-prettier": "^10.1.8",
"inflected": "^2.1.0",
"less-loader": "^12.3.0",
"orval": "^7.11.2",
"pinyin": "^4.0.0",
"postcss": "^8.5.6",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.5.14",
"sass-loader": "^16.0.5",
"style-loader": "^4.0.0",
"tailwindcss": "^3.4.17",
"typescript": "^5.9.2"

View File

@@ -1,6 +1,4 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
export const plugins = {
'tailwindcss': {},
'autoprefixer': {},
};

View File

@@ -1,631 +0,0 @@
/* Apple Design System - 苹果设计系统 */
/* 基于 Apple Human Interface Guidelines 和最新的 iOS/macOS 设计规范 */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Apple Design Tokens - 苹果设计令牌 */
:root {
/* Apple Color System - 苹果色彩系统 */
--apple-blue: #007AFF;
--apple-green: #34C759;
--apple-indigo: #5856D6;
--apple-orange: #FF9500;
--apple-pink: #FF2D92;
--apple-purple: #AF52DE;
--apple-red: #FF3B30;
--apple-teal: #5AC8FA;
--apple-yellow: #FFCC00;
/* Apple Gray Scale - 苹果灰度系统 */
--apple-gray-1: #8E8E93;
--apple-gray-2: #AEAEB2;
--apple-gray-3: #C7C7CC;
--apple-gray-4: #D1D1D6;
--apple-gray-5: #E5E5EA;
--apple-gray-6: #F2F2F7;
/* Dynamic Colors - 动态颜色 (支持明暗模式) */
--apple-label: #000000;
--apple-secondary-label: rgba(60, 60, 67, 0.6);
--apple-tertiary-label: rgba(60, 60, 67, 0.3);
--apple-quaternary-label: rgba(60, 60, 67, 0.18);
--apple-separator: rgba(60, 60, 67, 0.36);
--apple-opaque-separator: #C6C6C8;
--apple-system-background: #FFFFFF;
--apple-secondary-system-background: #F2F2F7;
--apple-tertiary-system-background: #FFFFFF;
--apple-system-grouped-background: #F2F2F7;
--apple-secondary-system-grouped-background: #FFFFFF;
--apple-tertiary-system-grouped-background: #F2F2F7;
--apple-system-fill: rgba(120, 120, 128, 0.2);
--apple-secondary-system-fill: rgba(120, 120, 128, 0.16);
--apple-tertiary-system-fill: rgba(118, 118, 128, 0.12);
--apple-quaternary-system-fill: rgba(116, 116, 128, 0.08);
/* Apple Glass Effects - 苹果玻璃效果 */
--apple-material-ultra-thin: rgba(255, 255, 255, 0.8);
--apple-material-thin: rgba(255, 255, 255, 0.85);
--apple-material-regular: rgba(255, 255, 255, 0.9);
--apple-material-thick: rgba(255, 255, 255, 0.95);
--apple-blur-ultra-thin: blur(2px);
--apple-blur-thin: blur(4px);
--apple-blur-regular: blur(8px);
--apple-blur-thick: blur(20px);
--apple-blur-ultra-thick: blur(40px);
/* Apple Typography Scale - 苹果字体比例 */
--apple-large-title: 34px;
--apple-title-1: 28px;
--apple-title-2: 22px;
--apple-title-3: 20px;
--apple-headline: 17px;
--apple-body: 17px;
--apple-callout: 16px;
--apple-subhead: 15px;
--apple-footnote: 13px;
--apple-caption-1: 12px;
--apple-caption-2: 11px;
/* Apple Spacing System - 苹果间距系统 */
--apple-spacing-xs: 4px;
--apple-spacing-s: 8px;
--apple-spacing-m: 16px;
--apple-spacing-l: 24px;
--apple-spacing-xl: 32px;
--apple-spacing-xxl: 44px;
/* Apple Corner Radius - 苹果圆角系统 */
--apple-radius-s: 8px;
--apple-radius-m: 12px;
--apple-radius-l: 16px;
--apple-radius-xl: 20px;
--apple-radius-continuous: 16px;
/* 连续曲线 */
/* Apple Shadows - 苹果阴影系统 */
--apple-shadow-s: 0 1px 3px rgba(0, 0, 0, 0.12);
--apple-shadow-m: 0 4px 16px rgba(0, 0, 0, 0.12);
--apple-shadow-l: 0 8px 24px rgba(0, 0, 0, 0.15);
--apple-shadow-xl: 0 16px 48px rgba(0, 0, 0, 0.18);
/* Apple Animation - 苹果动画系统 */
--apple-timing-standard: cubic-bezier(0.4, 0.0, 0.2, 1);
--apple-timing-decelerate: cubic-bezier(0.0, 0.0, 0.2, 1);
--apple-timing-accelerate: cubic-bezier(0.4, 0.0, 1, 1);
--apple-timing-sharp: cubic-bezier(0.4, 0.0, 0.6, 1);
--apple-duration-immediate: 100ms;
--apple-duration-quick: 200ms;
--apple-duration-moderate: 300ms;
--apple-duration-slow: 500ms;
/* Enhanced Color Gradients - 增强色彩渐变 */
--apple-gradient-blue: linear-gradient(135deg, #007AFF 0%, #0A84FF 100%);
--apple-gradient-green: linear-gradient(135deg, #34C759 0%, #30D158 100%);
--apple-gradient-indigo: linear-gradient(135deg, #5856D6 0%, #5E5CE6 100%);
--apple-gradient-orange: linear-gradient(135deg, #FF9500 0%, #FF9F0A 100%);
--apple-gradient-pink: linear-gradient(135deg, #FF2D92 0%, #FF375F 100%);
--apple-gradient-purple: linear-gradient(135deg, #AF52DE 0%, #BF5AF2 100%);
--apple-gradient-red: linear-gradient(135deg, #FF3B30 0%, #FF453A 100%);
--apple-gradient-teal: linear-gradient(135deg, #5AC8FA 0%, #64D2FF 100%);
--apple-gradient-yellow: linear-gradient(135deg, #FFCC00 0%, #FFD60A 100%);
/* Apple Vibrant Colors - 苹果活力色彩 */
--apple-vibrant-blue: #007AFF;
--apple-vibrant-green: #30D158;
--apple-vibrant-indigo: #5E5CE6;
--apple-vibrant-orange: #FF9F0A;
--apple-vibrant-pink: #FF375F;
--apple-vibrant-purple: #BF5AF2;
--apple-vibrant-red: #FF453A;
--apple-vibrant-teal: #64D2FF;
--apple-vibrant-yellow: #FFD60A;
/* Apple Dynamic Backgrounds - 苹果动态背景 */
--apple-dynamic-background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
--apple-dynamic-background-dark: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%);
}
/* Dark Mode - 暗色模式 */
.dark {
--apple-label: #FFFFFF;
--apple-secondary-label: rgba(235, 235, 245, 0.6);
--apple-tertiary-label: rgba(235, 235, 245, 0.3);
--apple-quaternary-label: rgba(235, 235, 245, 0.18);
--apple-separator: rgba(84, 84, 88, 0.65);
--apple-opaque-separator: #38383A;
--apple-system-background: #000000;
--apple-secondary-system-background: #1C1C1E;
--apple-tertiary-system-background: #2C2C2E;
--apple-system-grouped-background: #1C1C1E;
--apple-secondary-system-grouped-background: #2C2C2E;
--apple-tertiary-system-grouped-background: #3A3A3C;
--apple-system-fill: rgba(120, 120, 128, 0.36);
--apple-secondary-system-fill: rgba(120, 120, 128, 0.32);
--apple-tertiary-system-fill: rgba(118, 118, 128, 0.28);
--apple-quaternary-system-fill: rgba(116, 116, 128, 0.24);
--apple-material-ultra-thin: rgba(28, 28, 30, 0.8);
--apple-material-thin: rgba(28, 28, 30, 0.85);
--apple-material-regular: rgba(28, 28, 30, 0.9);
--apple-material-thick: rgba(28, 28, 30, 0.95);
/* Dark Mode Gradients - 暗色模式渐变 */
--apple-gradient-blue: linear-gradient(135deg, #0A84FF 0%, #007AFF 100%);
--apple-gradient-green: linear-gradient(135deg, #30D158 0%, #34C759 100%);
--apple-gradient-indigo: linear-gradient(135deg, #5E5CE6 0%, #5856D6 100%);
--apple-gradient-orange: linear-gradient(135deg, #FF9F0A 0%, #FF9500 100%);
--apple-gradient-pink: linear-gradient(135deg, #FF375F 0%, #FF2D92 100%);
--apple-gradient-purple: linear-gradient(135deg, #BF5AF2 0%, #AF52DE 100%);
--apple-gradient-red: linear-gradient(135deg, #FF453A 0%, #FF3B30 100%);
--apple-gradient-teal: linear-gradient(135deg, #64D2FF 0%, #5AC8FA 100%);
--apple-gradient-yellow: linear-gradient(135deg, #FFD60A 0%, #FFCC00 100%);
}
/* Base Styles - 基础样式 */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', system-ui, sans-serif;
font-feature-settings: 'kern';
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
body {
background: var(--apple-system-background);
color: var(--apple-label);
font-size: var(--apple-body);
line-height: 1.4;
transition: background-color var(--apple-duration-moderate) var(--apple-timing-standard),
color var(--apple-duration-moderate) var(--apple-timing-standard);
}
/* Apple Glass Card - 苹果玻璃卡片 */
.apple-card {
background: var(--apple-material-regular);
backdrop-filter: var(--apple-blur-regular);
-webkit-backdrop-filter: var(--apple-blur-regular);
border-radius: var(--apple-radius-continuous);
border: 0.5px solid var(--apple-separator);
box-shadow: var(--apple-shadow-m);
padding: var(--apple-spacing-m);
transition: all var(--apple-duration-moderate) var(--apple-timing-standard);
position: relative;
overflow: hidden;
}
.apple-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 0.5px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
opacity: 0.6;
}
.apple-card:hover {
background: var(--apple-material-thick);
backdrop-filter: var(--apple-blur-thick);
-webkit-backdrop-filter: var(--apple-blur-thick);
box-shadow: var(--apple-shadow-l);
transform: translateY(-2px) scale(1.02);
}
/* Apple Glass Card - 苹果玻璃卡片 (与链接管理卡片一致的样式) */
.apple-glass-card {
background: var(--apple-material-regular);
backdrop-filter: var(--apple-blur-regular);
-webkit-backdrop-filter: var(--apple-blur-regular);
border-radius: var(--apple-radius-continuous);
border: 0.5px solid var(--apple-separator);
box-shadow: var(--apple-shadow-m);
transition: all var(--apple-duration-moderate) var(--apple-timing-standard);
position: relative;
overflow: hidden;
}
.apple-glass-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 0.5px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
opacity: 0.6;
}
.apple-glass-card:hover {
background: var(--apple-material-thick);
backdrop-filter: var(--apple-blur-thick);
-webkit-backdrop-filter: var(--apple-blur-thick);
box-shadow: var(--apple-shadow-l);
transform: translateY(-2px) scale(1.02);
}
/* Apple Button - 苹果按钮 */
.apple-button {
background: var(--apple-blue);
color: white;
border: none;
border-radius: var(--apple-radius-m);
padding: var(--apple-spacing-s) var(--apple-spacing-m);
font-size: var(--apple-body);
font-weight: 600;
cursor: pointer;
transition: all var(--apple-duration-quick) var(--apple-timing-standard);
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--apple-spacing-s);
min-height: 44px;
/* Apple 推荐的最小触摸目标 */
}
.apple-button:hover {
background: color-mix(in srgb, var(--apple-blue) 85%, black);
transform: scale(1.02);
}
.apple-button:active {
transform: scale(0.98);
transition-duration: var(--apple-duration-immediate);
}
.apple-button--secondary {
background: var(--apple-system-fill);
color: var(--apple-blue);
}
.apple-button--secondary:hover {
background: var(--apple-secondary-system-fill);
}
/* Apple List - 苹果列表 */
.apple-list {
background: var(--apple-secondary-system-grouped-background);
border-radius: var(--apple-radius-l);
overflow: hidden;
box-shadow: var(--apple-shadow-s);
}
.apple-list-item {
background: var(--apple-secondary-system-grouped-background);
padding: var(--apple-spacing-m);
border-bottom: 0.5px solid var(--apple-separator);
transition: background-color var(--apple-duration-quick) var(--apple-timing-standard);
}
.apple-list-item:last-child {
border-bottom: none;
}
.apple-list-item:hover {
background: var(--apple-tertiary-system-grouped-background);
}
/* Apple Typography - 苹果字体 */
.apple-large-title {
font-size: var(--apple-large-title);
font-weight: 700;
line-height: 1.2;
letter-spacing: -0.5px;
}
.apple-title-1 {
font-size: var(--apple-title-1);
font-weight: 700;
line-height: 1.25;
letter-spacing: -0.3px;
}
.apple-title-2 {
font-size: var(--apple-title-2);
font-weight: 600;
line-height: 1.3;
letter-spacing: -0.2px;
}
.apple-title-3 {
font-size: var(--apple-title-3);
font-weight: 600;
line-height: 1.35;
}
.apple-headline {
font-size: var(--apple-headline);
font-weight: 600;
line-height: 1.4;
}
.apple-body {
font-size: var(--apple-body);
font-weight: 400;
line-height: 1.4;
}
.apple-callout {
font-size: var(--apple-callout);
font-weight: 400;
line-height: 1.4;
}
.apple-subhead {
font-size: var(--apple-subhead);
font-weight: 400;
line-height: 1.4;
color: var(--apple-secondary-label);
}
.apple-footnote {
font-size: var(--apple-footnote);
font-weight: 400;
line-height: 1.4;
color: var(--apple-secondary-label);
}
.apple-caption {
font-size: var(--apple-caption-1);
font-weight: 400;
line-height: 1.4;
color: var(--apple-tertiary-label);
}
/* Apple Icons - 苹果图标 */
.apple-icon {
width: 24px;
height: 24px;
color: var(--apple-blue);
transition: color var(--apple-duration-quick) var(--apple-timing-standard);
}
.apple-icon--secondary {
color: var(--apple-secondary-label);
}
/* Apple Navigation - 苹果导航 */
.apple-navigation {
background: var(--apple-material-regular);
backdrop-filter: var(--apple-blur-thick);
-webkit-backdrop-filter: var(--apple-blur-thick);
border-bottom: 0.5px solid var(--apple-separator);
padding: var(--apple-spacing-m);
position: sticky;
top: 0;
z-index: 100;
}
/* Apple Tab Bar - 苹果标签栏 */
.apple-tab-bar {
background: var(--apple-material-thick);
backdrop-filter: var(--apple-blur-ultra-thick);
-webkit-backdrop-filter: var(--apple-blur-ultra-thick);
border-top: 0.5px solid var(--apple-separator);
padding: var(--apple-spacing-s) var(--apple-spacing-m);
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
}
.apple-tab-item {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--apple-spacing-xs);
padding: var(--apple-spacing-s);
border-radius: var(--apple-radius-s);
transition: all var(--apple-duration-quick) var(--apple-timing-standard);
cursor: pointer;
min-width: 44px;
}
.apple-tab-item:hover {
background: var(--apple-system-fill);
}
.apple-tab-item--active {
color: var(--apple-blue);
}
/* Apple Status Indicators - 苹果状态指示器 */
.apple-status-success {
color: var(--apple-green);
}
.apple-status-warning {
color: var(--apple-orange);
}
.apple-status-error {
color: var(--apple-red);
}
.apple-status-info {
color: var(--apple-blue);
}
/* Apple Dividers - 苹果分割线 */
.apple-divider {
height: 0.5px;
background: var(--apple-separator);
margin: var(--apple-spacing-m) 0;
}
.apple-divider--thick {
height: 1px;
background: var(--apple-opaque-separator);
}
/* Apple Input - 苹果输入框 */
.apple-input {
background: var(--apple-tertiary-system-fill);
border: 1px solid var(--apple-separator);
border-radius: var(--apple-radius-m);
padding: var(--apple-spacing-s) var(--apple-spacing-m);
font-size: var(--apple-body);
color: var(--apple-label);
width: 100%;
min-height: 44px;
transition: all var(--apple-duration-quick) var(--apple-timing-standard);
}
.apple-input:focus {
outline: none;
border-color: var(--apple-blue);
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.2);
}
.apple-input::placeholder {
color: var(--apple-tertiary-label);
}
/* Apple Gradient Utilities - 苹果渐变工具类 */
.apple-gradient-blue {
background: var(--apple-gradient-blue);
color: white;
}
.apple-gradient-green {
background: var(--apple-gradient-green);
color: white;
}
.apple-gradient-indigo {
background: var(--apple-gradient-indigo);
color: white;
}
.apple-gradient-orange {
background: var(--apple-gradient-orange);
color: white;
}
.apple-gradient-pink {
background: var(--apple-gradient-pink);
color: white;
}
.apple-gradient-purple {
background: var(--apple-gradient-purple);
color: white;
}
.apple-gradient-red {
background: var(--apple-gradient-red);
color: white;
}
.apple-gradient-teal {
background: var(--apple-gradient-teal);
color: white;
}
.apple-gradient-yellow {
background: var(--apple-gradient-yellow);
color: black;
}
/* Apple Vibrant Colors - 苹果活力色彩工具类 */
.apple-vibrant-blue {
color: var(--apple-vibrant-blue);
}
.apple-vibrant-green {
color: var(--apple-vibrant-green);
}
.apple-vibrant-indigo {
color: var(--apple-vibrant-indigo);
}
.apple-vibrant-orange {
color: var(--apple-vibrant-orange);
}
.apple-vibrant-pink {
color: var(--apple-vibrant-pink);
}
.apple-vibrant-purple {
color: var(--apple-vibrant-purple);
}
.apple-vibrant-red {
color: var(--apple-vibrant-red);
}
.apple-vibrant-teal {
color: var(--apple-vibrant-teal);
}
.apple-vibrant-yellow {
color: var(--apple-vibrant-yellow);
}
/* Apple Dynamic Backgrounds - 苹果动态背景 */
.apple-dynamic-bg {
background: var(--apple-dynamic-background);
}
.dark .apple-dynamic-bg {
background: var(--apple-dynamic-background-dark);
}
/* Accessibility - 无障碍功能 */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
@media (prefers-contrast: high) {
.apple-card {
border-width: 2px;
}
.apple-button {
border: 2px solid currentColor;
}
}
/* Focus indicators for keyboard navigation */
*:focus-visible {
outline: 2px solid var(--apple-blue);
outline-offset: 2px;
border-radius: var(--apple-radius-s);
}
/* Hide focus indicators for mouse users */
*:focus:not(:focus-visible) {
outline: none;
}
/* Reset any existing styles */
.stat-card,
.circular-progress {
all: unset;
display: block;
}

View File

@@ -1,4 +1,4 @@
import "../styles/less/main.less";
import "../styles/sass/main.module.scss";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import TelemetryInitializer from "@/lib/telemetry/telemetry-initializer";

View File

@@ -4,66 +4,211 @@ import React from 'react';
import { motion } from 'framer-motion';
/**
* 页面加载动画组件
* Apple 风格页面加载动画组件
* 遵循 Apple Human Interface Guidelines
*/
export function LoadingScreen() {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
className="fixed inset-0 flex flex-col items-center justify-center bg-white dark:bg-gray-950 z-50"
transition={{ duration: 0.3, ease: [0.4, 0.0, 0.2, 1] }}
className="fixed inset-0 flex flex-col items-center justify-center z-50"
style={{
background: 'linear-gradient(135deg, #f5f5f7 0%, #e8e8ed 100%)'
}}
>
{/* Apple 风格玻璃背景卡片 */}
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.5 }}
className="flex flex-col items-center"
transition={{
duration: 0.6,
ease: [0.34, 1.56, 0.64, 1],
delay: 0.1
}}
className="apple-glass-card premium max-w-sm w-full mx-4 text-center"
style={{
backdropFilter: 'blur(40px) saturate(1.8)',
WebkitBackdropFilter: 'blur(40px) saturate(1.8)'
}}
>
<div className="relative w-10 h-10 mb-4">
{/* Apple 风格加载动画 */}
<div className="flex justify-center mb-6">
<div className="relative">
{/* 外圈脉冲效果 */}
<motion.div
animate={{
scale: [1, 1.3, 1],
opacity: [0.8, 0.3, 0.8],
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut",
}}
className="absolute inset-0 w-16 h-16 rounded-full border-2 border-blue-500"
style={{ borderColor: 'rgba(0, 122, 255, 0.6)' }}
/>
{/* 中圈旋转效果 */}
<motion.div
animate={{ rotate: 360 }}
transition={{
duration: 1.5,
repeat: Infinity,
ease: "linear",
}}
className="absolute inset-2 w-12 h-12 rounded-full border-2 border-transparent border-t-blue-500"
style={{ borderTopColor: 'rgba(0, 122, 255, 0.8)' }}
/>
{/* 内圈核心 */}
<motion.div
animate={{
scale: [1, 1.1, 1],
opacity: [0.9, 1, 0.9],
}}
transition={{
duration: 1.2,
repeat: Infinity,
ease: "easeInOut",
}}
className="absolute inset-4 w-8 h-8 rounded-full bg-gradient-to-br from-blue-500 to-blue-600"
style={{
background: 'linear-gradient(135deg, #007AFF 0%, #0051D5 100%)'
}}
/>
{/* 高光效果 */}
<div className="absolute inset-4 w-8 h-8 rounded-full bg-white opacity-30 pointer-events-none"
style={{
background: 'radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.8) 0%, transparent 50%)'
}}
/>
</div>
</div>
{/* 标题文字 */}
<motion.h2
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.4,
ease: [0.4, 0.0, 0.2, 1],
delay: 0.3
}}
className="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-2"
style={{
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
}}
>
</motion.h2>
{/* 副标题 */}
<motion.p
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.4,
ease: [0.4, 0.0, 0.2, 1],
delay: 0.4
}}
className="text-sm text-gray-600 dark:text-gray-400 mb-4"
>
...
</motion.p>
{/* 进度指示器 */}
<motion.div
initial={{ opacity: 0, scaleX: 0 }}
animate={{ opacity: 1, scaleX: 1 }}
transition={{
duration: 0.6,
ease: [0.4, 0.0, 0.2, 1],
delay: 0.5
}}
className="w-full h-1 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden"
>
<motion.div
animate={{
scale: [1, 1.2, 1],
opacity: [0.5, 1, 0.5],
}}
animate={{ x: ['-100%', '100%'] }}
transition={{
duration: 2,
duration: 1.5,
repeat: Infinity,
ease: "easeInOut",
}}
className="absolute inset-0 rounded-full bg-gradient-to-r from-gray-700 to-gray-900"
/>
<motion.div
animate={{ rotate: 360 }}
transition={{
duration: 2,
repeat: Infinity,
ease: "linear",
className="h-full w-1/2 bg-gradient-to-r from-blue-500 to-blue-600"
style={{
background: 'linear-gradient(90deg, #007AFF 0%, #5856D6 50%, #007AFF 100%)'
}}
className="absolute inset-2 rounded-full border-t-4 border-gray-300"
/>
</div>
<motion.h1
animate={{
opacity: [0.5, 1, 0.5],
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut",
}}
className="text-2xl font-bold text-gray-800 dark:text-gray-200"
>
...
</motion.h1>
<motion.p
</motion.div>
{/* 状态文字 */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5, duration: 0.5 }}
className="mt-2 text-sm text-gray-500 dark:text-gray-400"
transition={{
duration: 0.4,
ease: [0.4, 0.0, 0.2, 1],
delay: 0.6
}}
className="mt-4 flex justify-center space-x-1"
>
</motion.p>
{['●', '●', '●'].map((dot, index) => (
<motion.span
key={index}
animate={{
opacity: [0.3, 1, 0.3],
scale: [0.8, 1, 0.8],
}}
transition={{
duration: 1.5,
repeat: Infinity,
delay: index * 0.2,
ease: "easeInOut",
}}
className="text-blue-500 text-lg"
style={{ color: 'rgba(0, 122, 255, 0.8)' }}
>
{dot}
</motion.span>
))}
</motion.div>
</motion.div>
{/* 背景装饰元素 */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 0.1 }}
transition={{ duration: 1, delay: 0.8 }}
className="absolute inset-0 pointer-events-none"
>
{/* 装饰圆圈 */}
{[...Array(6)].map((_, i) => (
<motion.div
key={i}
animate={{
scale: [1, 1.2, 1],
opacity: [0.1, 0.05, 0.1],
}}
transition={{
duration: 3 + i * 0.5,
repeat: Infinity,
delay: i * 0.3,
ease: "easeInOut",
}}
className="absolute rounded-full border border-gray-300"
style={{
width: `${100 + i * 50}px`,
height: `${100 + i * 50}px`,
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
}}
/>
))}
</motion.div>
</motion.div>
);

View File

@@ -4,23 +4,23 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const appleButtonVariants = cva(
"apple-button inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-blue-500/30 disabled:pointer-events-none disabled:opacity-30 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
"apple-button inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/20 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-blue-600 text-white hover:bg-blue-700 hover:shadow-lg hover:-translate-y-0.5 active:translate-y-0",
destructive: "bg-red-600 text-white hover:bg-red-700 hover:shadow-lg hover:-translate-y-0.5 active:translate-y-0",
outline: "bg-gray-100 text-gray-900 border border-gray-300 hover:bg-gray-200 hover:-translate-y-0.5 dark:bg-gray-800 dark:text-gray-100 dark:border-gray-600 dark:hover:bg-gray-700",
secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 hover:-translate-y-0.5 dark:bg-gray-800 dark:text-gray-100 dark:hover:bg-gray-700",
ghost: "bg-transparent text-blue-600 hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-blue-900/20",
link: "bg-transparent text-blue-600 underline-offset-4 hover:underline dark:text-blue-400",
glass: "glass-effect text-gray-900 dark:text-gray-100 hover:backdrop-blur-lg",
default: "bg-blue-600 text-white hover:bg-blue-700 focus:bg-blue-700 active:bg-blue-800 shadow-sm hover:shadow-md active:shadow-sm",
destructive: "bg-red-600 text-white hover:bg-red-700 focus:bg-red-700 active:bg-red-800 shadow-sm hover:shadow-md active:shadow-sm",
outline: "bg-white/80 text-gray-900 border border-gray-300 hover:bg-gray-50 focus:bg-gray-50 active:bg-gray-100 backdrop-blur-sm dark:bg-gray-800/80 dark:text-gray-100 dark:border-gray-600 dark:hover:bg-gray-700 dark:focus:bg-gray-700 dark:active:bg-gray-600",
secondary: "bg-gray-100/80 text-gray-900 hover:bg-gray-200 focus:bg-gray-200 active:bg-gray-300 backdrop-blur-sm dark:bg-gray-800/80 dark:text-gray-100 dark:hover:bg-gray-700 dark:focus:bg-gray-700 dark:active:bg-gray-600",
ghost: "bg-transparent text-blue-600 hover:bg-blue-50 focus:bg-blue-50 active:bg-blue-100 dark:text-blue-400 dark:hover:bg-blue-900/30 dark:focus:bg-blue-900/30 dark:active:bg-blue-900/40",
link: "bg-transparent text-blue-600 underline-offset-4 hover:underline focus:underline dark:text-blue-400",
glass: "bg-white/20 backdrop-blur-md border border-white/30 text-gray-900 dark:text-gray-100 hover:bg-white/30 focus:bg-white/30 active:bg-white/40 shadow-sm",
},
size: {
default: "h-11 px-6 py-3 rounded-xl text-base",
sm: "h-8 px-4 py-2 rounded-lg text-sm",
lg: "h-14 px-8 py-4 rounded-2xl text-lg",
icon: "h-11 w-11 rounded-xl",
default: "h-11 px-6 py-3 rounded-[0.75rem] text-base font-medium",
sm: "h-9 px-4 py-2 rounded-[0.625rem] text-sm font-medium",
lg: "h-12 px-8 py-4 rounded-[0.875rem] text-lg font-medium",
icon: "h-11 w-11 rounded-[0.75rem]",
},
},
defaultVariants: {

View File

@@ -62,7 +62,6 @@ export * from "./userDataCreate";
export * from "./userDataListResponse";
export * from "./userDataResponse";
export * from "./userDataUploadResponse";
export * from "./userDataUploadResponseOrderTaskId";
export * from "./userInfoResponse";
export * from "./validationError";
export * from "./validationErrorLocItem";

View File

@@ -6,15 +6,12 @@
* OpenAPI spec version: 2.0.0
*/
import type { UserDataResponse } from "./userDataResponse";
import type { UserDataUploadResponseOrderTaskId } from "./userDataUploadResponseOrderTaskId";
/**
* 用户数据上传响应模型
*/
export interface UserDataUploadResponse {
user_data: UserDataResponse;
/** 创建的订单任务ID */
order_task_id?: UserDataUploadResponseOrderTaskId;
/** 响应消息 */
message: string;
}

View File

@@ -1,12 +0,0 @@
/**
* Generated by orval v7.11.2 🍺
* Do not edit manually.
* Apple Gift Card Exchange
* Apple礼品卡兑换服务后端API - 基于FastAPI的现代异步微服务架构
* OpenAPI spec version: 2.0.0
*/
/**
* 创建的订单任务ID
*/
export type UserDataUploadResponseOrderTaskId = string | null;

View File

@@ -1,3 +0,0 @@
import { useGetUserDataListApiV1UserDataGet } from "./generated/user-data-management.gen";
import type { GetUserDataListApiV1UserDataGetParams } from "./generated/schemas/getUserDataListApiV1UserDataGetParams";

View File

@@ -1,447 +0,0 @@
// Apple Design System - Less Version
// 基于苹果人机界面指南的设计系统
// ===== CSS自定义属性定义 =====
:root {
// 苹果HIG颜色系统
--apple-blue: #007AFF;
--apple-green: #34C759;
--apple-indigo: #5856D6;
--apple-orange: #FF9500;
--apple-pink: #FF2D92;
--apple-purple: #AF52DE;
--apple-red: #FF3B30;
--apple-teal: #5AC8FA;
--apple-yellow: #FFCC00;
// 灰度系统
--apple-gray: #8E8E93;
--apple-gray2: #AEAEB2;
--apple-gray3: #C7C7CC;
--apple-gray4: #D1D1D6;
--apple-gray5: #E5E5EA;
--apple-gray6: #F2F2F7;
// 语义化颜色
--apple-label: #000000;
--apple-secondary-label: rgba(60, 60, 67, 0.6);
--apple-tertiary-label: rgba(60, 60, 67, 0.3);
--apple-quaternary-label: rgba(60, 60, 67, 0.18);
// 背景颜色
--apple-system-background: #FFFFFF;
--apple-secondary-system-background: #F2F2F7;
--apple-tertiary-system-background: #FFFFFF;
// 分组背景
--apple-system-grouped-background: #F2F2F7;
--apple-secondary-system-grouped-background: #FFFFFF;
--apple-tertiary-system-grouped-background: #F2F2F7;
// 填充颜色
--apple-system-fill: rgba(120, 120, 128, 0.2);
--apple-secondary-system-fill: rgba(120, 120, 128, 0.16);
--apple-tertiary-system-fill: rgba(118, 118, 128, 0.12);
--apple-quaternary-system-fill: rgba(116, 116, 128, 0.08);
// 分隔符
--apple-separator: rgba(60, 60, 67, 0.29);
--apple-opaque-separator: #C6C6C8;
// 阴影
--apple-shadow-color: rgba(0, 0, 0, 0.1);
--apple-shadow-elevated: 0 4px 16px rgba(0, 0, 0, 0.12);
--apple-shadow-card: 0 2px 8px rgba(0, 0, 0, 0.08);
// 圆角
--apple-corner-radius-small: 8px;
--apple-corner-radius-medium: 12px;
--apple-corner-radius-large: 16px;
--apple-corner-radius-extra-large: 24px;
// 间距
--apple-spacing-xs: 4px;
--apple-spacing-sm: 8px;
--apple-spacing-md: 16px;
--apple-spacing-lg: 24px;
--apple-spacing-xl: 32px;
--apple-spacing-xxl: 48px;
// 字体
--apple-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--apple-font-size-caption2: 11px;
--apple-font-size-caption1: 12px;
--apple-font-size-footnote: 13px;
--apple-font-size-subheadline: 15px;
--apple-font-size-callout: 16px;
--apple-font-size-body: 17px;
--apple-font-size-headline: 17px;
--apple-font-size-title3: 20px;
--apple-font-size-title2: 22px;
--apple-font-size-title1: 28px;
--apple-font-size-large-title: 34px;
// 动画
--apple-animation-duration-fast: 0.2s;
--apple-animation-duration-normal: 0.3s;
--apple-animation-duration-slow: 0.5s;
--apple-animation-easing: cubic-bezier(0.25, 0.1, 0.25, 1);
}
// 暗色主题
[data-theme="dark"] {
--apple-label: #FFFFFF;
--apple-secondary-label: rgba(235, 235, 245, 0.6);
--apple-tertiary-label: rgba(235, 235, 245, 0.3);
--apple-quaternary-label: rgba(235, 235, 245, 0.18);
--apple-system-background: #000000;
--apple-secondary-system-background: #1C1C1E;
--apple-tertiary-system-background: #2C2C2E;
--apple-system-grouped-background: #000000;
--apple-secondary-system-grouped-background: #1C1C1E;
--apple-tertiary-system-grouped-background: #2C2C2E;
--apple-system-fill: rgba(120, 120, 128, 0.36);
--apple-secondary-system-fill: rgba(120, 120, 128, 0.32);
--apple-tertiary-system-fill: rgba(118, 118, 128, 0.28);
--apple-quaternary-system-fill: rgba(116, 116, 128, 0.24);
--apple-separator: rgba(84, 84, 88, 0.65);
--apple-opaque-separator: #38383A;
--apple-shadow-color: rgba(0, 0, 0, 0.3);
}
// ===== 基础样式重置 =====
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--apple-font-family);
font-size: var(--apple-font-size-body);
line-height: 1.4;
color: var(--apple-label);
background-color: var(--apple-system-background);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
// ===== 按钮组件 =====
.apple-button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px 24px;
border: none;
border-radius: var(--apple-corner-radius-medium);
font-family: var(--apple-font-family);
font-size: var(--apple-font-size-body);
font-weight: 600;
text-decoration: none;
cursor: pointer;
transition: all var(--apple-animation-duration-fast) var(--apple-animation-easing);
position: relative;
overflow: hidden;
&:focus {
outline: none;
box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.3);
}
&:disabled {
opacity: 0.3;
cursor: not-allowed;
}
// 主要按钮
&.primary {
background-color: var(--apple-blue);
color: white;
&:hover:not(:disabled) {
background-color: #0056CC;
transform: translateY(-1px);
box-shadow: var(--apple-shadow-elevated);
}
&:active {
transform: translateY(0);
}
}
// 次要按钮
&.secondary {
background-color: var(--apple-secondary-system-fill);
color: var(--apple-label);
&:hover:not(:disabled) {
background-color: var(--apple-tertiary-system-fill);
transform: translateY(-1px);
}
}
// 危险按钮
&.destructive {
background-color: var(--apple-red);
color: white;
&:hover:not(:disabled) {
background-color: #D70015;
transform: translateY(-1px);
}
}
// 文本按钮
&.text {
background-color: transparent;
color: var(--apple-blue);
padding: 8px 16px;
&:hover:not(:disabled) {
background-color: rgba(0, 122, 255, 0.1);
}
}
}
// ===== 卡片组件 =====
.apple-card {
background-color: var(--apple-secondary-system-grouped-background);
border-radius: var(--apple-corner-radius-large);
padding: var(--apple-spacing-lg);
box-shadow: var(--apple-shadow-card);
border: 1px solid var(--apple-separator);
transition: all var(--apple-animation-duration-normal) var(--apple-animation-easing);
&:hover {
transform: translateY(-2px);
box-shadow: var(--apple-shadow-elevated);
}
&.glass {
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
[data-theme="dark"] & {
background: rgba(28, 28, 30, 0.8);
border: 1px solid rgba(255, 255, 255, 0.1);
}
}
}
// ===== 输入框组件 =====
.apple-input {
width: 100%;
padding: 12px 16px;
border: 1px solid var(--apple-separator);
border-radius: var(--apple-corner-radius-medium);
background-color: var(--apple-tertiary-system-background);
color: var(--apple-label);
font-family: var(--apple-font-family);
font-size: var(--apple-font-size-body);
transition: all var(--apple-animation-duration-fast) var(--apple-animation-easing);
&:focus {
outline: none;
border-color: var(--apple-blue);
box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.1);
}
&::placeholder {
color: var(--apple-tertiary-label);
}
}
// ===== 标签组件 =====
.apple-badge {
display: inline-flex;
align-items: center;
padding: 4px 12px;
border-radius: var(--apple-corner-radius-small);
font-size: var(--apple-font-size-caption1);
font-weight: 600;
&.default {
background-color: var(--apple-system-fill);
color: var(--apple-label);
}
&.success {
background-color: var(--apple-green);
color: white;
}
&.warning {
background-color: var(--apple-orange);
color: white;
}
&.error {
background-color: var(--apple-red);
color: white;
}
}
// ===== 响应式网格系统 =====
.apple-grid {
display: grid;
gap: var(--apple-spacing-lg);
&.cols-1 { grid-template-columns: 1fr; }
&.cols-2 { grid-template-columns: repeat(2, 1fr); }
&.cols-3 { grid-template-columns: repeat(3, 1fr); }
&.cols-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 768px) {
&.cols-2, &.cols-3, &.cols-4 {
grid-template-columns: 1fr;
}
}
@media (min-width: 769px) and (max-width: 1024px) {
&.cols-3, &.cols-4 {
grid-template-columns: repeat(2, 1fr);
}
}
}
// ===== 动态背景效果 =====
.apple-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
45deg,
rgba(0, 122, 255, 0.1) 0%,
rgba(175, 82, 222, 0.1) 25%,
rgba(255, 45, 146, 0.1) 50%,
rgba(255, 149, 0, 0.1) 75%,
rgba(52, 199, 89, 0.1) 100%
);
animation: rotate 20s linear infinite;
}
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(
circle at 30% 70%,
rgba(0, 122, 255, 0.15) 0%,
transparent 50%
),
radial-gradient(
circle at 70% 30%,
rgba(175, 82, 222, 0.15) 0%,
transparent 50%
);
animation: pulse 8s ease-in-out infinite alternate;
}
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes pulse {
from { opacity: 0.3; }
to { opacity: 0.8; }
}
// ===== 玻璃质感效果 =====
.glass-effect {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
[data-theme="dark"] & {
background: rgba(28, 28, 30, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
}
}
// ===== 动画类 =====
.fade-in {
animation: fadeIn var(--apple-animation-duration-normal) var(--apple-animation-easing);
}
.slide-up {
animation: slideUp var(--apple-animation-duration-normal) var(--apple-animation-easing);
}
.scale-in {
animation: scaleIn var(--apple-animation-duration-fast) var(--apple-animation-easing);
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
// ===== 辅助功能支持 =====
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
@media (prefers-contrast: high) {
:root {
--apple-separator: #000000;
--apple-secondary-label: rgba(0, 0, 0, 0.8);
}
[data-theme="dark"] {
--apple-separator: #FFFFFF;
--apple-secondary-label: rgba(255, 255, 255, 0.8);
}
}

View File

@@ -1,719 +0,0 @@
// Apple Human Interface Guidelines Components
// 苹果人机界面指南组件样式
// ==================== 按钮组件 ====================
.apple-button {
display: inline-flex;
align-items: center;
justify-content: center;
height: 44px;
padding: 0 var(--spacing-lg);
border: none;
border-radius: var(--radius-sm);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
font-size: var(--font-size-body);
font-weight: var(--font-weight-medium);
cursor: pointer;
text-decoration: none;
transition: all var(--transition-normal);
position: relative;
overflow: hidden;
user-select: none;
-webkit-tap-highlight-color: transparent;
// 添加微妙的内阴影增强立体感
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: inherit;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 50%);
pointer-events: none;
opacity: 0;
transition: opacity var(--transition-fast);
}
&:hover::before {
opacity: 1;
}
&:disabled {
opacity: var(--opacity-disabled);
cursor: not-allowed;
pointer-events: none;
&::before {
display: none;
}
}
// 主要按钮
&.primary {
background: linear-gradient(135deg, var(--color-system-blue) 0%, #0066CC 100%);
color: white;
box-shadow:
0 1px 3px rgba(0, 122, 255, 0.3),
0 4px 12px rgba(0, 122, 255, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
&:hover:not(:disabled) {
background: linear-gradient(135deg, #0066CC 0%, #0056b3 100%);
box-shadow:
0 2px 8px rgba(0, 122, 255, 0.4),
0 8px 24px rgba(0, 122, 255, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
&:active {
background: linear-gradient(135deg, #004499 0%, #003d82 100%);
box-shadow:
0 1px 2px rgba(0, 122, 255, 0.3),
inset 0 2px 4px rgba(0, 0, 0, 0.2);
transform: translateY(0) scale(0.98);
}
&:focus-visible {
outline: none;
box-shadow:
0 2px 8px rgba(0, 122, 255, 0.4),
0 8px 24px rgba(0, 122, 255, 0.2),
0 0 0 4px rgba(0, 122, 255, 0.3);
}
}
// 次要按钮
&.secondary {
background-color: var(--color-background-secondary);
color: var(--color-label-primary);
border: 1px solid var(--color-separator-opaque);
&:hover:not(:disabled) {
background-color: var(--color-background-tertiary);
box-shadow: var(--shadow-sm);
}
}
// 文本按钮
&.text {
background-color: transparent;
color: var(--color-system-blue);
&:hover:not(:disabled) {
background-color: rgba(0, 122, 255, 0.1);
}
}
// 危险按钮
&.danger {
background-color: var(--color-system-red);
color: white;
&:hover:not(:disabled) {
background-color: #d32f2f;
}
}
// 成功按钮
&.success {
background-color: var(--color-system-green);
color: white;
&:hover:not(:disabled) {
background-color: #2e7d32;
}
}
// 尺寸变体
&.small {
height: 32px;
padding: 0 var(--spacing-md);
font-size: var(--font-size-footnote);
}
&.large {
height: 56px;
padding: 0 var(--spacing-xl);
font-size: var(--font-size-headline);
}
// 圆形按钮
&.round {
border-radius: var(--radius-full);
width: 44px;
padding: 0;
&.small {
width: 32px;
height: 32px;
}
&.large {
width: 56px;
height: 56px;
}
}
// 加载状态
&.loading {
pointer-events: none;
&::after {
content: '';
position: absolute;
width: 16px;
height: 16px;
border: 2px solid transparent;
border-top-color: currentColor;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.button-text {
opacity: 0;
}
}
}
@keyframes spin {
to { transform: rotate(360deg); }
}
// ==================== 输入框组件 ====================
.apple-input {
width: 100%;
height: 44px;
padding: 0 var(--spacing-md);
border: 1px solid var(--color-separator-opaque);
border-radius: var(--radius-sm);
background-color: var(--color-background-primary);
color: var(--color-label-primary);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
font-size: var(--font-size-body);
transition: all var(--transition-normal);
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.05),
0 1px 0 rgba(255, 255, 255, 0.05);
[data-theme="dark"] & {
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.3),
0 1px 0 rgba(255, 255, 255, 0.02);
}
&::placeholder {
color: var(--color-label-tertiary);
font-weight: var(--font-weight-regular);
}
&:hover:not(:disabled):not(:focus) {
border-color: var(--color-separator-non-opaque);
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.05),
0 2px 4px rgba(0, 0, 0, 0.05);
}
&:focus {
outline: none;
border-color: var(--color-system-blue);
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.05),
0 0 0 4px rgba(0, 122, 255, 0.15),
0 2px 8px rgba(0, 122, 255, 0.1);
}
&:disabled {
opacity: var(--opacity-disabled);
background-color: var(--color-background-secondary);
cursor: not-allowed;
}
&.error {
border-color: var(--color-system-red);
&:focus {
box-shadow: 0 0 0 3px rgba(255, 59, 48, 0.1);
}
}
&.success {
border-color: var(--color-system-green);
&:focus {
box-shadow: 0 0 0 3px rgba(52, 199, 89, 0.1);
}
}
// 尺寸变体
&.small {
height: 32px;
padding: 0 var(--spacing-sm);
font-size: var(--font-size-footnote);
}
&.large {
height: 56px;
padding: 0 var(--spacing-lg);
font-size: var(--font-size-headline);
}
}
// 文本域
.apple-textarea {
.apple-input();
height: auto;
min-height: 88px;
padding: var(--spacing-md);
resize: vertical;
}
// ==================== 选择器组件 ====================
.apple-select {
position: relative;
select {
.apple-input();
appearance: none;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
background-position: right var(--spacing-md) center;
background-repeat: no-repeat;
background-size: 16px 12px;
padding-right: calc(var(--spacing-md) * 2.5);
}
}
// ==================== 开关组件 ====================
.apple-switch {
position: relative;
display: inline-block;
width: 52px;
height: 32px;
input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #E5E5EA 0%, #D1D1D6 100%);
transition: all var(--transition-normal);
border-radius: 16px;
box-shadow:
inset 0 2px 4px rgba(0, 0, 0, 0.1),
0 1px 2px rgba(0, 0, 0, 0.05);
[data-theme="dark"] & {
background: linear-gradient(135deg, #39393D 0%, #2C2C2E 100%);
box-shadow:
inset 0 2px 4px rgba(0, 0, 0, 0.3),
0 1px 2px rgba(0, 0, 0, 0.2);
}
&:before {
position: absolute;
content: "";
height: 28px;
width: 28px;
left: 2px;
bottom: 2px;
background: linear-gradient(135deg, #FFFFFF 0%, #F8F8F8 100%);
transition: all var(--transition-normal);
border-radius: 50%;
box-shadow:
0 2px 8px rgba(0, 0, 0, 0.15),
0 1px 2px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.8);
[data-theme="dark"] & {
background: linear-gradient(135deg, #F2F2F7 0%, #E5E5EA 100%);
box-shadow:
0 2px 8px rgba(0, 0, 0, 0.4),
0 1px 2px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
}
&:hover:before {
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.2),
0 2px 4px rgba(0, 0, 0, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.8);
[data-theme="dark"] & {
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.5),
0 2px 4px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
}
}
input:checked + .slider {
background: linear-gradient(135deg, var(--color-system-green) 0%, #2E7D32 100%);
box-shadow:
inset 0 2px 4px rgba(52, 199, 89, 0.3),
0 1px 2px rgba(52, 199, 89, 0.2);
}
input:checked + .slider:before {
transform: translateX(20px);
}
input:disabled + .slider {
opacity: var(--opacity-disabled);
cursor: not-allowed;
&:before {
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.5);
}
}
}
// ==================== 卡片组件 ====================
.apple-card {
background-color: var(--color-background-secondary);
border: 1px solid var(--color-separator-opaque);
border-radius: var(--radius-lg);
padding: var(--spacing-lg);
box-shadow: var(--shadow-sm);
transition: all var(--transition-normal);
&:hover {
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
&.glass {
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.7) 100%);
backdrop-filter: blur(var(--blur-lg)) saturate(1.2);
-webkit-backdrop-filter: blur(var(--blur-lg)) saturate(1.2);
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.1),
0 2px 8px rgba(0, 0, 0, 0.05),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
inset 0 -1px 0 rgba(255, 255, 255, 0.1);
[data-theme="dark"] & {
background:
linear-gradient(135deg, rgba(28, 28, 30, 0.9) 0%, rgba(28, 28, 30, 0.7) 100%);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
0 2px 8px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1),
inset 0 -1px 0 rgba(255, 255, 255, 0.05);
}
&:hover {
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.8) 100%);
border-color: rgba(255, 255, 255, 0.4);
[data-theme="dark"] & {
background:
linear-gradient(135deg, rgba(28, 28, 30, 0.95) 0%, rgba(28, 28, 30, 0.8) 100%);
border-color: rgba(255, 255, 255, 0.2);
}
}
}
.card-header {
margin-bottom: var(--spacing-md);
.card-title {
font-size: var(--font-size-title3);
font-weight: var(--font-weight-semibold);
color: var(--color-label-primary);
margin: 0 0 var(--spacing-xs) 0;
}
.card-subtitle {
font-size: var(--font-size-subheadline);
color: var(--color-label-secondary);
margin: 0;
}
}
.card-content {
font-size: var(--font-size-body);
color: var(--color-label-primary);
line-height: var(--line-height-relaxed);
}
.card-footer {
margin-top: var(--spacing-md);
padding-top: var(--spacing-md);
border-top: 1px solid var(--color-separator-opaque);
}
// 卡片变体
&.elevated {
box-shadow: var(--shadow-lg);
&:hover {
box-shadow: var(--shadow-xl);
transform: translateY(-4px);
}
}
&.flat {
box-shadow: none;
border: 1px solid var(--color-separator-opaque);
}
}
// ==================== 警告框组件 ====================
.apple-alert {
padding: var(--spacing-md);
border-radius: var(--radius-md);
border: 1px solid transparent;
margin-bottom: var(--spacing-md);
.alert-title {
font-size: var(--font-size-headline);
font-weight: var(--font-weight-semibold);
margin: 0 0 var(--spacing-xs) 0;
}
.alert-message {
font-size: var(--font-size-body);
margin: 0;
}
// 警告框类型
&.info {
background-color: rgba(0, 122, 255, 0.1);
border-color: rgba(0, 122, 255, 0.3);
color: var(--color-system-blue);
}
&.success {
background-color: rgba(52, 199, 89, 0.1);
border-color: rgba(52, 199, 89, 0.3);
color: var(--color-system-green);
}
&.warning {
background-color: rgba(255, 149, 0, 0.1);
border-color: rgba(255, 149, 0, 0.3);
color: var(--color-system-orange);
}
&.error {
background-color: rgba(255, 59, 48, 0.1);
border-color: rgba(255, 59, 48, 0.3);
color: var(--color-system-red);
}
}
// ==================== 导航组件 ====================
.apple-navbar {
position: sticky;
top: 0;
z-index: var(--z-sticky);
height: 44px;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(var(--blur-md));
-webkit-backdrop-filter: blur(var(--blur-md));
border-bottom: 1px solid var(--color-separator-opaque);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 var(--spacing-md);
[data-theme="dark"] & {
background: rgba(28, 28, 30, 0.8);
}
@media (min-width: 768px) {
padding: 0 var(--spacing-lg);
}
.navbar-brand {
font-size: var(--font-size-headline);
font-weight: var(--font-weight-semibold);
color: var(--color-label-primary);
text-decoration: none;
transition: color var(--transition-normal);
}
.navbar-menu {
display: flex;
align-items: center;
gap: var(--spacing-md);
}
.navbar-link {
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-sm);
color: var(--color-label-secondary);
text-decoration: none;
font-size: var(--font-size-body);
transition: all var(--transition-normal);
&:hover {
background-color: var(--color-background-secondary);
color: var(--color-label-primary);
}
&.active {
background-color: var(--color-system-blue);
color: white;
}
}
}
// ==================== 标签页组件 ====================
.apple-tabs {
.tab-list {
display: flex;
border-bottom: 1px solid var(--color-separator-opaque);
margin-bottom: var(--spacing-lg);
.tab-item {
flex: 1;
text-align: center;
padding: var(--spacing-md);
border-bottom: 2px solid transparent;
color: var(--color-label-secondary);
cursor: pointer;
transition: all var(--transition-normal);
&:hover {
color: var(--color-label-primary);
background-color: var(--color-background-secondary);
}
&.active {
color: var(--color-system-blue);
border-bottom-color: var(--color-system-blue);
}
}
}
.tab-content {
.tab-panel {
display: none;
&.active {
display: block;
animation: fadeIn var(--transition-normal);
}
}
}
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
// ==================== 分段控制器 ====================
.apple-segmented-control {
display: inline-flex;
background-color: var(--color-background-secondary);
border-radius: var(--radius-sm);
padding: 2px;
position: relative;
.segment {
padding: var(--spacing-sm) var(--spacing-md);
border-radius: calc(var(--radius-sm) - 2px);
color: var(--color-label-primary);
cursor: pointer;
transition: all var(--transition-normal);
position: relative;
z-index: 1;
&.active {
background-color: var(--color-background-primary);
box-shadow: var(--shadow-sm);
}
}
}
// ==================== 统计卡片 ====================
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--spacing-lg);
.stat-card {
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(16px);
border: 1px solid var(--color-separator-opaque);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
transition: all var(--transition-normal);
overflow: hidden;
text-align: center;
.stat-icon {
width: 48px;
height: 48px;
margin: 0 auto var(--spacing-md);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--shadow-sm);
}
.stat-value {
font-size: var(--font-size-large-title);
font-weight: var(--font-weight-bold);
color: var(--color-label-primary);
margin-bottom: var(--spacing-xs);
}
.stat-label {
font-size: var(--font-size-subheadline);
color: var(--color-label-secondary);
}
// 颜色变体
&.primary .stat-icon {
background-color: rgba(0, 122, 255, 0.15);
color: var(--color-system-blue);
}
&.success .stat-icon {
background-color: rgba(52, 199, 89, 0.15);
color: var(--color-system-green);
}
&.warning .stat-icon {
background-color: rgba(255, 149, 0, 0.15);
color: var(--color-system-orange);
}
&.danger .stat-icon {
background-color: rgba(255, 59, 48, 0.15);
color: var(--color-system-red);
}
}
}

View File

@@ -1,490 +0,0 @@
// Apple Human Interface Guidelines Design System
// 苹果人机界面指南设计系统主入口
// ==================== 0. Tailwind CSS 基础样式 ====================
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
// ==================== 1. 设计令牌 (Design Tokens) ====================
@import 'themes/apple-design-tokens.less';
// ==================== 2. 工具库 (Utilities) ====================
@import 'utils/apple-mixins.less';
@import 'utils/grid-system.less';
@import 'utils/visual-effects.less';
// ==================== 3. 组件样式 (Components) ====================
@import 'components/apple-components.less';
@import 'components/dynamic-backgrounds.less';
// ==================== 4. 基础样式 (Base Styles) ====================
// 全局重置
* {
box-sizing: border-box;
}
html {
font-size: 16px;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
body {
margin: 0;
padding: 0;
.apple-font();
background-color: var(--color-background-primary);
color: var(--color-label-primary);
.apple-transition(background-color);
.apple-transition(color);
// 支持安全区域 (iOS设备)
.safe-area-padding();
// 动态背景效果 (可选)
&.dynamic-background {
.dynamic-background();
}
}
// ==================== 4. 主题切换支持 ====================
// 主题切换过渡效果
.theme-transition {
* {
.apple-transition(background-color);
.apple-transition(color);
.apple-transition(border-color);
.apple-transition(box-shadow);
}
}
// 主题切换按钮
.theme-toggle {
position: relative;
width: 44px;
height: 44px;
border: none;
border-radius: var(--radius-full);
background: var(--color-background-secondary);
color: var(--color-label-primary);
cursor: pointer;
.flex-center();
.apple-transition();
.apple-shadow(sm);
&:hover {
.apple-shadow(md);
transform: scale(1.05);
}
&:active {
transform: scale(0.95);
}
.icon {
width: 20px;
height: 20px;
.apple-transition(transform);
}
&:hover .icon {
transform: rotate(180deg);
}
}
// ==================== 5. 布局组件 ====================
// 应用主容器
.app-container {
min-height: 100vh;
background-color: var(--color-background-primary);
.apple-transition(background-color);
}
// 导航栏
.app-navbar {
position: sticky;
top: 0;
z-index: var(--z-sticky);
height: 44px;
.glass-effect();
border-bottom: 1px solid var(--color-separator-opaque);
.navbar-layout();
.navbar-brand {
.apple-font(var(--font-size-headline), var(--font-weight-semibold));
color: var(--color-label-primary);
text-decoration: none;
.apple-transition(color);
}
.navbar-menu {
display: flex;
align-items: center;
gap: var(--spacing-md);
}
.navbar-link {
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-sm);
color: var(--color-label-secondary);
text-decoration: none;
.apple-font(var(--font-size-body));
.apple-transition();
&:hover {
background-color: var(--color-background-secondary);
color: var(--color-label-primary);
}
&.active {
background-color: var(--color-system-blue);
color: white;
}
}
}
// 侧边栏
.app-sidebar {
width: 280px;
height: 100vh;
background-color: var(--color-background-secondary);
border-right: 1px solid var(--color-separator-opaque);
.apple-transition(background-color);
@media (max-width: calc(var(--breakpoint-lg) - 1px)) {
position: fixed;
left: -280px;
z-index: var(--z-modal);
.apple-transition(left);
&.open {
left: 0;
}
}
}
// 主内容区
.app-main {
flex: 1;
padding: var(--spacing-lg);
@media (max-width: calc(var(--breakpoint-md) - 1px)) {
padding: var(--spacing-md);
}
}
// ==================== 6. 卡片组件 ====================
.apple-card {
background-color: var(--color-background-secondary);
border: 1px solid var(--color-separator-opaque);
border-radius: var(--radius-lg);
padding: var(--spacing-lg);
.apple-shadow(sm);
.apple-transition();
&:hover {
.apple-shadow(md);
transform: translateY(-2px);
}
&.glass {
.glass-effect();
}
.card-header {
margin-bottom: var(--spacing-md);
.card-title {
.apple-font(var(--font-size-title3), var(--font-weight-semibold));
color: var(--color-label-primary);
margin: 0 0 var(--spacing-xs) 0;
}
.card-subtitle {
.apple-font(var(--font-size-subheadline));
color: var(--color-label-secondary);
margin: 0;
}
}
.card-content {
.apple-font(var(--font-size-body));
color: var(--color-label-primary);
line-height: var(--line-height-relaxed);
}
.card-footer {
margin-top: var(--spacing-md);
padding-top: var(--spacing-md);
border-top: 1px solid var(--color-separator-opaque);
}
}
// ==================== 7. 按钮组件 ====================
.apple-button {
display: inline-flex;
align-items: center;
justify-content: center;
height: 44px;
padding: 0 var(--spacing-lg);
border: none;
border-radius: var(--radius-sm);
.apple-font(var(--font-size-body), var(--font-weight-medium));
cursor: pointer;
text-decoration: none;
.apple-transition();
&:disabled {
.disabled-state();
}
// 主要按钮
&.primary {
background-color: var(--color-system-blue);
color: white;
&:hover:not(:disabled) {
background-color: color-mix(in srgb, var(--color-system-blue) 90%, black);
.apple-shadow(md);
}
&:active {
transform: scale(0.98);
}
}
// 次要按钮
&.secondary {
background-color: var(--color-background-secondary);
color: var(--color-label-primary);
border: 1px solid var(--color-separator-opaque);
&:hover:not(:disabled) {
background-color: var(--color-background-tertiary);
.apple-shadow(sm);
}
}
// 文本按钮
&.text {
background-color: transparent;
color: var(--color-system-blue);
&:hover:not(:disabled) {
background-color: color-mix(in srgb, var(--color-system-blue) 10%, transparent);
}
}
// 危险按钮
&.danger {
background-color: var(--color-system-red);
color: white;
&:hover:not(:disabled) {
background-color: color-mix(in srgb, var(--color-system-red) 90%, black);
}
}
// 尺寸变体
&.small {
height: 32px;
padding: 0 var(--spacing-md);
.apple-font(var(--font-size-footnote), var(--font-weight-medium));
}
&.large {
height: 56px;
padding: 0 var(--spacing-xl);
.apple-font(var(--font-size-headline), var(--font-weight-medium));
}
}
// ==================== 8. 输入框组件 ====================
.apple-input {
width: 100%;
height: 44px;
padding: 0 var(--spacing-md);
border: 1px solid var(--color-separator-opaque);
border-radius: var(--radius-sm);
background-color: var(--color-background-primary);
color: var(--color-label-primary);
.apple-font(var(--font-size-body));
.apple-transition();
&::placeholder {
color: var(--color-label-tertiary);
}
&:focus {
outline: none;
border-color: var(--color-system-blue);
.apple-shadow(sm);
}
&:disabled {
.disabled-state();
background-color: var(--color-background-secondary);
}
&.error {
border-color: var(--color-system-red);
}
}
// ==================== 9. 统计卡片 ====================
.stats-grid {
.grid-auto-fit();
.stat-card {
.apple-card();
text-align: center;
.stat-icon {
width: 48px;
height: 48px;
margin: 0 auto var(--spacing-md);
border-radius: var(--radius-md);
.flex-center();
.apple-shadow(sm);
}
.stat-value {
.apple-font(var(--font-size-large-title), var(--font-weight-bold));
color: var(--color-label-primary);
margin-bottom: var(--spacing-xs);
}
.stat-label {
.apple-font(var(--font-size-subheadline));
color: var(--color-label-secondary);
}
// 颜色变体
&.primary .stat-icon {
background-color: color-mix(in srgb, var(--color-system-blue) 15%, transparent);
color: var(--color-system-blue);
}
&.success .stat-icon {
background-color: color-mix(in srgb, var(--color-system-green) 15%, transparent);
color: var(--color-system-green);
}
&.warning .stat-icon {
background-color: color-mix(in srgb, var(--color-system-orange) 15%, transparent);
color: var(--color-system-orange);
}
&.danger .stat-icon {
background-color: color-mix(in srgb, var(--color-system-red) 15%, transparent);
color: var(--color-system-red);
}
}
}
// ==================== 10. 响应式工具类 ====================
// 显示/隐藏工具
.show-sm { display: none; @media (min-width: var(--breakpoint-sm)) { display: block; } }
.show-md { display: none; @media (min-width: var(--breakpoint-md)) { display: block; } }
.show-lg { display: none; @media (min-width: var(--breakpoint-lg)) { display: block; } }
.hide-sm { @media (min-width: var(--breakpoint-sm)) { display: none; } }
.hide-md { @media (min-width: var(--breakpoint-md)) { display: none; } }
.hide-lg { @media (min-width: var(--breakpoint-lg)) { display: none; } }
// ==================== 11. 无障碍性支持 ====================
// 减少动画 (用户偏好)
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
// 高对比度模式
@media (prefers-contrast: high) {
.apple-card,
.apple-button,
.apple-input {
border-width: 2px;
}
}
// 聚焦指示器
.apple-button:focus-visible,
.apple-input:focus-visible,
.theme-toggle:focus-visible {
.focus-ring();
}
// ==================== 12. 打印样式 ====================
@media print {
.apple-card,
.apple-button,
.apple-input {
background: white !important;
color: black !important;
box-shadow: none !important;
border: 1px solid #ccc !important;
}
.app-navbar,
.app-sidebar,
.theme-toggle {
display: none !important;
}
}
// ==================== 13. 增强的动态背景和毛玻璃效果 ====================
// 毛玻璃卡片效果
.apple-glass-card {
background: rgba(255, 255, 255, 0.85) !important;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2) !important;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.1),
0 4px 16px rgba(0, 0, 0, 0.05),
inset 0 1px 0 rgba(255, 255, 255, 0.3) !important;
[data-theme="dark"] & {
background: rgba(30, 41, 59, 0.85) !important;
border: 1px solid rgba(148, 163, 184, 0.2) !important;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
0 4px 16px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(148, 163, 184, 0.1) !important;
}
&:hover {
background: rgba(255, 255, 255, 0.9) !important;
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.15),
0 6px 20px rgba(0, 0, 0, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.4) !important;
transform: translateY(-2px);
[data-theme="dark"] & {
background: rgba(30, 41, 59, 0.9) !important;
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.4),
0 6px 20px rgba(0, 0, 0, 0.25),
inset 0 1px 0 rgba(148, 163, 184, 0.15) !important;
}
}
}

View File

@@ -1,184 +0,0 @@
// Apple Human Interface Guidelines Design Tokens
// 基于苹果人机界面指南的设计令牌系统
// ==================== 色彩系统 ====================
// 系统色彩 - 明亮主题
:root {
// 主要系统色彩
--color-system-blue: #007AFF;
--color-system-green: #34C759;
--color-system-indigo: #5856D6;
--color-system-orange: #FF9500;
--color-system-pink: #FF2D92;
--color-system-purple: #AF52DE;
--color-system-red: #FF3B30;
--color-system-teal: #5AC8FA;
--color-system-yellow: #FFCC00;
// 中性色彩
--color-label-primary: #000000;
--color-label-secondary: #3C3C43;
--color-label-tertiary: #3C3C43;
--color-label-quaternary: #3C3C43;
--color-fill-primary: #787880;
--color-fill-secondary: #787880;
--color-fill-tertiary: #767680;
--color-fill-quaternary: #747480;
// 背景色彩
--color-background-primary: #FFFFFF;
--color-background-secondary: #F2F2F7;
--color-background-tertiary: #FFFFFF;
// 分组背景
--color-grouped-background-primary: #F2F2F7;
--color-grouped-background-secondary: #FFFFFF;
--color-grouped-background-tertiary: #F2F2F7;
// 分隔线
--color-separator-opaque: #C6C6C8;
--color-separator-non-opaque: #3C3C43;
// 链接色
--color-link: #007AFF;
}
// 深色主题
[data-theme="dark"] {
// 主要系统色彩 (深色模式下稍作调整)
--color-system-blue: #0A84FF;
--color-system-green: #30D158;
--color-system-indigo: #5E5CE6;
--color-system-orange: #FF9F0A;
--color-system-pink: #FF375F;
--color-system-purple: #BF5AF2;
--color-system-red: #FF453A;
--color-system-teal: #64D2FF;
--color-system-yellow: #FFD60A;
// 中性色彩
--color-label-primary: #FFFFFF;
--color-label-secondary: #EBEBF5;
--color-label-tertiary: #EBEBF5;
--color-label-quaternary: #EBEBF5;
--color-fill-primary: #787880;
--color-fill-secondary: #787880;
--color-fill-tertiary: #767680;
--color-fill-quaternary: #747480;
// 背景色彩
--color-background-primary: #000000;
--color-background-secondary: #1C1C1E;
--color-background-tertiary: #2C2C2E;
// 分组背景
--color-grouped-background-primary: #000000;
--color-grouped-background-secondary: #1C1C1E;
--color-grouped-background-tertiary: #2C2C2E;
// 分隔线
--color-separator-opaque: #38383A;
--color-separator-non-opaque: #EBEBF5;
// 链接色
--color-link: #0A84FF;
}
// ==================== 尺寸系统 ====================
:root {
// 基础间距 (基于8pt网格系统)
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--spacing-xxl: 48px;
--spacing-xxxl: 64px;
// 圆角半径
--radius-xs: 4px;
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--radius-xxl: 24px;
--radius-full: 50%;
// 字体大小
--font-size-caption2: 11px;
--font-size-caption1: 12px;
--font-size-footnote: 13px;
--font-size-subheadline: 15px;
--font-size-callout: 16px;
--font-size-body: 17px;
--font-size-headline: 17px;
--font-size-title3: 20px;
--font-size-title2: 22px;
--font-size-title1: 28px;
--font-size-large-title: 34px;
// 字体粗细
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
// 行高
--line-height-tight: 1.2;
--line-height-normal: 1.4;
--line-height-relaxed: 1.6;
// 阴影
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1);
--shadow-2xl: 0 25px 50px rgba(0, 0, 0, 0.25);
// 深色模式阴影
--shadow-dark-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-dark-md: 0 4px 6px rgba(0, 0, 0, 0.4);
--shadow-dark-lg: 0 10px 15px rgba(0, 0, 0, 0.5);
--shadow-dark-xl: 0 20px 25px rgba(0, 0, 0, 0.6);
--shadow-dark-2xl: 0 25px 50px rgba(0, 0, 0, 0.8);
// Z-index层级
--z-dropdown: 1000;
--z-sticky: 1020;
--z-fixed: 1030;
--z-modal-backdrop: 1040;
--z-modal: 1050;
--z-popover: 1060;
--z-tooltip: 1070;
--z-toast: 1080;
// 过渡动画
--transition-fast: 0.15s ease-in-out;
--transition-normal: 0.3s ease-in-out;
--transition-slow: 0.5s ease-in-out;
// 模糊效果
--blur-sm: 4px;
--blur-md: 8px;
--blur-lg: 16px;
--blur-xl: 24px;
// 透明度
--opacity-disabled: 0.3;
--opacity-secondary: 0.6;
--opacity-overlay: 0.8;
}
// ==================== 响应式断点 ====================
:root {
--breakpoint-sm: 576px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-xxl: 1536px;
}

View File

@@ -1,313 +0,0 @@
// 玻璃拟态主题
// Glass Morphism Themes
@import '../utils/variables.less';
@import '../utils/mixins.less';
// ===== 基础重置样式 =====
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-family: @font-family-base;
line-height: 1.5;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: @font-family-base;
font-size: @font-size-md;
font-weight: @font-weight-normal;
line-height: 1.6;
color: @gray-900;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
transition: all @transition-normal @easing-standard;
// 暗色主题
&.dark {
color: @white;
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
}
}
// ===== 亮色主题 =====
.light-theme {
--bg-primary: @white;
--bg-secondary: @gray-50;
--bg-tertiary: @gray-100;
--text-primary: @gray-900;
--text-secondary: @gray-600;
--text-tertiary: @gray-400;
--border-primary: @gray-200;
--border-secondary: @gray-100;
--glass-bg: rgba(255, 255, 255, @glass-opacity-medium);
--glass-border: rgba(255, 255, 255, @glass-border-opacity);
--shadow-color: rgba(0, 0, 0, 0.1);
}
// ===== 暗色主题 =====
.dark-theme,
.dark {
--bg-primary: @gray-900;
--bg-secondary: @gray-800;
--bg-tertiary: @gray-700;
--text-primary: @white;
--text-secondary: @gray-300;
--text-tertiary: @gray-500;
--border-primary: @gray-700;
--border-secondary: @gray-800;
--glass-bg: rgba(0, 0, 0, @glass-opacity-medium);
--glass-border: rgba(255, 255, 255, @glass-border-opacity * 0.5);
--shadow-color: rgba(0, 0, 0, 0.3);
}
// ===== 玻璃容器主题 =====
.glass-container {
.glass-base(@glass-opacity-light, @glass-blur-lg);
border-radius: @border-radius-2xl;
padding: @spacing-xl;
max-width: 1200px;
margin: 0 auto;
box-shadow: @glass-shadow-xl;
// 响应式
@media (min-width: @breakpoint-md) {
padding: @spacing-lg;
}
@media (min-width: @breakpoint-sm) {
padding: @spacing-md;
border-radius: @border-radius-lg;
}
}
// ===== 玻璃网格主题 =====
.glass-grid {
display: grid;
gap: @spacing-lg;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
@media (min-width: @breakpoint-md) {
gap: @spacing-md;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
@media (min-width: @breakpoint-sm) {
gap: @spacing-sm;
grid-template-columns: 1fr;
}
}
// ===== 玻璃文字主题 =====
.glass-text {
&-primary {
color: var(--text-primary);
font-weight: @font-weight-semibold;
}
&-secondary {
color: var(--text-secondary);
font-weight: @font-weight-normal;
}
&-tertiary {
color: var(--text-tertiary);
font-weight: @font-weight-normal;
}
&-gradient {
background: linear-gradient(135deg, @primary-color 0%, @accent-purple 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: @font-weight-bold;
}
}
// ===== 玻璃标题主题 =====
.glass-heading {
&-1 {
font-size: @font-size-4xl;
font-weight: @font-weight-bold;
line-height: 1.1;
margin-bottom: @spacing-lg;
@media (min-width: @breakpoint-md) {
font-size: @font-size-3xl;
}
@media (min-width: @breakpoint-sm) {
font-size: @font-size-2xl;
}
}
&-2 {
font-size: @font-size-3xl;
font-weight: @font-weight-bold;
line-height: 1.2;
margin-bottom: @spacing-md;
@media (min-width: @breakpoint-md) {
font-size: @font-size-2xl;
}
@media (min-width: @breakpoint-sm) {
font-size: @font-size-xl;
}
}
&-3 {
font-size: @font-size-2xl;
font-weight: @font-weight-semibold;
line-height: 1.3;
margin-bottom: @spacing-md;
@media (min-width: @breakpoint-sm) {
font-size: @font-size-lg;
}
}
}
// ===== 玻璃动画主题 =====
@keyframes glass-fade-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes glass-slide-in {
from {
opacity: 0;
transform: translateX(-100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes glass-scale-in {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes glass-glow {
0%,
100% {
box-shadow: @glass-glow-sm;
}
50% {
box-shadow: @glass-glow-lg;
}
}
// 动画类
.glass-animate {
&-fade-in {
animation: glass-fade-in 0.6s @easing-decelerate;
}
&-slide-in {
animation: glass-slide-in 0.5s @easing-decelerate;
}
&-scale-in {
animation: glass-scale-in 0.4s @easing-decelerate;
}
&-glow {
animation: glass-glow 2s ease-in-out infinite;
}
}
// ===== 全局工具类 =====
.glass-elevated {
.glass-base(@glass-opacity-heavy, @glass-blur-xl);
box-shadow: @glass-shadow-xl;
}
.glass-subtle {
.glass-base(@glass-opacity-light, @glass-blur-sm);
box-shadow: @glass-shadow-sm;
}
.glass-interactive {
cursor: pointer;
transition: all @transition-normal @easing-standard;
&:hover {
.glass-base(@glass-opacity-heavy, @glass-blur-lg);
transform: translateY(-2px);
box-shadow: @glass-shadow-lg;
}
&:active {
transform: translateY(0);
}
}
// ===== 滚动条主题 =====
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: @border-radius-sm;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: @border-radius-sm;
transition: background @transition-normal;
&:hover {
background: rgba(255, 255, 255, 0.5);
}
}
// 暗色主题滚动条
.dark {
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
&:hover {
background: rgba(255, 255, 255, 0.3);
}
}
}

View File

@@ -1,290 +0,0 @@
// 玻璃拟态混合 (Mixins)
// Glass Morphism Mixins
// ===== 基础玻璃效果混合 =====
.glass-base(@opacity: @glass-opacity-medium, @blur: @glass-blur-md) {
backdrop-filter: blur(@blur);
-webkit-backdrop-filter: blur(@blur);
background: rgba(255, 255, 255, @opacity);
border: 1px solid rgba(255, 255, 255, @glass-border-opacity);
position: relative;
overflow: hidden;
// 暗色主题下的玻璃效果
.dark & {
background: rgba(0, 0, 0, @opacity);
border-color: rgba(255, 255, 255, @glass-border-opacity * 0.5);
}
}
// ===== 玻璃卡片混合 =====
.glass-card(@size: md) {
.glass-base();
border-radius: @border-radius-lg;
transition: all @transition-normal @easing-standard;
// 大小变体
& when (@size =sm) {
padding: @spacing-sm @spacing-md;
border-radius: @border-radius-md;
}
& when (@size =md) {
padding: @spacing-md @spacing-lg;
border-radius: @border-radius-lg;
}
& when (@size =lg) {
padding: @spacing-lg @spacing-xl;
border-radius: @border-radius-xl;
}
// 悬停效果
&:hover {
.glass-base(@glass-opacity-heavy, @glass-blur-lg);
box-shadow: @glass-shadow-lg;
transform: translateY(-2px);
}
// 焦点效果
&:focus-visible {
outline: 2px solid @primary-color;
outline-offset: 2px;
}
}
// ===== 玻璃按钮混合 =====
.glass-button(@variant: primary) {
.glass-base();
border-radius: @border-radius-md;
padding: @spacing-sm @spacing-md;
cursor: pointer;
transition: all @transition-fast @easing-standard;
font-weight: @font-weight-medium;
text-align: center;
display: inline-flex;
align-items: center;
justify-content: center;
gap: @spacing-xs;
// 变体样式
& when (@variant =primary) {
background: rgba(99, 102, 241, 0.2);
color: @primary-color;
border-color: rgba(99, 102, 241, 0.3);
&:hover {
background: rgba(99, 102, 241, 0.3);
box-shadow: @glass-glow-sm;
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
background: rgba(99, 102, 241, 0.4);
}
}
& when (@variant =secondary) {
.glass-base(@glass-opacity-light);
&:hover {
.glass-base(@glass-opacity-medium);
box-shadow: @glass-shadow-md;
}
}
& when (@variant =danger) {
background: rgba(239, 68, 68, 0.2);
color: @accent-red;
border-color: rgba(239, 68, 68, 0.3);
&:hover {
background: rgba(239, 68, 68, 0.3);
box-shadow: 0 0 20px rgba(239, 68, 68, 0.3);
}
}
}
// ===== 玻璃输入框混合 =====
.glass-input() {
.glass-base(@glass-opacity-light, @glass-blur-sm);
border-radius: @border-radius-md;
padding: @spacing-sm @spacing-md;
font-family: @font-family-base;
font-size: @font-size-md;
color: @gray-900;
transition: all @transition-normal @easing-standard;
width: 100%;
.dark & {
color: @white;
}
&::placeholder {
color: @gray-400;
.dark & {
color: @gray-500;
}
}
&:focus {
.glass-base(@glass-opacity-medium, @glass-blur-md);
outline: none;
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.5);
border-color: @primary-color;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
// ===== 玻璃导航栏混合 =====
.glass-navbar() {
.glass-base(@glass-opacity-heavy, @glass-blur-xl);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: @z-index-navbar;
border-bottom: 1px solid rgba(255, 255, 255, @glass-border-opacity);
border-left: none;
border-right: none;
border-top: none;
transition: all @transition-normal @easing-standard;
// 滚动时的效果
&.scrolled {
.glass-base(@glass-opacity-heavy, @glass-blur-xl);
box-shadow: @glass-shadow-lg;
}
}
// ===== 玻璃模态框混合 =====
.glass-modal() {
.glass-base(@glass-opacity-heavy, @glass-blur-xl);
border-radius: @border-radius-2xl;
box-shadow: @glass-shadow-xl;
max-width: 90vw;
max-height: 90vh;
overflow: auto;
// 背景遮罩
&::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(@glass-blur-sm);
z-index: -1;
}
}
// ===== 玻璃侧边栏混合 =====
.glass-sidebar() {
.glass-base(@glass-opacity-heavy, @glass-blur-lg);
position: fixed;
top: 0;
left: 0;
height: 100vh;
border-right: 1px solid rgba(255, 255, 255, @glass-border-opacity);
border-left: none;
border-top: none;
border-bottom: none;
transition: transform @transition-normal @easing-standard;
z-index: @z-index-navbar - 10;
}
// ===== 玻璃通知混合 =====
.glass-notification(@type: info) {
.glass-base(@glass-opacity-medium, @glass-blur-md);
border-radius: @border-radius-lg;
padding: @spacing-md;
position: relative;
& when (@type =success) {
border-color: rgba(16, 185, 129, 0.3);
background: rgba(16, 185, 129, 0.1);
}
& when (@type =warning) {
border-color: rgba(245, 158, 11, 0.3);
background: rgba(245, 158, 11, 0.1);
}
& when (@type =error) {
border-color: rgba(239, 68, 68, 0.3);
background: rgba(239, 68, 68, 0.1);
}
& when (@type =info) {
border-color: rgba(59, 130, 246, 0.3);
background: rgba(59, 130, 246, 0.1);
}
}
// ===== 响应式混合 =====
.responsive(@breakpoint) {
& when (@breakpoint =sm) {
@media (min-width: @breakpoint-sm) {
.content();
}
}
& when (@breakpoint =md) {
@media (min-width: @breakpoint-md) {
.content();
}
}
& when (@breakpoint =lg) {
@media (min-width: @breakpoint-lg) {
.content();
}
}
& when (@breakpoint =xl) {
@media (min-width: @breakpoint-xl) {
.content();
}
}
}
// ===== 工具混合 =====
// 截断文本
.text-truncate() {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 居中对齐
.center-flex() {
display: flex;
align-items: center;
justify-content: center;
}
// 绝对居中
.absolute-center() {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
// 隐藏滚动条
.hide-scrollbar() {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}

View File

@@ -1,168 +0,0 @@
// 玻璃拟态设计系统变量
// Glass Morphism Design System Variables
// ===== 颜色系统 =====
// 主要颜色
@primary-color: #6366f1;
@primary-light: #818cf8;
@primary-dark: #4f46e5;
// 中性色
@white: #ffffff;
@black: #000000;
@gray-50: #f9fafb;
@gray-100: #f3f4f6;
@gray-200: #e5e7eb;
@gray-300: #d1d5db;
@gray-400: #9ca3af;
@gray-500: #6b7280;
@gray-600: #4b5563;
@gray-700: #374151;
@gray-800: #1f2937;
@gray-900: #111827;
// 强调色
@accent-blue: #3b82f6;
@accent-purple: #8b5cf6;
@accent-pink: #ec4899;
@accent-green: #10b981;
@accent-yellow: #f59e0b;
@accent-red: #ef4444;
// ===== 玻璃拟态效果变量 =====
// 背景透明度
@glass-opacity-light: 0.1;
@glass-opacity-medium: 0.15;
@glass-opacity-heavy: 0.25;
// 边框透明度
@glass-border-opacity: 0.2;
// 模糊程度
@glass-blur-xs: 4px;
@glass-blur-sm: 8px;
@glass-blur-md: 12px;
@glass-blur-lg: 16px;
@glass-blur-xl: 24px;
// ===== 阴影系统 =====
// 玻璃阴影
@glass-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
@glass-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.06);
@glass-shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
@glass-shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.04);
// 发光效果
@glass-glow-sm: 0 0 10px rgba(99, 102, 241, 0.3);
@glass-glow-md: 0 0 20px rgba(99, 102, 241, 0.4);
@glass-glow-lg: 0 0 30px rgba(99, 102, 241, 0.5);
// ===== 圆角系统 =====
@border-radius-xs: 4px;
@border-radius-sm: 8px;
@border-radius-md: 12px;
@border-radius-lg: 16px;
@border-radius-xl: 20px;
@border-radius-2xl: 24px;
@border-radius-full: 50%;
// ===== 间距系统 =====
@spacing-xs: 4px;
@spacing-sm: 8px;
@spacing-md: 16px;
@spacing-lg: 24px;
@spacing-xl: 32px;
@spacing-2xl: 48px;
@spacing-3xl: 64px;
// ===== 动画系统 =====
@transition-fast: 0.15s ease-in-out;
@transition-normal: 0.3s ease-in-out;
@transition-slow: 0.5s ease-in-out;
// 动画缓动函数
@easing-standard: cubic-bezier(0.4, 0.0, 0.2, 1);
@easing-decelerate: cubic-bezier(0.0, 0.0, 0.2, 1);
@easing-accelerate: cubic-bezier(0.4, 0.0, 1, 1);
// ===== 字体系统 =====
@font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
@font-family-mono: "SF Mono", Monaco, Inconsolata, "Roboto Mono", Consolas, "Courier New", monospace;
// 字体大小
@font-size-xs: 12px;
@font-size-sm: 14px;
@font-size-md: 16px;
@font-size-lg: 18px;
@font-size-xl: 20px;
@font-size-2xl: 24px;
@font-size-3xl: 30px;
@font-size-4xl: 36px;
// 字重
@font-weight-normal: 400;
@font-weight-medium: 500;
@font-weight-semibold: 600;
@font-weight-bold: 700;
// ===== 响应式断点 =====
@breakpoint-sm: 640px;
@breakpoint-md: 768px;
@breakpoint-lg: 1024px;
@breakpoint-xl: 1280px;
@breakpoint-2xl: 1536px;
// ===== Z-index 层级 =====
@z-index-dropdown: 1000;
@z-index-modal: 1050;
@z-index-popover: 1060;
@z-index-tooltip: 1070;
@z-index-navbar: 1080;
@z-index-notification: 1090;
// ===== 主题变量 =====
// 亮色主题
@light-theme: {
// 背景色
bg-primary: @white;
bg-secondary: @gray-50;
bg-tertiary: @gray-100;
// 文字颜色
text-primary: @gray-900;
text-secondary: @gray-600;
text-tertiary: @gray-400;
// 边框颜色
border-primary: @gray-200;
border-secondary: @gray-100;
// 玻璃效果
glass-bg: rgba(255, 255, 255, @glass-opacity-medium);
glass-border: rgba(255, 255, 255, @glass-border-opacity);
}
;
// 暗色主题
@dark-theme: {
// 背景色
bg-primary: @gray-900;
bg-secondary: @gray-800;
bg-tertiary: @gray-700;
// 文字颜色
text-primary: @white;
text-secondary: @gray-300;
text-tertiary: @gray-500;
// 边框颜色
border-primary: @gray-700;
border-secondary: @gray-800;
// 玻璃效果
glass-bg: rgba(0, 0, 0, @glass-opacity-medium);
glass-border: rgba(255, 255, 255, @glass-border-opacity);
}
;

View File

@@ -1,26 +1,38 @@
/* Apple Design System Animations */
/* 苹果设计系统动画效果 */
// Apple Design System Animations
// animations.css 合并的 Apple 动画系统
/* ==================== 基础动画变量 ==================== */
// ==================== 基础动画变量 ====================
:root {
/* 缓动函数 */
// Apple 特有缓动函数
--apple-timing-standard: cubic-bezier(0.4, 0.0, 0.2, 1);
--apple-timing-decelerate: cubic-bezier(0.0, 0.0, 0.2, 1);
--apple-timing-accelerate: cubic-bezier(0.4, 0.0, 1, 1);
--apple-timing-sharp: cubic-bezier(0.4, 0.0, 0.6, 1);
// Apple 特有动画持续时间
--apple-duration-immediate: 100ms;
--apple-duration-quick: 200ms;
--apple-duration-moderate: 300ms;
--apple-duration-slow: 500ms;
// 通用缓动函数
--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
--ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
--ease-spring: cubic-bezier(0.68, -0.55, 0.265, 1.55);
/* 动画持续时间 */
// 通用动画持续时间
--duration-fast: 0.15s;
--duration-normal: 0.3s;
--duration-slow: 0.5s;
--duration-slower: 0.8s;
}
/* ==================== 按钮微交互 ==================== */
// ==================== 按钮微交互 ====================
.apple-button {
position: relative;
overflow: hidden;
transform: translateZ(0); /* 启用硬件加速 */
transform: translateZ(0);
}
.apple-button::before {
@@ -50,41 +62,23 @@
transition: all var(--duration-fast) var(--ease-out-quart);
}
/* 主要按钮特殊效果 */
.apple-button.primary {
box-shadow:
0 4px 14px rgba(0, 122, 255, 0.3),
0 2px 6px rgba(0, 0, 0, 0.1);
transition: all var(--duration-normal) var(--ease-out-quart);
}
.apple-button.primary:hover {
box-shadow:
0 8px 25px rgba(0, 122, 255, 0.4),
0 4px 12px rgba(0, 0, 0, 0.15);
transform: translateY(-2px) translateZ(0);
}
.apple-button.primary:active {
box-shadow:
0 2px 8px rgba(0, 122, 255, 0.3),
0 1px 3px rgba(0, 0, 0, 0.1);
}
/* ==================== 输入框微交互 ==================== */
.apple-input, .apple-textarea {
// ==================== 输入框微交互 ====================
.apple-input,
.apple-textarea {
transition: all var(--duration-normal) var(--ease-out-quart);
position: relative;
}
.apple-input:hover, .apple-textarea:hover {
.apple-input:hover,
.apple-textarea:hover {
border-color: var(--color-system-blue);
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.05),
0 0 0 1px rgba(0, 122, 255, 0.1);
}
.apple-input:focus, .apple-textarea:focus {
.apple-input:focus,
.apple-textarea:focus {
transform: scale(1.02);
border-color: var(--color-system-blue);
box-shadow:
@@ -93,7 +87,7 @@
0 4px 12px rgba(0, 122, 255, 0.1);
}
/* ==================== 卡片悬浮效果 ==================== */
// ==================== 卡片悬浮效果 ====================
.apple-card {
transition: all var(--duration-normal) var(--ease-out-quart);
transform: translateZ(0);
@@ -111,7 +105,7 @@
transition: color var(--duration-normal) var(--ease-out-quart);
}
/* ==================== 开关动画 ==================== */
// ==================== 开关动画 ====================
.apple-switch .slider {
transition: all var(--duration-normal) var(--ease-spring);
}
@@ -131,7 +125,7 @@
100% { transform: translateX(20px) scale(1); }
}
/* ==================== 标签页切换动画 ==================== */
// ==================== 标签页切换动画 ====================
.tab-item {
transition: all var(--duration-normal) var(--ease-out-quart);
position: relative;
@@ -173,7 +167,7 @@
}
}
/* ==================== 加载动画 ==================== */
// ==================== 加载动画 ====================
.loading-spinner {
width: 20px;
height: 20px;
@@ -188,7 +182,7 @@
100% { transform: rotate(360deg); }
}
/* ==================== 脉冲效果 ==================== */
// ==================== 脉冲效果 ====================
.pulse-effect {
animation: pulse 2s ease-in-out infinite;
}
@@ -204,7 +198,7 @@
}
}
/* ==================== 弹跳进入动画 ==================== */
// ==================== 弹跳进入动画 ====================
.bounce-in {
animation: bounceIn var(--duration-slower) var(--ease-spring);
}
@@ -227,7 +221,7 @@
}
}
/* ==================== 淡入动画 ==================== */
// ==================== 淡入动画 ====================
.fade-in {
animation: fadeIn var(--duration-slow) var(--ease-out-quart);
}
@@ -243,7 +237,7 @@
}
}
/* ==================== 滑入动画 ==================== */
// ==================== 滑入动画 ====================
.slide-in-left {
animation: slideInLeft var(--duration-slow) var(--ease-out-expo);
}
@@ -274,7 +268,7 @@
}
}
/* ==================== 悬浮提示动画 ==================== */
// ==================== 悬浮提示动画 ====================
.tooltip {
position: relative;
display: inline-block;
@@ -303,7 +297,7 @@
transform: translateX(-50%) translateY(-4px);
}
/* ==================== 通知动画 ==================== */
// ==================== 通知动画 ====================
.notification-enter {
animation: notificationSlideIn var(--duration-slow) var(--ease-out-expo);
}
@@ -334,7 +328,7 @@
}
}
/* ==================== 模态框动画 ==================== */
// ==================== 模态框动画 ====================
.modal-backdrop {
animation: backdropFadeIn var(--duration-normal) ease-out;
}
@@ -359,7 +353,7 @@
}
}
/* ==================== 页面过渡动画 ==================== */
// ==================== 页面过渡动画 ====================
.page-transition-enter {
animation: pageEnter var(--duration-slow) var(--ease-out-expo);
}
@@ -390,9 +384,9 @@
}
}
/* ==================== 响应式动画控制 ==================== */
// ==================== 响应式动画控制 ====================
@media (max-width: 768px) {
/* 移动端减少动画复杂度 */
// 移动端减少动画复杂度
.apple-button:hover {
transform: none;
}
@@ -401,12 +395,13 @@
transform: translateY(-2px);
}
.apple-input:focus, .apple-textarea:focus {
.apple-input:focus,
.apple-textarea:focus {
transform: none;
}
}
/* ==================== 减少动画偏好 ==================== */
// ==================== 减少动画偏好 ====================
@media (prefers-reduced-motion: reduce) {
*,
*::before,
@@ -425,7 +420,7 @@
}
}
/* ==================== 高性能动画优化 ==================== */
// ==================== 高性能动画优化 ====================
.apple-button,
.apple-card,
.apple-input,
@@ -434,7 +429,7 @@
will-change: transform, box-shadow;
}
/* 动画完成后移除will-change */
// 动画完成后移除will-change
.apple-button:not(:hover):not(:active),
.apple-card:not(:hover),
.apple-input:not(:focus),

View File

@@ -0,0 +1,394 @@
// Apple Extended Components
// 从 globals.css 合并的 Apple 组件样式
// ==================== Apple Glass Card Enhancements ====================
// 这些是增强的玻璃卡片效果,基础样式在 apple-components.scss 中
.apple-card.glass {
// Apple 特有的玻璃效果增强
background: var(--apple-material-regular);
backdrop-filter: var(--apple-blur-regular);
-webkit-backdrop-filter: var(--apple-blur-regular);
border-radius: var(--apple-radius-continuous);
border: 0.5px solid var(--apple-separator);
box-shadow: var(--apple-shadow-m);
transition: all var(--apple-duration-moderate) var(--apple-timing-standard);
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 0.5px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
opacity: 0.6;
}
&:hover {
background: var(--apple-material-thick);
backdrop-filter: var(--apple-blur-thick);
-webkit-backdrop-filter: var(--apple-blur-thick);
box-shadow: var(--apple-shadow-l);
transform: translateY(-2px) scale(1.02);
}
}
// ==================== Apple Button Enhancements ====================
// 这些是增强的按钮效果,基础样式在 apple-components.scss 中
.apple-button {
// 添加 Apple 特有的动画效果
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(circle, rgba(255, 255, 255, 0.3) 0%, transparent 70%);
transform: scale(0);
transition: transform var(--apple-duration-quick) var(--apple-timing-decelerate);
pointer-events: none;
}
&:active::before {
transform: scale(1);
}
&:hover {
transform: translateY(-1px) translateZ(0);
background: color-mix(in srgb, var(--apple-blue) 85%, black);
}
&:active {
transform: translateY(0) scale(0.98) translateZ(0);
transition-duration: var(--apple-duration-immediate);
}
}
.apple-button--secondary {
background: var(--apple-system-fill);
color: var(--apple-blue);
&:hover {
background: var(--apple-secondary-system-fill);
}
}
// 主要按钮特殊效果
.apple-button.primary {
box-shadow:
0 4px 14px rgba(0, 122, 255, 0.3),
0 2px 6px rgba(0, 0, 0, 0.1);
&:hover {
box-shadow:
0 8px 25px rgba(0, 122, 255, 0.4),
0 4px 12px rgba(0, 0, 0, 0.15);
transform: translateY(-2px) translateZ(0);
}
&:active {
box-shadow:
0 2px 8px rgba(0, 122, 255, 0.3),
0 1px 3px rgba(0, 0, 0, 0.1);
}
}
// ==================== Apple Input Enhancements ====================
// 这些是增强的输入框效果,基础样式在 apple-components.scss 中
.apple-input {
// 添加 Apple 特有的交互动画
&:hover {
border-color: var(--color-system-blue);
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.05),
0 0 0 1px rgba(0, 122, 255, 0.1);
}
&:focus {
outline: none;
transform: scale(1.02);
border-color: var(--color-system-blue);
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.05),
0 0 0 3px rgba(0, 122, 255, 0.15),
0 4px 12px rgba(0, 122, 255, 0.1);
}
&::placeholder {
color: var(--apple-tertiary-label);
}
}
// ==================== Apple Typography Classes ====================
.apple-large-title {
font-size: var(--apple-large-title);
font-weight: 700;
line-height: 1.2;
letter-spacing: -0.5px;
}
.apple-title-1 {
font-size: var(--apple-title-1);
font-weight: 700;
line-height: 1.25;
letter-spacing: -0.3px;
}
.apple-title-2 {
font-size: var(--apple-title-2);
font-weight: 600;
line-height: 1.3;
letter-spacing: -0.2px;
}
.apple-title-3 {
font-size: var(--apple-title-3);
font-weight: 600;
line-height: 1.35;
}
.apple-headline {
font-size: var(--apple-headline);
font-weight: 600;
line-height: 1.4;
}
.apple-body {
font-size: var(--apple-body);
font-weight: 400;
line-height: 1.4;
}
.apple-callout {
font-size: var(--apple-callout);
font-weight: 400;
line-height: 1.4;
}
.apple-subhead {
font-size: var(--apple-subhead);
font-weight: 400;
line-height: 1.4;
color: var(--apple-secondary-label);
}
.apple-footnote {
font-size: var(--apple-footnote);
font-weight: 400;
line-height: 1.4;
color: var(--apple-secondary-label);
}
.apple-caption {
font-size: var(--apple-caption-1);
font-weight: 400;
line-height: 1.4;
color: var(--apple-tertiary-label);
}
// ==================== Apple Navigation ====================
.apple-navigation {
background: var(--apple-material-regular);
backdrop-filter: var(--apple-blur-thick);
-webkit-backdrop-filter: var(--apple-blur-thick);
border-bottom: 0.5px solid var(--apple-separator);
padding: var(--apple-spacing-m);
position: sticky;
top: 0;
z-index: 100;
}
// ==================== Apple Tab Bar ====================
.apple-tab-bar {
background: var(--apple-material-thick);
backdrop-filter: var(--apple-blur-ultra-thick);
-webkit-backdrop-filter: var(--apple-blur-ultra-thick);
border-top: 0.5px solid var(--apple-separator);
padding: var(--apple-spacing-s) var(--apple-spacing-m);
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
}
.apple-tab-item {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--apple-spacing-xs);
padding: var(--apple-spacing-s);
border-radius: var(--apple-radius-s);
transition: all var(--apple-duration-quick) var(--apple-timing-standard);
cursor: pointer;
min-width: 44px;
}
.apple-tab-item:hover {
background: var(--apple-system-fill);
}
.apple-tab-item--active {
color: var(--apple-blue);
}
// ==================== Apple Status Indicators ====================
.apple-status-success {
color: var(--apple-green);
}
.apple-status-warning {
color: var(--apple-orange);
}
.apple-status-error {
color: var(--apple-red);
}
.apple-status-info {
color: var(--apple-blue);
}
// ==================== Apple Dividers ====================
.apple-divider {
height: 0.5px;
background: var(--apple-separator);
margin: var(--apple-spacing-m) 0;
}
.apple-divider--thick {
height: 1px;
background: var(--apple-opaque-separator);
}
// ==================== Apple Icons ====================
.apple-icon {
width: 24px;
height: 24px;
color: var(--apple-blue);
transition: color var(--apple-duration-quick) var(--apple-timing-standard);
}
.apple-icon--secondary {
color: var(--apple-secondary-label);
}
// ==================== Apple Gradient Utilities ====================
.apple-gradient-blue {
background: var(--apple-gradient-blue);
color: white;
}
.apple-gradient-green {
background: var(--apple-gradient-green);
color: white;
}
.apple-gradient-indigo {
background: var(--apple-gradient-indigo);
color: white;
}
.apple-gradient-orange {
background: var(--apple-gradient-orange);
color: white;
}
.apple-gradient-pink {
background: var(--apple-gradient-pink);
color: white;
}
.apple-gradient-purple {
background: var(--apple-gradient-purple);
color: white;
}
.apple-gradient-red {
background: var(--apple-gradient-red);
color: white;
}
.apple-gradient-teal {
background: var(--apple-gradient-teal);
color: white;
}
.apple-gradient-yellow {
background: var(--apple-gradient-yellow);
color: black;
}
// ==================== Apple Vibrant Colors ====================
.apple-vibrant-blue {
color: var(--apple-vibrant-blue);
}
.apple-vibrant-green {
color: var(--apple-vibrant-green);
}
.apple-vibrant-indigo {
color: var(--apple-vibrant-indigo);
}
.apple-vibrant-orange {
color: var(--apple-vibrant-orange);
}
.apple-vibrant-pink {
color: var(--apple-vibrant-pink);
}
.apple-vibrant-purple {
color: var(--apple-vibrant-purple);
}
.apple-vibrant-red {
color: var(--apple-vibrant-red);
}
.apple-vibrant-teal {
color: var(--apple-vibrant-teal);
}
.apple-vibrant-yellow {
color: var(--apple-vibrant-yellow);
}
// ==================== Apple Dynamic Backgrounds ====================
.apple-dynamic-bg {
background: var(--apple-dynamic-background);
}
.dark .apple-dynamic-bg {
background: var(--apple-dynamic-background-dark);
}
// ==================== High Contrast Mode ====================
@media (prefers-contrast: high) {
.apple-glass-card {
border-width: 2px;
}
.apple-button {
border: 2px solid currentColor;
}
}
// ==================== Focus Indicators ====================
*:focus-visible {
outline: 2px solid var(--apple-blue);
outline-offset: 2px;
border-radius: var(--apple-radius-s);
}
*:focus:not(:focus-visible) {
outline: none;
}

View File

@@ -0,0 +1,683 @@
// Apple Human Interface Guidelines Components
// 苹果人机界面指南组件样式
// ==================== 按钮组件 ====================
.apple-button {
display: inline-flex;
align-items: center;
justify-content: center;
height: 44px;
padding: 0 var(--spacing-lg);
border: none;
border-radius: 22px; // 更圆滑的圆角,高度的一半
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
font-size: var(--font-size-body);
font-weight: var(--font-weight-medium);
cursor: pointer;
text-decoration: none;
transition: all var(--transition-normal);
position: relative;
overflow: hidden;
user-select: none;
-webkit-tap-highlight-color: transparent;
// 添加微妙的内阴影增强立体感
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: inherit;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 50%);
pointer-events: none;
opacity: 0;
transition: opacity var(--transition-fast);
}
&:hover::before {
opacity: 1;
}
&:disabled {
opacity: var(--opacity-disabled);
cursor: not-allowed;
pointer-events: none;
&::before {
display: none;
}
}
// 主要按钮 - 玻璃拟态风格
&.primary {
background: linear-gradient(135deg, var(--color-system-blue) 0%, #0066CC 100%);
color: white;
box-shadow:
0 4px 16px rgba(0, 122, 255, 0.4),
0 2px 8px rgba(0, 122, 255, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
&:hover:not(:disabled) {
background: linear-gradient(135deg, #0066CC 0%, #0056b3 100%);
transform: translateY(-2px) scale(1.02);
box-shadow:
0 6px 20px rgba(0, 122, 255, 0.5),
0 3px 12px rgba(0, 122, 255, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
&:active {
background: linear-gradient(135deg, #004499 0%, #003d82 100%);
transform: translateY(0) scale(0.98);
box-shadow:
0 2px 8px rgba(0, 122, 255, 0.3),
inset 0 2px 4px rgba(0, 0, 0, 0.2);
}
&:focus-visible {
outline: none;
box-shadow:
0 4px 16px rgba(0, 122, 255, 0.4),
0 2px 8px rgba(0, 122, 255, 0.2),
0 0 0 4px rgba(0, 122, 255, 0.3);
}
}
// 次要按钮 - 玻璃拟态风格
&.secondary {
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.6) 100%);
backdrop-filter: blur(var(--blur-lg)) saturate(1.2);
-webkit-backdrop-filter: blur(var(--blur-lg)) saturate(1.2);
color: var(--color-label-primary);
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow:
0 2px 8px rgba(0, 0, 0, 0.1),
0 1px 0 rgba(255, 255, 255, 0.2);
[data-theme="dark"] & {
background:
linear-gradient(135deg, rgba(28, 28, 30, 0.8) 0%, rgba(28, 28, 30, 0.6) 100%);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow:
0 2px 8px rgba(0, 0, 0, 0.3),
0 1px 0 rgba(255, 255, 255, 0.08);
}
&:hover:not(:disabled) {
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.7) 100%);
transform: translateY(-1px);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.15),
0 2px 0 rgba(255, 255, 255, 0.3);
[data-theme="dark"] & {
background:
linear-gradient(135deg, rgba(28, 28, 30, 0.9) 0%, rgba(28, 28, 30, 0.7) 100%);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.4),
0 2px 0 rgba(255, 255, 255, 0.12);
}
}
}
// 文本按钮
&.text {
background-color: transparent;
color: var(--color-system-blue);
border-radius: 22px; // 保持圆角一致
&:hover:not(:disabled) {
background-color: rgba(0, 122, 255, 0.1);
transform: translateY(-1px);
}
}
// 危险按钮
&.danger {
background: linear-gradient(135deg, var(--color-system-red) 0%, #d32f2f 100%);
color: white;
box-shadow:
0 4px 16px rgba(255, 59, 48, 0.4),
0 2px 8px rgba(255, 59, 48, 0.2);
&:hover:not(:disabled) {
background: linear-gradient(135deg, #d32f2f 0%, #c62828 100%);
transform: translateY(-2px) scale(1.02);
box-shadow:
0 6px 20px rgba(255, 59, 48, 0.5),
0 3px 12px rgba(255, 59, 48, 0.3);
}
}
// 成功按钮
&.success {
background: linear-gradient(135deg, var(--color-system-green) 0%, #2e7d32 100%);
color: white;
box-shadow:
0 4px 16px rgba(52, 199, 89, 0.4),
0 2px 8px rgba(52, 199, 89, 0.2);
&:hover:not(:disabled) {
background: linear-gradient(135deg, #2e7d32 0%, #1b5e20 100%);
transform: translateY(-2px) scale(1.02);
box-shadow:
0 6px 20px rgba(52, 199, 89, 0.5),
0 3px 12px rgba(52, 199, 89, 0.3);
}
}
// 尺寸变体 - 调整圆角以适应不同尺寸
&.small {
height: 32px;
padding: 0 var(--spacing-md);
font-size: var(--font-size-footnote);
border-radius: 16px; // 高度的一半
}
&.large {
height: 56px;
padding: 0 var(--spacing-xl);
font-size: var(--font-size-headline);
border-radius: 28px; // 高度的一半
}
// 圆形按钮
&.round {
border-radius: var(--radius-full);
width: 44px;
padding: 0;
&.small {
width: 32px;
height: 32px;
}
&.large {
width: 56px;
height: 56px;
}
}
// 加载状态
&.loading {
pointer-events: none;
&::after {
content: '';
position: absolute;
width: 16px;
height: 16px;
border: 2px solid transparent;
border-top-color: currentColor;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.button-text {
opacity: 0;
}
}
}
@keyframes spin {
to { transform: rotate(360deg); }
}
// ==================== 输入框组件 ====================
.apple-input {
width: 100%;
height: 44px;
padding: 0 var(--spacing-md);
border: 1px solid var(--color-separator-opaque);
border-radius: 22px; // 更圆滑的圆角
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.7) 100%);
backdrop-filter: blur(var(--blur-lg)) saturate(1.2);
-webkit-backdrop-filter: blur(var(--blur-lg)) saturate(1.2);
color: var(--color-label-primary);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
font-size: var(--font-size-body);
transition: all var(--transition-normal);
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.05),
0 2px 8px rgba(0, 0, 0, 0.1),
0 1px 0 rgba(255, 255, 255, 0.3);
[data-theme="dark"] & {
background:
linear-gradient(135deg, rgba(28, 28, 30, 0.9) 0%, rgba(28, 28, 30, 0.7) 100%);
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.3),
0 2px 8px rgba(0, 0, 0, 0.3),
0 1px 0 rgba(255, 255, 255, 0.1);
}
&::placeholder {
color: var(--color-label-tertiary);
font-weight: var(--font-weight-regular);
}
&:hover:not(:disabled):not(:focus) {
border-color: var(--color-separator-non-opaque);
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.05),
0 2px 4px rgba(0, 0, 0, 0.05);
}
&:focus {
outline: none;
border-color: var(--color-system-blue);
box-shadow:
inset 0 1px 2px rgba(0, 0, 0, 0.05),
0 0 0 4px rgba(0, 122, 255, 0.15),
0 2px 8px rgba(0, 122, 255, 0.1);
}
&:disabled {
opacity: var(--opacity-disabled);
background-color: var(--color-background-secondary);
cursor: not-allowed;
}
&.error {
border-color: var(--color-system-red);
&:focus {
box-shadow: 0 0 0 3px rgba(255, 59, 48, 0.1);
}
}
&.success {
border-color: var(--color-system-green);
&:focus {
box-shadow: 0 0 0 3px rgba(52, 199, 89, 0.1);
}
}
// 尺寸变体
&.small {
height: 32px;
padding: 0 var(--spacing-sm);
font-size: var(--font-size-footnote);
}
&.large {
height: 56px;
padding: 0 var(--spacing-lg);
font-size: var(--font-size-headline);
}
}
// 文本域
.apple-textarea {
@extend .apple-input;
height: auto;
min-height: 88px;
padding: var(--spacing-md);
resize: vertical;
}
// ==================== 卡片组件 ====================
.apple-card {
// 默认使用玻璃拟态效果 - 加深浅色背景
background:
linear-gradient(135deg, rgba(245, 245, 245, 0.90) 0%, rgba(235, 235, 235, 0.75) 100%);
backdrop-filter: blur(var(--blur-xl)) saturate(1.3);
-webkit-backdrop-filter: blur(var(--blur-xl)) saturate(1.3);
border: 1px solid rgba(180, 180, 180, 0.4);
border-radius: var(--radius-xl);
padding: var(--spacing-lg);
box-shadow:
0 12px 48px rgba(0, 0, 0, 0.15),
0 6px 16px rgba(0, 0, 0, 0.10),
0 1px 0 rgba(255, 255, 255, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
transition: all var(--transition-normal);
// 暗色主题适配 - 适应 #1C273C 背景的玻璃质感
[data-theme="dark"] & {
background:
linear-gradient(135deg, rgba(48, 64, 82, 0.88) 0%, rgba(40, 54, 72, 0.82) 100%);
border: 1px solid rgba(100, 149, 237, 0.15);
box-shadow:
0 12px 48px rgba(28, 39, 60, 0.4),
0 6px 16px rgba(28, 39, 60, 0.3),
0 1px 0 rgba(100, 149, 237, 0.10),
inset 0 1px 0 rgba(100, 149, 237, 0.15);
}
// 悬停效果 - 增强玻璃质感
&:hover {
transform: translateY(-3px);
box-shadow:
0 18px 56px rgba(0, 0, 0, 0.20),
0 10px 24px rgba(0, 0, 0, 0.15),
0 2px 0 rgba(255, 255, 255, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.4);
[data-theme="dark"] & {
box-shadow:
0 18px 56px rgba(28, 39, 60, 0.5),
0 10px 24px rgba(28, 39, 60, 0.4),
0 2px 0 rgba(100, 149, 237, 0.15),
inset 0 1px 0 rgba(100, 149, 237, 0.25);
}
}
// 传统样式变体 (用于需要非玻璃效果的场景)
&.solid {
background-color: var(--color-background-secondary);
backdrop-filter: none;
-webkit-backdrop-filter: none;
border: 1px solid var(--color-separator-opaque);
box-shadow: var(--shadow-sm);
[data-theme="dark"] & {
background-color: var(--color-background-tertiary);
}
&:hover {
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
}
.card-header {
margin-bottom: var(--spacing-md);
.card-title {
font-size: var(--font-size-title3);
font-weight: var(--font-weight-semibold);
color: var(--color-label-primary);
margin: 0 0 var(--spacing-xs) 0;
}
.card-subtitle {
font-size: var(--font-size-subheadline);
color: var(--color-label-secondary);
margin: 0;
}
}
.card-content {
font-size: var(--font-size-body);
color: var(--color-label-primary);
line-height: var(--line-height-relaxed);
}
.card-footer {
margin-top: var(--spacing-md);
padding-top: var(--spacing-md);
border-top: 1px solid var(--color-separator-opaque);
}
// 卡片变体
&.elevated {
box-shadow: var(--shadow-lg);
&:hover {
box-shadow: var(--shadow-xl);
transform: translateY(-4px);
}
}
&.flat {
box-shadow: none;
border: 1px solid var(--color-separator-opaque);
}
}
// ==================== 专用玻璃卡片组件 ====================
.apple-glass-card {
// Apple 设计规范玻璃卡片 - 加深浅色模式背景,深色模式适应背景
background:
linear-gradient(135deg,
rgba(248, 248, 248, 0.98) 0%,
rgba(240, 240, 240, 0.96) 50%,
rgba(235, 235, 235, 0.94) 100%);
backdrop-filter: blur(24px) saturate(1.8);
-webkit-backdrop-filter: blur(24px) saturate(1.8);
border: 1px solid rgba(200, 200, 200, 0.6);
border-radius: 16px;
padding: var(--spacing-xl);
box-shadow:
0 6px 20px rgba(0, 0, 0, 0.08),
0 3px 10px rgba(0, 0, 0, 0.04),
0 1px 3px rgba(0, 0, 0, 0.02),
0 0 0 1px rgba(255, 255, 255, 0.8),
inset 0 1px 2px rgba(255, 255, 255, 0.9),
inset 0 -1px 2px rgba(0, 0, 0, 0.08);
transition: all var(--transition-normal);
position: relative;
overflow: hidden;
// 背景纹理增强质感
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
repeating-linear-gradient(
45deg,
transparent,
transparent 2px,
rgba(255, 255, 255, 0.03) 2px,
rgba(255, 255, 255, 0.03) 4px
),
repeating-linear-gradient(
-45deg,
transparent,
transparent 2px,
rgba(0, 0, 0, 0.01) 2px,
rgba(0, 0, 0, 0.01) 4px
);
pointer-events: none;
opacity: 0.6;
}
// 顶部高光效果 - Apple 风格
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg,
rgba(255, 255, 255, 0.4) 0%,
rgba(255, 255, 255, 0.8) 50%,
rgba(255, 255, 255, 0.4) 100%);
box-shadow: 0 1px 2px rgba(255, 255, 255, 0.6);
}
// 暗色主题适配 - 适应 #1C273C 背景的玻璃质感
[data-theme="dark"] & {
background:
linear-gradient(135deg,
rgba(44, 60, 80, 0.94) 0%,
rgba(35, 48, 65, 0.92) 50%,
rgba(28, 39, 60, 0.90) 100%);
border: 1px solid rgba(100, 149, 237, 0.18);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.5),
0 4px 16px rgba(28, 39, 60, 0.4),
0 1px 3px rgba(0, 0, 0, 0.08),
0 0 0 1px rgba(100, 149, 237, 0.12),
inset 0 1px 2px rgba(100, 149, 237, 0.18),
inset 0 -1px 2px rgba(0, 0, 0, 0.25);
&::before {
background: linear-gradient(90deg,
rgba(100, 149, 237, 0.10) 0%,
rgba(135, 206, 250, 0.18) 50%,
rgba(100, 149, 237, 0.10) 100%);
box-shadow: 0 1px 1px rgba(100, 149, 237, 0.12);
}
&::after {
background:
repeating-linear-gradient(
45deg,
transparent,
transparent 2px,
rgba(100, 149, 237, 0.010) 2px,
rgba(100, 149, 237, 0.010) 4px
),
repeating-linear-gradient(
-45deg,
transparent,
transparent 2px,
rgba(28, 39, 60, 0.025) 2px,
rgba(28, 39, 60, 0.025) 4px
);
}
}
// 悬停效果 - 轻微的悬停反馈
&:hover {
transform: translateY(-1px);
box-shadow:
0 6px 20px rgba(0, 0, 0, 0.06),
0 3px 10px rgba(0, 0, 0, 0.03),
0 1px 4px rgba(0, 0, 0, 0.015),
0 0 0 1px rgba(255, 255, 255, 1),
inset 0 1px 3px rgba(255, 255, 255, 1),
inset 0 -1px 3px rgba(0, 0, 0, 0.08);
[data-theme="dark"] & {
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.6),
0 6px 20px rgba(28, 39, 60, 0.5),
0 1px 4px rgba(0, 0, 0, 0.10),
0 0 0 1px rgba(100, 149, 237, 0.22),
inset 0 1px 3px rgba(100, 149, 237, 0.25),
inset 0 -1px 3px rgba(0, 0, 0, 0.35);
}
}
// 尺寸变体
&.small {
padding: var(--spacing-lg);
border-radius: var(--radius-xl);
}
&.large {
padding: var(--spacing-2xl);
border-radius: var(--radius-3xl);
}
// 内容结构
.glass-card-header {
margin-bottom: var(--spacing-lg);
padding-bottom: var(--spacing-md);
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
[data-theme="dark"] & {
border-bottom-color: rgba(255, 255, 255, 0.08);
}
.glass-card-title {
font-size: var(--font-size-title2);
font-weight: var(--font-weight-bold);
color: var(--color-label-primary);
margin: 0 0 var(--spacing-sm) 0;
}
.glass-card-subtitle {
font-size: var(--font-size-body);
color: var(--color-label-secondary);
margin: 0;
}
}
.glass-card-content {
color: var(--color-label-primary);
line-height: var(--line-height-relaxed);
font-size: var(--font-size-body);
}
.glass-card-footer {
margin-top: var(--spacing-lg);
padding-top: var(--spacing-md);
border-top: 1px solid rgba(255, 255, 255, 0.15);
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--spacing-md);
[data-theme="dark"] & {
border-top-color: rgba(255, 255, 255, 0.08);
}
}
// 特殊效果变体 - 轻量化设计
&.premium {
// 高级质感 - 加深的浅色背景
background:
linear-gradient(135deg,
rgba(245, 245, 245, 0.99) 0%,
rgba(235, 235, 235, 0.97) 100%);
backdrop-filter: blur(32px) saturate(2.0);
-webkit-backdrop-filter: blur(32px) saturate(2.0);
border: 1px solid rgba(180, 180, 180, 0.5);
box-shadow:
0 4px 16px rgba(0, 0, 0, 0.06),
0 2px 8px rgba(0, 0, 0, 0.03),
0 0 0 1px rgba(255, 255, 255, 0.7),
inset 0 1px 1px rgba(255, 255, 255, 0.8),
inset 0 -1px 1px rgba(0, 0, 0, 0.06);
[data-theme="dark"] & {
background:
linear-gradient(135deg,
rgba(50, 68, 88, 0.96) 0%,
rgba(42, 57, 75, 0.94) 100%);
border: 1px solid rgba(100, 149, 237, 0.25);
box-shadow:
0 4px 16px rgba(28, 39, 60, 0.2),
0 2px 8px rgba(28, 39, 60, 0.1),
0 0 0 1px rgba(100, 149, 237, 0.18),
inset 0 1px 1px rgba(100, 149, 237, 0.25),
inset 0 -1px 1px rgba(0, 0, 0, 0.15);
}
}
&.translucent {
// 半透明效果 - 轻量级玻璃,加深浅色背景
background:
linear-gradient(135deg,
rgba(250, 250, 250, 0.96) 0%,
rgba(240, 240, 240, 0.92) 100%);
backdrop-filter: blur(20px) saturate(1.6);
-webkit-backdrop-filter: blur(20px) saturate(1.6);
border: 1px solid rgba(190, 190, 190, 0.5);
box-shadow:
0 3px 10px rgba(0, 0, 0, 0.05),
0 1px 4px rgba(0, 0, 0, 0.02),
0 0 0 1px rgba(255, 255, 255, 0.8);
[data-theme="dark"] & {
background:
linear-gradient(135deg,
rgba(56, 74, 92, 0.90) 0%,
rgba(48, 64, 82, 0.86) 100%);
border: 1px solid rgba(100, 149, 237, 0.15);
box-shadow:
0 3px 12px rgba(28, 39, 60, 0.15),
0 1px 6px rgba(28, 39, 60, 0.08),
0 0 0 1px rgba(100, 149, 237, 0.12);
}
}
}

View File

@@ -1,130 +1,7 @@
/* Apple Human Interface Guidelines Design System */
/* 苹果人机界面指南设计系统 */
// Apple Design System Components
// apple-design-system.css 合并的组件样式
/* ==================== CSS变量定义 ==================== */
:root {
/* 色彩系统 - 浅色主题 */
--color-system-blue: #007AFF;
--color-system-green: #34C759;
--color-system-indigo: #5856D6;
--color-system-orange: #FF9500;
--color-system-pink: #FF2D92;
--color-system-purple: #AF52DE;
--color-system-red: #FF3B30;
--color-system-teal: #5AC8FA;
--color-system-yellow: #FFCC00;
/* 背景色 */
--color-background-primary: #FFFFFF;
--color-background-secondary: #F2F2F7;
--color-background-tertiary: #FFFFFF;
/* 标签色 */
--color-label-primary: #000000;
--color-label-secondary: #3C3C43;
--color-label-tertiary: #3C3C4399;
--color-label-quaternary: #3C3C432E;
/* 分隔线 */
--color-separator-opaque: #C6C6C8;
--color-separator-non-opaque: #3C3C4349;
/* 填充色 */
--color-fill-primary: #78788033;
--color-fill-secondary: #78788028;
--color-fill-tertiary: #7676801E;
--color-fill-quaternary: #74748014;
/* 字体大小 */
--font-size-largeTitle: 34px;
--font-size-title1: 28px;
--font-size-title2: 22px;
--font-size-title3: 20px;
--font-size-headline: 17px;
--font-size-body: 17px;
--font-size-callout: 16px;
--font-size-subheadline: 15px;
--font-size-footnote: 13px;
--font-size-caption1: 12px;
--font-size-caption2: 11px;
/* 字体粗细 */
--font-weight-ultraLight: 100;
--font-weight-thin: 200;
--font-weight-light: 300;
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--font-weight-heavy: 800;
--font-weight-black: 900;
/* 间距 */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--spacing-2xl: 48px;
/* 圆角 */
--radius-xs: 4px;
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
/* 阴影 */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.04);
/* 过渡 */
--transition-fast: 0.15s ease-out;
--transition-normal: 0.3s ease-out;
--transition-slow: 0.5s ease-out;
/* 模糊 */
--blur-sm: 4px;
--blur-md: 8px;
--blur-lg: 16px;
--blur-xl: 24px;
/* 透明度 */
--opacity-disabled: 0.3;
--opacity-secondary: 0.6;
}
/* 深色主题 */
[data-theme="dark"] {
--color-background-primary: #000000;
--color-background-secondary: #1C1C1E;
--color-background-tertiary: #2C2C2E;
--color-label-primary: #FFFFFF;
--color-label-secondary: #EBEBF5;
--color-label-tertiary: #EBEBF599;
--color-label-quaternary: #EBEBF52E;
--color-separator-opaque: #38383A;
--color-separator-non-opaque: #54545899;
--color-fill-primary: #78788033;
--color-fill-secondary: #78788028;
--color-fill-tertiary: #7676801E;
--color-fill-quaternary: #74748014;
}
/* 主题切换过渡 */
.theme-transition * {
transition: background-color var(--transition-normal),
color var(--transition-normal),
border-color var(--transition-normal),
box-shadow var(--transition-normal) !important;
}
/* ==================== 基础样式 ==================== */
// ==================== 基础样式 ====================
* {
box-sizing: border-box;
}
@@ -139,7 +16,15 @@ body {
transition: background-color var(--transition-normal), color var(--transition-normal);
}
/* ==================== 按钮组件 ==================== */
// ==================== 主题切换过渡 ====================
.theme-transition * {
transition: background-color var(--transition-normal),
color var(--transition-normal),
border-color var(--transition-normal),
box-shadow var(--transition-normal) !important;
}
// ==================== 按钮组件 ====================
.apple-button {
display: inline-flex;
align-items: center;
@@ -217,7 +102,7 @@ body {
font-size: var(--font-size-headline);
}
/* ==================== 卡片组件 ==================== */
// ==================== 卡片组件 ====================
.apple-card {
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(var(--blur-lg));
@@ -259,7 +144,7 @@ body {
padding: var(--spacing-lg);
}
/* ==================== 输入框组件 ==================== */
// ==================== 输入框组件 ====================
.apple-input {
width: 100%;
height: 44px;
@@ -310,7 +195,7 @@ body {
0 0 0 3px rgba(0, 122, 255, 0.1);
}
/* ==================== 开关组件 ==================== */
// ==================== 开关组件 ====================
.apple-switch {
position: relative;
display: inline-block;
@@ -358,7 +243,7 @@ body {
transform: translateX(20px);
}
/* ==================== 动态背景 ==================== */
// ==================== 动态背景 ====================
.dynamic-bg-container {
position: relative;
overflow: hidden;
@@ -395,7 +280,7 @@ body {
}
}
/* ==================== 响应式布局 ==================== */
// ==================== 响应式布局 ====================
.container {
width: 100%;
max-width: 1200px;
@@ -426,7 +311,7 @@ body {
}
}
/* ==================== 标签页 ==================== */
// ==================== 标签页 ====================
.tab-navigation {
display: flex;
background: rgba(255, 255, 255, 0.8);
@@ -481,7 +366,7 @@ body {
to { opacity: 1; transform: translateY(0); }
}
/* ==================== 工具类 ==================== */
// ==================== 工具类 ====================
.mb-4 { margin-bottom: var(--spacing-lg); }
.text-center { text-align: center; }
.d-flex { display: flex; }
@@ -489,7 +374,7 @@ body {
.justify-content-center { justify-content: center; }
.gap-md { gap: var(--spacing-md); }
/* ==================== 减少动画偏好 ==================== */
// ==================== 减少动画偏好 ====================
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;

View File

@@ -1,24 +1,24 @@
// 玻璃拟态组件样式
// Glass Morphism Components
@import '../utils/variables.less';
@import '../utils/mixins.less';
@use '../utils/variables.module' as vars;
@use '../utils/mixins.module' as mixins;
// ===== 玻璃卡片组件 =====
.glass-card {
.glass-card(md);
@include mixins.glass-card(md);
&--small {
.glass-card(sm);
@include mixins.glass-card(sm);
}
&--large {
.glass-card(lg);
@include mixins.glass-card(lg);
}
&__header {
margin-bottom: @spacing-md;
padding-bottom: @spacing-sm;
margin-bottom: vars.$spacing-md;
padding-bottom: vars.$spacing-sm;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
.dark & {
@@ -27,14 +27,14 @@
}
&__title {
font-size: @font-size-lg;
font-weight: @font-weight-semibold;
font-size: vars.$font-size-lg;
font-weight: vars.$font-weight-semibold;
color: var(--text-primary);
margin-bottom: @spacing-xs;
margin-bottom: vars.$spacing-xs;
}
&__subtitle {
font-size: @font-size-sm;
font-size: vars.$font-size-sm;
color: var(--text-secondary);
}
@@ -44,12 +44,12 @@
}
&__footer {
margin-top: @spacing-md;
padding-top: @spacing-sm;
margin-top: vars.$spacing-md;
padding-top: vars.$spacing-sm;
border-top: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
justify-content: flex-end;
gap: @spacing-sm;
gap: vars.$spacing-sm;
.dark & {
border-top-color: rgba(255, 255, 255, 0.05);
@@ -59,26 +59,26 @@
// ===== 玻璃按钮组件 =====
.glass-button {
.glass-button(primary);
@include mixins.glass-button(primary);
&--secondary {
.glass-button(secondary);
@include mixins.glass-button(secondary);
}
&--danger {
.glass-button(danger);
@include mixins.glass-button(danger);
}
&--small {
padding: @spacing-xs @spacing-sm;
font-size: @font-size-sm;
border-radius: @border-radius-sm;
padding: vars.$spacing-xs vars.$spacing-sm;
font-size: vars.$font-size-sm;
border-radius: vars.$border-radius-sm;
}
&--large {
padding: @spacing-md @spacing-lg;
font-size: @font-size-lg;
border-radius: @border-radius-lg;
padding: vars.$spacing-md vars.$spacing-lg;
font-size: vars.$font-size-lg;
border-radius: vars.$border-radius-lg;
}
&--full-width {
@@ -102,22 +102,22 @@
// ===== 玻璃输入框组件 =====
.glass-input {
.glass-input();
@include mixins.glass-input();
&--small {
padding: @spacing-xs @spacing-sm;
font-size: @font-size-sm;
border-radius: @border-radius-sm;
padding: vars.$spacing-xs vars.$spacing-sm;
font-size: vars.$font-size-sm;
border-radius: vars.$border-radius-sm;
}
&--large {
padding: @spacing-md @spacing-lg;
font-size: @font-size-lg;
border-radius: @border-radius-lg;
padding: vars.$spacing-md vars.$spacing-lg;
font-size: vars.$font-size-lg;
border-radius: vars.$border-radius-lg;
}
&--error {
border-color: @accent-red;
border-color: vars.$accent-red;
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2);
&:focus {
@@ -126,7 +126,7 @@
}
&--success {
border-color: @accent-green;
border-color: vars.$accent-green;
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2);
&:focus {
@@ -137,51 +137,51 @@
// ===== 玻璃导航栏组件 =====
.glass-navbar {
.glass-navbar();
@include mixins.glass-navbar();
&__container {
max-width: 1200px;
margin: 0 auto;
padding: 0 @spacing-lg;
padding: 0 vars.$spacing-lg;
display: flex;
align-items: center;
justify-content: space-between;
height: 64px;
@media (min-width: @breakpoint-md) {
padding: 0 @spacing-md;
@media (min-width: vars.$breakpoint-md) {
padding: 0 vars.$spacing-md;
}
}
&__brand {
font-size: @font-size-xl;
font-weight: @font-weight-bold;
font-size: vars.$font-size-xl;
font-weight: vars.$font-weight-bold;
color: var(--text-primary);
text-decoration: none;
transition: color @transition-fast;
transition: color vars.$transition-fast;
&:hover {
color: @primary-color;
color: vars.$primary-color;
}
}
&__menu {
display: flex;
align-items: center;
gap: @spacing-lg;
gap: vars.$spacing-lg;
@media (min-width: @breakpoint-md) {
gap: @spacing-md;
@media (min-width: vars.$breakpoint-md) {
gap: vars.$spacing-md;
}
}
&__link {
color: var(--text-secondary);
text-decoration: none;
font-weight: @font-weight-medium;
padding: @spacing-sm @spacing-md;
border-radius: @border-radius-md;
transition: all @transition-fast;
font-weight: vars.$font-weight-medium;
padding: vars.$spacing-sm vars.$spacing-md;
border-radius: vars.$border-radius-md;
transition: all vars.$transition-fast;
&:hover {
color: var(--text-primary);
@@ -189,7 +189,7 @@
}
&--active {
color: @primary-color;
color: vars.$primary-color;
background: rgba(99, 102, 241, 0.1);
}
}
@@ -197,9 +197,9 @@
// ===== 玻璃侧边栏组件 =====
.glass-sidebar {
.glass-sidebar();
@include mixins.glass-sidebar();
width: 280px;
padding: @spacing-lg;
padding: vars.$spacing-lg;
&--collapsed {
width: 80px;
@@ -210,13 +210,13 @@
}
&__header {
margin-bottom: @spacing-xl;
margin-bottom: vars.$spacing-xl;
text-align: center;
}
&__brand {
font-size: @font-size-xl;
font-weight: @font-weight-bold;
font-size: vars.$font-size-xl;
font-weight: vars.$font-weight-bold;
color: var(--text-primary);
}
@@ -225,18 +225,18 @@
}
&__item {
margin-bottom: @spacing-sm;
margin-bottom: vars.$spacing-sm;
}
&__link {
display: flex;
align-items: center;
gap: @spacing-md;
padding: @spacing-md;
gap: vars.$spacing-md;
padding: vars.$spacing-md;
color: var(--text-secondary);
text-decoration: none;
border-radius: @border-radius-md;
transition: all @transition-fast;
border-radius: vars.$border-radius-md;
transition: all vars.$transition-fast;
&:hover {
color: var(--text-primary);
@@ -244,7 +244,7 @@
}
&--active {
color: @primary-color;
color: vars.$primary-color;
background: rgba(99, 102, 241, 0.15);
}
}
@@ -256,7 +256,7 @@
}
&__text {
font-weight: @font-weight-medium;
font-weight: vars.$font-weight-medium;
}
}
@@ -267,11 +267,11 @@
left: 0;
right: 0;
bottom: 0;
z-index: @z-index-modal;
z-index: vars.$z-index-modal;
display: flex;
align-items: center;
justify-content: center;
padding: @spacing-lg;
padding: vars.$spacing-lg;
&__backdrop {
position: absolute;
@@ -280,20 +280,20 @@
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(@glass-blur-sm);
backdrop-filter: blur(vars.$glass-blur-sm);
cursor: pointer;
}
&__content {
.glass-modal();
@include mixins.glass-modal();
position: relative;
z-index: 1;
width: 100%;
max-width: 500px;
padding: @spacing-xl;
padding: vars.$spacing-xl;
@media (min-width: @breakpoint-sm) {
padding: @spacing-lg;
@media (min-width: vars.$breakpoint-sm) {
padding: vars.$spacing-lg;
max-width: 95vw;
}
}
@@ -302,52 +302,52 @@
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: @spacing-lg;
margin-bottom: vars.$spacing-lg;
}
&__title {
font-size: @font-size-xl;
font-weight: @font-weight-semibold;
font-size: vars.$font-size-xl;
font-weight: vars.$font-weight-semibold;
color: var(--text-primary);
}
&__close {
.glass-button(secondary);
padding: @spacing-sm;
border-radius: @border-radius-sm;
@include mixins.glass-button(secondary);
padding: vars.$spacing-sm;
border-radius: vars.$border-radius-sm;
}
&__body {
color: var(--text-primary);
line-height: 1.6;
margin-bottom: @spacing-lg;
margin-bottom: vars.$spacing-lg;
}
&__footer {
display: flex;
justify-content: flex-end;
gap: @spacing-sm;
gap: vars.$spacing-sm;
}
}
// ===== 玻璃通知组件 =====
.glass-notification {
.glass-notification(info);
@include mixins.glass-notification(info);
display: flex;
align-items: flex-start;
gap: @spacing-md;
gap: vars.$spacing-md;
max-width: 400px;
&--success {
.glass-notification(success);
@include mixins.glass-notification(success);
}
&--warning {
.glass-notification(warning);
@include mixins.glass-notification(warning);
}
&--error {
.glass-notification(error);
@include mixins.glass-notification(error);
}
&__icon {
@@ -362,30 +362,30 @@
}
&__title {
font-weight: @font-weight-semibold;
font-weight: vars.$font-weight-semibold;
color: var(--text-primary);
margin-bottom: @spacing-xs;
margin-bottom: vars.$spacing-xs;
}
&__message {
color: var(--text-secondary);
font-size: @font-size-sm;
font-size: vars.$font-size-sm;
line-height: 1.5;
}
&__close {
.glass-button(secondary);
padding: @spacing-xs;
border-radius: @border-radius-sm;
margin-left: @spacing-sm;
@include mixins.glass-button(secondary);
padding: vars.$spacing-xs;
border-radius: vars.$border-radius-sm;
margin-left: vars.$spacing-sm;
}
}
// ===== 玻璃加载器组件 =====
.glass-loader {
.center-flex();
.glass-base(@glass-opacity-medium, @glass-blur-md);
border-radius: @border-radius-full;
@include mixins.center-flex;
@include mixins.glass-base(vars.$glass-opacity-medium, vars.$glass-blur-md);
border-radius: vars.$border-radius-full;
width: 60px;
height: 60px;
@@ -393,8 +393,8 @@
width: 30px;
height: 30px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top: 3px solid @primary-color;
border-radius: @border-radius-full;
border-top: 3px solid vars.$primary-color;
border-radius: vars.$border-radius-full;
animation: glass-spinner 1s linear infinite;
}
@@ -433,14 +433,14 @@
// ===== 玻璃工具提示组件 =====
.glass-tooltip {
.glass-base(@glass-opacity-heavy, @glass-blur-md);
padding: @spacing-xs @spacing-sm;
border-radius: @border-radius-sm;
font-size: @font-size-xs;
@include mixins.glass-base(vars.$glass-opacity-heavy, vars.$glass-blur-md);
padding: vars.$spacing-xs vars.$spacing-sm;
border-radius: vars.$border-radius-sm;
font-size: vars.$font-size-xs;
color: var(--text-primary);
position: absolute;
z-index: @z-index-tooltip;
box-shadow: @glass-shadow-md;
z-index: vars.$z-index-tooltip;
box-shadow: vars.$glass-shadow-md;
pointer-events: none;
&::before {

View File

@@ -0,0 +1,403 @@
// Apple Human Interface Guidelines Design System
// 苹果人机界面指南设计系统主入口
// ==================== 1. SASS Modules ====================
@use 'themes/apple-design-tokens.module' as tokens;
@use 'utils/apple-mixins.module' as mixins;
@use 'utils/grid-system.module' as grid;
@use 'utils/visual-effects.module' as effects;
@use 'utils/mixins.module' as utils;
@use 'utils/variables.module' as vars;
@use 'components/apple-components.module' as components;
@use 'components/apple-components-extended.module' as extended;
@use 'components/apple-system-components.module' as system;
@use 'components/glass-components.module' as glass;
@use 'components/dynamic-backgrounds.module' as backgrounds;
@use 'animations/apple-animations.module' as animations;
@use 'themes/glass.module' as theme;
// ==================== 2. Tailwind CSS ====================
@tailwind base;
@tailwind components;
@tailwind utilities;
// ==================== 3. 基础样式 (Base Styles) ====================
// 全局重置
* {
box-sizing: border-box;
}
// 引入网格系统布局
.navbar-layout {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 var(--spacing-md);
height: 100%;
}
.grid-auto-fit {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--spacing-lg);
}
// ==================== 4. 全局样式 ====================
html {
font-size: 16px;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
body {
margin: 0;
padding: 0;
@include mixins.apple-font;
background-color: var(--color-background-primary);
color: var(--color-label-primary);
@include mixins.apple-transition(background-color);
@include mixins.apple-transition(color);
// 支持安全区域 (iOS设备)
@include mixins.safe-area-padding;
// 动态背景效果 (可选)
&.dynamic-background {
@include mixins.dynamic-background;
}
}
// ==================== 7. 主题切换支持 ====================
// 主题切换过渡效果
.theme-transition {
* {
@include mixins.apple-transition(background-color);
@include mixins.apple-transition(color);
@include mixins.apple-transition(border-color);
@include mixins.apple-transition(box-shadow);
}
}
// 主题切换按钮
.theme-toggle {
position: relative;
width: 44px;
height: 44px;
border: none;
border-radius: var(--radius-full);
background: var(--color-background-secondary);
color: var(--color-label-primary);
cursor: pointer;
@include mixins.flex-center;
@include mixins.apple-transition;
@include mixins.apple-shadow(sm);
&:hover {
@include mixins.apple-shadow(md);
transform: scale(1.05);
}
&:active {
transform: scale(0.95);
}
.icon {
width: 20px;
height: 20px;
@include mixins.apple-transition(transform);
}
&:hover .icon {
transform: rotate(180deg);
}
}
// ==================== 8. 布局组件 ====================
// 应用主容器
.app-container {
min-height: 100vh;
background-color: var(--color-background-primary);
@include mixins.apple-transition(background-color);
}
// 导航栏 - 玻璃拟态风格
.app-navbar {
position: sticky;
top: 0;
z-index: var(--z-sticky);
height: 44px;
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0.6) 100%);
backdrop-filter: blur(var(--blur-xl)) saturate(1.3);
-webkit-backdrop-filter: blur(var(--blur-xl)) saturate(1.3);
border-bottom: 1px solid rgba(255, 255, 255, 0.25);
box-shadow:
0 2px 12px rgba(0, 0, 0, 0.1),
0 1px 0 rgba(255, 255, 255, 0.3);
@extend .navbar-layout;
[data-theme="dark"] & {
background:
linear-gradient(135deg, rgba(28, 28, 30, 0.85) 0%, rgba(28, 28, 30, 0.6) 100%);
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
box-shadow:
0 2px 12px rgba(0, 0, 0, 0.3),
0 1px 0 rgba(255, 255, 255, 0.1);
}
.navbar-brand {
@include mixins.apple-font(var(--font-size-headline), var(--font-weight-semibold));
color: var(--color-label-primary);
text-decoration: none;
@include mixins.apple-transition(color);
}
.navbar-menu {
display: flex;
align-items: center;
gap: var(--spacing-md);
}
.navbar-link {
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-sm);
color: var(--color-label-secondary);
text-decoration: none;
@include mixins.apple-font(var(--font-size-body));
@include mixins.apple-transition;
&:hover {
background-color: var(--color-background-secondary);
color: var(--color-label-primary);
}
&.active {
background-color: var(--color-system-blue);
color: white;
}
}
}
// 侧边栏 - 玻璃拟态风格
.app-sidebar {
width: 280px;
height: 100vh;
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.7) 100%);
backdrop-filter: blur(var(--blur-xl)) saturate(1.3);
-webkit-backdrop-filter: blur(var(--blur-xl)) saturate(1.3);
border-right: 1px solid rgba(255, 255, 255, 0.25);
box-shadow:
0 2px 12px rgba(0, 0, 0, 0.1),
inset -1px 0 0 rgba(255, 255, 255, 0.3);
transition: all var(--transition-normal);
[data-theme="dark"] & {
background:
linear-gradient(135deg, rgba(28, 28, 30, 0.9) 0%, rgba(28, 28, 30, 0.7) 100%);
border-right: 1px solid rgba(255, 255, 255, 0.12);
box-shadow:
0 2px 12px rgba(0, 0, 0, 0.3),
inset -1px 0 0 rgba(255, 255, 255, 0.1);
}
@media (max-width: calc(var(--breakpoint-lg) - 1px)) {
position: fixed;
left: -280px;
z-index: var(--z-modal);
transition: left var(--transition-normal);
&.open {
left: 0;
}
}
}
// 主内容区
.app-main {
flex: 1;
padding: var(--spacing-lg);
@media (max-width: calc(var(--breakpoint-md) - 1px)) {
padding: var(--spacing-md);
}
}
// ==================== 9. 统计卡片 ====================
.stats-grid {
@extend .grid-auto-fit;
.stat-card {
@extend .apple-card;
text-align: center;
padding: var(--spacing-xl) var(--spacing-lg);
.stat-icon {
width: 56px;
height: 56px;
margin: 0 auto var(--spacing-lg);
border-radius: 28px; // 完全圆形
@include mixins.flex-center;
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.7) 100%);
backdrop-filter: blur(var(--blur-lg)) saturate(1.2);
-webkit-backdrop-filter: blur(var(--blur-lg)) saturate(1.2);
box-shadow:
0 4px 16px rgba(0, 0, 0, 0.1),
0 2px 8px rgba(0, 0, 0, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.4);
[data-theme="dark"] & {
background:
linear-gradient(135deg, rgba(28, 28, 30, 0.9) 0%, rgba(28, 28, 30, 0.7) 100%);
box-shadow:
0 4px 16px rgba(0, 0, 0, 0.3),
0 2px 8px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.15);
}
}
.stat-value {
@include mixins.apple-font(var(--font-size-large-title), var(--font-weight-bold));
color: var(--color-label-primary);
margin-bottom: var(--spacing-xs);
}
.stat-label {
@include mixins.apple-font(var(--font-size-subheadline));
color: var(--color-label-secondary);
}
// 颜色变体 - 增强玻璃效果
&.primary .stat-icon {
background:
linear-gradient(135deg, rgba(0, 122, 255, 0.2) 0%, rgba(0, 122, 255, 0.1) 100%);
color: var(--color-system-blue);
border: 1px solid rgba(0, 122, 255, 0.3);
}
&.success .stat-icon {
background:
linear-gradient(135deg, rgba(52, 199, 89, 0.2) 0%, rgba(52, 199, 89, 0.1) 100%);
color: var(--color-system-green);
border: 1px solid rgba(52, 199, 89, 0.3);
}
&.warning .stat-icon {
background:
linear-gradient(135deg, rgba(255, 149, 0, 0.2) 0%, rgba(255, 149, 0, 0.1) 100%);
color: var(--color-system-orange);
border: 1px solid rgba(255, 149, 0, 0.3);
}
&.danger .stat-icon {
background:
linear-gradient(135deg, rgba(255, 59, 48, 0.2) 0%, rgba(255, 59, 48, 0.1) 100%);
color: var(--color-system-red);
border: 1px solid rgba(255, 59, 48, 0.3);
}
}
}
// ==================== 10. 响应式工具类 ====================
// 显示/隐藏工具
.show-sm {
display: none;
@media (min-width: var(--breakpoint-sm)) {
display: block;
}
}
.show-md {
display: none;
@media (min-width: var(--breakpoint-md)) {
display: block;
}
}
.show-lg {
display: none;
@media (min-width: var(--breakpoint-lg)) {
display: block;
}
}
.hide-sm {
@media (min-width: var(--breakpoint-sm)) {
display: none;
}
}
.hide-md {
@media (min-width: var(--breakpoint-md)) {
display: none;
}
}
.hide-lg {
@media (min-width: var(--breakpoint-lg)) {
display: none;
}
}
// ==================== 11. 无障碍性支持 ====================
// 减少动画 (用户偏好)
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
// 高对比度模式
@media (prefers-contrast: high) {
.apple-card,
.apple-button,
.apple-input {
border-width: 2px;
}
}
// 聚焦指示器
.apple-button:focus-visible,
.apple-input:focus-visible,
.theme-toggle:focus-visible {
@include mixins.focus-ring;
}
// ==================== 12. 打印样式 ====================
@media print {
.apple-card,
.apple-button,
.apple-input {
background: white !important;
color: black !important;
box-shadow: none !important;
border: 1px solid #ccc !important;
}
.app-navbar,
.app-sidebar,
.theme-toggle {
display: none !important;
}
}
// ==================== 13. 增强的动态背景和毛玻璃效果 ====================
// 玻璃效果样式已移至 components/apple-components.scss themes/glass.scss

View File

@@ -0,0 +1,3 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

View File

@@ -0,0 +1,430 @@
// Apple Human Interface Guidelines Design Tokens
// 基于苹果人机界面指南的设计令牌系统
// ==================== 色彩系统 ====================
// 系统色彩 - 明亮主题
:root {
// 主要系统色彩
--color-system-blue: #007AFF;
--color-system-green: #34C759;
--color-system-indigo: #5856D6;
--color-system-orange: #FF9500;
--color-system-pink: #FF2D92;
--color-system-purple: #AF52DE;
--color-system-red: #FF3B30;
--color-system-teal: #5AC8FA;
--color-system-yellow: #FFCC00;
// 中性色彩
--color-label-primary: #000000;
--color-label-secondary: #3C3C43;
--color-label-tertiary: #3C3C43;
--color-label-quaternary: #3C3C43;
--color-fill-primary: #787880;
--color-fill-secondary: #787880;
--color-fill-tertiary: #767680;
--color-fill-quaternary: #747480;
// 背景色彩
--color-background-primary: #FFFFFF;
--color-background-secondary: #F2F2F7;
--color-background-tertiary: #FFFFFF;
// 分组背景
--color-grouped-background-primary: #F2F2F7;
--color-grouped-background-secondary: #FFFFFF;
--color-grouped-background-tertiary: #F2F2F7;
// 分隔线
--color-separator-opaque: #C6C6C8;
--color-separator-non-opaque: #3C3C43;
// 链接色
--color-link: #007AFF;
// Apple 特有颜色系统 (从 globals.css 合并)
--apple-blue: #007AFF;
--apple-green: #34C759;
--apple-indigo: #5856D6;
--apple-orange: #FF9500;
--apple-pink: #FF2D92;
--apple-purple: #AF52DE;
--apple-red: #FF3B30;
--apple-teal: #5AC8FA;
--apple-yellow: #FFCC00;
// Apple 灰度系统
--apple-gray-1: #8E8E93;
--apple-gray-2: #AEAEB2;
--apple-gray-3: #C7C7CC;
--apple-gray-4: #D1D1D6;
--apple-gray-5: #E5E5EA;
--apple-gray-6: #F2F2F7;
// Apple 动态颜色
--apple-label: #000000;
--apple-secondary-label: rgba(60, 60, 67, 0.6);
--apple-tertiary-label: rgba(60, 60, 67, 0.3);
--apple-quaternary-label: rgba(60, 60, 67, 0.18);
--apple-separator: rgba(60, 60, 67, 0.36);
--apple-opaque-separator: #C6C6C8;
--apple-system-background: #FFFFFF;
--apple-secondary-system-background: #F2F2F7;
--apple-tertiary-system-background: #FFFFFF;
--apple-system-grouped-background: #F2F2F7;
--apple-secondary-system-grouped-background: #FFFFFF;
--apple-tertiary-system-grouped-background: #F2F2F7;
--apple-system-fill: rgba(120, 120, 128, 0.2);
--apple-secondary-system-fill: rgba(120, 120, 128, 0.16);
--apple-tertiary-system-fill: rgba(118, 118, 128, 0.12);
--apple-quaternary-system-fill: rgba(116, 116, 128, 0.08);
// Apple 玻璃效果
--apple-material-ultra-thin: rgba(255, 255, 255, 0.8);
--apple-material-thin: rgba(255, 255, 255, 0.85);
--apple-material-regular: rgba(255, 255, 255, 0.9);
--apple-material-thick: rgba(255, 255, 255, 0.95);
--apple-blur-ultra-thin: blur(2px);
--apple-blur-thin: blur(4px);
--apple-blur-regular: blur(8px);
--apple-blur-thick: blur(20px);
--apple-blur-ultra-thick: blur(40px);
// Apple 圆角系统
--apple-radius-s: 8px;
--apple-radius-m: 12px;
--apple-radius-l: 16px;
--apple-radius-xl: 20px;
--apple-radius-continuous: 16px;
// Apple 阴影系统
--apple-shadow-s: 0 1px 3px rgba(0, 0, 0, 0.12);
--apple-shadow-m: 0 4px 16px rgba(0, 0, 0, 0.12);
--apple-shadow-l: 0 8px 24px rgba(0, 0, 0, 0.15);
--apple-shadow-xl: 0 16px 48px rgba(0, 0, 0, 0.18);
// Apple 动画系统
--apple-timing-standard: cubic-bezier(0.4, 0.0, 0.2, 1);
--apple-timing-decelerate: cubic-bezier(0.0, 0.0, 0.2, 1);
--apple-timing-accelerate: cubic-bezier(0.4, 0.0, 1, 1);
--apple-timing-sharp: cubic-bezier(0.4, 0.0, 0.6, 1);
--apple-duration-immediate: 100ms;
--apple-duration-quick: 200ms;
--apple-duration-moderate: 300ms;
--apple-duration-slow: 500ms;
// Apple 渐变色彩
--apple-gradient-blue: linear-gradient(135deg, #007AFF 0%, #0A84FF 100%);
--apple-gradient-green: linear-gradient(135deg, #34C759 0%, #30D158 100%);
--apple-gradient-indigo: linear-gradient(135deg, #5856D6 0%, #5E5CE6 100%);
--apple-gradient-orange: linear-gradient(135deg, #FF9500 0%, #FF9F0A 100%);
--apple-gradient-pink: linear-gradient(135deg, #FF2D92 0%, #FF375F 100%);
--apple-gradient-purple: linear-gradient(135deg, #AF52DE 0%, #BF5AF2 100%);
--apple-gradient-red: linear-gradient(135deg, #FF3B30 0%, #FF453A 100%);
--apple-gradient-teal: linear-gradient(135deg, #5AC8FA 0%, #64D2FF 100%);
--apple-gradient-yellow: linear-gradient(135deg, #FFCC00 0%, #FFD60A 100%);
// Apple 活力色彩
--apple-vibrant-blue: #007AFF;
--apple-vibrant-green: #30D158;
--apple-vibrant-indigo: #5E5CE6;
--apple-vibrant-orange: #FF9F0A;
--apple-vibrant-pink: #FF375F;
--apple-vibrant-purple: #BF5AF2;
--apple-vibrant-red: #FF453A;
--apple-vibrant-teal: #64D2FF;
--apple-vibrant-yellow: #FFD60A;
// Apple 动态背景
--apple-dynamic-background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
--apple-dynamic-background-dark: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%);
// Apple 动态颜色
--apple-label: #000000;
--apple-secondary-label: rgba(60, 60, 67, 0.6);
--apple-tertiary-label: rgba(60, 60, 67, 0.3);
--apple-quaternary-label: rgba(60, 60, 67, 0.18);
--apple-separator: rgba(60, 60, 67, 0.36);
--apple-opaque-separator: #C6C6C8;
--apple-system-background: #FFFFFF;
--apple-secondary-system-background: #F2F2F7;
--apple-tertiary-system-background: #FFFFFF;
--apple-system-grouped-background: #F2F2F7;
--apple-secondary-system-grouped-background: #FFFFFF;
--apple-tertiary-system-grouped-background: #F2F2F7;
--apple-system-fill: rgba(120, 120, 128, 0.2);
--apple-secondary-system-fill: rgba(120, 120, 128, 0.16);
--apple-tertiary-system-fill: rgba(118, 118, 128, 0.12);
--apple-quaternary-system-fill: rgba(116, 116, 128, 0.08);
// Apple 玻璃效果
--apple-material-ultra-thin: rgba(255, 255, 255, 0.8);
--apple-material-thin: rgba(255, 255, 255, 0.85);
--apple-material-regular: rgba(255, 255, 255, 0.9);
--apple-material-thick: rgba(255, 255, 255, 0.95);
--apple-blur-ultra-thin: blur(2px);
--apple-blur-thin: blur(4px);
--apple-blur-regular: blur(8px);
--apple-blur-thick: blur(20px);
--apple-blur-ultra-thick: blur(40px);
// Apple 圆角系统
--apple-radius-s: 8px;
--apple-radius-m: 12px;
--apple-radius-l: 16px;
--apple-radius-xl: 20px;
--apple-radius-continuous: 16px;
// Apple 阴影系统
--apple-shadow-s: 0 1px 3px rgba(0, 0, 0, 0.12);
--apple-shadow-m: 0 4px 16px rgba(0, 0, 0, 0.12);
--apple-shadow-l: 0 8px 24px rgba(0, 0, 0, 0.15);
--apple-shadow-xl: 0 16px 48px rgba(0, 0, 0, 0.18);
// Apple 动画系统
--apple-timing-standard: cubic-bezier(0.4, 0.0, 0.2, 1);
--apple-timing-decelerate: cubic-bezier(0.0, 0.0, 0.2, 1);
--apple-timing-accelerate: cubic-bezier(0.4, 0.0, 1, 1);
--apple-timing-sharp: cubic-bezier(0.4, 0.0, 0.6, 1);
--apple-duration-immediate: 100ms;
--apple-duration-quick: 200ms;
--apple-duration-moderate: 300ms;
--apple-duration-slow: 500ms;
// Apple 渐变色彩
--apple-gradient-blue: linear-gradient(135deg, #007AFF 0%, #0A84FF 100%);
--apple-gradient-green: linear-gradient(135deg, #34C759 0%, #30D158 100%);
--apple-gradient-indigo: linear-gradient(135deg, #5856D6 0%, #5E5CE6 100%);
--apple-gradient-orange: linear-gradient(135deg, #FF9500 0%, #FF9F0A 100%);
--apple-gradient-pink: linear-gradient(135deg, #FF2D92 0%, #FF375F 100%);
--apple-gradient-purple: linear-gradient(135deg, #AF52DE 0%, #BF5AF2 100%);
--apple-gradient-red: linear-gradient(135deg, #FF3B30 0%, #FF453A 100%);
--apple-gradient-teal: linear-gradient(135deg, #5AC8FA 0%, #64D2FF 100%);
--apple-gradient-yellow: linear-gradient(135deg, #FFCC00 0%, #FFD60A 100%);
// Apple 活力色彩
--apple-vibrant-blue: #007AFF;
--apple-vibrant-green: #30D158;
--apple-vibrant-indigo: #5E5CE6;
--apple-vibrant-orange: #FF9F0A;
--apple-vibrant-pink: #FF375F;
--apple-vibrant-purple: #BF5AF2;
--apple-vibrant-red: #FF453A;
--apple-vibrant-teal: #64D2FF;
--apple-vibrant-yellow: #FFD60A;
// Apple 动态背景
--apple-dynamic-background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
--apple-dynamic-background-dark: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%);
}
// 深色主题
[data-theme="dark"] {
// 主要系统色彩 (深色模式下稍作调整)
--color-system-blue: #0A84FF;
--color-system-green: #30D158;
--color-system-indigo: #5E5CE6;
--color-system-orange: #FF9F0A;
--color-system-pink: #FF375F;
--color-system-purple: #BF5AF2;
--color-system-red: #FF453A;
--color-system-teal: #64D2FF;
--color-system-yellow: #FFD60A;
// 中性色彩
--color-label-primary: #FFFFFF;
--color-label-secondary: #EBEBF5;
--color-label-tertiary: #EBEBF5;
--color-label-quaternary: #EBEBF5;
--color-fill-primary: #787880;
--color-fill-secondary: #787880;
--color-fill-tertiary: #767680;
--color-fill-quaternary: #747480;
// 背景色彩
--color-background-primary: #000000;
--color-background-secondary: #1C1C1E;
--color-background-tertiary: #2C2C2E;
// 分组背景
--color-grouped-background-primary: #000000;
--color-grouped-background-secondary: #1C1C1E;
--color-grouped-background-tertiary: #2C2C2E;
// 分隔线
--color-separator-opaque: #38383A;
--color-separator-non-opaque: #EBEBF5;
// 链接色
--color-link: #0A84FF;
// Apple 深色主题变量 (从 globals.css 合并)
--apple-label: #FFFFFF;
--apple-secondary-label: rgba(235, 235, 245, 0.6);
--apple-tertiary-label: rgba(235, 235, 245, 0.3);
--apple-quaternary-label: rgba(235, 235, 245, 0.18);
--apple-separator: rgba(84, 84, 88, 0.65);
--apple-opaque-separator: #38383A;
--apple-system-background: #000000;
--apple-secondary-system-background: #1C1C1E;
--apple-tertiary-system-background: #2C2C2E;
--apple-system-grouped-background: #1C1C1E;
--apple-secondary-system-grouped-background: #2C2C2E;
--apple-tertiary-system-grouped-background: #3A3A3C;
--apple-system-fill: rgba(120, 120, 128, 0.36);
--apple-secondary-system-fill: rgba(120, 120, 128, 0.32);
--apple-tertiary-system-fill: rgba(118, 118, 128, 0.28);
--apple-quaternary-system-fill: rgba(116, 116, 128, 0.24);
--apple-material-ultra-thin: rgba(28, 28, 30, 0.8);
--apple-material-thin: rgba(28, 28, 30, 0.85);
--apple-material-regular: rgba(28, 28, 30, 0.9);
--apple-material-thick: rgba(28, 28, 30, 0.95);
// 深色模式渐变
--apple-gradient-blue: linear-gradient(135deg, #0A84FF 0%, #007AFF 100%);
--apple-gradient-green: linear-gradient(135deg, #30D158 0%, #34C759 100%);
--apple-gradient-indigo: linear-gradient(135deg, #5E5CE6 0%, #5856D6 100%);
--apple-gradient-orange: linear-gradient(135deg, #FF9F0A 0%, #FF9500 100%);
--apple-gradient-pink: linear-gradient(135deg, #FF375F 0%, #FF2D92 100%);
--apple-gradient-purple: linear-gradient(135deg, #BF5AF2 0%, #AF52DE 100%);
--apple-gradient-red: linear-gradient(135deg, #FF453A 0%, #FF3B30 100%);
--apple-gradient-teal: linear-gradient(135deg, #64D2FF 0%, #5AC8FA 100%);
--apple-gradient-yellow: linear-gradient(135deg, #FFD60A 0%, #FFCC00 100%);
}
// ==================== 尺寸系统 ====================
:root {
// 基础间距 (基于8pt网格系统)
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--spacing-xxl: 48px;
--spacing-xxxl: 64px;
// Apple 特有间距 (从 globals.css 合并)
--apple-spacing-xs: 4px;
--apple-spacing-s: 8px;
--apple-spacing-m: 16px;
--apple-spacing-l: 24px;
--apple-spacing-xl: 32px;
--apple-spacing-xxl: 44px;
// 圆角半径
--radius-xs: 4px;
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--radius-xxl: 24px;
--radius-full: 50%;
// 字体大小
--font-size-caption2: 11px;
--font-size-caption1: 12px;
--font-size-footnote: 13px;
--font-size-subheadline: 15px;
--font-size-callout: 16px;
--font-size-body: 17px;
--font-size-headline: 17px;
--font-size-title3: 20px;
--font-size-title2: 22px;
--font-size-title1: 28px;
--font-size-large-title: 34px;
// Apple 特有字体大小 (从 globals.css 合并)
--apple-large-title: 34px;
--apple-title-1: 28px;
--apple-title-2: 22px;
--apple-title-3: 20px;
--apple-headline: 17px;
--apple-body: 17px;
--apple-callout: 16px;
--apple-subhead: 15px;
--apple-footnote: 13px;
--apple-caption-1: 12px;
--apple-caption-2: 11px;
// 字体粗细
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
// 行高
--line-height-tight: 1.2;
--line-height-normal: 1.4;
--line-height-relaxed: 1.6;
// 阴影
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1);
--shadow-2xl: 0 25px 50px rgba(0, 0, 0, 0.25);
// 深色模式阴影
--shadow-dark-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-dark-md: 0 4px 6px rgba(0, 0, 0, 0.4);
--shadow-dark-lg: 0 10px 15px rgba(0, 0, 0, 0.5);
--shadow-dark-xl: 0 20px 25px rgba(0, 0, 0, 0.6);
--shadow-dark-2xl: 0 25px 50px rgba(0, 0, 0, 0.8);
// Z-index层级
--z-dropdown: 1000;
--z-sticky: 1020;
--z-fixed: 1030;
--z-modal-backdrop: 1040;
--z-modal: 1050;
--z-popover: 1060;
--z-tooltip: 1070;
--z-toast: 1080;
// 过渡动画
--transition-fast: 0.15s ease-in-out;
--transition-normal: 0.3s ease-in-out;
--transition-slow: 0.5s ease-in-out;
// 模糊效果
--blur-sm: 4px;
--blur-md: 8px;
--blur-lg: 16px;
--blur-xl: 24px;
// 透明度
--opacity-disabled: 0.3;
--opacity-secondary: 0.6;
--opacity-overlay: 0.8;
}
// ==================== 响应式断点 ====================
:root {
--breakpoint-sm: 576px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-xxl: 1536px;
}

View File

@@ -0,0 +1,313 @@
// 玻璃拟态主题
// Glass Morphism Themes
@use '../utils/variables.module' as vars;
@use '../utils/mixins.module' as mixins;
// ===== 基础重置样式 =====
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-family: vars.$font-family-base;
line-height: 1.5;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: vars.$font-family-base;
font-size: vars.$font-size-md;
font-weight: vars.$font-weight-normal;
line-height: 1.6;
color: vars.$gray-900;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
transition: all vars.$transition-normal vars.$easing-standard;
// 暗色主题
&.dark {
color: vars.$white;
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
}
}
// ===== 亮色主题 =====
.light-theme {
--bg-primary: vars.$white;
--bg-secondary: vars.$gray-50;
--bg-tertiary: vars.$gray-100;
--text-primary: vars.$gray-900;
--text-secondary: vars.$gray-600;
--text-tertiary: vars.$gray-400;
--border-primary: vars.$gray-200;
--border-secondary: vars.$gray-100;
--glass-bg: rgba(255, 255, 255, vars.$glass-opacity-medium);
--glass-border: rgba(255, 255, 255, vars.$glass-border-opacity);
--shadow-color: rgba(0, 0, 0, 0.1);
}
// ===== 暗色主题 =====
.dark-theme,
.dark {
--bg-primary: vars.$gray-900;
--bg-secondary: vars.$gray-800;
--bg-tertiary: vars.$gray-700;
--text-primary: vars.$white;
--text-secondary: vars.$gray-300;
--text-tertiary: vars.$gray-500;
--border-primary: vars.$gray-700;
--border-secondary: vars.$gray-800;
--glass-bg: rgba(0, 0, 0, vars.$glass-opacity-medium);
--glass-border: rgba(255, 255, 255, vars.$glass-border-opacity * 0.5);
--shadow-color: rgba(0, 0, 0, 0.3);
}
// ===== 玻璃容器主题 =====
.glass-container {
@include mixins.glass-base(vars.$glass-opacity-light, vars.$glass-blur-lg);
border-radius: vars.$border-radius-2xl;
padding: vars.$spacing-xl;
max-width: 1200px;
margin: 0 auto;
box-shadow: vars.$glass-shadow-xl;
// 响应式
@media (min-width: vars.$breakpoint-md) {
padding: vars.$spacing-lg;
}
@media (min-width: vars.$breakpoint-sm) {
padding: vars.$spacing-md;
border-radius: vars.$border-radius-lg;
}
}
// ===== 玻璃网格主题 =====
.glass-grid {
display: grid;
gap: vars.$spacing-lg;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
@media (min-width: vars.$breakpoint-md) {
gap: vars.$spacing-md;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
@media (min-width: vars.$breakpoint-sm) {
gap: vars.$spacing-sm;
grid-template-columns: 1fr;
}
}
// ===== 玻璃文字主题 =====
.glass-text {
&-primary {
color: var(--text-primary);
font-weight: vars.$font-weight-semibold;
}
&-secondary {
color: var(--text-secondary);
font-weight: vars.$font-weight-normal;
}
&-tertiary {
color: var(--text-tertiary);
font-weight: vars.$font-weight-normal;
}
&-gradient {
background: linear-gradient(135deg, vars.$primary-color 0%, vars.$accent-purple 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: vars.$font-weight-bold;
}
}
// ===== 玻璃标题主题 =====
.glass-heading {
&-1 {
font-size: vars.$font-size-4xl;
font-weight: vars.$font-weight-bold;
line-height: 1.1;
margin-bottom: vars.$spacing-lg;
@media (min-width: vars.$breakpoint-md) {
font-size: vars.$font-size-3xl;
}
@media (min-width: vars.$breakpoint-sm) {
font-size: vars.$font-size-2xl;
}
}
&-2 {
font-size: vars.$font-size-3xl;
font-weight: vars.$font-weight-bold;
line-height: 1.2;
margin-bottom: vars.$spacing-md;
@media (min-width: vars.$breakpoint-md) {
font-size: vars.$font-size-2xl;
}
@media (min-width: vars.$breakpoint-sm) {
font-size: vars.$font-size-xl;
}
}
&-3 {
font-size: vars.$font-size-2xl;
font-weight: vars.$font-weight-semibold;
line-height: 1.3;
margin-bottom: vars.$spacing-md;
@media (min-width: vars.$breakpoint-sm) {
font-size: vars.$font-size-lg;
}
}
}
// ===== 玻璃动画主题 =====
@keyframes glass-fade-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes glass-slide-in {
from {
opacity: 0;
transform: translateX(-100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes glass-scale-in {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes glass-glow {
0%,
100% {
box-shadow: vars.$glass-glow-sm;
}
50% {
box-shadow: vars.$glass-glow-lg;
}
}
// 动画类
.glass-animate {
&-fade-in {
animation: glass-fade-in 0.6s vars.$easing-decelerate;
}
&-slide-in {
animation: glass-slide-in 0.5s vars.$easing-decelerate;
}
&-scale-in {
animation: glass-scale-in 0.4s vars.$easing-decelerate;
}
&-glow {
animation: glass-glow 2s ease-in-out infinite;
}
}
// ===== 全局工具类 =====
.glass-elevated {
@include mixins.glass-base(vars.$glass-opacity-heavy, vars.$glass-blur-xl);
box-shadow: vars.$glass-shadow-xl;
}
.glass-subtle {
@include mixins.glass-base(vars.$glass-opacity-light, vars.$glass-blur-sm);
box-shadow: vars.$glass-shadow-sm;
}
.glass-interactive {
cursor: pointer;
transition: all vars.$transition-normal vars.$easing-standard;
&:hover {
@include mixins.glass-base(vars.$glass-opacity-heavy, vars.$glass-blur-lg);
transform: translateY(-2px);
box-shadow: vars.$glass-shadow-lg;
}
&:active {
transform: translateY(0);
}
}
// ===== 滚动条主题 =====
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: vars.$border-radius-sm;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: vars.$border-radius-sm;
transition: background vars.$transition-normal;
&:hover {
background: rgba(255, 255, 255, 0.5);
}
}
// 暗色主题滚动条
.dark {
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
&:hover {
background: rgba(255, 255, 255, 0.3);
}
}
}

View File

@@ -4,32 +4,32 @@
// ==================== 玻璃质感效果 ====================
// 毛玻璃背景效果
.glass-effect(@blur: var(--blur-md), @opacity: 0.8, @background: var(--color-background-secondary)) {
background: rgba(242, 242, 247, @opacity);
backdrop-filter: blur(@blur);
-webkit-backdrop-filter: blur(@blur);
@mixin glass-effect($blur: var(--blur-md), $opacity: 0.8, $background: var(--color-background-secondary)) {
background: rgba(242, 242, 247, $opacity);
backdrop-filter: blur($blur);
-webkit-backdrop-filter: blur($blur);
border: 1px solid rgba(255, 255, 255, 0.2);
}
// 强玻璃质感 (用于重要元素)
.glass-strong() {
.glass-effect(var(--blur-lg), 0.9);
@mixin glass-strong {
@include glass-effect(var(--blur-lg), 0.9);
}
// 轻玻璃质感 (用于次要元素)
.glass-light() {
.glass-effect(var(--blur-sm), 0.6);
@mixin glass-light {
@include glass-effect(var(--blur-sm), 0.6);
}
// ==================== 渐变效果 ====================
// 苹果风格径向渐变
.apple-gradient(@color1: var(--color-system-blue), @color2: var(--color-system-purple)) {
@mixin apple-gradient($color1: var(--color-system-blue), $color2: var(--color-system-purple)) {
background: radial-gradient(circle at 30% 20%, rgba(0, 122, 255, 0.2), rgba(175, 82, 222, 0.1));
}
// 动态背景渐变
.dynamic-background() {
@mixin dynamic-background {
background:
radial-gradient(circle at 20% 80%, rgba(0, 122, 255, 0.15) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(175, 82, 222, 0.15) 0%, transparent 50%),
@@ -53,40 +53,36 @@
// ==================== 阴影系统 ====================
// 苹果风格阴影
.apple-shadow(@level: md) when (@level = sm) {
box-shadow: var(--shadow-sm);
[data-theme="dark"] & {
box-shadow: var(--shadow-dark-sm);
}
}
.apple-shadow(@level: md) when (@level = md) {
box-shadow: var(--shadow-md);
[data-theme="dark"] & {
box-shadow: var(--shadow-dark-md);
}
}
.apple-shadow(@level: md) when (@level = lg) {
box-shadow: var(--shadow-lg);
[data-theme="dark"] & {
box-shadow: var(--shadow-dark-lg);
}
}
.apple-shadow(@level: md) when (@level = xl) {
box-shadow: var(--shadow-xl);
[data-theme="dark"] & {
box-shadow: var(--shadow-dark-xl);
@mixin apple-shadow($level: md) {
@if $level == sm {
box-shadow: var(--shadow-sm);
[data-theme="dark"] & {
box-shadow: var(--shadow-dark-sm);
}
} @else if $level == md {
box-shadow: var(--shadow-md);
[data-theme="dark"] & {
box-shadow: var(--shadow-dark-md);
}
} @else if $level == lg {
box-shadow: var(--shadow-lg);
[data-theme="dark"] & {
box-shadow: var(--shadow-dark-lg);
}
} @else if $level == xl {
box-shadow: var(--shadow-xl);
[data-theme="dark"] & {
box-shadow: var(--shadow-dark-xl);
}
}
}
// 浮动阴影 (悬停状态)
.floating-shadow() {
@mixin floating-shadow {
transition: box-shadow var(--transition-normal);
&:hover {
.apple-shadow(lg);
@include apple-shadow(lg);
transform: translateY(-2px);
}
}
@@ -94,65 +90,59 @@
// ==================== 响应式工具 ====================
// 响应式断点混合器
.respond-to(@breakpoint) when (@breakpoint = sm) {
@media (min-width: var(--breakpoint-sm)) {
@content();
}
}
.respond-to(@breakpoint) when (@breakpoint = md) {
@media (min-width: var(--breakpoint-md)) {
@content();
}
}
.respond-to(@breakpoint) when (@breakpoint = lg) {
@media (min-width: var(--breakpoint-lg)) {
@content();
}
}
.respond-to(@breakpoint) when (@breakpoint = xl) {
@media (min-width: var(--breakpoint-xl)) {
@content();
@mixin respond-to($breakpoint) {
@if $breakpoint == sm {
@media (min-width: var(--breakpoint-sm)) {
@content;
}
} @else if $breakpoint == md {
@media (min-width: var(--breakpoint-md)) {
@content;
}
} @else if $breakpoint == lg {
@media (min-width: var(--breakpoint-lg)) {
@content;
}
} @else if $breakpoint == xl {
@media (min-width: var(--breakpoint-xl)) {
@content;
}
}
}
// 容器查询 (现代浏览器支持)
.container-query(@size) when (@size = sm) {
@container (min-width: 320px) {
@content();
}
}
.container-query(@size) when (@size = md) {
@container (min-width: 768px) {
@content();
}
}
.container-query(@size) when (@size = lg) {
@container (min-width: 1024px) {
@content();
@mixin container-query($size) {
@if $size == sm {
@container (min-width: 320px) {
@content;
}
} @else if $size == md {
@container (min-width: 768px) {
@content;
}
} @else if $size == lg {
@container (min-width: 1024px) {
@content;
}
}
}
// ==================== 动画效果 ====================
// 苹果风格过渡
.apple-transition(@property: all, @duration: var(--transition-normal)) {
transition: @property @duration;
@mixin apple-transition($property: all, $duration: var(--transition-normal)) {
transition: $property $duration;
}
// 弹性动画
.spring-animation(@duration: 0.6s) {
animation-duration: @duration;
@mixin spring-animation($duration: 0.6s) {
animation-duration: $duration;
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
// 淡入动画
.fade-in(@duration: var(--transition-normal)) {
animation: fadeIn @duration ease-in-out;
@mixin fade-in($duration: var(--transition-normal)) {
animation: fadeIn $duration ease-in-out;
}
@keyframes fadeIn {
@@ -167,8 +157,8 @@
}
// 缩放动画
.scale-in(@duration: var(--transition-normal)) {
animation: scaleIn @duration ease-out;
@mixin scale-in($duration: var(--transition-normal)) {
animation: scaleIn $duration ease-out;
}
@keyframes scaleIn {
@@ -185,32 +175,32 @@
// ==================== 布局工具 ====================
// Flexbox 居中
.flex-center() {
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
// Flexbox 垂直居中
.flex-center-vertical() {
@mixin flex-center-vertical {
display: flex;
align-items: center;
}
// Flexbox 水平居中
.flex-center-horizontal() {
@mixin flex-center-horizontal {
display: flex;
justify-content: center;
}
// Grid 居中
.grid-center() {
@mixin grid-center {
display: grid;
place-items: center;
}
// 安全区域适配 (iOS设备)
.safe-area-padding() {
@mixin safe-area-padding {
padding-top: env(safe-area-inset-top);
padding-right: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
@@ -220,39 +210,39 @@
// ==================== 文本工具 ====================
// 文本截断
.text-ellipsis() {
@mixin text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 多行文本截断
.text-ellipsis-multiline(@lines: 2) {
@mixin text-ellipsis-multiline($lines: 2) {
display: -webkit-box;
-webkit-line-clamp: @lines;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
overflow: hidden;
}
// 苹果系统字体
.apple-font(@size: var(--font-size-body), @weight: var(--font-weight-regular)) {
@mixin apple-font($size: var(--font-size-body), $weight: var(--font-weight-regular)) {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
font-size: @size;
font-weight: @weight;
font-size: $size;
font-weight: $weight;
line-height: var(--line-height-normal);
}
// ==================== 状态工具 ====================
// 禁用状态
.disabled-state() {
@mixin disabled-state {
opacity: var(--opacity-disabled);
cursor: not-allowed;
pointer-events: none;
}
// 加载状态
.loading-state() {
@mixin loading-state {
position: relative;
pointer-events: none;
@@ -278,7 +268,7 @@
}
// 聚焦状态
.focus-ring(@color: var(--color-system-blue)) {
outline: 2px solid @color;
@mixin focus-ring($color: var(--color-system-blue)) {
outline: 2px solid $color;
outline-offset: 2px;
}

View File

@@ -0,0 +1,292 @@
// 玻璃拟态混合 (Mixins)
// Glass Morphism Mixins
@use './variables.module' as vars;
// ===== 基础玻璃效果混合 =====
// 注意:此 mixin 已被 utils/apple-mixins.scss 中的 glass-effect 替代
// 为保持向后兼容性,保留此别名
@mixin glass-base($opacity: vars.$glass-opacity-medium, $blur: vars.$glass-blur-md) {
backdrop-filter: blur($blur);
-webkit-backdrop-filter: blur($blur);
background: rgba(255, 255, 255, $opacity);
border: 1px solid rgba(255, 255, 255, vars.$glass-border-opacity);
position: relative;
overflow: hidden;
// 暗色主题下的玻璃效果
.dark & {
background: rgba(0, 0, 0, $opacity);
border-color: rgba(255, 255, 255, vars.$glass-border-opacity * 0.5);
}
}
// ===== 玻璃卡片混合 =====
@mixin glass-card($size: md) {
@include glass-base();
border-radius: vars.$border-radius-lg;
transition: all vars.$transition-normal vars.$easing-standard;
// 大小变体
@if $size == sm {
padding: vars.$spacing-sm vars.$spacing-md;
border-radius: vars.$border-radius-md;
}
@if $size == md {
padding: vars.$spacing-md vars.$spacing-lg;
border-radius: vars.$border-radius-lg;
}
@if $size == lg {
padding: vars.$spacing-lg vars.$spacing-xl;
border-radius: vars.$border-radius-xl;
}
// 悬停效果
&:hover {
@include glass-base(vars.$glass-opacity-heavy, vars.$glass-blur-lg);
box-shadow: vars.$glass-shadow-lg;
transform: translateY(-2px);
}
// 焦点效果
&:focus-visible {
outline: 2px solid vars.$primary-color;
outline-offset: 2px;
}
}
// ===== 玻璃按钮混合 =====
@mixin glass-button($variant: primary) {
@include glass-base();
border-radius: vars.$border-radius-md;
padding: vars.$spacing-sm vars.$spacing-md;
cursor: pointer;
transition: all vars.$transition-fast vars.$easing-standard;
font-weight: vars.$font-weight-medium;
text-align: center;
display: inline-flex;
align-items: center;
justify-content: center;
gap: vars.$spacing-xs;
// 变体样式
@if $variant == primary {
background: rgba(99, 102, 241, 0.2);
color: vars.$primary-color;
border-color: rgba(99, 102, 241, 0.3);
&:hover {
background: rgba(99, 102, 241, 0.3);
box-shadow: vars.$glass-glow-sm;
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
background: rgba(99, 102, 241, 0.4);
}
}
@if $variant == secondary {
@include glass-base(vars.$glass-opacity-light);
&:hover {
@include glass-base(vars.$glass-opacity-medium);
box-shadow: vars.$glass-shadow-md;
}
}
@if $variant == danger {
background: rgba(239, 68, 68, 0.2);
color: vars.$accent-red;
border-color: rgba(239, 68, 68, 0.3);
&:hover {
background: rgba(239, 68, 68, 0.3);
box-shadow: 0 0 20px rgba(239, 68, 68, 0.3);
}
}
}
// ===== 玻璃输入框混合 =====
@mixin glass-input {
@include glass-base(vars.$glass-opacity-light, vars.$glass-blur-sm);
border-radius: vars.$border-radius-md;
padding: vars.$spacing-sm vars.$spacing-md;
font-family: vars.$font-family-base;
font-size: vars.$font-size-md;
color: vars.$gray-900;
transition: all vars.$transition-normal vars.$easing-standard;
width: 100%;
.dark & {
color: vars.$white;
}
&::placeholder {
color: vars.$gray-400;
.dark & {
color: vars.$gray-500;
}
}
&:focus {
@include glass-base(vars.$glass-opacity-medium, vars.$glass-blur-md);
outline: none;
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.5);
border-color: vars.$primary-color;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
// ===== 玻璃导航栏混合 =====
@mixin glass-navbar {
@include glass-base(vars.$glass-opacity-heavy, vars.$glass-blur-xl);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: vars.$z-index-navbar;
border-bottom: 1px solid rgba(255, 255, 255, vars.$glass-border-opacity);
border-left: none;
border-right: none;
border-top: none;
transition: all vars.$transition-normal vars.$easing-standard;
// 滚动时的效果
&.scrolled {
@include glass-base(vars.$glass-opacity-heavy, vars.$glass-blur-xl);
box-shadow: vars.$glass-shadow-lg;
}
}
// ===== 玻璃模态框混合 =====
@mixin glass-modal {
@include glass-base(vars.$glass-opacity-heavy, vars.$glass-blur-xl);
border-radius: vars.$border-radius-2xl;
box-shadow: vars.$glass-shadow-xl;
max-width: 90vw;
max-height: 90vh;
overflow: auto;
// 背景遮罩
&::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(vars.$glass-blur-sm);
z-index: -1;
}
}
// ===== 玻璃侧边栏混合 =====
@mixin glass-sidebar {
@include glass-base(vars.$glass-opacity-heavy, vars.$glass-blur-lg);
position: fixed;
top: 0;
left: 0;
height: 100vh;
border-right: 1px solid rgba(255, 255, 255, vars.$glass-border-opacity);
border-left: none;
border-top: none;
border-bottom: none;
transition: transform vars.$transition-normal vars.$easing-standard;
z-index: vars.$z-index-navbar - 10;
}
// ===== 玻璃通知混合 =====
@mixin glass-notification($type: info) {
@include glass-base(vars.$glass-opacity-medium, vars.$glass-blur-md);
border-radius: vars.$border-radius-lg;
padding: vars.$spacing-md;
position: relative;
@if $type == success {
border-color: rgba(16, 185, 129, 0.3);
background: rgba(16, 185, 129, 0.1);
}
@if $type == warning {
border-color: rgba(245, 158, 11, 0.3);
background: rgba(245, 158, 11, 0.1);
}
@if $type == error {
border-color: rgba(239, 68, 68, 0.3);
background: rgba(239, 68, 68, 0.1);
}
@if $type == info {
border-color: rgba(59, 130, 246, 0.3);
background: rgba(59, 130, 246, 0.1);
}
}
// ===== 响应式混合 =====
@mixin responsive($breakpoint) {
@if $breakpoint == sm {
@media (min-width: $breakpoint-sm) {
@content;
}
}
@if $breakpoint == md {
@media (min-width: $breakpoint-md) {
@content;
}
}
@if $breakpoint == lg {
@media (min-width: $breakpoint-lg) {
@content;
}
}
@if $breakpoint == xl {
@media (min-width: $breakpoint-xl) {
@content;
}
}
}
// ===== 工具混合 =====
// 截断文本
@mixin text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 居中对齐
@mixin center-flex {
display: flex;
align-items: center;
justify-content: center;
}
// 绝对居中
@mixin absolute-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
// 隐藏滚动条
@mixin hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}

View File

@@ -0,0 +1,125 @@
// 玻璃拟态设计系统变量
// Glass Morphism Design System Variables
// 主要变量已移至 themes/apple-design-tokens.scss
// 这里只保留玻璃效果特有的变量
// Sass 变量定义 (与CSS变量配合使用)
// ===== 玻璃拟态效果变量 =====
// 背景透明度
$glass-opacity-light: 0.1;
$glass-opacity-medium: 0.15;
$glass-opacity-heavy: 0.25;
// 边框透明度
$glass-border-opacity: 0.2;
// 模糊程度
$glass-blur-xs: 4px;
$glass-blur-sm: 8px;
$glass-blur-md: 12px;
$glass-blur-lg: 16px;
$glass-blur-xl: 24px;
// ===== 玻璃阴影系统 =====
$glass-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
$glass-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.06);
$glass-shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
$glass-shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.04);
// 发光效果
$glass-glow-sm: 0 0 10px rgba(99, 102, 241, 0.3);
$glass-glow-md: 0 0 20px rgba(99, 102, 241, 0.4);
$glass-glow-lg: 0 0 30px rgba(99, 102, 241, 0.5);
// ===== 玻璃特有颜色 =====
$primary-color: #6366f1;
$primary-light: #818cf8;
$primary-dark: #4f46e5;
// 强调色
$accent-blue: #3b82f6;
$accent-purple: #8b5cf6;
$accent-pink: #ec4899;
$accent-green: #10b981;
$accent-yellow: #f59e0b;
$accent-red: #ef4444;
// 中性色
$white: #ffffff;
$black: #000000;
$gray-50: #f9fafb;
$gray-100: #f3f4f6;
$gray-200: #e5e7eb;
$gray-300: #d1d5db;
$gray-400: #9ca3af;
$gray-500: #6b7280;
$gray-600: #4b5563;
$gray-700: #374151;
$gray-800: #1f2937;
$gray-900: #111827;
// ===== 玻璃主题变量 =====
// 这些变量与 themes/apple-design-tokens.scss 中的 CSS 变量配合使用
// ===== 基础变量 =====
// 圆角系统
$border-radius-xs: 4px;
$border-radius-sm: 8px;
$border-radius-md: 12px;
$border-radius-lg: 16px;
$border-radius-xl: 20px;
$border-radius-2xl: 24px;
$border-radius-full: 50%;
// 间距系统
$spacing-xs: 4px;
$spacing-sm: 8px;
$spacing-md: 16px;
$spacing-lg: 24px;
$spacing-xl: 32px;
$spacing-2xl: 48px;
$spacing-3xl: 64px;
// 字体系统
$font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
$font-size-xs: 12px;
$font-size-sm: 14px;
$font-size-md: 16px;
$font-size-lg: 18px;
$font-size-xl: 20px;
$font-size-2xl: 24px;
$font-size-3xl: 30px;
$font-size-4xl: 36px;
// 字重
$font-weight-normal: 400;
$font-weight-medium: 500;
$font-weight-semibold: 600;
$font-weight-bold: 700;
// 动画系统
$transition-fast: 0.15s ease-in-out;
$transition-normal: 0.3s ease-in-out;
$transition-slow: 0.5s ease-in-out;
// 动画缓动函数
$easing-standard: cubic-bezier(0.4, 0.0, 0.2, 1);
$easing-decelerate: cubic-bezier(0.0, 0.0, 0.2, 1);
$easing-accelerate: cubic-bezier(0.4, 0.0, 1, 1);
// 响应式断点
$breakpoint-sm: 640px;
$breakpoint-md: 768px;
$breakpoint-lg: 1024px;
$breakpoint-xl: 1280px;
$breakpoint-2xl: 1536px;
// Z-index 层级
$z-index-dropdown: 1000;
$z-index-modal: 1050;
$z-index-popover: 1060;
$z-index-tooltip: 1070;
$z-index-navbar: 1080;
$z-index-notification: 1090;

View File

@@ -4,21 +4,21 @@
// ==================== 增强玻璃质感效果 ====================
// 高级玻璃质感混合器
.glass-enhanced(@opacity: 0.8, @blur: var(--blur-lg), @saturation: 1.2) {
background: rgba(255, 255, 255, @opacity);
backdrop-filter: blur(@blur) saturate(@saturation);
-webkit-backdrop-filter: blur(@blur) saturate(@saturation);
@mixin glass-enhanced($opacity: 0.8, $blur: var(--blur-lg), $saturation: 1.2) {
background: rgba(255, 255, 255, $opacity);
backdrop-filter: blur($blur) saturate($saturation);
-webkit-backdrop-filter: blur($blur) saturate($saturation);
border: 1px solid rgba(255, 255, 255, 0.3);
[data-theme="dark"] & {
background: rgba(28, 28, 30, @opacity);
background: rgba(28, 28, 30, $opacity);
border: 1px solid rgba(255, 255, 255, 0.15);
}
}
// 毛玻璃导航栏
.glass-navbar() {
.glass-enhanced(0.85, var(--blur-xl), 1.3);
@mixin glass-navbar {
@include glass-enhanced(0.85, var(--blur-xl), 1.3);
box-shadow:
0 1px 0 rgba(255, 255, 255, 0.4),
0 1px 3px rgba(0, 0, 0, 0.05),
@@ -33,45 +33,43 @@
}
// 毛玻璃卡片
.glass-card(@intensity: medium) when (@intensity = light) {
.glass-enhanced(0.6, var(--blur-md), 1.1);
box-shadow:
0 4px 16px rgba(0, 0, 0, 0.05),
0 1px 4px rgba(0, 0, 0, 0.03),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.glass-card(@intensity: medium) when (@intensity = medium) {
.glass-enhanced(0.8, var(--blur-lg), 1.2);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.1),
0 2px 8px rgba(0, 0, 0, 0.05),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
inset 0 -1px 0 rgba(255, 255, 255, 0.1);
}
.glass-card(@intensity: medium) when (@intensity = strong) {
.glass-enhanced(0.9, var(--blur-xl), 1.4);
box-shadow:
0 16px 64px rgba(0, 0, 0, 0.15),
0 4px 16px rgba(0, 0, 0, 0.1),
inset 0 2px 0 rgba(255, 255, 255, 0.5),
inset 0 -1px 0 rgba(255, 255, 255, 0.2);
@mixin glass-card($intensity: medium) {
@if $intensity == light {
@include glass-enhanced(0.6, var(--blur-md), 1.1);
box-shadow:
0 4px 16px rgba(0, 0, 0, 0.05),
0 1px 4px rgba(0, 0, 0, 0.03),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
} @else if $intensity == medium {
@include glass-enhanced(0.8, var(--blur-lg), 1.2);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.1),
0 2px 8px rgba(0, 0, 0, 0.05),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
inset 0 -1px 0 rgba(255, 255, 255, 0.1);
} @else if $intensity == strong {
@include glass-enhanced(0.9, var(--blur-xl), 1.4);
box-shadow:
0 16px 64px rgba(0, 0, 0, 0.15),
0 4px 16px rgba(0, 0, 0, 0.1),
inset 0 2px 0 rgba(255, 255, 255, 0.5),
inset 0 -1px 0 rgba(255, 255, 255, 0.2);
}
}
// ==================== 动态渐变背景 ====================
// 基础渐变背景
.gradient-background(@color1: #007AFF, @color2: #AF52DE, @opacity: 10) {
@mixin gradient-background($color1: #007AFF, $color2: #AF52DE, $opacity: 10) {
background:
radial-gradient(circle at 20% 80%, rgba(0, 122, 255, @opacity / 100) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(175, 82, 222, @opacity / 100) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(127, 102, 208, @opacity / 200) 0%, transparent 50%);
radial-gradient(circle at 20% 80%, rgba(0, 122, 255, $opacity / 100) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(175, 82, 222, $opacity / 100) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(127, 102, 208, $opacity / 200) 0%, transparent 50%);
}
// 动态渐变背景
.dynamic-gradient-background() {
.gradient-background(#007AFF, #AF52DE, 15);
@mixin dynamic-gradient-background {
@include gradient-background(#007AFF, #AF52DE, 15);
background-size: 100% 100%, 100% 100%, 100% 100%;
animation: gradientShift 20s ease-in-out infinite;
}
@@ -92,7 +90,7 @@
}
// 彩虹渐变背景
.rainbow-gradient-background() {
@mixin rainbow-gradient-background {
background:
radial-gradient(circle at 10% 20%, rgba(0, 122, 255, 0.1) 0%, transparent 50%),
radial-gradient(circle at 80% 80%, rgba(52, 199, 89, 0.1) 0%, transparent 50%),
@@ -114,100 +112,92 @@
// ==================== 多层次阴影系统 ====================
// 浮动阴影 (多层次)
.floating-shadow(@level: 1) when (@level = 1) {
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
[data-theme="dark"] & {
@mixin floating-shadow($level: 1) {
@if $level == 1 {
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.4),
0 1px 2px rgba(0, 0, 0, 0.6);
}
}
.floating-shadow(@level: 1) when (@level = 2) {
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.16),
0 3px 6px rgba(0, 0, 0, 0.23);
[data-theme="dark"] & {
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
[data-theme="dark"] & {
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.4),
0 1px 2px rgba(0, 0, 0, 0.6);
}
} @else if $level == 2 {
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.5),
0 3px 6px rgba(0, 0, 0, 0.7);
}
}
.floating-shadow(@level: 1) when (@level = 3) {
box-shadow:
0 10px 20px rgba(0, 0, 0, 0.19),
0 6px 6px rgba(0, 0, 0, 0.23);
[data-theme="dark"] & {
0 3px 6px rgba(0, 0, 0, 0.16),
0 3px 6px rgba(0, 0, 0, 0.23);
[data-theme="dark"] & {
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.5),
0 3px 6px rgba(0, 0, 0, 0.7);
}
} @else if $level == 3 {
box-shadow:
0 10px 20px rgba(0, 0, 0, 0.6),
0 6px 6px rgba(0, 0, 0, 0.8);
}
}
.floating-shadow(@level: 1) when (@level = 4) {
box-shadow:
0 14px 28px rgba(0, 0, 0, 0.25),
0 10px 10px rgba(0, 0, 0, 0.22);
[data-theme="dark"] & {
0 10px 20px rgba(0, 0, 0, 0.19),
0 6px 6px rgba(0, 0, 0, 0.23);
[data-theme="dark"] & {
box-shadow:
0 10px 20px rgba(0, 0, 0, 0.6),
0 6px 6px rgba(0, 0, 0, 0.8);
}
} @else if $level == 4 {
box-shadow:
0 14px 28px rgba(0, 0, 0, 0.7),
0 10px 10px rgba(0, 0, 0, 0.9);
}
}
.floating-shadow(@level: 1) when (@level = 5) {
box-shadow:
0 19px 38px rgba(0, 0, 0, 0.30),
0 15px 12px rgba(0, 0, 0, 0.22);
[data-theme="dark"] & {
0 14px 28px rgba(0, 0, 0, 0.25),
0 10px 10px rgba(0, 0, 0, 0.22);
[data-theme="dark"] & {
box-shadow:
0 14px 28px rgba(0, 0, 0, 0.7),
0 10px 10px rgba(0, 0, 0, 0.9);
}
} @else if $level == 5 {
box-shadow:
0 19px 38px rgba(0, 0, 0, 0.8),
0 15px 12px rgba(0, 0, 0, 0.95);
0 19px 38px rgba(0, 0, 0, 0.30),
0 15px 12px rgba(0, 0, 0, 0.22);
[data-theme="dark"] & {
box-shadow:
0 19px 38px rgba(0, 0, 0, 0.8),
0 15px 12px rgba(0, 0, 0, 0.95);
}
}
}
// 内阴影效果
.inset-shadow(@depth: light) when (@depth = light) {
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
[data-theme="dark"] & {
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3);
}
}
.inset-shadow(@depth: light) when (@depth = medium) {
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
[data-theme="dark"] & {
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4);
}
}
.inset-shadow(@depth: light) when (@depth = deep) {
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.2);
[data-theme="dark"] & {
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
@mixin inset-shadow($depth: light) {
@if $depth == light {
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
[data-theme="dark"] & {
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3);
}
} @else if $depth == medium {
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
[data-theme="dark"] & {
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4);
}
} @else if $depth == deep {
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.2);
[data-theme="dark"] & {
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
}
}
}
// ==================== 发光效果 ====================
// 系统色发光
.system-glow(@color: #007AFF, @intensity: 30) {
box-shadow: 0 0 20px rgba(0, 122, 255, @intensity / 100);
@mixin system-glow($color: #007AFF, $intensity: 30) {
box-shadow: 0 0 20px rgba(0, 122, 255, $intensity / 100);
}
// 脉冲发光动画
.pulse-glow(@color: #007AFF) {
@mixin pulse-glow($color: #007AFF) {
animation: pulseGlow 2s ease-in-out infinite alternate;
@keyframes pulseGlow {
@@ -223,13 +213,13 @@
// ==================== 材质效果 ====================
// 金属质感
.metallic-effect(@color: #C0C0C0) {
@mixin metallic-effect($color: #C0C0C0) {
background: linear-gradient(135deg,
lighten(@color, 20%) 0%,
@color 25%,
darken(@color, 10%) 50%,
@color 75%,
lighten(@color, 15%) 100%);
lighten($color, 20%) 0%,
$color 25%,
darken($color, 10%) 50%,
$color 75%,
lighten($color, 15%) 100%);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.5),
inset 0 -1px 0 rgba(0, 0, 0, 0.2),
@@ -237,7 +227,7 @@
}
// 珍珠质感
.pearl-effect() {
@mixin pearl-effect {
background:
linear-gradient(135deg,
rgba(255, 255, 255, 0.9) 0%,
@@ -253,7 +243,7 @@
// ==================== 交互效果 ====================
// 按压效果
.press-effect() {
@mixin press-effect {
transition: all var(--transition-fast);
&:active {
@@ -265,28 +255,28 @@
}
// 悬浮效果
.hover-lift() {
@mixin hover-lift {
transition: all var(--transition-normal);
&:hover {
transform: translateY(-2px);
.floating-shadow(2);
@include floating-shadow(2);
}
}
// 磁性效果 (鼠标接近时轻微移动)
.magnetic-effect(@distance: 5px) {
@mixin magnetic-effect($distance: 5px) {
transition: transform var(--transition-normal);
&:hover {
transform: translateY(-@distance);
transform: translateY(-$distance);
}
}
// ==================== 粒子效果 ====================
// 闪烁粒子背景
.sparkle-background() {
@mixin sparkle-background {
position: relative;
overflow: hidden;

View File

@@ -4,10 +4,7 @@ import animate from "tailwindcss-animate";
module.exports = {
darkMode: ["class"],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
"./src/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
container: {
@@ -139,4 +136,4 @@ module.exports = {
},
},
plugins: [animate],
}
}