mirror of
https://git.oceanpay.cc/danial/kami_apple_exchage.git
synced 2025-12-18 21:23:49 +00:00
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:
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
"""获取任务状态"""
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()]
|
||||
|
||||
@@ -72,7 +72,6 @@ class UserDataUploadResponse(BaseModel):
|
||||
"""用户数据上传响应模型"""
|
||||
|
||||
user_data: UserDataResponse
|
||||
order_task_id: str | None = Field(None, description="创建的订单任务ID")
|
||||
message: str = Field(..., description="响应消息")
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
"""处理单个订单(关联链接并更新状态)"""
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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..."
|
||||
|
||||
@@ -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
13
frontend/.sassrc.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"quietDeps": true,
|
||||
"silenceDeprecations": [
|
||||
"import",
|
||||
"global-builtin",
|
||||
"mixed-decls",
|
||||
"function-units",
|
||||
"slash-div",
|
||||
"moz-document",
|
||||
"inconsistent-units"
|
||||
],
|
||||
"fatalDeprecations": []
|
||||
}
|
||||
@@ -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=="],
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
const ovalCOnfig = {
|
||||
// 爬虫监控系统 API 配置
|
||||
petstore: {
|
||||
prettier: true,
|
||||
@@ -65,3 +65,5 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default ovalCOnfig;
|
||||
@@ -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"
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
export const plugins = {
|
||||
'tailwindcss': {},
|
||||
'autoprefixer': {},
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -1,3 +0,0 @@
|
||||
import { useGetUserDataListApiV1UserDataGet } from "./generated/user-data-management.gen";
|
||||
import type { GetUserDataListApiV1UserDataGetParams } from "./generated/schemas/getUserDataListApiV1UserDataGetParams";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
;
|
||||
@@ -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),
|
||||
@@ -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;
|
||||
}
|
||||
683
frontend/src/styles/sass/components/apple-components.module.scss
Normal file
683
frontend/src/styles/sass/components/apple-components.module.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -292,5 +292,4 @@
|
||||
.pulse-bg-container::after {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
403
frontend/src/styles/sass/main.module.scss
Normal file
403
frontend/src/styles/sass/main.module.scss
Normal 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
|
||||
3
frontend/src/styles/sass/tailwind.module.scss
Normal file
3
frontend/src/styles/sass/tailwind.module.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
@import 'tailwindcss/base';
|
||||
@import 'tailwindcss/components';
|
||||
@import 'tailwindcss/utilities';
|
||||
430
frontend/src/styles/sass/themes/apple-design-tokens.module.scss
Normal file
430
frontend/src/styles/sass/themes/apple-design-tokens.module.scss
Normal 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;
|
||||
}
|
||||
313
frontend/src/styles/sass/themes/glass.module.scss
Normal file
313
frontend/src/styles/sass/themes/glass.module.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
292
frontend/src/styles/sass/utils/mixins.module.scss
Normal file
292
frontend/src/styles/sass/utils/mixins.module.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
125
frontend/src/styles/sass/utils/variables.module.scss
Normal file
125
frontend/src/styles/sass/utils/variables.module.scss
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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],
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user