From 21c75461b4b8c15690e59d5d329375932bad4bfc Mon Sep 17 00:00:00 2001 From: xiao12feng8 <16507319+xiao12feng8@user.noreply.gitee.com> Date: Tue, 3 Feb 2026 17:13:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B7=E5=BC=8F=EF=BC=9ATab=E6=A0=8F?= =?UTF-8?q?=E7=BE=8E=E5=8C=96=E5=B9=B6=E5=B0=86=E6=8E=A5=E5=8F=A3=E6=8E=A5?= =?UTF-8?q?=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lover/main.py | 2 + lover/models.py | 29 + lover/response.py | 4 + lover/routers/music_library.py | 398 ++++ start_php_advanced.bat | 161 ++ test_reset.py | 51 - test_vip.py | 33 - xuniYou/pages/index/index.vue | 3113 +++++++++++++++++++++++++++-- 开发/2026年2月3日/2026年2月3日.md | 2 + 开发/2026年2月3日/音乐库.sql | 34 + 10 files changed, 3600 insertions(+), 227 deletions(-) create mode 100644 lover/routers/music_library.py create mode 100644 start_php_advanced.bat delete mode 100644 test_reset.py delete mode 100644 test_vip.py create mode 100644 开发/2026年2月3日/2026年2月3日.md create mode 100644 开发/2026年2月3日/音乐库.sql diff --git a/lover/main.py b/lover/main.py index e29015d..98a9c12 100644 --- a/lover/main.py +++ b/lover/main.py @@ -21,6 +21,7 @@ from lover.routers import friend as friend_router from lover.routers import msg as msg_router from lover.routers import huanxin as huanxin_router from lover.routers import user as user_router +from lover.routers import music_library as music_library_router from lover.task_queue import start_sing_workers from lover.config import settings @@ -82,6 +83,7 @@ app.include_router(friend_router.router) app.include_router(msg_router.router) app.include_router(huanxin_router.router) app.include_router(user_router.router) +app.include_router(music_library_router.router) @app.exception_handler(HTTPException) diff --git a/lover/models.py b/lover/models.py index 2f5cf79..a0b1cf9 100644 --- a/lover/models.py +++ b/lover/models.py @@ -422,6 +422,35 @@ class OutfitItem(Base): updatetime = Column(BigInteger) +class MusicLibrary(Base): + __tablename__ = "nf_music_library" + + id = Column(BigInteger, primary_key=True, autoincrement=True) + user_id = Column(BigInteger, nullable=False, index=True) + title = Column(String(255), nullable=False) + artist = Column(String(255)) + music_url = Column(String(500), nullable=False) + cover_url = Column(String(500)) + duration = Column(Integer) + upload_type = Column(String(10), nullable=False, default="link") # file, link + is_public = Column(Integer, nullable=False, default=1) + play_count = Column(Integer, nullable=False, default=0) + like_count = Column(Integer, nullable=False, default=0) + status = Column(String(20), nullable=False, default="approved") # pending, approved, rejected + created_at = Column(DateTime, nullable=False, default=datetime.utcnow) + updated_at = Column(DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow) + deleted_at = Column(DateTime) + + +class MusicLike(Base): + __tablename__ = "nf_music_likes" + + id = Column(BigInteger, primary_key=True, autoincrement=True) + user_id = Column(BigInteger, nullable=False, index=True) + music_id = Column(BigInteger, nullable=False, index=True) + created_at = Column(DateTime, nullable=False, default=datetime.utcnow) + + class OutfitLook(Base): __tablename__ = "nf_outfit_looks" diff --git a/lover/response.py b/lover/response.py index 321f242..8a00081 100644 --- a/lover/response.py +++ b/lover/response.py @@ -18,3 +18,7 @@ class ApiResponse(BaseModel, Generic[T]): def success_response(data: Optional[T] = None, msg: str = "ok") -> ApiResponse[T]: return ApiResponse[T](code=1, msg=msg, data=data) + + +def error_response(msg: str = "error", code: int = 0, data: Optional[T] = None) -> ApiResponse[T]: + return ApiResponse[T](code=code, msg=msg, data=data) diff --git a/lover/routers/music_library.py b/lover/routers/music_library.py new file mode 100644 index 0000000..feec0a1 --- /dev/null +++ b/lover/routers/music_library.py @@ -0,0 +1,398 @@ +""" +音乐库路由 +""" +from typing import List, Optional +from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form +from sqlalchemy.orm import Session +from pydantic import BaseModel, Field +from datetime import datetime +import os +import uuid + +from lover.db import get_db +from lover.deps import get_current_user +from lover.models import User, MusicLibrary, MusicLike +from lover.response import success_response, error_response, ApiResponse +from lover.config import settings + +router = APIRouter(prefix="/music", tags=["音乐库"]) + + +# ========== Pydantic 模型 ========== +class MusicOut(BaseModel): + id: int + user_id: int + username: str = "" + user_avatar: str = "" + title: str + artist: Optional[str] = None + music_url: str + cover_url: Optional[str] = None + duration: Optional[int] = None + upload_type: str + play_count: int = 0 + like_count: int = 0 + is_liked: bool = False + created_at: datetime + + class Config: + from_attributes = True + + +class MusicListResponse(BaseModel): + total: int + list: List[MusicOut] + + +class MusicAddLinkRequest(BaseModel): + title: str = Field(..., min_length=1, max_length=255, description="歌曲标题") + artist: Optional[str] = Field(None, max_length=255, description="艺术家") + music_url: str = Field(..., min_length=1, max_length=500, description="音乐链接") + cover_url: Optional[str] = Field(None, max_length=500, description="封面图链接") + duration: Optional[int] = Field(None, description="时长(秒)") + + +# ========== 辅助函数 ========== +def _cdnize(url: str) -> str: + """将相对路径转换为 CDN 完整路径""" + if not url: + return "" + if url.startswith("http://") or url.startswith("https://"): + return url + cdn_domain = getattr(settings, "CDN_DOMAIN", "") + if cdn_domain: + return f"{cdn_domain.rstrip('/')}/{url.lstrip('/')}" + return url + + +def _save_upload_file(file: UploadFile, folder: str = "music") -> str: + """保存上传的文件""" + # 生成唯一文件名 + ext = os.path.splitext(file.filename)[1] + filename = f"{uuid.uuid4().hex}{ext}" + + # 创建保存路径 + upload_dir = os.path.join("public", folder) + os.makedirs(upload_dir, exist_ok=True) + + file_path = os.path.join(upload_dir, filename) + + # 保存文件 + with open(file_path, "wb") as f: + f.write(file.file.read()) + + # 返回相对路径 + return f"{folder}/{filename}" + + +# ========== API 路由 ========== +@router.get("/library", response_model=ApiResponse[MusicListResponse]) +def get_music_library( + page: int = 1, + page_size: int = 20, + keyword: Optional[str] = None, + user: User = Depends(get_current_user), + db: Session = Depends(get_db), +): + """ + 获取音乐库列表(所有公开的音乐) + """ + query = db.query(MusicLibrary).filter( + MusicLibrary.deleted_at.is_(None), + MusicLibrary.is_public == 1, + MusicLibrary.status == "approved" + ) + + # 关键词搜索 + if keyword: + query = query.filter( + (MusicLibrary.title.like(f"%{keyword}%")) | + (MusicLibrary.artist.like(f"%{keyword}%")) + ) + + # 总数 + total = query.count() + + # 分页 + offset = (page - 1) * page_size + music_list = query.order_by(MusicLibrary.created_at.desc()).offset(offset).limit(page_size).all() + + # 获取用户点赞信息 + liked_music_ids = set() + if user: + likes = db.query(MusicLike.music_id).filter(MusicLike.user_id == user.id).all() + liked_music_ids = {like.music_id for like in likes} + + # 获取上传用户信息 + user_ids = [m.user_id for m in music_list] + users_map = {} + if user_ids: + users = db.query(User).filter(User.id.in_(user_ids)).all() + users_map = {u.id: u for u in users} + + # 构建返回数据 + result = [] + for music in music_list: + uploader = users_map.get(music.user_id) + result.append(MusicOut( + id=music.id, + user_id=music.user_id, + username=uploader.username if uploader else "未知用户", + user_avatar=_cdnize(uploader.avatar) if uploader and uploader.avatar else "", + title=music.title, + artist=music.artist, + music_url=_cdnize(music.music_url), + cover_url=_cdnize(music.cover_url) if music.cover_url else "", + duration=music.duration, + upload_type=music.upload_type, + play_count=music.play_count, + like_count=music.like_count, + is_liked=music.id in liked_music_ids, + created_at=music.created_at + )) + + return success_response(MusicListResponse(total=total, list=result)) + + +@router.post("/add-link", response_model=ApiResponse[MusicOut]) +def add_music_link( + data: MusicAddLinkRequest, + user: User = Depends(get_current_user), + db: Session = Depends(get_db), +): + """ + 添加音乐链接到音乐库 + """ + # 创建音乐记录 + music = MusicLibrary( + user_id=user.id, + title=data.title, + artist=data.artist, + music_url=data.music_url, + cover_url=data.cover_url, + duration=data.duration, + upload_type="link", + is_public=1, + status="approved" + ) + + db.add(music) + db.commit() + db.refresh(music) + + return success_response(MusicOut( + id=music.id, + user_id=music.user_id, + username=user.username, + user_avatar=_cdnize(user.avatar) if user.avatar else "", + title=music.title, + artist=music.artist, + music_url=_cdnize(music.music_url), + cover_url=_cdnize(music.cover_url) if music.cover_url else "", + duration=music.duration, + upload_type=music.upload_type, + play_count=music.play_count, + like_count=music.like_count, + is_liked=False, + created_at=music.created_at + )) + + +@router.post("/upload", response_model=ApiResponse[MusicOut]) +async def upload_music_file( + title: str = Form(...), + artist: Optional[str] = Form(None), + duration: Optional[int] = Form(None), + music_file: UploadFile = File(...), + cover_file: Optional[UploadFile] = File(None), + user: User = Depends(get_current_user), + db: Session = Depends(get_db), +): + """ + 上传音乐文件到音乐库 + """ + # 检查文件类型 + allowed_audio = [".mp3", ".wav", ".m4a", ".flac", ".ogg"] + allowed_image = [".jpg", ".jpeg", ".png", ".gif", ".webp"] + + music_ext = os.path.splitext(music_file.filename)[1].lower() + if music_ext not in allowed_audio: + return error_response("不支持的音频格式") + + # 保存音乐文件 + music_path = _save_upload_file(music_file, "music") + + # 保存封面文件 + cover_path = None + if cover_file: + cover_ext = os.path.splitext(cover_file.filename)[1].lower() + if cover_ext in allowed_image: + cover_path = _save_upload_file(cover_file, "music/covers") + + # 创建音乐记录 + music = MusicLibrary( + user_id=user.id, + title=title, + artist=artist, + music_url=music_path, + cover_url=cover_path, + duration=duration, + upload_type="file", + is_public=1, + status="approved" + ) + + db.add(music) + db.commit() + db.refresh(music) + + return success_response(MusicOut( + id=music.id, + user_id=music.user_id, + username=user.username, + user_avatar=_cdnize(user.avatar) if user.avatar else "", + title=music.title, + artist=music.artist, + music_url=_cdnize(music.music_url), + cover_url=_cdnize(music.cover_url) if music.cover_url else "", + duration=music.duration, + upload_type=music.upload_type, + play_count=music.play_count, + like_count=music.like_count, + is_liked=False, + created_at=music.created_at + )) + + +@router.post("/{music_id}/like", response_model=ApiResponse[dict]) +def like_music( + music_id: int, + user: User = Depends(get_current_user), + db: Session = Depends(get_db), +): + """ + 点赞音乐 + """ + # 检查音乐是否存在 + music = db.query(MusicLibrary).filter( + MusicLibrary.id == music_id, + MusicLibrary.deleted_at.is_(None) + ).first() + + if not music: + return error_response("音乐不存在") + + # 检查是否已点赞 + existing_like = db.query(MusicLike).filter( + MusicLike.user_id == user.id, + MusicLike.music_id == music_id + ).first() + + if existing_like: + # 取消点赞 + db.delete(existing_like) + music.like_count = max(0, music.like_count - 1) + db.commit() + return success_response({"is_liked": False, "like_count": music.like_count}) + else: + # 添加点赞 + like = MusicLike(user_id=user.id, music_id=music_id) + db.add(like) + music.like_count += 1 + db.commit() + return success_response({"is_liked": True, "like_count": music.like_count}) + + +@router.post("/{music_id}/play", response_model=ApiResponse[dict]) +def record_play( + music_id: int, + db: Session = Depends(get_db), +): + """ + 记录播放次数 + """ + music = db.query(MusicLibrary).filter( + MusicLibrary.id == music_id, + MusicLibrary.deleted_at.is_(None) + ).first() + + if not music: + return error_response("音乐不存在") + + music.play_count += 1 + db.commit() + + return success_response({"play_count": music.play_count}) + + +@router.delete("/{music_id}", response_model=ApiResponse[dict]) +def delete_music( + music_id: int, + user: User = Depends(get_current_user), + db: Session = Depends(get_db), +): + """ + 删除音乐(仅限上传者) + """ + music = db.query(MusicLibrary).filter( + MusicLibrary.id == music_id, + MusicLibrary.deleted_at.is_(None) + ).first() + + if not music: + return error_response("音乐不存在") + + if music.user_id != user.id: + return error_response("无权删除此音乐") + + music.deleted_at = datetime.utcnow() + db.commit() + + return success_response({"message": "删除成功"}) + + +@router.get("/my", response_model=ApiResponse[MusicListResponse]) +def get_my_music( + page: int = 1, + page_size: int = 20, + user: User = Depends(get_current_user), + db: Session = Depends(get_db), +): + """ + 获取我上传的音乐 + """ + query = db.query(MusicLibrary).filter( + MusicLibrary.user_id == user.id, + MusicLibrary.deleted_at.is_(None) + ) + + total = query.count() + + offset = (page - 1) * page_size + music_list = query.order_by(MusicLibrary.created_at.desc()).offset(offset).limit(page_size).all() + + # 获取点赞信息 + liked_music_ids = set() + likes = db.query(MusicLike.music_id).filter(MusicLike.user_id == user.id).all() + liked_music_ids = {like.music_id for like in likes} + + result = [] + for music in music_list: + result.append(MusicOut( + id=music.id, + user_id=music.user_id, + username=user.username, + user_avatar=_cdnize(user.avatar) if user.avatar else "", + title=music.title, + artist=music.artist, + music_url=_cdnize(music.music_url), + cover_url=_cdnize(music.cover_url) if music.cover_url else "", + duration=music.duration, + upload_type=music.upload_type, + play_count=music.play_count, + like_count=music.like_count, + is_liked=music.id in liked_music_ids, + created_at=music.created_at + )) + + return success_response(MusicListResponse(total=total, list=result)) diff --git a/start_php_advanced.bat b/start_php_advanced.bat new file mode 100644 index 0000000..855be97 --- /dev/null +++ b/start_php_advanced.bat @@ -0,0 +1,161 @@ +@echo off +chcp 65001 >nul +title PHP 开发服务器 + +:MENU +cls +echo ======================================== +echo PHP 开发服务器启动脚本 (高级版) +echo ======================================== +echo. +echo 请选择启动模式: +echo. +echo [1] 快速启动 (端口 8080) +echo [2] 自定义端口 +echo [3] 查看 PHP 信息 +echo [4] 退出 +echo. +echo ======================================== +set /p choice=请输入选项 (1-4): + +if "%choice%"=="1" goto QUICK_START +if "%choice%"=="2" goto CUSTOM_PORT +if "%choice%"=="3" goto PHP_INFO +if "%choice%"=="4" goto END +echo [错误] 无效选项,请重新选择 +timeout /t 2 >nul +goto MENU + +:QUICK_START +set PORT=8080 +goto START_SERVER + +:CUSTOM_PORT +echo. +set /p PORT=请输入端口号 (例如: 8080): +if "%PORT%"=="" ( + echo [错误] 端口号不能为空 + timeout /t 2 >nul + goto MENU +) +goto START_SERVER + +:START_SERVER +cls +echo ======================================== +echo 正在启动 PHP 开发服务器... +echo ======================================== +echo. + +REM 设置 PHP 路径 +set PHP_PATH=D:\2_part\php-8.0.0-Win32-vs16-x64\php.exe + +REM 检查 PHP 是否存在 +if not exist "%PHP_PATH%" ( + echo [错误] PHP 未找到: %PHP_PATH% + echo. + echo 请修改脚本中的 PHP_PATH 变量 + pause + goto MENU +) + +REM 显示 PHP 版本 +echo [信息] PHP 版本: +"%PHP_PATH%" -v | findstr /C:"PHP" +echo. + +REM 设置项目根目录 +set PROJECT_ROOT=%~dp0xunifriend_RaeeC\public + +REM 检查项目目录是否存在 +if not exist "%PROJECT_ROOT%" ( + echo [错误] 项目目录未找到: %PROJECT_ROOT% + pause + goto MENU +) + +REM 获取本机 IP 地址 +for /f "tokens=2 delims=:" %%a in ('ipconfig ^| findstr /C:"IPv4"') do ( + set LOCAL_IP=%%a + goto :IP_FOUND +) +:IP_FOUND +set LOCAL_IP=%LOCAL_IP: =% + +REM 设置服务器参数 +set HOST=0.0.0.0 + +echo [信息] 项目目录: %PROJECT_ROOT% +echo [信息] 服务器端口: %PORT% +echo. +echo ======================================== +echo 访问地址: +echo ======================================== +echo. +echo [本地访问] +echo http://127.0.0.1:%PORT% +echo http://localhost:%PORT% +echo. +echo [局域网访问] +echo http://%LOCAL_IP%:%PORT% +echo. +echo [管理后台] +echo http://127.0.0.1:%PORT%/admin +echo. +echo ======================================== +echo. +echo [提示] 按 Ctrl+C 停止服务器 +echo. + +REM 询问是否打开浏览器 +set /p OPEN_BROWSER=是否自动打开浏览器? (Y/N): +if /i "%OPEN_BROWSER%"=="Y" ( + echo [信息] 正在打开浏览器... + start http://127.0.0.1:%PORT% +) + +echo. +echo [信息] 服务器启动中... +echo ======================================== +echo. + +REM 启动 PHP 内置服务器 +cd /d "%PROJECT_ROOT%" +"%PHP_PATH%" -S %HOST%:%PORT% -t . + +pause +goto MENU + +:PHP_INFO +cls +echo ======================================== +echo PHP 信息 +echo ======================================== +echo. + +set PHP_PATH=D:\2_part\php-8.0.0-Win32-vs16-x64\php.exe + +if not exist "%PHP_PATH%" ( + echo [错误] PHP 未找到: %PHP_PATH% + pause + goto MENU +) + +echo [PHP 版本] +"%PHP_PATH%" -v +echo. +echo [PHP 配置文件] +"%PHP_PATH%" --ini +echo. +echo [已加载的扩展] +"%PHP_PATH%" -m +echo. + +pause +goto MENU + +:END +echo. +echo 感谢使用! +timeout /t 1 >nul +exit diff --git a/test_reset.py b/test_reset.py deleted file mode 100644 index 9ab0b28..0000000 --- a/test_reset.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -"""测试视频生成次数重置功能""" -from datetime import datetime, date -from lover.db import SessionLocal -from lover.models import User - -def test_reset_logic(): - db = SessionLocal() - try: - user = db.query(User).filter(User.id == 70).with_for_update().first() - if not user: - print("用户不存在") - return - - print(f"重置前:") - print(f" video_gen_remaining: {user.video_gen_remaining}") - print(f" video_gen_reset_date: {user.video_gen_reset_date}") - - # 模拟重置逻辑 - current_timestamp = int(datetime.utcnow().timestamp()) - is_vip = user.vip_endtime and user.vip_endtime > current_timestamp - - if is_vip: - last_reset = user.video_gen_reset_date - today = datetime.utcnow().date() - - print(f"\n检查:") - print(f" 是否 VIP: {is_vip}") - print(f" 上次重置日期: {last_reset}") - print(f" 今天日期: {today}") - print(f" 需要重置: {not last_reset or last_reset < today}") - - if not last_reset or last_reset < today: - user.video_gen_remaining = 2 - user.video_gen_reset_date = today - db.add(user) - db.commit() - - print(f"\n重置后:") - print(f" video_gen_remaining: {user.video_gen_remaining}") - print(f" video_gen_reset_date: {user.video_gen_reset_date}") - else: - print("\n今天已经重置过了,不需要再次重置") - else: - print("用户不是 VIP,不重置") - - finally: - db.close() - -if __name__ == "__main__": - test_reset_logic() diff --git a/test_vip.py b/test_vip.py deleted file mode 100644 index 88447f2..0000000 --- a/test_vip.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 -"""测试 VIP 功能""" -from datetime import datetime -from lover.db import SessionLocal -from lover.models import User - -def test_vip_status(): - db = SessionLocal() - try: - # 测试用户 70 和 84 - for user_id in [70, 84]: - user = db.query(User).filter(User.id == user_id).first() - if not user: - print(f"用户 {user_id} 不存在") - continue - - current_timestamp = int(datetime.utcnow().timestamp()) - is_vip = user.vip_endtime and user.vip_endtime > current_timestamp - - print(f"\n用户 {user_id} ({user.nickname}):") - print(f" VIP 到期时间戳: {user.vip_endtime}") - if user.vip_endtime: - vip_end_date = datetime.fromtimestamp(user.vip_endtime) - print(f" VIP 到期日期: {vip_end_date}") - print(f" 当前时间戳: {current_timestamp}") - print(f" 是否 VIP: {is_vip}") - print(f" 视频生成次数: {user.video_gen_remaining}") - print(f" 上次重置日期: {user.video_gen_reset_date}") - finally: - db.close() - -if __name__ == "__main__": - test_vip_status() diff --git a/xuniYou/pages/index/index.vue b/xuniYou/pages/index/index.vue index 8d29ec6..9ca18bf 100644 --- a/xuniYou/pages/index/index.vue +++ b/xuniYou/pages/index/index.vue @@ -64,81 +64,210 @@ :duration="300" :indicator-dots="false"> - + - - - - - - - 你好,{{ getBobbiesList.nickname ? getBobbiesList.nickname : '匿名' }} - - - - {{ getBobbiesList.level ? getBobbiesList.level : 0 }} - + + + + + + + + + + + + - - - Lv.{{ getBobbiesList.level ? getBobbiesList.level : 0 }}({{ - getBobbiesList.intimacy ? getBobbiesList.intimacy : 0 }}/{{ - getBobbiesList.next_level_intimacy ? getBobbiesList.next_level_intimacy : 0 - }}) + + + + + + 💎 + 精品商城 + + + + + + + + + + 超值优惠 + 海量道具 · 限时特惠 · 每日上新 + + + + + 🎁 + 新人礼包 + + + + 限时秒杀 + + + 💰 + 超值折扣 + + + 🔥 + 热门推荐 + + + + + + 立即进入商城 + + + + + + + 点击任意位置进入商城 - - - - - - - {{ getBobbiesList.bond_today ? getBobbiesList.bond_today : 0 }}/{{ - getBobbiesList.bond_today_all ? getBobbiesList.bond_today_all : 0 - }} + + + + + + + + + + + 装扮设置 + + + + + 朋友圈 + + + + 邀请入驻 + + + + + + + 选择一位互动对象 + + 搜索更多 + + + + + + + {{ loverBasicList && loverBasicList.name ? loverBasicList.name : '简寻' }} + + + + + + + + 多面伪神 + 甜蜜贴心 + + + + + 靠近点,再近点…你就能看清我眼里藏着什么了。 + + + + + + + + + 和TA聊天 - 每日牵绊 - - - - - - - - - - - 去聊天 - + + + + + + + + + + + - - - - - - 礼物匣 + + + + + + + + + + + + + + + + + HOT + + + + 精品商城 + 🛒 + + 海量道具 · 超值优惠 + + + + + + 立即前往 + + + + + + + + + + + Lv.{{ getBobbiesList.level ? getBobbiesList.level : 0 }} + + + + {{ getBobbiesList.intimacy ? getBobbiesList.intimacy : 0 }}/{{ getBobbiesList.next_level_intimacy ? getBobbiesList.next_level_intimacy : 0 }} - - - - - 装束设置 - - - @@ -190,19 +319,97 @@ - - 🎤 - 唱歌功能 - 让她为你唱一首歌 - - - - - 历史视频 - 查看全部 + + + + + 系统歌曲 + + + 音乐库 + + + 历史记录 + + + + + + + 选择歌曲 + + + + {{item.title}} + + + - - + + + + + + + + + 添加音乐 + + + + + + + + + + + + + + + {{item.title}} + {{item.artist || '未知艺术家'}} + + 👤 {{item.username}} + ▶ {{item.play_count}} + + {{item.is_liked ? '❤️' : '🤍'}} {{item.like_count}} + + + + + + + + 加载更多 + + + + + 🎵 + 音乐库还没有歌曲 + 快来添加第一首歌吧! + + + + + + + + {{item.song_title}} {{formatDate(item.created_at)}} @@ -212,16 +419,9 @@ - - - - 选择歌曲 - - - - {{item.title}} - - + + 📼 + 还没有历史记录 @@ -265,23 +465,128 @@ - + - - 👗 - 换装搭配 - 正在跳转到装束设置页面... - + + + + 换装 + 剩余次数:{{ outfitListInfo.clothes_num }}次 + + 加载中... + + + 上衣 + + + + + + 使用中 + 🔒 + + + + + + 下装 + + + + + + 使用中 + 🔒 + + + + + + 连体服 + + + + + + 使用中 + 🔒 + + + + + + {{ outfitSaveToLook ? '已勾选:保存到形象栏' : '勾选:保存到形象栏' }} + 换装 + + + - - - 🎁 - 送礼物 - 送她一份心意 - 打开礼物匣 + + + 🎁 礼物匣 + 送她一份心意 + + 当前好感度 + + Lv.{{ getBobbiesList.level ? getBobbiesList.level : 0 }} + + + + {{ getBobbiesList.intimacy ? getBobbiesList.intimacy : 0 }}/{{ getBobbiesList.next_level_intimacy ? getBobbiesList.next_level_intimacy : 0 }} + + + + + + + + + + {{ item.name }} + {{ item.price }}金币 + +{{ item.intimacy_value }}好感 + + + + + + + + + + 礼物详情 + + + + + + + + {{ giftInfoOptions && giftInfoOptions.name ? giftInfoOptions.name : '' }} + +{{ giftInfoOptions && giftInfoOptions.intimacy_value ? giftInfoOptions.intimacy_value : '0' }} 好感度 + {{ giftInfoOptions && giftInfoOptions.price ? giftInfoOptions.price : '0' }} 金币 + + + + + 数量 + + + + + + + + 余额:{{ giftUserInfo && giftUserInfo.money != null ? giftUserInfo.money : 0 }} 金币 + + + 充值并赠送 + 赠送Ta + + @@ -304,41 +609,75 @@ - - 🎬 - 短剧 - 观看精彩短剧内容 - - - - - - 推荐 - - 🎬 精彩短剧推荐 - 点击观看更多精彩内容 - 立即观看 → - + + + + + + + + + + + + + + + 🔥 + 热门推荐 - - - - - 短剧标题 {{i}} - 精彩剧情简介... - 立即播放 - + + 精彩短剧 + 海量热门短剧等你来看 + + + + 💕 甜宠 + 👑 霸总 + 🎭 逆袭 + ⚡ 爽文 + + + + + + 立即观看 + + + + + + 点击任意位置进入短剧世界 - + + + + + + 📺 + 海量剧集 + + + 🆓 + 免费观看 + + + + 每日更新 + + - + - + @@ -397,6 +736,9 @@ 正在生成视频中 预计等待15分钟 + + 取消生成 + @@ -404,6 +746,48 @@ 正在生成视频中 预计等待15分钟 + + 取消生成 + + + + + + + + + 添加音乐 + 关闭 + + + + 歌曲标题 * + + + + 艺术家 + + + + 音乐链接 * +