彩先知pc

This commit is contained in:
rain
2026-06-04 11:43:38 +08:00
parent 934046b8ef
commit 3a2081f395
243 changed files with 76952 additions and 3 deletions
+26
View File
@@ -0,0 +1,26 @@
{
"permissions": {
"allow": [
"Bash(npm list *)",
"Bash(npm install *)",
"Bash(node *)",
"Bash(python -c ' *)",
"Bash(python -c \"print\\('python works'\\)\")",
"Bash(awk '{print $5, $NF}')",
"Bash(ls -la \"C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\Desktop\\\\\\\\ceshi\\\\\\\\html\\\\\\\\\")",
"Bash(dir \"C:\\\\\\\\Users\\\\\\\\Administrator\\\\\\\\Desktop\\\\\\\\ceshi\\\\\\\\html\\\\\\\\\")",
"Bash(dir \"C:\\\\Users\\\\Administrator\\\\Desktop\\\\ceshi\\\\public\\\\css\\\\*.css\")",
"Bash(awk '{printf \"%6d bytes %s\\\\n\", $5, $NF}')",
"Bash(sort -t/ -k6)",
"Bash(dir \"C:\\\\Users\\\\Administrator\\\\Desktop\\\\ceshi\" /s /b)",
"Read(//c/Users/Administrator/.claude/**)",
"Read(//c/Users/Administrator/.claude//**)",
"Read(//c/Users/Administrator/**)",
"Bash(dir *)",
"Bash(xxd \"c:/Users/Administrator/Desktop/ceshi/public/css/public.css\")",
"Bash(iconv -f UTF-8 -t UTF-8 \"c:/Users/Administrator/Desktop/ceshi/public/css/public.css\")",
"Bash(powershell *)",
"Bash(xxd \"C:/Users/Administrator/Desktop/caixiansheng/html/fabumianfeiwenzhang.html\")"
]
}
}
+3
View File
@@ -0,0 +1,3 @@
{
"liveServer.settings.port": 5503
}
-3
View File
@@ -1,3 +0,0 @@
# caixianzhi
彩先知pc
+93
View File
@@ -0,0 +1,93 @@
method,path,summary,tags
"GET","/api/web/account/info","获取当前登录用户信息接口",""
"PUT","/api/web/account/profile","个人信息修改",""
"GET","/api/web/account/profile/avatar","头像上传",""
"GET","/api/web/ad/list","广告按位置筛选",""
"GET","/api/web/agreement/{type}","隐私/协议",""
"GET","/api/web/auth/code","获取验证码",""
"POST","/api/web/auth/heartbeat","登录心跳接口-前端可不接",""
"POST","/api/web/auth/login","登录接口",""
"POST","/api/web/auth/logout","退出登录",""
"POST","/api/web/auth/register","注册",""
"GET","/api/web/auth/slide-captcha","滑动验证",""
"POST","/api/web/auth/slide-captcha/verify","校验滑动结果",""
"GET","/api/web/auth/sms/code","发短信接口",""
"POST","/api/web/auth/social-bind","第三方账号绑定本地账号",""
"POST","/api/web/auth/social-callback","三方回调地址(前端不需要接,给平台用)",""
"POST","/api/web/auth/social-login","三方登录接口-暂时不接",""
"GET","/api/web/auth/verify-config","查询验证方式",""
"GET","/api/web/banner/list","轮播图",""
"GET","/api/web/config","网站基础信息接口-简化接口选一接就行",""
"GET","/api/web/config/list","网站基础信息",""
"GET","/api/web/config/site","网站图标",""
"POST","/api/web/expert/apply","专家申请",""
"GET","/api/web/expert/config","内容长度验证",""
"POST","/api/web/expert/upload-id-card-back","上传身份证反面",""
"POST","/api/web/expert/upload-id-card-front","上传身份证正面",""
"GET","/api/web/fc3d/free-predict/list","断组查询接口",""
"GET","/api/web/fc3d/free-predict/menus","断组杀组合菜单",""
"GET","/api/web/fc3d/scheme/{id}","方案详情",""
"GET","/api/web/fc3d/scheme/list","方案查询",""
"GET","/api/web/free-article/{id}","免费文章详情",""
"POST","/api/web/free-article/{id}/collect","收藏",""
"POST","/api/web/free-article/{id}/comment","评论",""
"GET","/api/web/free-article/{id}/comments","免费文章评论",""
"POST","/api/web/free-article/{id}/like","点赞",""
"GET","/api/web/free-article/{id}/related","相关推荐",""
"GET","/api/web/free-article/latest","最新推荐",""
"GET","/api/web/free-article/list","免费文章列表",""
"POST","/api/web/free-article/publish","发布",""
"GET","/api/web/free-article/recommend","免费文章",""
"GET","/api/web/link/friend","友情链接",""
"GET","/api/web/link/other-recommend","其他推荐",""
"GET","/api/web/lottery/result/latest","开奖结果",""
"GET","/api/web/lottery/result/list","开奖记录",""
"GET","/api/web/lottery/result/trend","走势图",""
"GET","/api/web/lottery/result/trend/tools","获取彩种信息排序",""
"GET","/api/web/menu/{id}/children","获取下级菜单ID",""
"GET","/api/web/menu/home-nav","导航栏",""
"GET","/api/web/mine/expert-collect/list","专家收藏",""
"GET","/api/web/mine/free-article/{id}","我的免费文章详情",""
"GET","/api/web/mine/free-article/list","我的免费文章列表",""
"GET","/api/web/mine/free-collect/list","免费文章收藏列表",""
"GET","/api/web/mine/pay-article/{id}","我的付费文章详情",""
"GET","/api/web/mine/pay-article/list","我的付费文章列表",""
"GET","/api/web/mine/pay-collect/list","付费文章收藏列表",""
"GET","/api/web/mine/purchased-pay-article/list","我的购买记录",""
"POST","/api/web/mine/sign-in","签到",""
"GET","/api/web/mine/sign-in/list","签到记录",""
"GET","/api/web/mine/sign-in/status","签到状态接口",""
"GET","/api/web/mine/summary","个人信息",""
"GET","/api/web/notice/{id}","公告详情",""
"GET","/api/web/notice/list","网站公告",""
"GET","/api/web/pay-article/{id}/related","相关推荐",""
"POST","/api/web/pay-article/article-collect/{articleId}","文章收藏",""
"POST","/api/web/pay-article/expert-collect","专家收藏",""
"GET","/api/web/pay-article/expert-list","专家预测详情列表",""
"GET","/api/web/pay-article/expert-profile","付费文章专家头部信息",""
"GET","/api/web/pay-article/expert-rank","专家预测列表",""
"GET","/api/web/pay-article/latest","最新推荐",""
"GET","/api/web/pay-article/publish","发布付费文章",""
"GET","/api/web/pay-article/publish/options","发布规则查询",""
"POST","/api/web/referral/bind","绑定推荐人",""
"GET","/api/web/referral/bind-preview","先查推荐人信息,用来弹确认框",""
"GET","/api/web/referral/invite-list","邀请用户列表",""
"GET","/api/web/referral/rebate-list","返利流水列表",""
"GET","/api/web/referral/share-info","邀请码和推广链接",""
"GET","/api/web/referral/summary","返利比例",""
"GET","/api/web/wallet/flow/list","资金流水按类型",""
"GET","/api/web/wallet/overview","钱包",""
"POST","/api/web/wallet/purchase/fc3d-scheme","购买接口",""
"POST","/api/web/wallet/purchase/pay-article","购买付费文章/专家预测",""
"POST","/api/web/wallet/receive-account","新增收款账户",""
"DELETE","/api/web/wallet/receive-account/{id}","删除",""
"PUT","/api/web/wallet/receive-account/{id}","修改收款账户",""
"PUT","/api/web/wallet/receive-account/{id}/default","把支付方式设置默认",""
"GET","/api/web/wallet/receive-account/list","查询收款账户",""
"POST","/api/web/wallet/receive-account/upload-qrcode","上传收款二维码",""
"GET","/api/web/wallet/recharge/config/list","创建充值订单",""
"GET","/api/web/wallet/recharge/notify","充值回调-前端不接(平台对接)",""
"POST","/api/web/wallet/recharge/order","创建充值订单",""
"POST","/api/web/wallet/transfer-to-account","分销余额或专家佣金转到账户余额",""
"POST","/api/web/wallet/withdraw","分销余额或专家佣金申请提现到收款账户",""
"GET","/api/web/wallet/withdraw/list","提现申请记录-单独接口",""
1 method path summary tags
2 GET /api/web/account/info 获取当前登录用户信息接口
3 PUT /api/web/account/profile 个人信息修改
4 GET /api/web/account/profile/avatar 头像上传
5 GET /api/web/ad/list 广告按位置筛选
6 GET /api/web/agreement/{type} 隐私/协议
7 GET /api/web/auth/code 获取验证码
8 POST /api/web/auth/heartbeat 登录心跳接口-前端可不接
9 POST /api/web/auth/login 登录接口
10 POST /api/web/auth/logout 退出登录
11 POST /api/web/auth/register 注册
12 GET /api/web/auth/slide-captcha 滑动验证
13 POST /api/web/auth/slide-captcha/verify 校验滑动结果
14 GET /api/web/auth/sms/code 发短信接口
15 POST /api/web/auth/social-bind 第三方账号绑定本地账号
16 POST /api/web/auth/social-callback 三方回调地址(前端不需要接,给平台用)
17 POST /api/web/auth/social-login 三方登录接口-暂时不接
18 GET /api/web/auth/verify-config 查询验证方式
19 GET /api/web/banner/list 轮播图
20 GET /api/web/config 网站基础信息接口-简化接口选一接就行
21 GET /api/web/config/list 网站基础信息
22 GET /api/web/config/site 网站图标
23 POST /api/web/expert/apply 专家申请
24 GET /api/web/expert/config 内容长度验证
25 POST /api/web/expert/upload-id-card-back 上传身份证反面
26 POST /api/web/expert/upload-id-card-front 上传身份证正面
27 GET /api/web/fc3d/free-predict/list 断组查询接口
28 GET /api/web/fc3d/free-predict/menus 断组杀组合菜单
29 GET /api/web/fc3d/scheme/{id} 方案详情
30 GET /api/web/fc3d/scheme/list 方案查询
31 GET /api/web/free-article/{id} 免费文章详情
32 POST /api/web/free-article/{id}/collect 收藏
33 POST /api/web/free-article/{id}/comment 评论
34 GET /api/web/free-article/{id}/comments 免费文章评论
35 POST /api/web/free-article/{id}/like 点赞
36 GET /api/web/free-article/{id}/related 相关推荐
37 GET /api/web/free-article/latest 最新推荐
38 GET /api/web/free-article/list 免费文章列表
39 POST /api/web/free-article/publish 发布
40 GET /api/web/free-article/recommend 免费文章
41 GET /api/web/link/friend 友情链接
42 GET /api/web/link/other-recommend 其他推荐
43 GET /api/web/lottery/result/latest 开奖结果
44 GET /api/web/lottery/result/list 开奖记录
45 GET /api/web/lottery/result/trend 走势图
46 GET /api/web/lottery/result/trend/tools 获取彩种信息排序
47 GET /api/web/menu/{id}/children 获取下级菜单ID
48 GET /api/web/menu/home-nav 导航栏
49 GET /api/web/mine/expert-collect/list 专家收藏
50 GET /api/web/mine/free-article/{id} 我的免费文章详情
51 GET /api/web/mine/free-article/list 我的免费文章列表
52 GET /api/web/mine/free-collect/list 免费文章收藏列表
53 GET /api/web/mine/pay-article/{id} 我的付费文章详情
54 GET /api/web/mine/pay-article/list 我的付费文章列表
55 GET /api/web/mine/pay-collect/list 付费文章收藏列表
56 GET /api/web/mine/purchased-pay-article/list 我的购买记录
57 POST /api/web/mine/sign-in 签到
58 GET /api/web/mine/sign-in/list 签到记录
59 GET /api/web/mine/sign-in/status 签到状态接口
60 GET /api/web/mine/summary 个人信息
61 GET /api/web/notice/{id} 公告详情
62 GET /api/web/notice/list 网站公告
63 GET /api/web/pay-article/{id}/related 相关推荐
64 POST /api/web/pay-article/article-collect/{articleId} 文章收藏
65 POST /api/web/pay-article/expert-collect 专家收藏
66 GET /api/web/pay-article/expert-list 专家预测详情列表
67 GET /api/web/pay-article/expert-profile 付费文章专家头部信息
68 GET /api/web/pay-article/expert-rank 专家预测列表
69 GET /api/web/pay-article/latest 最新推荐
70 GET /api/web/pay-article/publish 发布付费文章
71 GET /api/web/pay-article/publish/options 发布规则查询
72 POST /api/web/referral/bind 绑定推荐人
73 GET /api/web/referral/bind-preview 先查推荐人信息,用来弹确认框
74 GET /api/web/referral/invite-list 邀请用户列表
75 GET /api/web/referral/rebate-list 返利流水列表
76 GET /api/web/referral/share-info 邀请码和推广链接
77 GET /api/web/referral/summary 返利比例
78 GET /api/web/wallet/flow/list 资金流水按类型
79 GET /api/web/wallet/overview 钱包
80 POST /api/web/wallet/purchase/fc3d-scheme 购买接口
81 POST /api/web/wallet/purchase/pay-article 购买付费文章/专家预测
82 POST /api/web/wallet/receive-account 新增收款账户
83 DELETE /api/web/wallet/receive-account/{id} 删除
84 PUT /api/web/wallet/receive-account/{id} 修改收款账户
85 PUT /api/web/wallet/receive-account/{id}/default 把支付方式设置默认
86 GET /api/web/wallet/receive-account/list 查询收款账户
87 POST /api/web/wallet/receive-account/upload-qrcode 上传收款二维码
88 GET /api/web/wallet/recharge/config/list 创建充值订单
89 GET /api/web/wallet/recharge/notify 充值回调-前端不接(平台对接)
90 POST /api/web/wallet/recharge/order 创建充值订单
91 POST /api/web/wallet/transfer-to-account 分销余额或专家佣金转到账户余额
92 POST /api/web/wallet/withdraw 分销余额或专家佣金申请提现到收款账户
93 GET /api/web/wallet/withdraw/list 提现申请记录-单独接口
+147
View File
@@ -0,0 +1,147 @@
# 彩票 WEB_API 接口摘要
来源:C:/Users/Administrator/Desktop/彩票WEB_API.openapi.json
接口数量:92
## /api/web/account
- `GET` `/api/web/account/info`:获取当前登录用户信息接口
- `PUT` `/api/web/account/profile`:个人信息修改
- `GET` `/api/web/account/profile/avatar`:头像上传
## /api/web/ad
- `GET` `/api/web/ad/list`:广告按位置筛选
## /api/web/agreement
- `GET` `/api/web/agreement/{type}`:隐私/协议
## /api/web/auth
- `GET` `/api/web/auth/code`:获取验证码
- `POST` `/api/web/auth/heartbeat`:登录心跳接口-前端可不接
- `POST` `/api/web/auth/login`:登录接口
- `POST` `/api/web/auth/logout`:退出登录
- `POST` `/api/web/auth/register`:注册
- `GET` `/api/web/auth/slide-captcha`:滑动验证
- `POST` `/api/web/auth/slide-captcha/verify`:校验滑动结果
- `GET` `/api/web/auth/sms/code`:发短信接口
- `POST` `/api/web/auth/social-bind`:第三方账号绑定本地账号
- `POST` `/api/web/auth/social-callback`:三方回调地址(前端不需要接,给平台用)
- `POST` `/api/web/auth/social-login`:三方登录接口-暂时不接
- `GET` `/api/web/auth/verify-config`:查询验证方式
## /api/web/banner
- `GET` `/api/web/banner/list`:轮播图
## /api/web/config
- `GET` `/api/web/config`:网站基础信息接口-简化接口选一接就行
- `GET` `/api/web/config/list`:网站基础信息
- `GET` `/api/web/config/site`:网站图标
## /api/web/expert
- `POST` `/api/web/expert/apply`:专家申请
- `GET` `/api/web/expert/config`:内容长度验证
- `POST` `/api/web/expert/upload-id-card-back`:上传身份证反面
- `POST` `/api/web/expert/upload-id-card-front`:上传身份证正面
## /api/web/fc3d
- `GET` `/api/web/fc3d/free-predict/list`:断组查询接口
- `GET` `/api/web/fc3d/free-predict/menus`:断组杀组合菜单
- `GET` `/api/web/fc3d/scheme/{id}`:方案详情
- `GET` `/api/web/fc3d/scheme/list`:方案查询
## /api/web/free-article
- `GET` `/api/web/free-article/{id}`:免费文章详情
- `POST` `/api/web/free-article/{id}/collect`:收藏
- `POST` `/api/web/free-article/{id}/comment`:评论
- `GET` `/api/web/free-article/{id}/comments`:免费文章评论
- `POST` `/api/web/free-article/{id}/like`:点赞
- `GET` `/api/web/free-article/{id}/related`:相关推荐
- `GET` `/api/web/free-article/latest`:最新推荐
- `GET` `/api/web/free-article/list`:免费文章列表
- `POST` `/api/web/free-article/publish`:发布
- `GET` `/api/web/free-article/recommend`:免费文章
## /api/web/link
- `GET` `/api/web/link/friend`:友情链接
- `GET` `/api/web/link/other-recommend`:其他推荐
## /api/web/lottery
- `GET` `/api/web/lottery/result/latest`:开奖结果
- `GET` `/api/web/lottery/result/list`:开奖记录
- `GET` `/api/web/lottery/result/trend`:走势图
- `GET` `/api/web/lottery/result/trend/tools`:获取彩种信息排序
## /api/web/menu
- `GET` `/api/web/menu/{id}/children`:获取下级菜单ID
- `GET` `/api/web/menu/home-nav`:导航栏
## /api/web/mine
- `GET` `/api/web/mine/expert-collect/list`:专家收藏
- `GET` `/api/web/mine/free-article/{id}`:我的免费文章详情
- `GET` `/api/web/mine/free-article/list`:我的免费文章列表
- `GET` `/api/web/mine/free-collect/list`:免费文章收藏列表
- `GET` `/api/web/mine/pay-article/{id}`:我的付费文章详情
- `GET` `/api/web/mine/pay-article/list`:我的付费文章列表
- `GET` `/api/web/mine/pay-collect/list`:付费文章收藏列表
- `GET` `/api/web/mine/purchased-pay-article/list`:我的购买记录
- `POST` `/api/web/mine/sign-in`:签到
- `GET` `/api/web/mine/sign-in/list`:签到记录
- `GET` `/api/web/mine/sign-in/status`:签到状态接口
- `GET` `/api/web/mine/summary`:个人信息
## /api/web/notice
- `GET` `/api/web/notice/{id}`:公告详情
- `GET` `/api/web/notice/list`:网站公告
## /api/web/pay-article
- `GET` `/api/web/pay-article/{id}/related`:相关推荐
- `POST` `/api/web/pay-article/article-collect/{articleId}`:文章收藏
- `POST` `/api/web/pay-article/expert-collect`:专家收藏
- `GET` `/api/web/pay-article/expert-list`:专家预测详情列表
- `GET` `/api/web/pay-article/expert-profile`:付费文章专家头部信息
- `GET` `/api/web/pay-article/expert-rank`:专家预测列表
- `GET` `/api/web/pay-article/latest`:最新推荐
- `GET` `/api/web/pay-article/publish`:发布付费文章
- `GET` `/api/web/pay-article/publish/options`:发布规则查询
## /api/web/referral
- `POST` `/api/web/referral/bind`:绑定推荐人
- `GET` `/api/web/referral/bind-preview`:先查推荐人信息,用来弹确认框
- `GET` `/api/web/referral/invite-list`:邀请用户列表
- `GET` `/api/web/referral/rebate-list`:返利流水列表
- `GET` `/api/web/referral/share-info`:邀请码和推广链接
- `GET` `/api/web/referral/summary`:返利比例
## /api/web/wallet
- `GET` `/api/web/wallet/flow/list`:资金流水按类型
- `GET` `/api/web/wallet/overview`:钱包
- `POST` `/api/web/wallet/purchase/fc3d-scheme`:购买接口
- `POST` `/api/web/wallet/purchase/pay-article`:购买付费文章/专家预测
- `POST` `/api/web/wallet/receive-account`:新增收款账户
- `DELETE` `/api/web/wallet/receive-account/{id}`:删除
- `PUT` `/api/web/wallet/receive-account/{id}`:修改收款账户
- `PUT` `/api/web/wallet/receive-account/{id}/default`:把支付方式设置默认
- `GET` `/api/web/wallet/receive-account/list`:查询收款账户
- `POST` `/api/web/wallet/receive-account/upload-qrcode`:上传收款二维码
- `GET` `/api/web/wallet/recharge/config/list`:创建充值订单
- `GET` `/api/web/wallet/recharge/notify`:充值回调-前端不接(平台对接)
- `POST` `/api/web/wallet/recharge/order`:创建充值订单
- `POST` `/api/web/wallet/transfer-to-account`:分销余额或专家佣金转到账户余额
- `POST` `/api/web/wallet/withdraw`:分销余额或专家佣金申请提现到收款账户
- `GET` `/api/web/wallet/withdraw/list`:提现申请记录-单独接口
+34
View File
@@ -0,0 +1,34 @@
// config.js
window.ENV = {
// 开发环境配置
development: {
API_BASE_URL: 'http://47.108.24.205:9100',
// API_BASE_URL: 'http://192.168.10.119:9100',
// 后端租户标识,会作为 header tenantId 发送;后面更换租户只改这里。
TENANT_ID: '000000',
CLIENT_ID: '209ef407810e3856f40870a9f0e769d7',
DEBUG: true,
APP_NAME: 'Lottery App Dev'
},
// 生产环境配置
production: {
API_BASE_URL: 'http://47.108.24.205:9100',
// 后端租户标识,会作为 header tenantId 发送;后面更换租户只改这里。
TENANT_ID: '000000',
CLIENT_ID: '209ef407810e3856f40870a9f0e769d7',
DEBUG: false,
APP_NAME: 'Lottery App'
}
};
// 根据域名自动判断环境
window.CURRENT_ENV = (function () {
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1' || location.port !== '') {
return 'development';
} else {
return 'production';
}
})();
// 获取当前环境配置
window.CURRENT_CONFIG = window.ENV[window.CURRENT_ENV];
+266
View File
@@ -0,0 +1,266 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - APP下载</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<script src="../public/js/qrcode.min.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/appdown.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<a href="index.html" class="topbar__nav-link">首页</a>
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="page-main">
<!-- Hero with QR -->
<div class="download-hero">
<div class="hero-text">
<h1>下载彩先知<span>APP</span></h1>
<p>随时随地查看预测分析,不错过任何精彩推荐</p>
<p>扫码下载,畅享移动端体验</p>
</div>
<div class="hero-qr">
<div class="hero-qr-box">
<img id="downloadQrImg" src="" alt="下载二维码" />
</div>
<div class="hero-qr-label">扫描二维码下载APP</div>
</div>
</div>
<!-- Download Buttons -->
<div class="download-cards">
<div class="dl-card">
<div class="dl-card-icon android">
<i class="layui-icon layui-icon-android"></i>
</div>
<h3>Android 下载</h3>
<p>支持 Android 5.0 及以上系统<br />安装包大小约 15MB</p>
<a href="#" class="dl-btn android-btn" id="androidBtn"
>Android 下载</a
>
</div>
<div class="dl-card">
<div class="dl-card-icon ios">
<i class="layui-icon layui-icon-ios"></i>
</div>
<h3>iOS 下载</h3>
<p>支持 iOS 12.0 及以上系统<br />App Store 搜索"彩先知"</p>
<a href="#" class="dl-btn ios-btn" id="iosBtn">iOS 下载</a>
</div>
</div>
<!-- Features -->
<div class="features-section">
<div class="features-title">APP特色功能</div>
<div class="features-grid">
<div class="feature-item">
<div class="feature-icon">
<i class="layui-icon layui-icon-notice"></i>
</div>
<div class="feature-name">实时推送</div>
<div class="feature-desc">开奖结果即时通知</div>
</div>
<div class="feature-item">
<div class="feature-icon">
<i class="layui-icon layui-icon-chart"></i>
</div>
<div class="feature-name">走势分析</div>
<div class="feature-desc">专业走势图表工具</div>
</div>
<div class="feature-item">
<div class="feature-icon">
<i class="layui-icon layui-icon-star"></i>
</div>
<div class="feature-name">专家预测</div>
<div class="feature-desc">权威专家精准推荐</div>
</div>
<div class="feature-item">
<div class="feature-icon">
<i class="layui-icon layui-icon-group"></i>
</div>
<div class="feature-name">社区交流</div>
<div class="feature-desc">彩民互动交流平台</div>
</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
layui.use(["layer", "jquery"], function () {
var layer = layui.layer;
var $ = layui.$;
// Load page config
CommonUtil.loadPageConfig({ ads: false, links: false });
// Check login state for topbar
checkLogin();
renderNav();
function checkLogin() {
var token = CommonUtil.getToken();
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
if (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) {
$("#userAvatar").attr("src", data.avatar);
}
}
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(
function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
},
);
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
var downloadUrl = window.location.origin;
CommonUtil.generateQRCode(downloadUrl, "downloadQrImg", {
width: 180,
height: 180,
colorDark: "#102b6a",
colorLight: "#ffffff",
});
// Load download links from page config
ApiClient.post("/api/web/index", {})
.then(function (result) {
if (result.data && result.data.webConfigs) {
var configs = result.data.webConfigs;
configs.forEach(function (item) {
if (item.type === "android_download_url") {
$("#androidBtn").attr("href", item.content);
} else if (item.type === "ios_download_url") {
$("#iosBtn").attr("href", item.content);
} else if (item.type === "app_download_url") {
CommonUtil.generateQRCode(item.content, "downloadQrImg", {
width: 180,
height: 180,
colorDark: "#102b6a",
colorLight: "#ffffff",
});
}
});
}
})
.catch(function () {});
// Default fallback - generate QR for current origin
if (!$("#downloadQrImg").attr("src")) {
CommonUtil.generateQRCode(window.location.origin, "downloadQrImg", {
width: 180,
height: 180,
colorDark: "#102b6a",
colorLight: "#ffffff",
});
}
});
</script>
</body>
</html>
+385
View File
@@ -0,0 +1,385 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 专家申请</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/apply.css" />
</head>
<body>
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img src="../images/home-logo.png" alt="彩先知" class="topbar__logo" />
</a>
<div class="topbar__nav" id="navLotteryItems">
<a href="index.html" class="topbar__nav-link">首页</a>
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary">免费注册</a>
</div>
<div id="loggedInBox" class="topbar__logged apply-hidden">
<a href="usercenter.html" class="topbar__profile">
<img id="userAvatar" src="../public/img/userimg.png" alt="" class="topbar__avatar" />
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">退出登录</button>
</div>
</div>
</div>
</nav>
<main class="apply-page">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a>
<span class="sep">&gt;</span>
<a href="usercenter.html">个人中心</a>
<span class="sep">&gt;</span>
<span>专家申请</span>
</div>
<section class="apply-shell">
<div class="apply-hero">
<div>
<h1>申请成为专家</h1>
<p>提交身份资料和个人简介,审核通过后即可发布专家预测内容。</p>
</div>
<div class="apply-user-card">
<span>当前账号</span>
<strong id="currentUsername">--</strong>
</div>
</div>
<div class="apply-layout">
<aside class="apply-side">
<h2>申请说明</h2>
<ul>
<li>资料需真实有效,身份证照片需清晰完整。</li>
<li>简介用于审核您的分析经验和内容方向。</li>
<li>审核通过后,可在个人中心发布相关预测文章。</li>
</ul>
</aside>
<section class="apply-card">
<div class="apply-section-title">
<span></span>
资料上传
</div>
<div class="apply-upload-grid">
<button class="apply-upload-card" type="button" onclick="triggerUpload('uploadFront')">
<span class="apply-upload-plus" id="idCardFrontPlus">+</span>
<img class="apply-upload-preview" id="idCardFrontPreview" alt="身份证正面" />
<strong>身份证正面</strong>
<em>点击上传</em>
</button>
<button class="apply-upload-card" type="button" onclick="triggerUpload('uploadBack')">
<span class="apply-upload-plus" id="idCardBackPlus">+</span>
<img class="apply-upload-preview" id="idCardBackPreview" alt="身份证反面" />
<strong>身份证反面</strong>
<em>点击上传</em>
</button>
</div>
<input
class="apply-file-input"
type="file"
id="uploadFront"
accept="image/png,image/jpeg"
onchange="handleFileUpload(this, 'front')"
/>
<input
class="apply-file-input"
type="file"
id="uploadBack"
accept="image/png,image/jpeg"
onchange="handleFileUpload(this, 'back')"
/>
<div class="apply-section-title apply-intro-title">
<span></span>
个人简介
</div>
<textarea
class="apply-intro-textarea"
id="introTextarea"
placeholder="请输入个人简介"
oninput="updateIntro(this)"
></textarea>
<div class="apply-intro-count">
<span id="introCount">0</span> / <span id="introMinCount">80</span> 字(最低)
</div>
<button class="apply-submit" id="applyBtn" type="button" onclick="submitApply()">提交申请</button>
</section>
</div>
</section>
</main>
<footer class="site-footer">
<div class="site-footer__inner">
<img src="../images/home-logo.png" alt="彩先知" class="site-footer__logo" />
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a id="beian" href="https://beian.miit.gov.cn/" target="_blank">工信部备案号</a>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button class="gotop" id="backTop" type="button" onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"></button>
<script>
var updateData = {
idCardFrontId: "",
idCardBackId: "",
intro: "",
};
var expertIntroMinLength = 80;
layui.use(["layer", "jquery"], function () {
var $ = layui.$;
CommonUtil.loadPageConfig({ ads: false, links: false });
renderLoginState();
renderNav();
loadExpertApplyConfig();
CommonUtil.requireAuth(function () {
loadUserDetail();
});
$(window).on("scroll", function () {
$("#backTop").toggleClass("is-visible", $(window).scrollTop() > 320);
});
function renderLoginState() {
var token = CommonUtil.getAuthToken();
if (!token) {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
return;
}
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(function (data) {
if (!data) return;
$("#userName").text(data.nickName || data.username || "用户");
$("#userLevel").text(CommonUtil.getUserLevelText ? CommonUtil.getUserLevelText(data) : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
});
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 && res.code !== 200) return;
var list = ApiClient.asArray(ApiClient.pickData(res, []));
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(list, function (i, item) {
var code = item.suoxie || item.code || item.id || "";
var name = item.name || item.menuName || "";
if (!code || !name) return;
$nav.append(
'<a href="cai.html?id=' +
encodeURIComponent(code) +
'" class="topbar__nav-link">' +
safeText(name) +
"</a>"
);
});
});
}
function loadUserDetail() {
CommonUtil.getCurrentUserDetail(function (data) {
if (!data) return;
$("#currentUsername").text(data.nickName || data.username || data.phoneNumber || "用户");
var isExpert =
data.expert === true ||
data.regularExpert === true ||
data.isExpert === 1 ||
data.isExpert === "1";
if (isExpert) {
$("#applyBtn").text("已申请").prop("disabled", true);
}
});
}
});
function safeText(value) {
return CommonUtil.escapeHtml ? CommonUtil.escapeHtml(value) : String(value || "");
}
function isApiSuccess(res) {
return res && (res.code === 0 || res.code === 200);
}
function firstValue(source, keys, fallback) {
for (var i = 0; i < keys.length; i += 1) {
var key = keys[i];
if (source && source[key] !== undefined && source[key] !== null && source[key] !== "") {
return source[key];
}
}
return fallback === undefined ? "" : fallback;
}
function loadExpertApplyConfig() {
ApiClient.get(ApiClient.API.expertConfig)
.then(function (res) {
var data = ApiClient.pickData(res, {}) || {};
var minLength = Number(
firstValue(
data,
["bioMinLength", "introMinLength", "minBioLength", "minIntroLength", "bio_min_length"],
80
)
);
if (Number.isFinite(minLength) && minLength > 0) {
expertIntroMinLength = minLength;
}
})
.catch(function () {})
.finally(function () {
$("#introMinCount").text(expertIntroMinLength);
$("#introTextarea").attr("placeholder", "请输入个人简介(不低于" + expertIntroMinLength + "字)");
});
}
window.triggerUpload = function (inputId) {
var input = document.getElementById(inputId);
if (input) input.click();
};
function getUploadFileId(res) {
var data = ApiClient.pickData(res, null);
if (data === null || data === undefined || data === "") return "";
if (typeof data === "string" || typeof data === "number") return String(data);
return String(
firstValue(
data,
["objectId", "fileId", "id", "ossId", "resourceId", "attachmentId", "file_id"],
""
)
);
}
function getUploadFileUrl(res, file) {
var data = ApiClient.pickData(res, null);
if (data && typeof data === "object") {
return firstValue(data, ["url", "fileUrl", "fullUrl", "previewUrl"], "");
}
return URL.createObjectURL(file);
}
window.handleFileUpload = function (input, type) {
var file = input.files && input.files[0];
if (!file) return;
if (!/^image\//.test(file.type || "")) {
layui.layer.msg("请选择图片文件", { icon: 2 });
input.value = "";
return;
}
if (file.size > 5 * 1024 * 1024) {
layui.layer.msg("文件大小不能超过5MB", { icon: 2 });
input.value = "";
return;
}
var formData = new FormData();
formData.append("file", file);
var uploadUrl =
type === "front" ? ApiClient.API.expertUploadIdCardFront : ApiClient.API.expertUploadIdCardBack;
var plusId = type === "front" ? "idCardFrontPlus" : "idCardBackPlus";
var previewId = type === "front" ? "idCardFrontPreview" : "idCardBackPreview";
ApiClient.post(uploadUrl, formData)
.then(function (res) {
if (!isApiSuccess(res)) {
layui.layer.msg(res.msg || "上传失败", { icon: 2 });
return;
}
var fileId = getUploadFileId(res);
if (!fileId) {
layui.layer.msg("上传返回缺少文件编号", { icon: 2 });
return;
}
if (type === "front") updateData.idCardFrontId = fileId;
else updateData.idCardBackId = fileId;
$("#" + plusId).hide();
$("#" + previewId).attr("src", getUploadFileUrl(res, file)).show();
layui.layer.msg("上传成功", { icon: 1, time: 1000 });
})
.catch(function () {
layui.layer.msg("上传失败,请稍后重试", { icon: 2 });
})
.finally(function () {
input.value = "";
});
};
window.updateIntro = function (textarea) {
updateData.intro = textarea.value;
$("#introCount").text(textarea.value.length);
};
window.submitApply = function () {
CommonUtil.requireAuth(function () {
if (!updateData.intro || updateData.intro.trim().length < expertIntroMinLength) {
layui.layer.msg("个人简介不能低于" + expertIntroMinLength + "字", { icon: 2 });
return;
}
if (!updateData.idCardFrontId) {
layui.layer.msg("请上传身份证正面", { icon: 2 });
return;
}
if (!updateData.idCardBackId) {
layui.layer.msg("请上传身份证反面", { icon: 2 });
return;
}
var btn = document.getElementById("applyBtn");
btn.textContent = "申请中...";
btn.disabled = true;
ApiClient.post(ApiClient.API.expertApply, {
bio: updateData.intro.trim(),
idCardFrontImage: updateData.idCardFrontId,
idCardBackImage: updateData.idCardBackId,
})
.then(function (res) {
if (isApiSuccess(res)) {
layui.layer.msg(
"资料上传成功,请等待审核通过",
{ icon: 1, time: 1800 },
function () {
window.location.href = "usercenter.html";
}
);
} else {
layui.layer.msg(res.msg || "申请失败", { icon: 2 });
btn.textContent = "提交申请";
btn.disabled = false;
}
})
.catch(function () {
layui.layer.msg("网络错误,请稍后重试", { icon: 2 });
btn.textContent = "提交申请";
btn.disabled = false;
});
});
};
</script>
</body>
</html>
+267
View File
@@ -0,0 +1,267 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 免费文章收藏</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/mianfei.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main -->
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<a href="usercenter.html">个人中心</a><span class="sep">></span>
<span>我的收藏</span>
</div>
<div class="cxz-section-header">
<div class="cxz-section-title">免费文章收藏</div>
</div>
<div class="cxz-collect-grid" id="collectGrid"></div>
<div class="cxz-empty" id="emptyState" style="display: none">
<div class="cxz-empty-icon">
<i
class="layui-icon layui-icon-star-fill"
style="font-size: 48px"
></i>
</div>
<div class="cxz-empty-text">暂无收藏文章</div>
</div>
<div class="cxz-pagination" id="pagination"></div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
let currentPage = 1;
const pageSize = 12;
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
const token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(function (data) {
$("#userName").text(data.nickName || "用户");
$("#userAvatar").attr(
"src",
data.avatar || "../public/img/userimg.png",
);
});
} else {
window.location.href = "login.html";
return;
}
}
function authHeaders() {
const token = localStorage.getItem("token");
return { Authorization: "Bearer " + token };
}
// Load collection list
function loadCollections() {
const token = localStorage.getItem("token");
if (!token) {
window.location.href = "login.html";
return;
}
ApiClient.get(
"/api/user/collect/list?page=" +
currentPage +
"&pageSize=" +
pageSize,
{ headers: authHeaders() },
).then(function (res) {
if (res.code === 0 && res.data) {
const list = res.data.records || res.data || [];
const total = res.data.total || 0;
renderCollections(list);
renderPagination(total);
if (list.length === 0) {
$("#emptyState").show();
} else {
$("#emptyState").hide();
}
} else {
$("#collectGrid").empty();
$("#emptyState").show();
}
});
}
function renderCollections(list) {
const $grid = $("#collectGrid");
$grid.empty();
if (!list || list.length === 0) {
$grid.empty();
return;
}
$.each(list, function (i, item) {
let displayDate = "";
if (item.createTime) displayDate = item.createTime.substring(0, 10);
const articleId = item.articleId || item.id || "";
$grid.append(
'<div class="cxz-collect-card" data-collect-id="' +
(item.id || "") +
'">' +
'<div class="cxz-card-body">' +
'<div class="cxz-card-title"><a href="' +
CommonUtil.freeArticleHref(articleId) +
'">' +
(item.articleTitle || item.title || "无标题") +
"</a></div>" +
'<div class="cxz-card-meta">' +
"<span>" +
displayDate +
"</span>" +
'<button class="cxz-card-remove" onclick="removeCollect(this, ' +
(item.id || 0) +
')">取消收藏</button>' +
"</div>" +
"</div>" +
"</div>",
);
});
}
function removeCollect(btn, collectId) {
layui.layer.confirm(
"确定要取消收藏吗?",
{ icon: 3, title: "提示" },
function (index) {
layui.layer.close(index);
ApiClient.post(
"/api/user/collect/delete",
{ id: collectId },
{ headers: authHeaders() },
).then(function (res) {
if (res.code === 0) {
layui.layer.msg("已取消收藏", { icon: 1 });
const $card = $(btn).closest(".cxz-collect-card");
$card.addClass("removing");
setTimeout(function () {
$card.remove();
// Check if empty
if ($("#collectGrid .cxz-collect-card").length === 0) {
loadCollections();
}
}, 400);
} else {
layui.layer.msg(res.msg || "操作失败", { icon: 2 });
}
});
},
);
}
function renderPagination(total) {
layui.use("laypage", function () {
layui.laypage.render({
elem: "pagination",
count: total,
limit: pageSize,
curr: currentPage,
jump: function (obj, first) {
if (!first) {
currentPage = obj.curr;
loadCollections();
}
},
});
});
}
$(function () {
checkLogin();
loadCollections();
CommonUtil.loadPageConfig();
});
</script>
</body>
</html>
+774
View File
@@ -0,0 +1,774 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 彩票分类预测</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/cai.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main -->
<main class="cxz-main">
<!-- Breadcrumb -->
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<span id="breadcrumbLottery">彩票预测</span>
</div>
<!-- Filter Section -->
<div class="cxz-filter-box">
<div class="cxz-filter-row">
<div class="cxz-filter-label">彩种:</div>
<div class="cxz-filter-options" id="lotteryFilter"></div>
</div>
<div class="cxz-filter-row">
<div class="cxz-filter-label">方案:</div>
<div class="cxz-filter-options" id="planFilter"></div>
</div>
<div class="cxz-filter-row">
<div class="cxz-filter-label">类型:</div>
<div class="cxz-filter-options" id="typeFilter"></div>
</div>
</div>
<!-- Period Bar -->
<div class="cxz-period-bar">
<div class="cxz-period-title" id="tableTitle">预测列表</div>
<div class="cxz-period-select">
<button class="cxz-period-btn active" data-size="10">近10期</button>
<button class="cxz-period-btn" data-size="20">近20期</button>
<button class="cxz-period-btn" data-size="50">近50期</button>
</div>
</div>
<!-- Article Table -->
<div class="cxz-table-box">
<table class="cxz-data-table">
<thead>
<tr>
<th>专家</th>
<th>最新期号</th>
<th>最新推荐</th>
<th>正确率</th>
<th>操作</th>
</tr>
</thead>
<tbody id="articleBody">
<tr>
<td
colspan="5"
style="
text-align: center;
color: var(--text-muted);
padding: 40px;
"
>
加载中...
</td>
</tr>
</tbody>
</table>
<div class="cxz-pagination" id="pagination"></div>
</div>
<!-- Expert Ranking -->
<div class="cxz-ranking-box">
<div class="cxz-ranking-header">
<div class="cxz-ranking-title">专家排行榜</div>
</div>
<table class="cxz-rank-table">
<thead>
<tr>
<th>排名</th>
<th>专家</th>
<th>命中率</th>
<th>方案数</th>
<th>操作</th>
</tr>
</thead>
<tbody id="rankBody">
<tr>
<td
colspan="5"
style="
text-align: center;
color: var(--text-muted);
padding: 30px;
"
>
加载中...
</td>
</tr>
</tbody>
</table>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
// URL params
const urlParams = new URLSearchParams(window.location.search);
let currentCode = urlParams.get("id") || "ssq";
let currentParentId = urlParams.get("planId") || "";
let currentTypeId =
urlParams.get("menuId") ||
urlParams.get("parentId") ||
urlParams.get("typeId") ||
"";
let currentPageSize = 10;
const currentRowPageSize = 10;
let currentPage = 1;
let menuData = [];
let currentLottery = null;
// Login status
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
const token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
// Load menu data (3-level cascade)
function loadMenuData() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code === 0 && res.data) {
menuData = res.data;
renderLotteryFilter();
}
});
}
function renderLotteryFilter() {
const $box = $("#lotteryFilter");
$box.empty();
$.each(menuData, function (i, item) {
const btn = $(
'<button class="cxz-filter-btn" data-code="' +
item.suoxie +
'">' +
item.name +
"</button>",
);
if (item.suoxie === currentCode) {
btn.addClass("active");
currentLottery = item;
}
btn.on("click", function () {
currentCode = item.suoxie;
currentLottery = item;
currentParentId = "";
currentTypeId = "";
currentPage = 1;
$box.find(".cxz-filter-btn").removeClass("active");
$(this).addClass("active");
renderPlanFilter();
renderTypeFilter();
loadArticles();
loadRanking();
updateBreadcrumb();
});
$box.append(btn);
});
if (!currentLottery && menuData.length > 0) {
currentLottery = menuData[0];
currentCode = currentLottery.suoxie;
}
renderPlanFilter();
renderTypeFilter();
syncCurrentUrl();
}
function syncCurrentUrl() {
const params = new URLSearchParams();
params.set("id", currentCode || "ssq");
if (currentTypeId) params.set("parentId", currentTypeId);
if (currentParentId) params.set("planId", currentParentId);
const nextUrl = "cai.html?" + params.toString();
if (window.location.pathname.split("/").pop() + window.location.search !== nextUrl) {
window.history.replaceState(null, "", nextUrl);
}
}
function resolveParentIdByTypeId(list) {
if (!currentTypeId || currentParentId) return;
$.each(list || [], function (_, parent) {
if (currentParentId) return false;
const children = parent && parent.children ? parent.children : [];
const matched = children.find(function (child) {
return String(child.id) === String(currentTypeId);
});
if (matched) {
currentParentId = String(parent.id || "");
return false;
}
});
}
function renderPlanFilter() {
const $box = $("#planFilter");
$box.empty();
if (!currentLottery || !currentLottery.children) {
$box.append(
'<span style="color:var(--text-muted);font-size:13px;">暂无方案</span>',
);
return;
}
const list = CommonUtil.filterDuanzu(
currentLottery.children,
currentLottery.name,
);
resolveParentIdByTypeId(list);
$.each(list, function (i, item) {
const btn = $(
'<button class="cxz-filter-btn" data-id="' +
item.id +
'">' +
item.name +
"</button>",
);
if (i === 0 && !currentParentId) {
btn.addClass("active");
currentParentId = String(item.id || "");
} else if (String(item.id) === String(currentParentId)) {
btn.addClass("active");
}
btn.on("click", function () {
currentParentId = String(item.id || "");
currentTypeId = "";
currentPage = 1;
$box.find(".cxz-filter-btn").removeClass("active");
$(this).addClass("active");
renderTypeFilter();
syncCurrentUrl();
loadArticles();
loadRanking();
});
$box.append(btn);
});
}
function renderTypeFilter() {
const $box = $("#typeFilter");
$box.empty();
let children = [];
if (currentLottery && currentLottery.children) {
const list = CommonUtil.filterDuanzu(
currentLottery.children,
currentLottery.name,
);
if (currentParentId) {
const parent = list.find(function (p) {
return String(p.id) === String(currentParentId);
});
children = parent && parent.children ? parent.children : [];
} else {
// Flatten all children of level-2
$.each(list, function (i, p) {
if (p.children) {
children = children.concat(p.children);
}
});
}
}
if (children.length === 0) {
$box.append(
'<span style="color:var(--text-muted);font-size:13px;">暂无类型</span>',
);
return;
}
let hasActiveType = false;
$.each(children, function (i, item) {
const btn = $(
'<button class="cxz-filter-btn" data-id="' +
item.id +
'">' +
item.name +
"</button>",
);
if (String(item.id) === String(currentTypeId)) {
btn.addClass("active");
hasActiveType = true;
}
btn.on("click", function () {
currentTypeId = String(item.id || "");
currentPage = 1;
$box.find(".cxz-filter-btn").removeClass("active");
$(this).addClass("active");
syncCurrentUrl();
loadArticles();
loadRanking();
});
$box.append(btn);
});
if (!hasActiveType && children[0]) {
currentTypeId = String(children[0].id || "");
$box.find('.cxz-filter-btn[data-id="' + currentTypeId + '"]').addClass("active");
}
syncCurrentUrl();
}
function updateBreadcrumb() {
const name = currentLottery ? currentLottery.name : "彩票预测";
$("#breadcrumbLottery").text(name + " - 预测推荐");
$("#tableTitle").text(name + " 预测列表");
document.title = "彩先知 - " + name + "预测推荐";
}
function getCurrentTypeId() {
const activeTypeId = $("#typeFilter .cxz-filter-btn.active").data("id");
return String(activeTypeId || currentTypeId || "");
}
function escapeText(value) {
return CommonUtil.escapeHtml ? CommonUtil.escapeHtml(String(value || "")) : String(value || "");
}
function getExpertName(item) {
return (
item.nickname ||
item.nickName ||
item.createUserName ||
item.expertName ||
item.accountName ||
item.userName ||
"专家"
);
}
function getExpertAvatar(item) {
return item.avatar || item.accountAvatar || item.expertAvatar || item.headImg || item.headUrl || "";
}
function getExpertId(item) {
return item.createBy || item.expertId || item.accountId || item.userId || item.createUserId || "";
}
function getIssueText(item) {
const issue = item.latestIssue || item.issue || item.expect || item.period || item.issueNo || "";
if (!issue) return "--";
const lotteryName = currentLottery ? currentLottery.name : item.lotteryName || item.menuName || "";
return (lotteryName ? lotteryName + "-第" : "第") + issue + "期";
}
function getRecommendTitle(item) {
return (
item.latestTitle ||
item.title ||
item.articleTitle ||
item.recommendTitle ||
item.latestRecommendTitle ||
item.contentTitle ||
"--"
);
}
function getCorrectDisplayText(item) {
const raw =
item.status ??
item.resultText ??
item.statusText ??
item.drawStatusText ??
item.openStatusText ??
item.hitText ??
item.hitResult ??
item.correctText ??
item.latestStatus ??
item.result ??
"";
const text = String(raw ?? "").trim();
if (text) {
if (/^中/.test(text)) return text;
if (/^-?\d+(?:\.\d+)?$/.test(text) || /^\d+\+\d+$/.test(text)) return "中" + text;
return text;
}
const rate =
item.hitRate ??
item.successRate ??
item.correctRate ??
item.winRate ??
item.rate ??
"";
if (rate !== "" && rate !== null && rate !== undefined) {
if (String(rate).indexOf("%") >= 0) return String(rate);
const value = Number(rate);
if (Number.isFinite(value)) {
return (value > 0 && value <= 1 ? value * 100 : value).toFixed(2) + "%";
}
}
return "--";
}
function getRatePercentText(item) {
const raw =
item.hitRate ??
item.successRate ??
item.correctRate ??
item.winRate ??
item.rate ??
"";
if (raw !== "" && raw !== null && raw !== undefined) {
if (String(raw).indexOf("%") >= 0) return String(raw);
const value = Number(raw);
if (Number.isFinite(value)) {
return (value > 0 && value <= 1 ? value * 100 : value).toFixed(2) + "%";
}
}
const hit = Number(item.correctCount ?? item.hitCount ?? item.hit ?? 0);
const total = Number(item.totalCount ?? item.total ?? item.articleCount ?? 0);
return total > 0 ? ((hit / total) * 100).toFixed(2) + "%" : "0.00%";
}
function buildCaiinfoUrl(item) {
const params = new URLSearchParams();
params.set("createBy", getExpertId(item));
params.set("code", currentCode || "");
params.set("parentId", getCurrentTypeId());
if (item.accountType) params.set("accountType", item.accountType);
return "caiinfo.html?" + params.toString();
}
// Load articles
function loadArticles(page) {
currentPage = page || 1;
const typeId = getCurrentTypeId();
if (!typeId) {
$("#articleBody").html(
'<tr><td colspan="5" style="text-align:center;color:var(--text-muted);padding:40px;">暂无数据</td></tr>',
);
renderPagination(0);
return;
}
currentTypeId = typeId;
ApiClient.get("/api/web/pay-article/expert-rank", {
menuId: typeId,
issueCount: currentPageSize,
pageNum: currentPage,
pageSize: currentRowPageSize,
}).then(function (res) {
if (ApiClient.isSuccess(res) && ApiClient.pickData(res, null)) {
const data = ApiClient.pickData(res, {}) || {};
const list = data.records || data.rows || ApiClient.asArray(data);
renderArticles(list);
renderPagination(data.totalRow || data.total || data.count || list.length || 0);
} else {
$("#articleBody").html(
'<tr><td colspan="5" style="text-align:center;color:var(--text-muted);padding:40px;">暂无数据</td></tr>',
);
}
});
}
function renderArticles(list) {
const $body = $("#articleBody");
$body.empty();
if (!list || list.length === 0) {
$body.html(
'<tr><td colspan="5" style="text-align:center;color:var(--text-muted);padding:40px;">暂无数据</td></tr>',
);
return;
}
$.each(list, function (i, item) {
const expertName = getExpertName(item);
const avatar = getExpertAvatar(item);
const periodText = getIssueText(item);
const predictHtml = getRecommendTitle(item);
const avatarHtml = avatar
? '<img src="' + escapeText(avatar) + '" class="table-img" />'
: '<span class="cxz-expert-avatar">' +
escapeText(expertName).charAt(0) +
"</span>";
const linkUrl = buildCaiinfoUrl(item);
$body.append(
"<tr>" +
'<td><a href="' +
escapeText(linkUrl) +
'" class="cxz-expert-cell">' +
avatarHtml +
"<span>" +
escapeText(expertName) +
"</span></a></td>" +
"<td>" +
escapeText(periodText) +
"</td>" +
"<td>" +
escapeText(predictHtml) +
"</td>" +
'<td><span class="cxz-rate" style="color:#de2103;font-weight:700;">' +
escapeText(getCorrectDisplayText(item)) +
"</span></td>" +
'<td><a href="' +
escapeText(linkUrl) +
'" class="cxz-btn-buy">查看</a></td>' +
"</tr>",
);
});
}
function renderBalls(code) {
if (!code) return '<span style="color:var(--text-muted);">--</span>';
const parts = code.split("+");
let html = "";
const reds = parts[0] ? parts[0].split(",") : [];
$.each(reds, function (i, b) {
html += '<span class="red-q">' + b.trim() + "</span>";
});
for (let p = 1; p < parts.length; p++) {
const blues = parts[p].split(",");
$.each(blues, function (j, b) {
html += '<span class="bule-q">' + b.trim() + "</span>";
});
}
return html;
}
function renderPagination(total) {
layui.laypage.render({
elem: "pagination",
count: total,
limit: currentRowPageSize,
curr: currentPage,
jump: function (obj, first) {
if (!first) {
loadArticles(obj.curr);
}
},
});
}
// Load expert ranking
function loadRanking() {
const typeId = getCurrentTypeId();
if (!typeId) {
$("#rankBody").html(
'<tr><td colspan="5" style="text-align:center;color:var(--text-muted);padding:30px;">暂无数据</td></tr>',
);
return;
}
ApiClient.get("/api/web/pay-article/expert-rank", {
menuId: typeId,
issueCount: 50,
pageNum: 1,
pageSize: currentRowPageSize,
}).then(function (res) {
if (ApiClient.isSuccess(res) && ApiClient.pickData(res, null)) {
const data = ApiClient.pickData(res, {}) || {};
renderRanking(data.records || data.rows || ApiClient.asArray(data));
}
});
}
function renderRanking(list) {
const $body = $("#rankBody");
$body.empty();
if (!list || list.length === 0) {
$body.html(
'<tr><td colspan="5" style="text-align:center;color:var(--text-muted);padding:30px;">暂无数据</td></tr>',
);
return;
}
const experts = list
.map(function (item) {
const total = Number(item.totalCount || item.total || 0);
const hit = Number(item.correctCount || item.hit || 0);
const rateText = getRatePercentText(item);
const hitText = getCorrectDisplayText(item);
const rateNum = rateText
? parseFloat(String(rateText).replace("%", ""))
: total > 0
? (hit / total) * 100
: 0;
return {
raw: item,
name: getExpertName(item),
createBy: getExpertId(item),
total: total || item.articleCount || item.totalCount || 0,
hit,
hitText,
rate: Number.isFinite(rateNum) ? rateNum : 0,
rank: item.rank,
accountType: item.accountType,
};
})
.sort(function (a, b) {
return (a.rank || 9999) - (b.rank || 9999) || b.rate - a.rate;
})
.slice(0, 10);
$.each(experts, function (i, exp) {
const rate = exp.rate.toFixed(1);
const badgeClass =
i === 0
? "cxz-rank-1"
: i === 1
? "cxz-rank-2"
: i === 2
? "cxz-rank-3"
: "cxz-rank-n";
$body.append(
"<tr>" +
'<td><span class="cxz-rank-badge ' +
badgeClass +
'">' +
(i + 1) +
"</span></td>" +
'<td><a href="' +
escapeText(buildCaiinfoUrl(exp.raw)) +
'" class="cxz-expert-link">' +
escapeText(exp.name) +
"</a></td>" +
'<td><span style="color:#de2103;font-weight:700;">' +
escapeText(exp.hitText !== "--" ? exp.hitText : rate + "%") +
"</span></td>" +
"<td>" +
exp.total +
"</td>" +
'<td><a href="' +
escapeText(buildCaiinfoUrl(exp.raw)) +
'" class="cxz-btn-buy">查看</a></td>' +
"</tr>",
);
});
}
// Period selector
$(document).on("click", ".cxz-period-btn", function () {
$(".cxz-period-btn").removeClass("active");
$(this).addClass("active");
currentPageSize = parseInt($(this).data("size"));
loadArticles();
});
// Init
$(function () {
checkLogin();
loadMenuData();
CommonUtil.loadPageConfig();
setTimeout(function () {
loadArticles();
loadRanking();
updateBreadcrumb();
}, 300);
});
</script>
</body>
</html>
+1872
View File
File diff suppressed because it is too large Load Diff
+894
View File
@@ -0,0 +1,894 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 充值</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/publish.css" />
<link rel="stylesheet" href="../public/css/finance.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems"></div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main -->
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<a href="usercenter.html">个人中心</a><span class="sep">></span>
<span>充值</span>
</div>
<!-- Balance Overview -->
<div class="cz-balance-bar">
<div class="cz-balance-bar__item">
<div
class="cz-balance-bar__icon"
style="background: rgba(16, 43, 106, 0.08); color: var(--primary)"
>
<i class="layui-icon layui-icon-rmb"></i>
</div>
<div class="cz-balance-bar__info">
<div class="cz-balance-bar__label">账户余额</div>
<div class="cz-balance-bar__value" id="currentBalance">
0.00<span class="unit"></span>
</div>
</div>
</div>
</div>
<!-- Package Selection -->
<div class="cxz-form-card">
<div class="cxz-form-title">选择充值套餐</div>
<div class="cz-package-grid" id="packageGrid">
<div class="cz-package-loading">
<i
class="layui-icon layui-icon-loading-1 layui-anim layui-anim-rotate layui-anim-loop"
></i>
<span>加载套餐中...</span>
</div>
</div>
<button
class="cxz-submit-btn"
id="rechargeBtn"
onclick="handleRecharge()"
>
立即充值
</button>
</div>
<!-- QR Code Payment Section -->
<div class="cxz-form-card" id="qrcodeCard" style="display: none">
<div class="cz-qr-section" id="qrSection">
<div class="cz-qr-header">
<div class="cz-qr-icon">
<i class="layui-icon layui-icon-cellphone"></i>
</div>
<div class="cz-qr-title">微信扫码支付</div>
<div class="cz-qr-subtitle">请使用微信扫描下方二维码完成支付</div>
</div>
<div class="cz-qr-amount" id="payAmountDisplay">0<span></span></div>
<div class="cz-qr-wrap">
<div id="qrcodeContainer"></div>
</div>
<div class="cz-qr-hint">
二维码有效期 <strong>5分钟</strong>,请尽快完成支付
</div>
<div class="cz-qr-timer">
剩余时间:<span id="countdownTimer">05:00</span>
</div>
<button class="cz-qr-cancel" onclick="cancelPayment()">
取消支付
</button>
</div>
<div class="cz-pay-result" id="paymentSuccess" style="display: none">
<div class="cz-pay-result__icon success">
<i class="layui-icon layui-icon-ok-circle"></i>
</div>
<div class="cz-pay-result__title">支付成功</div>
<div class="cz-pay-result__desc">充值金额已到账,请查看余额</div>
<a href="usercenter.html" class="cz-pay-result__btn">返回个人中心</a>
</div>
<div class="cz-pay-result" id="paymentTimeout" style="display: none">
<div class="cz-pay-result__icon timeout">
<i class="layui-icon layui-icon-close-fill"></i>
</div>
<div class="cz-pay-result__title">支付超时</div>
<div class="cz-pay-result__desc">二维码已过期,请重新发起充值</div>
<button class="cz-pay-result__btn" onclick="resetPayment()">
重新充值
</button>
</div>
</div>
<!-- Tips -->
<div class="cz-tips-card">
<div class="cz-tips-title">
<i class="layui-icon layui-icon-about"></i> 充值说明
</div>
<ul class="cz-tips-list">
<li>充值后余额将实时到账,可用于购买付费方案</li>
<li>微信扫码支付请在5分钟内完成,超时需重新发起</li>
<li>如遇支付问题,请联系客服处理</li>
<li>充值金额不支持退款,请确认后再支付</li>
</ul>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script src="../public/js/qrcode.min.js"></script>
<style>
/* Balance Bar */
.cz-balance-bar {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.cz-balance-bar__item {
background: var(--white);
border-radius: var(--radius-lg);
padding: 20px 24px;
box-shadow: var(--shadow-sm);
display: flex;
align-items: center;
gap: 16px;
border: 1px solid var(--border-light);
transition: all 0.3s;
}
.cz-balance-bar__item:hover {
border-color: transparent;
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.cz-balance-bar__icon {
width: 48px;
height: 48px;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
flex-shrink: 0;
}
.cz-balance-bar__label {
font-size: 13px;
color: var(--text-muted);
}
.cz-balance-bar__value {
font-size: 24px;
font-weight: 800;
color: var(--text-dark);
line-height: 1.2;
}
.cz-balance-bar__value .unit {
font-size: 14px;
font-weight: 400;
color: var(--text-muted);
margin-left: 2px;
}
/* Package Grid */
.cz-package-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 14px;
margin-bottom: 24px;
}
.cz-package-loading {
grid-column: 1/-1;
text-align: center;
padding: 40px;
color: var(--text-muted);
}
.cz-package-loading i {
font-size: 28px;
display: block;
margin-bottom: 8px;
color: var(--primary);
}
.cz-pkg {
position: relative;
background: var(--bg-light);
border: 2px solid var(--border-light);
border-radius: var(--radius-lg);
padding: 20px 16px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
user-select: none;
overflow: hidden;
}
.cz-pkg:hover {
border-color: var(--primary);
transform: translateY(-2px);
box-shadow: var(--shadow-sm);
}
.cz-pkg.selected {
border-color: var(--primary);
background: rgba(16, 43, 106, 0.04);
box-shadow: 0 0 0 3px rgba(16, 43, 106, 0.12);
}
.cz-pkg.selected::after {
content: "\2713";
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 24px;
background: var(--primary);
color: #fff;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0 var(--radius-lg) 0 var(--radius-lg);
}
.cz-pkg__price {
font-size: 28px;
font-weight: 800;
color: var(--primary);
line-height: 1.2;
}
.cz-pkg__price span {
font-size: 14px;
font-weight: 600;
}
.cz-pkg__credit {
display: inline-block;
margin-top: 8px;
padding: 3px 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
background: rgba(253, 185, 51, 0.12);
color: var(--gold);
}
.cz-pkg__bonus {
display: inline-block;
margin-top: 4px;
padding: 2px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
background: rgba(241, 90, 34, 0.1);
color: var(--orange);
}
.cz-pkg.selected .cz-pkg__price {
color: var(--primary);
}
/* Hot / Recommend tag */
.cz-pkg__tag {
position: absolute;
top: 8px;
left: 8px;
padding: 2px 8px;
border-radius: 999px;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.5px;
color: #fff;
}
.cz-pkg__tag.hot {
background: linear-gradient(135deg, #ff6b35, #f15a22);
}
.cz-pkg__tag.rec {
background: linear-gradient(135deg, var(--primary), #1a3f8f);
}
/* QR Code Section */
.cz-qr-section {
text-align: center;
padding: 24px 0;
}
.cz-qr-header {
margin-bottom: 16px;
}
.cz-qr-icon {
width: 48px;
height: 48px;
border-radius: 50%;
background: rgba(7, 193, 96, 0.1);
color: #07c160;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-bottom: 8px;
}
.cz-qr-title {
font-size: 18px;
font-weight: 700;
color: var(--text-dark);
}
.cz-qr-subtitle {
font-size: 13px;
color: var(--text-muted);
margin-top: 4px;
}
.cz-qr-amount {
font-size: 32px;
font-weight: 800;
color: var(--orange);
margin: 16px 0;
}
.cz-qr-amount span {
font-size: 16px;
font-weight: 600;
color: var(--text-muted);
}
.cz-qr-wrap {
display: inline-block;
padding: 16px;
background: #fff;
border: 2px solid var(--border-light);
border-radius: var(--radius-lg);
margin-bottom: 12px;
}
.cz-qr-wrap img,
.cz-qr-wrap canvas {
display: block;
width: 200px;
height: 200px;
}
.cz-qr-hint {
font-size: 13px;
color: var(--text-muted);
margin-top: 8px;
}
.cz-qr-timer {
font-size: 14px;
color: var(--orange);
margin-top: 8px;
font-weight: 700;
}
.cz-qr-cancel {
margin-top: 16px;
padding: 8px 24px;
border: 1px solid var(--border-light);
border-radius: 999px;
background: transparent;
color: var(--text-muted);
font-size: 13px;
cursor: pointer;
transition: all 0.25s;
}
.cz-qr-cancel:hover {
border-color: var(--primary);
color: var(--primary);
}
/* Payment Result */
.cz-pay-result {
text-align: center;
padding: 40px 0;
}
.cz-pay-result__icon {
width: 72px;
height: 72px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 36px;
margin-bottom: 16px;
}
.cz-pay-result__icon.success {
background: rgba(40, 167, 69, 0.12);
color: var(--green);
}
.cz-pay-result__icon.timeout {
background: rgba(255, 193, 7, 0.15);
color: #ffc107;
}
.cz-pay-result__title {
font-size: 20px;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 8px;
}
.cz-pay-result__desc {
font-size: 13px;
color: var(--text-muted);
margin-bottom: 20px;
}
.cz-pay-result__btn {
display: inline-block;
padding: 10px 32px;
border-radius: 999px;
font-size: 14px;
font-weight: 700;
background: linear-gradient(135deg, var(--primary), #1a3f8f);
color: #fff;
text-decoration: none;
cursor: pointer;
border: none;
transition: all 0.3s;
}
.cz-pay-result__btn:hover {
box-shadow: 0 6px 20px rgba(16, 43, 106, 0.4);
transform: translateY(-2px);
}
/* Tips Card */
.cz-tips-card {
background: var(--white);
border-radius: var(--radius-lg);
padding: 20px 24px;
border: 1px solid var(--border-light);
margin-top: 24px;
}
.cz-tips-title {
font-size: 14px;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 12px;
}
.cz-tips-title i {
color: var(--primary);
margin-right: 4px;
}
.cz-tips-list {
margin: 0;
padding-left: 18px;
}
.cz-tips-list li {
font-size: 12px;
color: var(--text-muted);
line-height: 2;
position: relative;
}
/* Responsive */
@media (max-width: 768px) {
.cz-balance-bar {
grid-template-columns: 1fr;
}
.cz-package-grid {
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.cz-pkg {
padding: 16px 12px;
}
.cz-pkg__price {
font-size: 22px;
}
}
@media (max-width: 480px) {
.cz-package-grid {
grid-template-columns: repeat(2, 1fr);
gap: 8px;
}
.cz-pkg__price {
font-size: 20px;
}
.cz-pkg__credit {
font-size: 11px;
}
}
</style>
<script>
const $ = layui.$;
var configList = [];
var selectedConfigId = "";
var selectedConfigAmount = 0;
var pollingTimer = null;
var countdownTimer = null;
var countdownSeconds = 300;
var pollingStartTime = 0;
var MAX_POLLING_DURATION = 300000;
function escapeHtml(value) {
return String(value == null ? "" : value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
var accountBalance = Number(
data.accountBalance || data.balance || data.amount || 0,
);
$("#currentBalance").html(
(Number.isFinite(accountBalance) ? accountBalance : 0).toFixed(2) +
'<span class="unit">\u5143</span>',
);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
} else {
window.location.href = "login.html";
}
}
function renderNav() {
ApiClient.get(ApiClient.API.homeNav).then(function (res) {
if (!ApiClient.isSuccess(res) || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(ApiClient.asArray(res.data), function (i, item) {
if (item.type && item.type !== "lottery") return;
var code = item.code || item.suoxie || "";
if (!code) return;
$nav.append(
'<a href="cai.html?id=' +
encodeURIComponent(code) +
'" class="topbar__nav-link">' +
escapeHtml(item.name || item.lotteryName || code) +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
// Load packages from API
function loadConfigList() {
ApiClient.get(ApiClient.API.walletRechargeConfigList)
.then(function (res) {
var data = ApiClient.pickData ? ApiClient.pickData(res) : res.data;
var list = ApiClient.asArray(data);
if (ApiClient.isSuccess(res) && list.length > 0) {
configList = list;
// Sort by sort field
configList.sort(function (a, b) {
return (a.sort || 0) - (b.sort || 0);
});
renderPackages(configList);
} else {
renderEmptyPackages();
}
})
.catch(function () {
renderEmptyPackages("加载失败,请刷新重试");
});
}
function formatMoney(value) {
var number = Number(value || 0);
return Number.isFinite(number) ? number.toFixed(2) : "0.00";
}
function renderPackages(list) {
var $grid = $("#packageGrid");
$grid.empty();
$.each(list, function (i, item) {
var paidAmount =
parseFloat(item.paidAmount || item.amount || item.price) || 0;
var totalCredited =
parseFloat(
item.totalCredited ||
item.arrivalAmount ||
item.creditedAmount ||
item.receiveAmount,
) || paidAmount;
var bonus =
parseFloat(item.giftAmount || item.bonusAmount || item.giveAmount) ||
totalCredited - paidAmount;
var bonusRate =
paidAmount > 0 ? Math.round((bonus / paidAmount) * 100) : 0;
var tagHtml = "";
if (bonusRate >= 80) {
tagHtml = '<div class="cz-pkg__tag hot">超值</div>';
} else if (bonusRate >= 30) {
tagHtml = '<div class="cz-pkg__tag rec">推荐</div>';
}
var creditHtml =
'<div class="cz-pkg__credit">\u5230\u8d26 ' +
formatMoney(totalCredited) +
" \u5143</div>";
var bonusHtml =
bonus > 0
? '<div class="cz-pkg__bonus">\u8d60 ' +
formatMoney(bonus) +
" \u5143</div>"
: "";
var selectedClass = i === 0 ? " selected" : "";
if (i === 0) {
selectedConfigId = item.id;
selectedConfigAmount = paidAmount;
}
$grid.append(
'<div class="cz-pkg' +
selectedClass +
'" data-id="' +
item.id +
'" data-amount="' +
paidAmount +
'" onclick="selectPackage(this)">' +
tagHtml +
'<div class="cz-pkg__price">' +
formatMoney(paidAmount) +
"<span>\u5143</span></div>" +
creditHtml +
bonusHtml +
"</div>",
);
});
}
function renderEmptyPackages(msg) {
$("#packageGrid").html(
'<div class="cz-package-loading">' +
'<i class="layui-icon layui-icon-face-surprised"></i>' +
"<span>" +
(msg || "暂无充值套餐") +
"</span>" +
"</div>",
);
}
function selectPackage(el) {
$(".cz-pkg").removeClass("selected");
$(el).addClass("selected");
selectedConfigId = $(el).data("id");
selectedConfigAmount = parseFloat($(el).data("amount")) || 0;
}
// Handle recharge
function handleRecharge() {
if (!selectedConfigId) {
layui.layer.msg("请选择充值套餐", { icon: 2 });
return;
}
var $btn = $("#rechargeBtn");
$btn.text("正在生成订单...").prop("disabled", true);
ApiClient.post(ApiClient.API.walletRechargeOrder, {
rechargeId: selectedConfigId,
})
.then(function (res) {
$btn.text("\u7acb\u5373\u5145\u503c").prop("disabled", false);
var data = ApiClient.pickData ? ApiClient.pickData(res) : res.data;
if (ApiClient.isSuccess(res) && data) {
var payUrl = getRechargePaymentUrl(data);
var amount = data.amount || data.paidAmount || selectedConfigAmount;
if (!payUrl) {
layui.layer.msg("\u8ba2\u5355\u672a\u8fd4\u56de\u652f\u4ed8\u4e8c\u7ef4\u7801\u5185\u5bb9", { icon: 2 });
return;
}
showQRCode(payUrl, amount);
} else {
layui.layer.msg(res.msg || "创建订单失败", { icon: 2 });
}
})
.catch(function () {
$btn.text("立即充值").prop("disabled", false);
layui.layer.msg("网络错误,请稍后重试", { icon: 2 });
});
}
function getRechargePaymentUrl(data) {
if (typeof data === "string") return data;
if (!data || typeof data !== "object") return "";
return (
data.qrCode ||
data.payUrl ||
data.paymentUrl ||
data.codeUrl ||
data.qrCodeUrl ||
data.nativeUrl ||
data.url ||
""
);
}
// Show QR code
function showQRCode(url, amount) {
$("#qrcodeCard").show();
$("#qrSection").show();
$("#paymentSuccess").hide();
$("#paymentTimeout").hide();
$("#qrcodeContainer").empty();
$("#payAmountDisplay").html(amount + "<span>元</span>");
new QRCode(document.getElementById("qrcodeContainer"), {
text: url,
width: 200,
height: 200,
colorDark: "#102b6a",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H,
});
countdownSeconds = 300;
pollingStartTime = Date.now();
startCountdown();
$("html,body").animate(
{ scrollTop: $("#qrcodeCard").offset().top - 20 },
400,
);
}
// Countdown
function startCountdown() {
if (countdownTimer) clearInterval(countdownTimer);
countdownTimer = setInterval(function () {
countdownSeconds--;
if (countdownSeconds <= 0) {
clearInterval(countdownTimer);
stopPolling();
$("#qrSection").hide();
$("#paymentTimeout").show();
return;
}
var min = Math.floor(countdownSeconds / 60);
var sec = countdownSeconds % 60;
$("#countdownTimer").text(
(min < 10 ? "0" + min : min) + ":" + (sec < 10 ? "0" + sec : sec),
);
}, 1000);
}
// Polling for payment confirmation
function startPolling() {
stopPolling();
doPoll();
}
function doPoll() {
var elapsed = Date.now() - pollingStartTime;
if (elapsed >= MAX_POLLING_DURATION) {
stopPolling();
clearInterval(countdownTimer);
$("#qrSection").hide();
$("#paymentTimeout").show();
return;
}
scheduleNextPoll();
}
function scheduleNextPoll() {
var delay = 1000 + Math.floor(Math.random() * 4000);
pollingTimer = setTimeout(doPoll, delay);
}
function stopPolling() {
if (pollingTimer) {
clearTimeout(pollingTimer);
pollingTimer = null;
}
}
function cancelPayment() {
stopPolling();
if (countdownTimer) clearInterval(countdownTimer);
$("#qrcodeCard").hide();
}
function resetPayment() {
$("#paymentTimeout").hide();
$("#qrSection").hide();
$("#qrcodeCard").hide();
$("html,body").animate({ scrollTop: 0 }, 300);
}
// Init
$(function () {
checkLogin();
renderNav();
CommonUtil.loadPageConfig({ ads: false, links: false });
CommonUtil.requireAuth(function () {
loadConfigList();
});
});
$(window).on("beforeunload", function () {
stopPolling();
if (countdownTimer) clearInterval(countdownTimer);
});
</script>
</body>
</html>
+364
View File
@@ -0,0 +1,364 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>彩先知 - 大乐透走势图</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/trend.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<a href="index.html" class="topbar__nav-link">首页</a>
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<div class="breadcrumb-bar">
<a href="javascript:history.back();"
><i class="layui-icon layui-icon-left"></i> 返回</a
>
<span>/</span>
<span>大乐透走势图</span>
</div>
<div class="page-title-bar">
<div class="page-title">大乐透走势图</div>
<div class="qishu">
<div class="qishu-but active" data-count="30">30期</div>
<div class="qishu-but" data-count="60">60期</div>
<div class="qishu-but" data-count="100">100期</div>
</div>
</div>
<div class="table-wrapper">
<table class="trend-table" border="1" id="trend-table">
<thead>
<tr>
<td rowspan="2" class="col-issue">期号</td>
<td rowspan="2" class="col-code">开奖号</td>
<td colspan="35" class="zone-front-header">前区</td>
<td colspan="12" class="zone-back-header">后区</td>
</tr>
<tr id="header-row"></tr>
</thead>
<tbody id="data-body"></tbody>
</table>
</div>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
CommonUtil.loadPageConfig();
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
checkLogin();
renderNav();
(function () {
var FRONT_MAX = 35,
BACK_MAX = 12;
var currentCount = 30;
var allData = [];
function buildHeader() {
var html = "";
for (var i = 1; i <= FRONT_MAX; i++) {
html += "<td>" + (i < 10 ? "0" + i : i) + "</td>";
}
for (var i = 1; i <= BACK_MAX; i++) {
html += "<td>" + (i < 10 ? "0" + i : i) + "</td>";
}
$("#header-row").html(html);
}
function parseCode(code) {
var parts = code.split("+");
var fronts = parts[0].split(",").map(function (n) {
return parseInt(n);
});
var backs = [];
for (var p = 1; p < parts.length; p++) {
if (parts[p])
backs = backs.concat(
parts[p].split(",").map(function (n) {
return parseInt(n);
}),
);
}
return { fronts: fronts, backs: backs };
}
function renderData(count) {
var data = allData.slice(0, count);
var html = "";
for (var r = 0; r < data.length; r++) {
var item = data[r];
var parsed = parseCode(item.openCode);
var frontSet = {},
backSet = {};
parsed.fronts.forEach(function (n) {
frontSet[n] = true;
});
parsed.backs.forEach(function (n) {
backSet[n] = true;
});
html += "<tr>";
html += '<td class="col-issue">' + item.issue + "</td>";
html += '<td class="col-code">' + item.openCode + "</td>";
for (var i = 1; i <= FRONT_MAX; i++) {
if (frontSet[i]) {
html +=
'<td><span class="hit-red">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
} else {
html +=
'<td><span class="miss-num">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
}
}
for (var i = 1; i <= BACK_MAX; i++) {
if (backSet[i]) {
html +=
'<td><span class="hit-blue">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
} else {
html +=
'<td><span class="miss-num">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
}
}
html += "</tr>";
}
html += buildStats(data);
$("#data-body").html(html);
}
function calcStats(data, maxNum, getNums) {
var count = {},
maxOmit = {},
currOmit = {},
maxConsec = {},
avgOmit = {};
for (var i = 1; i <= maxNum; i++) {
count[i] = 0;
maxOmit[i] = 0;
currOmit[i] = 0;
maxConsec[i] = 0;
avgOmit[i] = 0;
}
for (var i = 1; i <= maxNum; i++) {
var omit = 0,
mo = 0,
consec = 0,
mc = 0,
totalOmit = 0,
omitCount = 0;
for (var r = 0; r < data.length; r++) {
var nums = getNums(data[r]);
if (nums.indexOf(i) >= 0) {
count[i]++;
if (omit > mo) mo = omit;
totalOmit += omit;
omitCount++;
omit = 0;
consec++;
if (consec > mc) mc = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > mo) mo = omit;
currOmit[i] = omit;
maxOmit[i] = mo;
maxConsec[i] = mc;
avgOmit[i] =
omitCount > 0 ? (totalOmit / omitCount).toFixed(1) : data.length;
}
return {
count: count,
maxOmit: maxOmit,
currOmit: currOmit,
maxConsec: maxConsec,
avgOmit: avgOmit,
};
}
function buildStats(data) {
var fs = calcStats(data, FRONT_MAX, function (d) {
return parseCode(d.openCode).fronts;
});
var bs = calcStats(data, BACK_MAX, function (d) {
return parseCode(d.openCode).backs;
});
var stats = "";
var labels = [
"出现次数",
"最大遗漏",
"平均遗漏",
"最大连出",
"当前遗漏",
];
var keys = ["count", "maxOmit", "avgOmit", "maxConsec", "currOmit"];
for (var li = 0; li < labels.length; li++) {
var cls = li % 2 === 0 ? "stat-row" : "stat-row alt";
stats +=
'<tr class="' +
cls +
'"><td colspan="2" class="stat-label">' +
labels[li] +
"</td>";
for (var i = 1; i <= FRONT_MAX; i++)
stats += "<td>" + fs[keys[li]][i] + "</td>";
for (var i = 1; i <= BACK_MAX; i++)
stats += "<td>" + bs[keys[li]][i] + "</td>";
stats += "</tr>";
}
return stats;
}
function loadData() {
ApiClient.post(
"/api/web/lotteryResult/getList",
{ suoxie: "cjdlt" },
function (res) {
if (res && res.data) {
allData = res.data;
renderData(currentCount);
}
},
);
}
$(document).on("click", ".qishu-but", function () {
$(".qishu-but").removeClass("active");
$(this).addClass("active");
currentCount = parseInt($(this).data("count"));
renderData(currentCount);
});
buildHeader();
loadData();
})();
</script>
</body>
</html>
+1076
View File
File diff suppressed because it is too large Load Diff
+622
View File
@@ -0,0 +1,622 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 发布免费文章</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/fabumianfeiwenzhang.css" />
</head>
<body>
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems"></div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged publish-hidden">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<main class="page-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a>
<span class="sep">&gt;</span>
<a href="usercenter.html">个人中心</a>
<span class="sep">&gt;</span>
<span>发布免费文章</span>
</div>
<div class="publish-card">
<div class="publish-card-header">
<h2 class="publish-card-header-h2">发布文章</h2>
<p>发布您的预测分析,与彩民分享您的见解</p>
</div>
<div class="publish-card-body">
<div class="publish-tab-bar">
<a href="fabumianfeiwenzhang.html" class="publish-tab-item active"
>发布免费文章</a
>
<a href="fufei.html" class="publish-tab-item" id="paidPublishTab"
>发布付费文章</a
>
</div>
<div class="publish-tip-box">
<div class="publish-tip-item">
提示:实习期为三天,连续每天发布2篇以上的优质免费文章审核通过后,自动升级为正式专家。
</div>
<div class="publish-tip-item">
免费文章标准:标题20字以上(含期号、彩种、专家名称、标题),正文500字以上,需原创。
</div>
</div>
<form id="publishForm">
<div class="form-row">
<label>彩种分类 <span class="required">*</span></label>
<select
class="form-select form-select--full is-empty"
id="lotterySelect"
>
<option value="">请选择彩种</option>
</select>
<input type="hidden" id="menuId" value="" />
</div>
<div class="form-row">
<label>文章标题 <span class="required">*</span></label>
<div class="form-input-with-count">
<input
type="text"
class="form-input"
id="articleTitle"
placeholder="请输入文章标题(含期号、彩种、标题)"
maxlength="100"
oninput="updateTitleCount()"
/>
<span class="form-input-count"
><span id="titleCount">0</span>/100</span
>
</div>
</div>
<div class="form-row">
<label>文章内容 <span class="required">*</span></label>
<textarea
placeholder="请输入内容"
id="input-content1"
class="meui-input publish-editor-textarea"
name="nr"
></textarea>
</div>
<div class="submit-row">
<button
type="button"
class="submit-btn"
id="submitBtn"
onclick="submitArticle()"
>
发布文章
</button>
</div>
</form>
</div>
</div>
</main>
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
class="publish-footer-link"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script src="../public/js/lay-config.js" charset="utf-8"></script>
<script charset="utf-8" src="../public/js/ke/kindeditor.min.js"></script>
<script>
var menuList = [];
var layer;
var editor;
var pageReady = false;
function autoFormatEditorContent() {
if (!editor) return;
try {
if (typeof editor.clickToolbar === "function") {
editor.clickToolbar("quickformat");
} else if (typeof editor.exec === "function") {
editor.exec("quickformat");
}
if (typeof editor.sync === "function") editor.sync();
} catch (e) {
console.warn("auto quickformat failed", e);
}
}
KindEditor.ready(function (K) {
editor = K.create('textarea[id="input-content1"]', {
width: "100%",
height: "400px",
afterBlur: function () {
autoFormatEditorContent();
},
items: [
"source",
"justifyleft",
"justifycenter",
"justifyright",
"justifyfull",
"fontsize",
"forecolor",
"hilitecolor",
"bold",
"italic",
"underline",
"strikethrough",
"table",
"clearhtml",
"quickformat",
"selectall",
"link",
],
});
});
layui.use(["layer", "jquery"], function () {
layer = layui.layer;
var $ = layui.$;
CommonUtil.loadPageConfig({ ads: false, links: false });
renderLoginState();
renderNav();
CommonUtil.requireAuth(function () {
loadPublishIdentity().then(function (allowed) {
if (!allowed) {
layer.alert(
"您还不是实习或正式专家,暂不能发布文章。",
{ title: "暂无权限" },
function () {
window.location.href = "apply.html";
},
);
return;
}
pageReady = true;
loadMenuList();
});
});
$(window).on("scroll", function () {
$("#backTop").toggleClass("is-visible", $(window).scrollTop() > 320);
});
$(document).on("change", "#lotterySelect", function () {
var value = $(this).val();
$("#menuId").val(value);
$(this).toggleClass("is-empty", !value);
});
$(document).on("click", "#paidPublishTab", function (event) {
event.preventDefault();
var targetHref = this.href || "fufei.html";
checkPaidPublishPermission().then(function (result) {
if (!result.canPaid) {
layer.alert(result.reason || "暂无付费文章发布权限。", {
title: "暂无权限",
});
return;
}
window.location.href = targetHref;
});
});
});
function safeText(value) {
return CommonUtil.escapeHtml
? CommonUtil.escapeHtml(value)
: String(value || "");
}
function isApiSuccess(res) {
return res && (res.code === 0 || res.code === 200);
}
function isTruthy(value) {
return (
value === true ||
value === 1 ||
value === "1" ||
String(value).toLowerCase() === "true"
);
}
function padDatePart(value) {
return String(value).padStart(2, "0");
}
function formatLocalDate(date) {
return (
date.getFullYear() +
"-" +
padDatePart(date.getMonth() + 1) +
"-" +
padDatePart(date.getDate())
);
}
function getArticleDateText(value) {
if (!value) return "";
if (typeof value === "number") {
var numericDate = new Date(value);
return isNaN(numericDate.getTime())
? ""
: formatLocalDate(numericDate);
}
var text = String(value).trim();
var match = text.match(/^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/);
if (match) {
return (
match[1] + "-" + padDatePart(match[2]) + "-" + padDatePart(match[3])
);
}
var parsedDate = new Date(text.replace(/-/g, "/"));
return isNaN(parsedDate.getTime()) ? "" : formatLocalDate(parsedDate);
}
function isPublishedFreeArticle(item) {
if (!item) return false;
var statusText = String(
item.statusText ||
item.auditStatusText ||
item.publishStatusText ||
item.stateText ||
"",
).toLowerCase();
if (/已发布|审核通过|通过|published|approved|pass/.test(statusText)) {
return true;
}
var status =
item.status != null
? item.status
: item.auditStatus != null
? item.auditStatus
: item.publishStatus;
return status === true || status === 1 || status === "1";
}
function getTodayPublishedFreeCount(rows) {
var today = formatLocalDate(new Date());
return ApiClient.asArray(rows).filter(function (item) {
var articleDate = getArticleDateText(
item.createTime ||
item.create_time ||
item.publishTime ||
item.publish_time ||
item.auditTime ||
item.audit_time ||
item.updateTime ||
item.update_time ||
item.createdAt,
);
return articleDate === today && isPublishedFreeArticle(item);
}).length;
}
function loadTodayPublishedFreeCount() {
return ApiClient.get(ApiClient.API.mineFreeArticleList, {
pageNum: 1,
pageSize: 100,
})
.then(function (res) {
if (!ApiClient.isSuccess(res)) return 0;
var data = res.data || res;
var rows = ApiClient.asArray(
data.rows || data.records || data.list || data.data || data,
);
return getTodayPublishedFreeCount(rows);
})
.catch(function () {
return 0;
});
}
function checkPaidPublishPermission() {
return ApiClient.get(ApiClient.API.mineSummary)
.then(function (res) {
if (!ApiClient.isSuccess(res) || !res.data) {
return { canPaid: false, reason: "暂时无法获取发布权限。" };
}
var status = String(
res.data.expertStatus || res.data.expert_status || "",
).toLowerCase();
var hasExpertIdentity =
isTruthy(res.data.is_expert) ||
isTruthy(res.data.isExpert) ||
isTruthy(res.data.expert) ||
isTruthy(res.data.is_regular_expert) ||
isTruthy(res.data.isRegularExpert) ||
isTruthy(res.data.regularExpert) ||
isTruthy(res.data.internExpert) ||
isTruthy(res.data.isInternExpert) ||
/实习|intern|trial|正式|regular|formal/.test(status);
if (!hasExpertIdentity) {
return { canPaid: false, reason: "专家才能发布付费文章。" };
}
return loadTodayPublishedFreeCount().then(function (count) {
return {
canPaid: count >= 2,
reason:
count >= 2
? ""
: "今天需先发布2篇审核通过的免费文章后,才能发布付费文章(当前" +
count +
"篇)。",
};
});
})
.catch(function () {
return { canPaid: false, reason: "暂时无法获取发布权限。" };
});
}
function isExpertUser(data) {
if (!data) return false;
var levelText = CommonUtil.getUserLevelText
? CommonUtil.getUserLevelText(data)
: "";
return (
data.expert === true ||
data.regularExpert === true ||
data.internExpert === true ||
data.isExpert === true ||
data.isExpert === 1 ||
data.isExpert === "1" ||
/专家/.test(levelText || "") ||
/regular|intern|formal|trial|expert|实习|正式/.test(
String(data.expertStatus || data.expert_status || ""),
)
);
}
function renderLoginState() {
var $ = layui.$;
var token = CommonUtil.getAuthToken();
if (!token) {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
return;
}
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(function (data) {
if (!data) return;
$("#userName").text(data.nickName || data.username || "用户");
$("#userLevel").text(
CommonUtil.getUserLevelText
? CommonUtil.getUserLevelText(data)
: "会员",
);
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
});
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (!isApiSuccess(res)) return;
var list = ApiClient.asArray(ApiClient.pickData(res, []));
var $nav = layui.$("#navLotteryItems");
$nav.empty();
list.forEach(function (item) {
var code = item.suoxie || item.code || item.id || "";
var name = item.name || item.menuName || item.lotteryName || "";
if (!code || !name) return;
$nav.append(
'<a href="cai.html?id=' +
encodeURIComponent(code) +
'" class="topbar__nav-link">' +
safeText(name) +
"</a>",
);
});
});
}
function loadPublishIdentity() {
return new Promise(function (resolve) {
CommonUtil.getCurrentUserDetail(
function (data) {
resolve(isExpertUser(data));
},
function () {
resolve(false);
},
);
});
}
function isPublishLotteryMenu(item) {
if (!item) return false;
var name = String(
item.name || item.menuName || item.title || "",
).trim();
var code = String(item.suoxie || item.code || "").trim();
if (["首页", "专家推荐"].indexOf(name) > -1) return false;
if (/^(home|index|zhuanjia|expert|expertRecommend)$/i.test(code))
return false;
return !!(item.id || item.menuId) && !!name;
}
function loadMenuList() {
ApiClient.post("/api/web/menu/getFormatList", {})
.then(function (res) {
if (!isApiSuccess(res)) throw new Error(res.msg || "获取彩种失败");
menuList = ApiClient.asArray(ApiClient.pickData(res, [])).filter(
isPublishLotteryMenu,
);
var $sel = layui.$("#lotterySelect");
$sel.find("option:not(:first)").remove();
menuList.forEach(function (item) {
var id = item.id || item.menuId || "";
var name = item.lotteryName || item.menuName || item.name || "";
if (!id || !name) return;
$sel.append(
'<option value="' +
safeText(id) +
'">' +
safeText(name) +
"</option>",
);
});
if (!menuList.length) {
$sel.append('<option value="" disabled>暂无可发布彩种</option>');
}
$sel.toggleClass("is-empty", !$sel.val());
})
.catch(function (err) {
layer.msg((err && err.message) || "获取彩种失败", { icon: 2 });
});
}
window.updateTitleCount = function () {
var titleInput = document.getElementById("articleTitle");
document.getElementById("titleCount").textContent = titleInput
? titleInput.value.length
: 0;
};
function getEditorHtml() {
autoFormatEditorContent();
if (editor && typeof editor.sync === "function") editor.sync();
return editor && typeof editor.html === "function"
? editor.html()
: layui.$("#input-content1").val();
}
function getEditorText() {
if (editor && typeof editor.text === "function")
return editor.text().trim();
return layui.$("<div>").html(getEditorHtml()).text().trim();
}
window.submitArticle = function () {
if (!pageReady) {
layer.msg("页面尚未准备完成", { icon: 0 });
return;
}
var $ = layui.$;
var menuId = $("#menuId").val();
var title = $("#articleTitle").val().trim();
var content = getEditorHtml();
var text = getEditorText();
if (!menuId) {
layer.msg("请选择彩种", { icon: 2 });
return;
}
if (title.length < 20) {
layer.msg("标题不能少于20字", { icon: 2 });
return;
}
if (title.length > 100) {
layer.msg("标题不能超过100字", { icon: 2 });
return;
}
if (!content || text.length < 500) {
layer.msg("正文内容不能少于500字", { icon: 2 });
return;
}
var $btn = $("#submitBtn");
$btn.text("发布中...").prop("disabled", true);
ApiClient.post(ApiClient.API.freeArticlePublish, {
menuId: menuId,
title: title,
content: content,
})
.then(function (res) {
if (isApiSuccess(res)) {
layer.msg(
"发布成功,请等待审核",
{ icon: 1, time: 1600 },
function () {
window.location.href = "usercenter.html";
},
);
} else {
layer.msg(res.msg || "发布失败", { icon: 2 });
$btn.text("发布文章").prop("disabled", false);
}
})
.catch(function () {
layer.msg("网络错误,请稍后重试", { icon: 2 });
$btn.text("发布文章").prop("disabled", false);
});
};
</script>
</body>
</html>
+421
View File
@@ -0,0 +1,421 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>彩先知 - 福彩3D走势图</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/trend.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<div class="breadcrumb-bar">
<a href="javascript:history.back();"
><i class="layui-icon layui-icon-left"></i> 返回</a
>
<span>/</span>
<span>福彩3D走势图</span>
</div>
<div class="page-title-bar">
<div class="page-title">福彩3D走势图</div>
<div class="qishu">
<div class="qishu-but active" data-count="30">30期</div>
<div class="qishu-but" data-count="60">60期</div>
<div class="qishu-but" data-count="100">100期</div>
</div>
</div>
<div class="table-wrapper">
<table class="trend-table" border="1" id="trend-table">
<thead>
<tr>
<td rowspan="2" class="col-issue">期号</td>
<td rowspan="2" class="col-code">开奖号</td>
<td colspan="3" rowspan="2" class="zone-type">类型</td>
<td colspan="10" class="zone-hundred">百位</td>
<td colspan="10" class="zone-ten">十位</td>
<td colspan="10" class="zone-unit">个位</td>
</tr>
<tr id="header-row"></tr>
</thead>
<tbody id="data-body"></tbody>
</table>
</div>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
CommonUtil.loadPageConfig();
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
checkLogin();
renderNav();
(function () {
var currentCount = 30;
var allData = [];
function buildHeader() {
var html = "";
for (var g = 0; g < 3; g++) {
for (var i = 0; i <= 9; i++) html += "<td>" + i + "</td>";
}
$("#header-row").html(html);
}
function parseCode(code) {
var nums = code.split(",").map(function (n) {
return parseInt(n);
});
return {
hundred: nums[0] || 0,
ten: nums[1] || 0,
unit: nums[2] || 0,
nums: nums,
};
}
function getType(h, t, u) {
if (h === t && t === u) return "baozi";
if (h === t || t === u || h === u) return "zu3";
return "zu6";
}
function renderData(count) {
var data = allData.slice(0, count);
var html = "";
for (var r = 0; r < data.length; r++) {
var item = data[r];
var parsed = parseCode(item.openCode);
var type = getType(parsed.hundred, parsed.ten, parsed.unit);
html += "<tr>";
html += '<td class="col-issue">' + item.issue + "</td>";
html += '<td class="col-code">' + item.openCode + "</td>";
// Type columns
var typeNames = ["baozi", "zu3", "zu6"];
var typeLabels = ["豹子", "组三", "组六"];
for (var ti = 0; ti < 3; ti++) {
if (type === typeNames[ti]) {
html +=
'<td><span class="type-' +
typeNames[ti] +
'">' +
typeLabels[ti] +
"</span></td>";
} else {
html +=
'<td><span class="type-empty">' +
typeLabels[ti] +
"</span></td>";
}
}
// Hundred position
for (var i = 0; i <= 9; i++) {
if (parsed.hundred === i) {
html += '<td><span class="hit-hundred">' + i + "</span></td>";
} else {
html += '<td><span class="miss-num">' + i + "</span></td>";
}
}
// Ten position
for (var i = 0; i <= 9; i++) {
if (parsed.ten === i) {
html += '<td><span class="hit-ten">' + i + "</span></td>";
} else {
html += '<td><span class="miss-num">' + i + "</span></td>";
}
}
// Unit position
for (var i = 0; i <= 9; i++) {
if (parsed.unit === i) {
html += '<td><span class="hit-unit">' + i + "</span></td>";
} else {
html += '<td><span class="miss-num">' + i + "</span></td>";
}
}
html += "</tr>";
}
html += buildStats(data);
$("#data-body").html(html);
}
function calcPosStats(data, getVal) {
var count = {},
maxOmit = {},
currOmit = {},
maxConsec = {},
avgOmit = {};
for (var i = 0; i <= 9; i++) {
count[i] = 0;
maxOmit[i] = 0;
currOmit[i] = 0;
maxConsec[i] = 0;
avgOmit[i] = 0;
}
for (var i = 0; i <= 9; i++) {
var omit = 0,
mo = 0,
consec = 0,
mc = 0,
totalOmit = 0,
oc = 0;
for (var r = 0; r < data.length; r++) {
if (getVal(data[r]) === i) {
count[i]++;
if (omit > mo) mo = omit;
totalOmit += omit;
oc++;
omit = 0;
consec++;
if (consec > mc) mc = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > mo) mo = omit;
currOmit[i] = omit;
maxOmit[i] = mo;
maxConsec[i] = mc;
avgOmit[i] = oc > 0 ? (totalOmit / oc).toFixed(1) : data.length;
}
return {
count: count,
maxOmit: maxOmit,
currOmit: currOmit,
maxConsec: maxConsec,
avgOmit: avgOmit,
};
}
function calcTypeStats(data, typeName) {
var count = 0,
maxOmit = 0,
currOmit = 0,
maxConsec = 0,
avgOmit = 0;
var omit = 0,
mo = 0,
consec = 0,
mc = 0,
totalOmit = 0,
oc = 0;
for (var r = 0; r < data.length; r++) {
var p = parseCode(data[r].openCode);
var t = getType(p.hundred, p.ten, p.unit);
if (t === typeName) {
count++;
if (omit > mo) mo = omit;
totalOmit += omit;
oc++;
omit = 0;
consec++;
if (consec > mc) mc = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > mo) mo = omit;
return {
count: count,
maxOmit: mo,
currOmit: omit,
maxConsec: mc,
avgOmit: oc > 0 ? (totalOmit / oc).toFixed(1) : data.length,
};
}
function buildStats(data) {
var hs = calcPosStats(data, function (d) {
return parseCode(d.openCode).hundred;
});
var ts = calcPosStats(data, function (d) {
return parseCode(d.openCode).ten;
});
var us = calcPosStats(data, function (d) {
return parseCode(d.openCode).unit;
});
var bz = calcTypeStats(data, "baozi");
var z3 = calcTypeStats(data, "zu3");
var z6 = calcTypeStats(data, "zu6");
var labels = [
"出现次数",
"最大遗漏",
"平均遗漏",
"最大连出",
"当前遗漏",
];
var keys = ["count", "maxOmit", "avgOmit", "maxConsec", "currOmit"];
var typeStats = [bz, z3, z6];
var stats = "";
for (var li = 0; li < labels.length; li++) {
var cls = li % 2 === 0 ? "stat-row" : "stat-row alt";
stats +=
'<tr class="' +
cls +
'"><td colspan="2" class="stat-label">' +
labels[li] +
"</td>";
// Type columns
for (var ti = 0; ti < 3; ti++)
stats += "<td>" + typeStats[ti][keys[li]] + "</td>";
// Position columns
for (var i = 0; i <= 9; i++)
stats += "<td>" + hs[keys[li]][i] + "</td>";
for (var i = 0; i <= 9; i++)
stats += "<td>" + ts[keys[li]][i] + "</td>";
for (var i = 0; i <= 9; i++)
stats += "<td>" + us[keys[li]][i] + "</td>";
stats += "</tr>";
}
return stats;
}
function loadData() {
ApiClient.post(
"/api/web/lotteryResult/getList",
{ suoxie: "fc3d" },
function (res) {
if (res && res.data) {
allData = res.data;
renderData(currentCount);
}
},
);
}
$(document).on("click", ".qishu-but", function () {
$(".qishu-but").removeClass("active");
$(this).addClass("active");
currentCount = parseInt($(this).data("count"));
renderData(currentCount);
});
buildHeader();
loadData();
})();
</script>
</body>
</html>
+410
View File
@@ -0,0 +1,410 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 分销中心</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/distribution.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="page-main">
<!-- User Info Card -->
<div class="user-card" id="userCard">
<div class="user-info-row">
<div class="user-avatar-placeholder" id="userAvatar">
<i class="layui-icon layui-icon-user"></i>
</div>
<div>
<div class="user-name">
<span id="userNickName">加载中...</span>
<span class="user-role-tag role-none" id="roleTag">未申请</span>
</div>
<div class="user-phone" id="userPhone">--</div>
</div>
</div>
<div class="balance-grid">
<div class="balance-item">
<div class="balance-label">可用余额</div>
<div class="balance-value">
<span id="balanceAvailable">0.00</span
><span class="unit"></span>
</div>
</div>
<div class="balance-item">
<div class="balance-label">累计收益</div>
<div class="balance-value">
<span id="balanceIncome">0.00</span><span class="unit"></span>
</div>
</div>
<div class="balance-item">
<div class="balance-label">冻结金额</div>
<div class="balance-value">
<span id="balanceFrozen">0.00</span><span class="unit"></span>
</div>
</div>
<div class="balance-item">
<div class="balance-label">累计提现</div>
<div class="balance-value">
<span id="balanceWithdraw">0.00</span><span class="unit"></span>
</div>
</div>
</div>
</div>
<!-- Action Section -->
<div class="action-section">
<div class="action-card">
<div class="action-card-title">
<i class="layui-icon layui-icon-group"></i> 申请分销
</div>
<div class="action-card-desc">
成为分销商,推广赚佣金。邀请好友注册即可获得分销收益,多级返佣,轻松赚钱。
</div>
<button
class="action-card-btn btn-apply-dist"
id="btnApplyDist"
onclick="applyDistribution()"
>
申请分销
</button>
</div>
<div class="action-card">
<div class="action-card-title">
<i class="layui-icon layui-icon-star"></i> 申请专家
</div>
<div class="action-card-desc">
成为平台专家,发布预测文章获取收益。展示你的分析能力,让更多彩民看到你的精彩预测。
</div>
<button
class="action-card-btn btn-apply-expert"
id="btnApplyExpert"
onclick="applyExpert()"
>
申请专家
</button>
</div>
</div>
<!-- Quick Links -->
<div class="links-section">
<div class="links-section-title">常用功能</div>
<div class="links-grid">
<a href="wodefenxiao.html" class="link-item">
<div class="link-item-icon icon-fenxiao">
<i class="layui-icon layui-icon-group"></i>
</div>
<div>
<div class="link-item-text">我的分销</div>
<div class="link-item-desc">查看下级用户和分销数据</div>
</div>
</a>
<a href="tuiguang.html" class="link-item">
<div class="link-item-icon icon-tuiguang">
<i class="layui-icon layui-icon-share"></i>
</div>
<div>
<div class="link-item-text">推广赚钱</div>
<div class="link-item-desc">生成推广链接和二维码</div>
</div>
</a>
<a href="tixian.html" class="link-item">
<div class="link-item-icon icon-tixian">
<i class="layui-icon layui-icon-rmb"></i>
</div>
<div>
<div class="link-item-text">我要提现</div>
<div class="link-item-desc">申请提现到账户</div>
</div>
</a>
</div>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
layui.use(["layer", "jquery"], function () {
var layer = layui.layer;
var $ = layui.$;
// Load page config
CommonUtil.loadPageConfig({ ads: false, links: false });
// Check login state for topbar
checkLogin();
renderNav();
// Check auth and load user detail
CommonUtil.requireAuth(function () {
loadUserDetail();
});
function checkLogin() {
var token = CommonUtil.getToken();
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
if (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) {
$("#userAvatar").attr("src", data.avatar);
}
}
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(
function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
},
);
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
function loadUserDetail() {
CommonUtil.getCurrentUserDetail(
function (data) {
if (!data) return;
// Display user info
$("#userNickName").text(
data.nickName || data.phoneNumber || "用户",
);
$("#userPhone").text(
data.phoneNumber
? data.phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2")
: "--",
);
// Avatar
if (data.avatar) {
$("#userAvatar").replaceWith(
'<img class="user-avatar" src="' +
data.avatar +
'" alt="头像" />',
);
}
// Balances
$("#balanceAvailable").text(
(data.availableBalance || 0).toFixed(2),
);
$("#balanceIncome").text((data.totalIncome || 0).toFixed(2));
$("#balanceFrozen").text((data.frozenBalance || 0).toFixed(2));
$("#balanceWithdraw").text((data.totalWithdraw || 0).toFixed(2));
// Role tags
var $roleTag = $("#roleTag");
if (data.isDistribution === 1 || data.isDistribution === true) {
$roleTag
.text("分销商")
.removeClass("role-none role-expert")
.addClass("role-distribution");
$("#btnApplyDist").text("已开通").addClass("btn-disabled");
}
if (data.isExpert === 1 || data.isExpert === true) {
$roleTag
.text("专家")
.removeClass("role-none role-distribution")
.addClass("role-expert");
$("#btnApplyExpert").text("已开通").addClass("btn-disabled");
}
if (
(data.isDistribution === 1 || data.isDistribution === true) &&
(data.isExpert === 1 || data.isExpert === true)
) {
$roleTag.text("分销商 / 专家");
}
},
function (err) {
layer.msg("获取用户信息失败", { icon: 2 });
},
);
}
// Apply for distribution
window.applyDistribution = function () {
CommonUtil.requireAuth(function () {
layer.confirm(
"确定要申请成为分销商吗?",
{
title: "申请分销",
btn: ["确定申请", "取消"],
btnAlign: "c",
},
function (index) {
layer.close(index);
ApiClient.post(
"/api/web/user/applyDistribution",
{},
{
headers: CommonUtil.authHeaders(),
},
)
.then(function (res) {
if (res.code === 0) {
layer.msg(
"申请成功!",
{ icon: 1, time: 2000 },
function () {
loadUserDetail();
},
);
} else {
layer.msg(res.msg || "申请失败", { icon: 2 });
}
})
.catch(function (err) {
layer.msg("网络错误,请稍后重试", { icon: 2 });
});
},
);
});
};
// Apply for expert
window.applyExpert = function () {
CommonUtil.requireAuth(function () {
layer.confirm(
"确定要申请成为专家吗?",
{
title: "申请专家",
btn: ["确定申请", "取消"],
btnAlign: "c",
},
function (index) {
layer.close(index);
ApiClient.post(
"/api/web/user/applyExpert",
{},
{
headers: CommonUtil.authHeaders(),
},
)
.then(function (res) {
if (res.code === 0) {
layer.msg(
"申请成功!",
{ icon: 1, time: 2000 },
function () {
loadUserDetail();
},
);
} else {
layer.msg(res.msg || "申请失败", { icon: 2 });
}
})
.catch(function (err) {
layer.msg("网络错误,请稍后重试", { icon: 2 });
});
},
);
});
};
});
</script>
</body>
</html>
+278
View File
@@ -0,0 +1,278 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 发布付费文章</title>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/fufei.css" />
</head>
<body>
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems"></div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged pay-hidden">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<main class="page-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a>
<span class="sep">&gt;</span>
<a href="usercenter.html">个人中心</a>
<span class="sep">&gt;</span>
<span>发布付费文章</span>
</div>
<section class="pay-publish-card">
<header class="pay-publish-header">
<h1>发布文章</h1>
<p>发布您的付费预测方案,与彩民分享您的专业判断</p>
</header>
<div class="pay-publish-body">
<div class="publish-tab-bar">
<a href="fabumianfeiwenzhang.html" class="publish-tab-item"
>发布免费文章</a
>
<a href="fufei.html" class="publish-tab-item active"
>发布付费文章</a
>
</div>
<div class="pay-form">
<div class="pay-form-group mianfeiThreeSelect">
<label class="pay-form-label"
>彩种方案 <span class="required">*</span></label
>
<input type="hidden" id="a1" value="" name="topid" />
<input type="hidden" id="a2" value="" name="seid" />
<input type="hidden" id="a3" value="" name="thid" />
<input type="hidden" id="qi" value="" name="qi" />
<input type="hidden" id="vv" value="" name="vv" />
<input type="hidden" id="payMenuId" value="" />
<input type="hidden" id="payCode" value="" />
<input type="hidden" id="gmjl" value="" />
</div>
<div class="mianfeiThreeText">
<div class="pay-form-group">
<label class="pay-form-label">方案标题 <span class="required">*</span></label>
<div class="pay-title-input">
<input class="pay-form-input" id="payTitle" maxlength="100" placeholder="请输入付费文章标题" />
<span id="payTitleCount">0/100</span>
</div>
</div>
<div class="pay-pool-section">
<div class="pay-pool-title">选号区域</div>
<div class="number-pool" id="numberPool"></div>
<div class="pay-selected-row mianfeiThreeTextSelectBox">
<span class="pay-selected-label">已选:</span>
<span id="jieguo">暂无号码</span>
<button id="resetBtn" type="button" class="pay-reset-btn">
重置
</button>
</div>
<div class="pay-issue-row mianfeiThreeTextISSUEBox">
<span>当前期号:</span>
<strong id="qihao">请选择方案</strong>
</div>
<div id="finalResult" class="pay-hidden"></div>
</div>
<div class="pay-info-grid">
<div class="pay-form-group">
<label class="pay-form-label">预测期号</label>
<input
class="pay-form-input"
id="expectInput"
placeholder="选择方案后自动获取"
readonly
/>
<div class="pay-form-help" id="expectText">
选择方案后自动获取
</div>
</div>
<div class="pay-form-group">
<label class="pay-form-label">定金</label>
<div class="pay-price-input">
<input
class="pay-form-input"
type="text"
id="priceInput"
readonly
/>
<span>积分</span>
</div>
<div class="pay-form-help">
当前方案定金:<em id="priceText">0</em> 积分
</div>
</div>
</div>
<div class="pay-form-group">
<label class="pay-form-label">价格说明</label>
<div class="pay-price-info" id="payPriceInfo">请选择方案后加载价格说明</div>
</div>
</div>
<div class="mianfeiThreeButBox">
<button
class="pay-submit-btn mianfeiThreeBut"
type="button"
onclick="chk()"
>
发布方案
</button>
</div>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
class="fufei-footer-link"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script src="../public/css/layui/layui.js"></script>
<script src="../public/js/lay-config.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<script src="../public/js/jquery.js"></script>
<script src="../public/js/fufei-3dyjs-publish.js"></script>
<script>
function isApiSuccess(res) {
return (
res && (res.code === 0 || res.code === 200 || res.success === true)
);
}
function safeText(value) {
return CommonUtil.escapeHtml
? CommonUtil.escapeHtml(value)
: String(value || "");
}
function logout() {
localStorage.removeItem("token");
window.location.href = "login.html";
}
function renderLoginState() {
var $ = layui.$;
var token = CommonUtil.getAuthToken
? CommonUtil.getAuthToken()
: localStorage.getItem("token");
if (!token) {
$("#notLoggedInBox").show();
$("#loggedInBox").addClass("pay-hidden").hide();
return;
}
$("#notLoggedInBox").hide();
$("#loggedInBox").removeClass("pay-hidden").css("display", "flex");
CommonUtil.getCurrentUserDetail(function (data) {
if (!data) return;
$("#userName").text(data.nickName || data.username || "用户");
$("#userLevel").text(
CommonUtil.getUserLevelText
? CommonUtil.getUserLevelText(data)
: "会员",
);
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
});
}
function renderTopNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (!isApiSuccess(res)) return;
var list = ApiClient.asArray(ApiClient.pickData(res, []));
var $nav = layui.$("#navLotteryItems");
$nav.empty();
list.forEach(function (item) {
var code = item.suoxie || item.code || item.id || "";
var name = item.name || item.menuName || item.lotteryName || "";
if (!code || !name) return;
$nav.append(
'<a href="cai.html?id=' +
encodeURIComponent(code) +
'" class="topbar__nav-link">' +
safeText(name) +
"</a>",
);
});
});
}
layui.use(["jquery", "layer"], function () {
var $ = layui.$;
CommonUtil.requireAuth(function () {
renderLoginState();
renderTopNav();
});
$(window).on("scroll", function () {
$("#backTop").toggleClass("is-visible", $(window).scrollTop() > 320);
});
});
</script>
</body>
</html>
+207
View File
@@ -0,0 +1,207 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 公告详情</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/gonggao.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main -->
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<a href="gonggaolist.html">网站公告</a><span class="sep">></span>
<span>公告详情</span>
</div>
<div class="cxz-notice-card">
<div class="cxz-notice-header">
<h1 class="cxz-notice-title" id="noticeTitle">加载中...</h1>
<div class="cxz-notice-meta" id="noticeTime">--</div>
</div>
<div class="cxz-notice-body" id="noticeContent">
<p style="text-align: center; color: var(--text-muted)">加载中...</p>
</div>
</div>
<div class="cxz-back-bar">
<a href="gonggaolist.html" class="cxz-back-btn">返回公告列表</a>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
const urlParams = new URLSearchParams(window.location.search);
const noticeId = urlParams.get("id") || "";
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
const token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
function loadNotice() {
ApiClient.post("/api/web/notice/getDetail?id=" + noticeId).then(
function (res) {
if (res.code === 0 && res.data) {
renderNotice(res.data);
} else {
layui.layer.msg("公告不存在", { icon: 2 });
$("#noticeTitle").text("公告不存在");
$("#noticeContent").html(
'<p style="text-align:center;color:var(--text-muted);">该公告不存在或已删除</p>',
);
}
},
);
}
function renderNotice(data) {
$("#noticeTitle").text(data.noticeTitle || "无标题");
if (data.createTime) {
$("#noticeTime").text(
"发布时间:" + DateUtil.formatDateToYearMonthDay(data.createTime),
);
}
$("#noticeContent").html(data.noticeContent || "");
document.title = "彩先知 - " + (data.noticeTitle || "公告详情");
}
$(function () {
checkLogin();
if (noticeId) {
loadNotice();
} else {
layui.layer.msg("公告ID不存在", { icon: 2 });
$("#noticeTitle").text("参数错误");
$("#noticeContent").html(
'<p style="text-align:center;color:var(--text-muted);">缺少公告ID参数</p>',
);
}
CommonUtil.loadPageConfig();
renderNav();
});
</script>
</body>
</html>
+230
View File
@@ -0,0 +1,230 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 网站公告</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/gonggao.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main -->
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<span>网站公告</span>
</div>
<div class="cxz-section-header">
<div class="cxz-section-title">网站公告</div>
</div>
<div class="cxz-notice-list" id="noticeList">
<div class="cxz-notice-item">
<span style="color: var(--text-muted)">加载中...</span>
</div>
</div>
<div class="cxz-pagination" id="pagination"></div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
let currentPage = 1;
const pageSize = 20;
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
const token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
// Load notice list
function loadNotices() {
ApiClient.post("/api/web/notice/getPageList", {
pageNum: currentPage,
pageSize: pageSize,
}).then(function (res) {
if (res.code === 0 && res.data) {
renderNotices(res.data.records || []);
renderPagination(res.data.totalRow || 0);
} else {
$("#noticeList").html(
'<div class="cxz-notice-item"><span style="color:var(--text-muted);">暂无公告</span></div>',
);
}
});
}
function renderNotices(list) {
const $container = $("#noticeList");
$container.empty();
if (!list || list.length === 0) {
$container.html(
'<div class="cxz-notice-item"><span style="color:var(--text-muted);">暂无公告</span></div>',
);
return;
}
$.each(list, function (i, item) {
let displayDate = "";
if (item.createTime) displayDate = item.createTime.substring(0, 10);
else if (item.date) displayDate = item.date.substring(0, 10);
$container.append(
'<div class="cxz-notice-item">' +
'<a href="gonggao.html?id=' +
(item.id || "") +
'">' +
'<span class="cxz-notice-dot"></span>' +
(item.noticeTitle || "无标题") +
"</a>" +
'<span class="cxz-notice-date">' +
displayDate +
"</span>" +
"</div>",
);
});
}
function renderPagination(total) {
layui.laypage.render({
elem: "pagination",
count: total,
limit: pageSize,
curr: currentPage,
jump: function (obj, first) {
if (!first) {
currentPage = obj.curr;
loadNotices();
}
},
});
}
$(function () {
checkLogin();
loadNotices();
CommonUtil.loadPageConfig();
renderNav();
});
</script>
</body>
</html>
+929
View File
@@ -0,0 +1,929 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 多文章彩票预测首页</title>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/index.css" />
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<script src="../public/js/qrcode.js"></script>
</head>
<body>
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="./" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link is-active">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt="用户头像"
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<main class="page-shell">
<section class="hero">
<div class="hero__inner">
<div class="hero__main">
<!-- <div class="panel-head panel-head--hero">
<div>
<h1>彩先知 PC 预测首页</h1>
</div>
<button
class="hero__history-btn"
type="button"
onclick="openHistoryModal()"
>
<img src="../images/home-icon-history.png" alt="" />
<span>历史查询</span>
</button>
</div> -->
<div class="hero__carousel-wrap">
<div class="layui-carousel" id="bannerCarousel">
<div carousel-item id="bannerBox"></div>
</div>
</div>
</div>
<aside class="hero__side">
<div class="hero-card hero-card--focus">
<div class="hero-card__title">
<img src="../images/home-icon-calendar.png" alt="" />
<span>开奖焦点</span>
</div>
<div class="hero-focus-list" id="heroFocusList"></div>
</div>
</aside>
</div>
</section>
<div class="section-divider section-divider--blue">
<span>彩票分类导航</span>
<!-- <p>八大彩种统一入口,快速进入对应文章与数据页</p> -->
</div>
<section class="lottery-nav">
<div class="lottery-nav__inner" id="lotteryNavGrid"></div>
</section>
<div class="section-divider section-divider--gold">
<span>最新开奖与免费文章</span>
<!-- <p>开奖结果、开奖日期、专家推荐文章同步聚合展示</p> -->
</div>
<section class="draw">
<div class="draw__inner">
<div class="draw__header">
<div class="draw__title">
<img src="../images/home-icon-history.png" alt="" />
<div>
<strong>最新开奖</strong>
</div>
</div>
</div>
<div class="draw__grid" id="resultsGrid"></div>
</div>
</section>
<section class="content">
<div class="content__inner">
<aside class="content__rail">
<div class="sidebar-card">
<div class="sidebar-card__head sidebar-card__head--navy">
<span>平台公告</span>
<a href="gonggaolist.html" class="sidebar-card__more">更多</a>
</div>
<ul class="notice-list" id="noticeList"></ul>
</div>
<div class="sidebar-card">
<div class="sidebar-card__head sidebar-card__head--gold">
<span>APP下载</span>
</div>
<div class="qrcode-box">
<img id="downloadQrImg" alt="二维码" />
<p>打开手机浏览器扫描二维码</p>
</div>
</div>
<div class="sidebar-card">
<div class="sidebar-card__head sidebar-card__head--teal">
<span>走势图</span>
</div>
<div class="trend-grid" id="trendGrid"></div>
</div>
<div class="sidebar-card combo-tool-card">
<div class="sidebar-card__head sidebar-card__head--gold">
<span>组合工具</span>
</div>
<div class="combo-tool-grid">
<a href="duanzuhe.html" class="combo-tool-item">
<img src="../images/icon-duanzuhe.png" alt="断组合" />
<span>断组合</span>
</a>
<a href="shazuhe.html" class="combo-tool-item">
<img src="../images/icon-shazuhe.png" alt="杀组合" />
<span>杀组合</span>
</a>
</div>
</div>
</aside>
<section class="content__main">
<div class="article-matrix" id="articleGroups"></div>
</section>
<aside class="content__ads">
<div id="sidebarAdContainer"></div>
</aside>
</div>
</section>
<section class="resource-section">
<div class="resource-section__inner">
<div class="resource-card">
<div class="resource-card__head">
<img src="../images/home-icon-article.png" alt="" />
<h2>其它推荐</h2>
</div>
<div class="resource-card__body" id="otherRecommend"></div>
</div>
<div class="resource-card">
<div class="resource-card__head">
<img src="../images/home-icon-link.png" alt="" />
<h2>友情链接</h2>
</div>
<div class="resource-card__body" id="friendLinks"></div>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">
彩先知以开奖数据、免费文章、资讯推荐为核心,提供清晰、密集、分层的 PC
端彩票信息首页。
</p>
<div class="site-footer__meta">
<a id="beian" href="https://beian.miit.gov.cn/" target="_blank" style="font-size:12px;color:rgba(255,255,255,.5)">工信部:蜀ICP备2024044594号-4</a>
<span id="additional-info">本站仅供数据分析参考,不构成任何投注建议</span>
</div>
</div>
</footer>
</div>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
var $ = layui.$;
var token = localStorage.getItem("token");
var lotteryOrder = [
"双色球",
"大乐透",
"福彩3D",
"排列三",
"排列五",
"快乐8",
"七乐彩",
"七星彩",
];
var currentLotteryMenu = [];
var lotteryAliasMap = {
排列3: "排列三",
排列三: "排列三",
排列5: "排列五",
排列五: "排列五",
};
var lotteryIconMap = {
双色球: {
icon: "../images/icon-ssq.png",
color: "red",
blueColor: "blue",
code: "ssq",
accent: "#ff5d6c",
},
大乐透: {
icon: "../images/icon-dlt.png",
color: "blue",
blueColor: "gold",
code: "cjdlt",
accent: "#4f8cff",
},
福彩3D: {
icon: "../images/icon-fc3d.png",
color: "orange",
blueColor: "orange",
code: "fc3d",
accent: "#ff9a3d",
},
排列三: {
icon: "../images/icon-pl3.png",
color: "green",
blueColor: "green",
code: "pl3",
accent: "#37c28f",
},
排列五: {
icon: "../images/icon-pl5.png",
color: "purple",
blueColor: "purple",
code: "pl5",
accent: "#8d62ff",
},
快乐8: {
icon: "../images/icon-kl8.png",
color: "gold",
blueColor: "gold",
code: "kl8",
accent: "#fdb933",
},
七乐彩: {
icon: "../images/icon-qlc.png",
color: "teal",
blueColor: "blue",
code: "qlc",
accent: "#29b6b0",
},
七星彩: {
icon: "../images/icon-qxc.png",
color: "violet",
blueColor: "violet",
code: "qxc",
accent: "#b06dff",
},
};
var fallbackAvatars = [
"../images/home-avatar-1.png",
"../images/home-avatar-2.png",
"../images/home-avatar-3.png",
"../images/home-avatar-4.png",
"../images/home-avatar-5.png",
"../images/home-avatar-6.png",
"../images/home-avatar-7.png",
"../images/home-avatar-8.png",
];
var historyCache = {};
function normalizeLotteryName(name) {
return lotteryAliasMap[name] || name || "";
}
function escapeHtml(text) {
return String(text || "").replace(/[&<>"']/g, function (char) {
return {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#39;",
}[char];
});
}
function formatDateValue(value) {
if (!value) return "--";
var date = new Date(value);
if (isNaN(date.getTime()))
return typeof value === "string" && value.length >= 10
? value.substring(0, 10)
: String(value);
var year = date.getFullYear();
var month = String(date.getMonth() + 1).padStart(2, "0");
var day = String(date.getDate()).padStart(2, "0");
return year + "-" + month + "-" + day;
}
function formatDateLabel(value) {
var date = new Date(value);
if (isNaN(date.getTime())) return formatDateValue(value);
return (
date.getFullYear() +
"年" +
(date.getMonth() + 1) +
"月" +
date.getDate() +
"日"
);
}
function sortLotteries(menuList) {
return (menuList || [])
.slice()
.map(function (item) {
var copy = $.extend(true, {}, item);
copy.name = normalizeLotteryName(copy.name);
return copy;
});
}
function getResultDate(item) {
return item && item.lotteryResult
? item.lotteryResult.time ||
item.lotteryResult.createTime ||
item.lotteryResult.date ||
""
: "";
}
function getResultPeriod(item) {
return item && item.lotteryResult
? item.lotteryResult.expect || item.lotteryResult.name || ""
: "";
}
function buildBallHtml(openCode, cfg, compact) {
if (!openCode) return '<span class="ball ball--empty">--</span>';
var html = "";
var parts = String(openCode).split("+");
$.each(parts[0] ? parts[0].split(",") : [], function (_, ball) {
html +=
'<span class="ball ball--' +
cfg.color +
(compact ? " ball--compact" : "") +
'">' +
escapeHtml($.trim(ball)) +
"</span>";
});
for (var i = 1; i < parts.length; i++) {
$.each(parts[i].split(","), function (_, ball) {
html +=
'<span class="ball ball--' +
cfg.blueColor +
(compact ? " ball--compact" : "") +
'">' +
escapeHtml($.trim(ball)) +
"</span>";
});
}
return html || '<span class="ball ball--empty">--</span>';
}
function renderBannerImages(bannerData) {
var $box = $("#bannerBox");
$box.empty();
if (!bannerData || !bannerData.length) {
$box.append(
'<div class="banner-fallback"><div class="banner-fallback__content"><span>暂无轮播数据</span><strong>彩先知 · 专注开奖与文章聚合</strong></div></div>',
);
} else {
$.each(bannerData, function (_, item) {
$box.append(
'<div><a href="' +
escapeHtml(item.link || "#") +
'" target="_blank" rel="noopener noreferrer" class="banner-link"><img class="carousel-img" src="' +
escapeHtml(item.touxiang || "") +
'" alt="轮播图" loading="lazy" /></a></div>',
);
});
}
layui.use(["carousel"], function () {
layui.carousel.render({
elem: "#bannerCarousel",
interval: 5000,
anim: "fade",
width: "100%",
height: "100%",
autoplay: true,
indicator: "inside",
});
});
}
function renderHeroSummary(menuList, noticeList) {
var articleCount = 0;
$.each(menuList, function (_, item) {
articleCount += (item.freeArticleList || []).slice(0, 5).length;
});
$("#heroLotteryCount").text(menuList.length);
$("#heroArticleCount").text(articleCount);
$("#heroNoticeCount").text((noticeList || []).length);
$("#heroMetaGrid").html(
[
'<div class="hero-meta"><span>彩种排序</span><strong>8 组固定顺序</strong><em>双色球至七星彩</em></div>',
'<div class="hero-meta"><span>免费文章</span><strong>' +
articleCount +
" 篇</strong><em>每组展示 5 篇以内</em></div>",
'<div class="hero-meta"><span>数据来源</span><strong>/api/web/index</strong><em>首页主数据接口</em></div>',
].join(""),
);
var $focus = $("#heroFocusList");
$focus.empty();
$.each(menuList.slice(0, 4), function (_, item) {
var name = item.name || "";
var cfg = lotteryIconMap[name] || lotteryIconMap["双色球"];
$focus.append(
'<a href="kaijiang.html?suoxie=' +
cfg.code +
'" class="hero-focus-item"><img src="' +
cfg.icon +
'" alt="' +
escapeHtml(name) +
'" /><div><strong>' +
escapeHtml(name) +
"</strong><span>第 " +
escapeHtml(getResultPeriod(item) || "--") +
" 期 · " +
escapeHtml(formatDateValue(getResultDate(item))) +
"</span></div></a>",
);
});
if (!$focus.children().length)
$focus.html('<div class="hero-focus-empty">暂无开奖焦点数据</div>');
}
function renderDynamicNav(menuList) {
var sortedList = sortLotteries(menuList);
var $nav = $("#navLotteryItems");
var $grid = $("#lotteryNavGrid");
var $trend = $("#trendGrid");
$nav.find(".topbar__nav-link").not(":first").remove();
$grid.empty();
$trend.empty();
$.each(sortedList, function (_, item) {
var cfg = lotteryIconMap[item.name];
if (!cfg) return;
$nav.append(
'<a href="cai.html?id=' +
cfg.code +
'" class="topbar__nav-link">' +
escapeHtml(item.name) +
"</a>",
);
$grid.append(
'<a href="cai.html?id=' +
cfg.code +
'" class="lottery-nav__card" style="--accent:' +
cfg.accent +
'"><div class="lottery-nav__icon"><img src="' +
cfg.icon +
'" alt="' +
escapeHtml(item.name) +
'" /></div><div class="lottery-nav__text"><strong>' +
escapeHtml(item.name) +
"</strong><span>进入预测文章</span></div><em>查看</em></a>",
);
$trend.append(
'<a href="' +
cfg.code +
'.html" class="trend-item"><img src="' +
cfg.icon +
'" alt="' +
escapeHtml(item.name) +
'" /><span>' +
escapeHtml(item.name) +
"</span></a>",
);
});
}
function updateResultsGrid(menuList) {
var $grid = $("#resultsGrid");
$grid.empty();
$.each(sortLotteries(menuList), function (_, item) {
var cfg = lotteryIconMap[item.name] || lotteryIconMap["双色球"];
var result = item.lotteryResult || {};
var compact = item.name === "快乐8";
$grid.append(
'<div class="draw-card" style="--accent:' +
cfg.accent +
'"><div class="draw-card__head"><div class="draw-card__title"><img src="' +
cfg.icon +
'" alt="' +
escapeHtml(item.name) +
'" /><div><strong>' +
escapeHtml(item.name) +
"</strong><span>第 " +
escapeHtml(result.expect || result.name || "--") +
' 期</span></div></div><a class="draw-card__link" href="kaijiang.html?suoxie=' +
cfg.code +
'">历史开奖</a></div><div class="draw-card__balls' +
(compact ? " draw-card__balls--compact" : "") +
'">' +
buildBallHtml(result.openCode || "", cfg, compact) +
'</div><div class="draw-card__foot"><span>开奖日期</span><strong>' +
escapeHtml(
formatDateLabel(
result.time || result.createTime || result.date || "",
),
) +
"</strong></div></div>",
);
});
}
function renderNoticeList(noticeData) {
var $container = $("#noticeList");
$container.empty();
$.each((noticeData || []).slice(0, 6), function (_, notice) {
$container.append(
'<li class="notice-item"><a href="gonggao.html?id=' +
escapeHtml(notice.id || "") +
'" target="_blank">' +
escapeHtml(notice.noticeTitle || notice.content || "公告信息") +
"</a></li>",
);
});
if (!$container.children().length)
$container.html(
'<li class="notice-item notice-item--empty">暂无公告</li>',
);
}
function renderChannelSections(menuList) {
var $container = $("#articleGroups");
$container.empty();
$.each(sortLotteries(menuList), function (index, lottery) {
var articles = (lottery.freeArticleList || []).slice(0, 5);
var cfg = lotteryIconMap[lottery.name] || lotteryIconMap["双色球"];
var listHtml = "";
$.each(articles, function (_, article) {
listHtml +=
`<li class="article-item"><img src="${article.accountAvatar || article.expertAvatar}" alt="" class="article-item__icon" /><a class="article-item__link" href="` +
CommonUtil.freeArticleHref(article.id || "") +
'" target="_blank">' +
escapeHtml(article.title || "文章标题") +
"</a></li>";
});
if (!listHtml)
listHtml =
'<li class="article-item article-item--empty"><span>暂无免费文章</span></li>';
$container.append(
'<section class="article-cluster" style="--accent:' +
cfg.accent +
'"><div class="article-cluster__head"><div class="article-cluster__title"><img src="' +
cfg.icon +
'" alt="' +
escapeHtml(lottery.name) +
'" /><div><strong>' +
escapeHtml(lottery.name) +
'</strong></div></div><a class="article-cluster__more" href="mianfeilist.html?id=' +
escapeHtml(lottery.id || "") +
'">更多文章</a></div><ul class="article-cluster__list">' +
listHtml +
"</ul></section>",
);
});
}
function renderResourceLinks($container, list) {
$container.empty();
if (!list.length) {
$container.html('<span class="resource-empty">暂无数据</span>');
return;
}
$.each(list, function (_, item) {
$container.append(
'<a class="resource-link" href="' +
escapeHtml(item.url || "#") +
'" target="_blank" rel="noopener noreferrer"><img src="../images/home-icon-link.png" alt="" /><span>' +
escapeHtml(item.name || "链接标题") +
"</span></a>",
);
});
}
function renderCompatLinks($container, list) {
$container.empty();
$.each(list, function (_, item) {
$container.append(
'<li class="link-listLi"><a href="' +
escapeHtml(item.url || "#") +
'" target="_blank"><div class="link-listLiText">' +
escapeHtml(item.name || "链接标题") +
"</div></a></li>",
);
});
}
function renderRecommendGrid(linkList) {
var list = (linkList || []).filter(function (item) {
return item.type === 1;
});
renderResourceLinks($("#otherRecommend"), list);
renderCompatLinks($("#otherRecommendCompat"), list);
}
function renderLinksGrid(linkList) {
var list = (linkList || []).filter(function (item) {
return item.type === 2;
});
renderResourceLinks($("#friendLinks"), list);
renderCompatLinks($("#friendLinksCompat"), list);
}
function renderAdGroups(adList) {
var $container = $("#sidebarAdContainer");
$container.empty();
var validAds = CommonUtil.filterValidAds(adList || []);
if (!validAds.length) {
$container.html(
'<div class="ad-group ad-group--empty">暂无广告数据</div>',
);
return;
}
for (var i = 0; i < validAds.length; i += 8) {
var html = "";
$.each(validAds.slice(i, i + 8), function (_, ad) {
html +=
'<a href="' +
escapeHtml(ad.link || "#") +
'" target="_blank" rel="noopener noreferrer" class="ad-item"><img src="' +
escapeHtml(ad.touxiang || "") +
'" alt="' +
escapeHtml(ad.title || "广告") +
'" loading="lazy" /></a>';
});
$container.append(
'<section class="ad-group">' +
'<div class="ad-group__grid">' +
html +
"</div></section>",
);
}
}
function getDefaultHistoryRange() {
var end = new Date();
var start = new Date();
start.setDate(end.getDate() - 30);
return { start: formatDateValue(start), end: formatDateValue(end) };
}
function openHistoryModal() {
var options = "";
var sourceList = currentLotteryMenu.length
? currentLotteryMenu
: lotteryOrder.map(function (name) {
return { name: name };
});
$.each(sourceList, function (_, item) {
var name = normalizeLotteryName(item.name);
var code =
item.suoxie ||
item.code ||
(lotteryIconMap[name] ? lotteryIconMap[name].code : "");
if (!code || !lotteryIconMap[name]) return;
options +=
'<option value="' +
code +
'">' +
name +
"</option>";
});
$("#historyLotterySelect").html(options);
var range = getDefaultHistoryRange();
$("#historyStartDate").val(range.start);
$("#historyEndDate").val(range.end);
$("#historyModal").addClass("is-open");
searchHistory();
}
function closeHistoryModal() {
$("#historyModal").removeClass("is-open");
}
function resolveLotteryNameByCode(code) {
var found = "";
$.each(lotteryIconMap, function (name, cfg) {
if (cfg.code === code) found = name;
});
return found || code;
}
function renderHistoryRows(code, list) {
var $body = $("#historyTableBody");
$body.empty();
if (!list.length) {
$body.html(
'<tr><td colspan="4" class="is-empty">暂无历史开奖数据</td></tr>',
);
return;
}
$.each(list.slice(0, 50), function (_, item) {
$body.append(
"<tr><td>" +
escapeHtml(resolveLotteryNameByCode(code)) +
"</td><td>" +
escapeHtml(item.expect || item.name || "--") +
'</td><td class="history-code">' +
escapeHtml(item.openCode || "--") +
"</td><td>" +
escapeHtml(
formatDateValue(
item.date || item.time || item.createTime || "",
),
) +
"</td></tr>",
);
});
}
function searchHistory() {
var code = $("#historyLotterySelect").val();
var startDate = $("#historyStartDate").val();
var endDate = $("#historyEndDate").val();
if (!code) return;
function applyFilter(list) {
var filtered = (list || []).filter(function (item) {
var current = formatDateValue(
item.date || item.time || item.createTime || "",
);
if (startDate && current < startDate) return false;
if (endDate && current > endDate) return false;
return true;
});
renderHistoryRows(code, filtered);
}
if (historyCache[code]) {
applyFilter(historyCache[code]);
return;
}
$("#historyTableBody").html(
'<tr><td colspan="4" class="is-empty">正在加载历史开奖数据...</td></tr>',
);
ApiClient.post("/api/web/lotteryResult/getList", { suoxie: code })
.then(function (res) {
if (res.code === 0 && res.data) {
historyCache[code] = res.data.records || res.data || [];
applyFilter(historyCache[code]);
} else {
$("#historyTableBody").html(
'<tr><td colspan="4" class="is-empty">历史开奖数据加载失败</td></tr>',
);
}
})
.catch(function () {
$("#historyTableBody").html(
'<tr><td colspan="4" class="is-empty">历史开奖数据加载失败</td></tr>',
);
});
}
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLoginStatus() {
if (!token) return;
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(
String(data.isExpert) === "1" ? "专家" : "会员",
);
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
function bindStaticEvents() {
$("#historyModal").on("click", function (event) {
if ($(event.target).is("#historyModal")) closeHistoryModal();
});
$(document).on("change", "#historyLotterySelect", searchHistory);
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
$(document).on("click", ".topbar__nav-link", function () {
$(".topbar__nav-link").removeClass("is-active");
$(this).addClass("is-active");
});
}
function hydrateHome(result) {
var data = result.data || {};
var sortedMenu = sortLotteries(data.menuList || []);
currentLotteryMenu = sortedMenu;
$("#currentDate").text(formatDateLabel(new Date()));
renderBannerImages(data.bannerList || []);
renderDynamicNav(sortedMenu);
updateResultsGrid(sortedMenu);
renderChannelSections(sortedMenu);
renderHeroSummary(sortedMenu, data.noticeList || []);
renderNoticeList(data.noticeList || []);
renderAdGroups(data.adList || []);
renderRecommendGrid(data.linkList || []);
renderLinksGrid(data.linkList || []);
ApiClient.post("/api/web/QRCode").then(function (res) {
if (res.code === 0 && res.data)
CommonUtil.generateQRCode(res.data, "downloadQrImg");
});
}
$(function () {
bindStaticEvents();
checkLoginStatus();
CommonUtil.loadPageConfig({
ads: false,
links: false,
onSuccess: hydrateHome,
});
});
</script>
<!-- Code injected by live-server -->
<script>
// <![CDATA[ <-- For SVG support
if ('WebSocket' in window) {
(function () {
function refreshCSS() {
var sheets = [].slice.call(document.getElementsByTagName("link"));
var head = document.getElementsByTagName("head")[0];
for (var i = 0; i < sheets.length; ++i) {
var elem = sheets[i];
var parent = elem.parentElement || head;
parent.removeChild(elem);
var rel = elem.rel;
if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") {
var url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, '');
elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());
}
parent.appendChild(elem);
}
}
var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';
var address = protocol + window.location.host + window.location.pathname + '/ws';
var socket = new WebSocket(address);
socket.onmessage = function (msg) {
if (msg.data == 'reload') window.location.reload();
else if (msg.data == 'refreshcss') refreshCSS();
};
if (sessionStorage && !sessionStorage.getItem('IsThisFirstTime_Log_From_LiveServer')) {
console.log('Live reload enabled.');
sessionStorage.setItem('IsThisFirstTime_Log_From_LiveServer', true);
}
})();
}
else {
console.error('Upgrade your browser. This Browser is NOT supported WebSocket for Live-Reloading.');
}
// ]]>
</script>
<!-- Code injected by live-server -->
<script>
// <![CDATA[ <-- For SVG support
if ('WebSocket' in window) {
(function () {
function refreshCSS() {
var sheets = [].slice.call(document.getElementsByTagName("link"));
var head = document.getElementsByTagName("head")[0];
for (var i = 0; i < sheets.length; ++i) {
var elem = sheets[i];
var parent = elem.parentElement || head;
parent.removeChild(elem);
var rel = elem.rel;
if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") {
var url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, '');
elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());
}
parent.appendChild(elem);
}
}
var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';
var address = protocol + window.location.host + window.location.pathname + '/ws';
var socket = new WebSocket(address);
socket.onmessage = function (msg) {
if (msg.data == 'reload') window.location.reload();
else if (msg.data == 'refreshcss') refreshCSS();
};
if (sessionStorage && !sessionStorage.getItem('IsThisFirstTime_Log_From_LiveServer')) {
console.log('Live reload enabled.');
sessionStorage.setItem('IsThisFirstTime_Log_From_LiveServer', true);
}
})();
}
else {
console.error('Upgrade your browser. This Browser is NOT supported WebSocket for Live-Reloading.');
}
// ]]>
</script>
</body>
</html>
+595
View File
@@ -0,0 +1,595 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 金额明细</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/finance.css" />
</head>
<body>
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems"></div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="headerAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="headerUserName" class="topbar__uname">用户</span>
<span id="headerUserLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button
class="topbar__btn topbar__btn--logout"
onclick="handleLogout()"
>
退出登录
</button>
</div>
</div>
</div>
</nav>
<div class="je-container">
<div class="je-card">
<div class="je-tabs">
<div
class="je-tab active"
data-type="POINTS"
onclick="switchTab(this)"
>
<span class="tab-icon icon-points">P</span>积分明细
</div>
<div class="je-tab" data-type="BALANCE" onclick="switchTab(this)">
<span class="tab-icon icon-balance">B</span>余额明细
</div>
<div
class="je-tab"
data-type="SALES_BALANCE"
onclick="switchTab(this)"
>
<span class="tab-icon icon-sales">S</span>销售余额明细
</div>
<div
class="je-tab"
data-type="EXPERT_BALANCE"
onclick="switchTab(this)"
>
<span class="tab-icon icon-expert">E</span>专家余额明细
</div>
</div>
<div class="je-summary">
<div>
<div class="je-summary-label">
当前 <span id="currentTypeName">积分</span>
</div>
<div class="je-summary-value" id="currentBalance">
0<span id="currentUnit"></span>
</div>
</div>
<div class="je-summary-actions" id="summaryActions">
<a href="chongzhi.html" class="je-btn-recharge" id="btnRecharge"
>充值</a
>
<a
href="tixian.html"
class="je-btn-withdraw"
id="btnWithdraw"
style="display: none"
>提现</a
>
</div>
</div>
<table class="je-table">
<thead>
<tr>
<th>类型</th>
<th>金额</th>
<th>操作类型</th>
<th class="balance-after-col" style="display: none">
变动后余额
</th>
<th>备注</th>
<th>时间</th>
</tr>
</thead>
<tbody id="flowTableBody">
<tr>
<td colspan="5" class="je-empty">
<div class="je-empty-icon">&#128203;</div>
<div class="je-empty-text">加载中...</div>
</td>
</tr>
</tbody>
</table>
<div class="je-pagination" id="flowPager"></div>
</div>
</div>
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
var currentCapitalType = "POINTS";
var currentPageNum = 1;
var pageSize = 10;
var userData = null;
var typeNames = {
POINTS: "积分",
BALANCE: "余额",
SALES_BALANCE: "销售余额",
EXPERT_BALANCE: "专家余额",
};
var typeUnits = {
POINTS: "点",
BALANCE: "元",
SALES_BALANCE: "元",
EXPERT_BALANCE: "元",
};
var typeBalanceKeys = {
POINTS: "points",
BALANCE: "accountBalance",
SALES_BALANCE: "salesBalance",
EXPERT_BALANCE: "expertBalance",
};
var typeCssClass = {
POINTS: "je-capital-points",
BALANCE: "je-capital-balance",
SALES_BALANCE: "je-capital-sales",
EXPERT_BALANCE: "je-capital-expert",
};
var flowTypeMap = {
BALANCE: "",
SALES_BALANCE: "sales",
EXPERT_BALANCE: "expert",
};
function checkAuth() {
if (CommonUtil.getAuthToken()) return true;
layui.use("layer", function () {
layui.layer.confirm(
"请先登录",
{ btn: ["去登录", "取消"] },
function (index) {
layui.layer.close(index);
window.location.href = "login.html";
},
);
});
return false;
}
function updateHeaderState() {
if (!CommonUtil.getAuthToken()) {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
return;
}
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
userData = data || {};
$("#headerUserName").text(
userData.nickName || userData.nickname || "用户",
);
$("#headerUserLevel").text(
CommonUtil.getUserLevelText
? CommonUtil.getUserLevelText(userData)
: "会员",
);
if (userData.avatar)
$("#headerAvatar").attr("src", userData.avatar);
updateSummary();
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
function renderNav() {
ApiClient.get(ApiClient.API.homeNav).then(function (res) {
if (!ApiClient.isSuccess(res) || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.empty();
$.each(ApiClient.asArray(res.data), function (i, item) {
if (item.type && item.type !== "lottery") return;
var code = item.code || item.suoxie || "";
if (!code) return;
$nav.append(
'<a href="cai.html?id=' +
encodeURIComponent(code) +
'" class="topbar__nav-link">' +
escapeHtml(item.name || item.lotteryName || code) +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
function switchTab(el) {
$(".je-tab").removeClass("active");
$(el).addClass("active");
currentCapitalType = $(el).data("type");
currentPageNum = 1;
updateSummary();
loadFlowList(1);
}
function updateSummary() {
$("#currentTypeName").text(typeNames[currentCapitalType]);
$("#currentUnit").text(typeUnits[currentCapitalType]);
if (userData) {
var key = typeBalanceKeys[currentCapitalType];
var val = firstValue(
userData,
[key, legacyBalanceKey(currentCapitalType)],
0,
);
$("#currentBalance").html(
escapeHtml(val) +
"<span>" +
typeUnits[currentCapitalType] +
"</span>",
);
}
if (currentCapitalType === "BALANCE") {
$("#btnRecharge").show();
$("#btnWithdraw").show();
} else if (currentCapitalType === "EXPERT_BALANCE") {
$("#btnRecharge").hide();
$("#btnWithdraw").show();
} else {
$("#btnRecharge").hide();
$("#btnWithdraw").hide();
}
}
function loadFlowList(pageNum) {
currentPageNum = pageNum || 1;
renderLoadingTable();
if (currentCapitalType === "POINTS") {
loadPointFlowList(currentPageNum);
return;
}
ApiClient.get(ApiClient.API.walletFlowList, {
flowType: flowTypeMap[currentCapitalType],
pageNum: currentPageNum,
pageSize: pageSize,
})
.then(function (res) {
if (ApiClient.isSuccess(res) && res.data) {
var page = normalizePageData(res.data);
renderFlowTable(page.records);
renderFlowPager(page.total);
} else {
renderEmptyTable();
}
})
.catch(function () {
renderEmptyTable();
});
}
function loadPointFlowList(pageNum) {
ApiClient.get(ApiClient.API.mineSignInList, {
pageNum: pageNum,
pageSize: pageSize,
})
.then(function (res) {
if (ApiClient.isSuccess(res) && res.data) {
var page = normalizePageData(res.data);
var records = page.records.map(function (item) {
return {
capitalType: "POINTS",
amount: item.pointsAwarded || item.points || item.amount || 0,
operationType: "ADD",
remark: item.remark || item.signDate || "签到奖励",
createTime:
item.createTime || item.signTime || item.signDate || "",
};
});
renderFlowTable(records);
renderFlowPager(page.total);
} else {
renderEmptyTable();
}
})
.catch(function () {
renderEmptyTable();
});
}
function renderFlowTable(records) {
var $tbody = $("#flowTableBody");
$tbody.empty();
if (!records || records.length === 0) {
renderEmptyTable();
return;
}
$.each(records, function (i, item) {
var amount = firstValue(
item,
[
"amountText",
"changeAmountText",
"amount",
"changeAmount",
"money",
"flowAmount",
],
0,
);
var opType = resolveOperationType(item, amount);
var opClass = opType === "ADD" ? "je-op-add" : "je-op-subtract";
var opText =
currentCapitalType === "POINTS"
? opType === "ADD"
? "收入"
: "支出"
: firstValue(
item,
["flowName", "typeText", "operationName"],
opType === "ADD" ? "收入" : "支出",
);
var amountClass =
opType === "ADD" ? "je-amount-add" : "je-amount-subtract";
var capitalType = normalizeCapitalType(item);
var capitalClass = typeCssClass[capitalType] || "je-capital-balance";
var remark = firstValue(
item,
["remark", "description", "title"],
"-",
);
var time = firstValue(
item,
["createTime", "flowTime", "payTime", "updateTime"],
"-",
);
$tbody.append(
"<tr>" +
'<td><span class="je-capital-type ' +
capitalClass +
'">' +
escapeHtml(typeNames[capitalType] || capitalType) +
"</span></td>" +
'<td><span class="je-amount ' +
amountClass +
'">' +
escapeHtml(formatSignedAmount(amount, opType)) +
"</span></td>" +
'<td><span class="je-op-type ' +
opClass +
'">' +
opText +
"</span></td>" +
'<td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="' +
escapeHtml(remark) +
'">' +
escapeHtml(remark) +
"</td>" +
'<td style="white-space:nowrap;color:var(--text-muted);">' +
escapeHtml(time) +
"</td>" +
"</tr>",
);
});
}
function renderLoadingTable() {
$("#flowTableBody").html(
'<tr><td colspan="' +
emptyColspan() +
'" class="je-empty">' +
'<div class="je-empty-icon">&#128203;</div>' +
'<div class="je-empty-text">加载中...</div>' +
"</td></tr>",
);
}
function renderEmptyTable() {
$("#flowTableBody").html(
'<tr><td colspan="' +
emptyColspan() +
'" class="je-empty">' +
'<div class="je-empty-icon">&#128203;</div>' +
'<div class="je-empty-text">暂无记录</div>' +
"</td></tr>",
);
}
function renderFlowPager(total) {
layui.laypage.render({
elem: "flowPager",
count: total,
limit: pageSize,
curr: currentPageNum,
jump: function (obj, first) {
if (!first) loadFlowList(obj.curr);
},
});
}
function normalizePageData(data) {
var records = ApiClient.asArray(data);
return {
records: records,
total: Number(
data.totalRow || data.total || data.count || records.length || 0,
),
};
}
function normalizeCapitalType(item) {
if (item.capitalType && typeNames[item.capitalType])
return item.capitalType;
var flowType = String(item.flowType || "").toLowerCase();
if (flowType === "sales") return "SALES_BALANCE";
if (flowType === "expert") return "EXPERT_BALANCE";
if (flowType === "points") return "POINTS";
return currentCapitalType;
}
function resolveOperationType(item, amount) {
var raw = String(
item.operationType ||
item.opType ||
item.direction ||
item.inOut ||
item.type ||
"",
).toLowerCase();
if (/expense|expenditure|debit|consume|purchase|withdraw|payment/.test(raw))
return "SUB";
if (/add|income|in|plus|收入|增加|充值|返利|佣金|奖励/.test(raw))
return "ADD";
if (/sub|out|minus|支出|减少|消费|购买|提现|扣除/.test(raw))
return "SUB";
return Number(amount) < 0 ? "SUB" : "ADD";
}
function formatSignedAmount(amount, opType) {
var value = String(
amount === undefined || amount === null || amount === "" ? 0 : amount,
)
.replace(/^\s*[+-]\s*/, "")
.trim();
if (!value) value = "0";
return opType === "ADD" ? value : "-" + value;
}
function emptyColspan() {
return 5;
}
function firstValue(source, keys, fallback) {
source = source || {};
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (
key &&
source[key] !== undefined &&
source[key] !== null &&
source[key] !== ""
) {
return source[key];
}
}
return fallback;
}
function legacyBalanceKey(type) {
if (type === "BALANCE") return "balance";
if (type === "SALES_BALANCE") return "distributionBalance";
if (type === "EXPERT_BALANCE") return "expertCommission";
return "";
}
function escapeHtml(value) {
return CommonUtil.escapeHtml
? CommonUtil.escapeHtml(value)
: String(value || "");
}
function handleLogout() {
ApiClient.post(ApiClient.API.authLogout, {})
.then(function () {
localStorage.removeItem("token");
window.location.href = "login.html";
})
.catch(function () {
localStorage.removeItem("token");
window.location.href = "login.html";
});
}
$(function () {
CommonUtil.loadPageConfig();
renderNav();
updateHeaderState();
if (!checkAuth()) return;
var urlParams = new URLSearchParams(window.location.search);
var typeParam = urlParams.get("type");
if (typeParam && typeNames[typeParam]) {
currentCapitalType = typeParam;
$(".je-tab").removeClass("active");
$('.je-tab[data-type="' + typeParam + '"]').addClass("active");
}
updateSummary();
loadFlowList(1);
});
</script>
</body>
</html>
+389
View File
@@ -0,0 +1,389 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 开奖公告</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/kaijiang.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main -->
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<span id="breadcrumbLottery">开奖公告</span>
</div>
<!-- Lottery Tabs -->
<div class="cxz-lottery-tabs" id="lotteryTabs"></div>
<!-- Latest Result Card -->
<div class="cxz-latest-card" id="latestCard" style="display: none">
<div class="cxz-latest-header">
<div>
<div class="cxz-latest-label">最新开奖</div>
<div class="cxz-latest-period" id="latestPeriod">--</div>
</div>
<div class="cxz-latest-date" id="latestDate">--</div>
</div>
<div class="cxz-latest-balls" id="latestBalls"></div>
</div>
<!-- Results Table -->
<div class="cxz-section-header">
<div class="cxz-section-title" id="tableTitle">历史开奖</div>
</div>
<div class="cxz-table-box">
<table class="cxz-data-table">
<thead>
<tr>
<th>期号</th>
<th>开奖日期</th>
<th>开奖号码</th>
</tr>
</thead>
<tbody id="resultBody">
<tr>
<td
colspan="3"
style="
text-align: center;
color: var(--text-muted);
padding: 40px;
"
>
加载中...
</td>
</tr>
</tbody>
</table>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
const urlParams = new URLSearchParams(window.location.search);
let currentSuoxie = urlParams.get("suoxie") || urlParams.get("code") || "";
let currentMenuId = urlParams.get("id") || "";
let lotteryMenus = [];
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function escapeHtml(str) {
if (!str) return "";
return String(str)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function normalizeLotteryMenus(list) {
return ApiClient.asArray(list).filter(function (item) {
return item && (!item.type || item.type === "lottery");
});
}
function menuCode(item) {
return item && (item.suoxie || item.code || "");
}
function currentMenu() {
return (
lotteryMenus.find(function (item) {
return String(item.id) === String(currentMenuId);
}) ||
lotteryMenus.find(function (item) {
return menuCode(item) && String(menuCode(item)) === String(currentSuoxie);
}) ||
null
);
}
function resolveCurrentMenu() {
if (!lotteryMenus.length) return null;
let selected = currentMenu();
if (!selected) selected = lotteryMenus[0];
currentMenuId = String(selected.id || "");
currentSuoxie = menuCode(selected);
return selected;
}
function renderNavFromMenus() {
var $nav = $("#navLotteryItems");
$nav.empty();
$.each(lotteryMenus, function (_, item) {
var code = menuCode(item);
$nav.append(
'<a href="cai.html?id=' +
encodeURIComponent(code || item.id || "") +
'" class="topbar__nav-link">' +
escapeHtml(item.name || "") +
"</a>",
);
});
}
function renderLotteryTabs() {
return ApiClient.get(ApiClient.API.homeNav)
.then(function (res) {
lotteryMenus = normalizeLotteryMenus(ApiClient.pickData(res, []));
resolveCurrentMenu();
renderNavFromMenus();
var $tabs = $("#lotteryTabs");
$tabs.empty();
$.each(lotteryMenus, function (_, item) {
var code = menuCode(item);
$tabs.append(
'<a href="kaijiang.html?suoxie=' +
encodeURIComponent(code || "") +
'" class="cxz-lottery-tab" data-code="' +
escapeHtml(code) +
'" data-id="' +
escapeHtml(item.id || "") +
'">' +
escapeHtml(item.name || "") +
"</a>",
);
});
highlightTab();
})
.catch(function () {
lotteryMenus = [];
$("#navLotteryItems").empty();
$("#lotteryTabs").empty();
$("#breadcrumbLottery").text("开奖公告");
$("#tableTitle").text("历史开奖");
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
// Highlight tab
function highlightTab() {
const selected = resolveCurrentMenu();
$(".cxz-lottery-tab").removeClass("active");
$('.cxz-lottery-tab[data-id="' + currentMenuId + '"]').addClass("active");
const name = (selected && selected.name) || "开奖公告";
$("#breadcrumbLottery").text(name + " - 开奖公告");
$("#tableTitle").text(name + " 历史开奖");
document.title = "彩先知 - " + name + "开奖公告";
}
// Load results
function loadResults() {
if (!currentMenuId) {
$("#resultBody").html(
'<tr><td colspan="3" style="text-align:center;color:var(--text-muted);padding:40px;">暂无数据</td></tr>',
);
return;
}
ApiClient.get("/api/web/lottery/result/list", {
menuId: currentMenuId,
pageNum: 1,
pageSize: 50,
}).then(function (res) {
if (ApiClient.isSuccess(res) && ApiClient.pickData(res, null)) {
const data = ApiClient.pickData(res, {}) || {};
const list = data.records || data.rows || data.list || ApiClient.asArray(data);
renderResults(list);
} else {
$("#resultBody").html(
'<tr><td colspan="3" style="text-align:center;color:var(--text-muted);padding:40px;">暂无数据</td></tr>',
);
}
});
}
function renderResults(list) {
const $body = $("#resultBody");
$body.empty();
if (!list || list.length === 0) {
$body.html(
'<tr><td colspan="3" style="text-align:center;color:var(--text-muted);padding:40px;">暂无数据</td></tr>',
);
return;
}
// Latest result card
if (list.length > 0) {
const latest = list[0];
$("#latestCard").show();
$("#latestPeriod").text(
(latest.name || "") + " 第" + (latest.expect || "") + "期",
);
$("#latestDate").text(
latest.date ||
(latest.createTime ? latest.createTime.substring(0, 10) : ""),
);
$("#latestBalls").html(parseOpenCode(latest.openCode, true));
}
$.each(list, function (i, item) {
const ballsHtml = parseOpenCode(item.openCode, false);
const date =
item.date ||
(item.createTime ? item.createTime.substring(0, 10) : "--");
$body.append(
"<tr>" +
"<td>" +
(item.expect || "") +
"</td>" +
"<td>" +
date +
"</td>" +
"<td>" +
ballsHtml +
"</td>" +
"</tr>",
);
});
}
// Parse openCode format "01,02,03+07" to display red/blue balls.
// Red/blue splitting follows the API data itself: numbers after "+" are blue.
function parseOpenCode(openCode, isLarge) {
if (!openCode)
return '<span style="color:var(--text-muted);">--</span>';
const parts = String(openCode).split("+");
const reds = splitCodes(parts[0]);
let blues = [];
for (let p = 1; p < parts.length; p++) {
blues = blues.concat(splitCodes(parts[p]));
}
let html = "";
const sizeStyle = isLarge
? ' style="width:40px;height:40px;font-size:16px;"'
: "";
$.each(reds, function (i, num) {
html += '<span class="red-q"' + sizeStyle + ">" + escapeHtml(num) + "</span>";
});
$.each(blues, function (i, num) {
html += '<span class="bule-q"' + sizeStyle + ">" + escapeHtml(num) + "</span>";
});
return html || '<span style="color:var(--text-muted);">--</span>';
}
function splitCodes(value) {
return String(value || "")
.split(/[,\s]+/)
.map(function (s) {
return s.trim();
})
.filter(Boolean);
}
$(function () {
checkLogin();
renderLotteryTabs().then(loadResults);
CommonUtil.loadPageConfig();
});
</script>
</body>
</html>
+330
View File
@@ -0,0 +1,330 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>彩先知 - 快乐8走势图</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/trend.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<div class="breadcrumb-bar">
<a href="javascript:history.back();"
><i class="layui-icon layui-icon-left"></i> 返回</a
>
<span>/</span>
<span>快乐8走势图</span>
</div>
<div class="page-title-bar">
<div class="page-title">快乐8走势图</div>
<div class="qishu">
<div class="qishu-but active" data-count="30">30期</div>
<div class="qishu-but" data-count="60">60期</div>
<div class="qishu-but" data-count="100">100期</div>
</div>
</div>
<div class="table-wrapper">
<table class="trend-table" border="1" id="trend-table">
<thead>
<tr>
<td rowspan="2" class="col-issue">期号</td>
<td rowspan="2" class="col-code">开奖号</td>
<td colspan="40" class="zone-g1">1-40</td>
<td colspan="40" class="zone-g2">41-80</td>
</tr>
<tr id="header-row"></tr>
</thead>
<tbody id="data-body"></tbody>
</table>
</div>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
CommonUtil.loadPageConfig();
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
checkLogin();
renderNav();
(function () {
var NUM_MAX = 80;
var currentCount = 30;
var allData = [];
function buildHeader() {
var html = "";
for (var i = 1; i <= NUM_MAX; i++) {
html += "<td>" + (i < 10 ? "0" + i : i) + "</td>";
}
$("#header-row").html(html);
}
// Parse openCode like "01,05,12,18,23,27,30,35,38,42,45,48,50,55,58,62,65,68,72,75"
function parseCode(code) {
return code.split(",").map(function (n) {
return parseInt(n);
});
}
function renderData(count) {
var data = allData.slice(0, count);
var html = "";
for (var r = 0; r < data.length; r++) {
var item = data[r];
var nums = parseCode(item.openCode);
var hitSet = {};
nums.forEach(function (n) {
hitSet[n] = true;
});
html += "<tr>";
html += '<td class="col-issue">' + item.issue + "</td>";
html += '<td class="col-code">' + item.openCode + "</td>";
for (var i = 1; i <= NUM_MAX; i++) {
if (hitSet[i]) {
var cls = i <= 40 ? "hit-num" : "hit-num g2";
html +=
'<td><span class="' +
cls +
'">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
} else {
html +=
'<td><span class="miss-num">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
}
}
html += "</tr>";
}
html += buildStats(data);
$("#data-body").html(html);
}
function calcStats(data, maxNum, getNums) {
var count = {},
maxOmit = {},
currOmit = {},
maxConsec = {},
avgOmit = {};
for (var i = 1; i <= maxNum; i++) {
count[i] = 0;
maxOmit[i] = 0;
currOmit[i] = 0;
maxConsec[i] = 0;
avgOmit[i] = 0;
}
for (var i = 1; i <= maxNum; i++) {
var omit = 0,
mo = 0,
consec = 0,
mc = 0,
totalOmit = 0,
oc = 0;
for (var r = 0; r < data.length; r++) {
var nums = getNums(data[r]);
if (nums.indexOf(i) >= 0) {
count[i]++;
if (omit > mo) mo = omit;
totalOmit += omit;
oc++;
omit = 0;
consec++;
if (consec > mc) mc = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > mo) mo = omit;
currOmit[i] = omit;
maxOmit[i] = mo;
maxConsec[i] = mc;
avgOmit[i] = oc > 0 ? (totalOmit / oc).toFixed(1) : data.length;
}
return {
count: count,
maxOmit: maxOmit,
currOmit: currOmit,
maxConsec: maxConsec,
avgOmit: avgOmit,
};
}
function buildStats(data) {
var stats = calcStats(data, NUM_MAX, function (d) {
return parseCode(d.openCode);
});
var labels = [
"出现次数",
"最大遗漏",
"平均遗漏",
"最大连出",
"当前遗漏",
];
var keys = ["count", "maxOmit", "avgOmit", "maxConsec", "currOmit"];
var html = "";
for (var li = 0; li < labels.length; li++) {
var cls = li % 2 === 0 ? "stat-row" : "stat-row alt";
html +=
'<tr class="' +
cls +
'"><td colspan="2" class="stat-label">' +
labels[li] +
"</td>";
for (var i = 1; i <= NUM_MAX; i++)
html += "<td>" + stats[keys[li]][i] + "</td>";
html += "</tr>";
}
return html;
}
function loadData() {
ApiClient.post(
"/api/web/lotteryResult/getList",
{ suoxie: "kl8" },
function (res) {
if (res && res.data) {
allData = res.data;
renderData(currentCount);
}
},
);
}
$(document).on("click", ".qishu-but", function () {
$(".qishu-but").removeClass("active");
$(this).addClass("active");
currentCount = parseInt($(this).data("count"));
renderData(currentCount);
});
buildHeader();
loadData();
})();
</script>
</body>
</html>
+373
View File
@@ -0,0 +1,373 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 登录</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<script src="../utils/AuthPageUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/auth.css" />
</head>
<body>
<main class="auth-container">
<div class="auth-card">
<div class="auth-header">
<a class="auth-logo" href="index.html" aria-label="返回首页">
<img src="../images/cxz-logo.png" alt="彩先知" />
</a>
<div class="auth-subtitle">CaiXianZhi - 欢迎登录</div>
</div>
<div class="auth-body">
<form id="loginForm" autocomplete="off">
<div class="form-group" id="phoneContainer">
<label for="phoneInput">手机号</label>
<input
type="text"
name="phoneNumber"
id="phoneInput"
placeholder="请输入手机号"
class="form-input"
maxlength="11"
/>
<div class="error-message" id="phoneError"></div>
</div>
<div class="form-group" id="passwordContainer">
<label for="passwordInput">密码</label>
<input
type="password"
name="password"
id="passwordInput"
placeholder="请输入密码"
class="form-input"
maxlength="20"
/>
<div class="error-message" id="passwordError"></div>
</div>
<div class="form-group" id="captchaGroup">
<label for="captchaInput">验证码</label>
<div class="captcha-row">
<input
type="text"
name="captcha"
placeholder="请输入验证码"
class="form-input"
id="captchaInput"
maxlength="6"
/>
<div
class="captcha-img-box"
id="captchaContainer"
onclick="refreshCaptcha()"
>
<img id="captchaImage" src="" alt="验证码" />
</div>
</div>
<div
class="captcha-refresh-link"
id="captchaRefreshLink"
onclick="refreshCaptcha()"
>
看不清?换一张
</div>
<div class="error-message" id="captchaError"></div>
<input type="hidden" name="captchaId" id="captchaId" value="" />
</div>
<button type="submit" class="auth-submit-btn" id="loginButton">
登 录
</button>
<div class="auth-agreement">
<input type="checkbox" id="agreementCheckbox" />
<span>已阅读并同意</span>
<a href="yonghuxieyi.html" target="_blank">《彩先知用户协议》</a>
<a href="yinsixieyi.html" target="_blank">《彩先知隐私政策》</a>
</div>
<div class="auth-links">
<a href="reg.html">立即注册</a>
<a href="repwd.html">忘记密码</a>
</div>
</form>
</div>
</div>
</main>
<div class="loading-overlay" id="loadingOverlay">
<div class="loading-spinner"></div>
</div>
<script src="../public/js/jquery.js"></script>
<script src="../public/js/md5.js"></script>
<script>
CommonUtil.loadPageConfig({ ads: false, links: false });
const $ = layui.$;
let layerInstance = null;
let currentCaptchaId = "";
let currentVerifyType = "captcha";
let currentSlideUuid = "";
let currentSlideX = 0;
function showError(field, message) {
const containerId =
field === "captcha" ? "captchaGroup" : field + "Container";
$("#" + field + "Error")
.text(message)
.show();
$("#" + containerId).addClass("error");
}
function clearError(field) {
const containerId =
field === "captcha" ? "captchaGroup" : field + "Container";
$("#" + field + "Error")
.hide()
.text("");
$("#" + containerId).removeClass("error");
}
function loadVerifyConfig() {
return AuthPageUtil.loadVerifyType().then(function (type) {
currentVerifyType = type;
});
}
function refreshCaptcha() {
currentCaptchaId = "";
currentSlideUuid = "";
currentSlideX = 0;
$("#captchaId").val("");
$("#captchaInput").val("");
$("#captchaGroup").removeClass("is-slide-mode");
if (currentVerifyType === "slide") {
renderSlideCaptchaEntry();
return;
}
$("#captchaInput").show();
$("#captchaRefreshLink").show().text("看不清?换一张");
$("#captchaContainer")
.attr("onclick", "refreshCaptcha()")
.html('<img id="captchaImage" src="" alt="验证码" />');
$("#captchaImage").attr("src", ApiClient.transparentGif());
ApiClient.get(ApiClient.API.authCode, {}, { publicRequest: true })
.then(function (res) {
if (ApiClient.isSuccess(res) && res.data) {
currentCaptchaId = res.data.uuid || res.data.id || "";
$("#captchaId").val(currentCaptchaId);
$("#captchaImage").attr(
"src",
res.data.img || res.data.image || "",
);
clearError("captcha");
return;
}
layerInstance.msg(
"获取验证码失败:" + ((res && res.msg) || "未知错误"),
{ icon: 2 },
);
})
.catch(function () {
layerInstance.msg("网络错误,无法获取验证码", { icon: 2 });
});
}
function completeSlideCaptcha(result) {
const data = result || {};
const nested = data.data || {};
currentSlideUuid =
data.slideUuid || data.uuid || nested.slideUuid || nested.uuid || "";
currentSlideX =
Number(data.slideX || data.x || nested.slideX || nested.x || 0) || 0;
if (!currentSlideUuid) {
layerInstance.msg("滑动验证未返回凭证,请重试", { icon: 2 });
renderSlideCaptchaEntry();
return;
}
$("#captchaContainer").html(
'<div class="slide-verify-card is-success"><div class="slide-verify-mark">✓</div><div class="slide-verify-main"><div class="slide-verify-title">验证已通过</div><div class="slide-verify-desc">可以继续登录</div></div><div class="slide-verify-action">完成</div></div>',
);
}
function openSlideCaptcha() {
AuthPageUtil.loadSlideCaptchaScript()
.then(function (SlideCaptcha) {
SlideCaptcha.open({
title: "登录安全验证",
subtitle: "拖动滑块完成验证后继续登录",
apiBase: ApiClient.baseUrl || "",
onSuccess: completeSlideCaptcha,
onCancel: function () {
currentSlideUuid = "";
currentSlideX = 0;
},
onError: function (error) {
layerInstance.msg(
(error && error.message) || "滑动验证失败,请重试",
{ icon: 2 },
);
renderSlideCaptchaEntry();
},
});
})
.catch(function (error) {
layerInstance.msg(error.message || "滑动验证组件加载失败", {
icon: 2,
});
});
}
function renderSlideCaptchaEntry() {
$("#captchaInput").hide();
$("#captchaRefreshLink").hide();
$("#captchaGroup").addClass("is-slide-mode");
$("#captchaContainer")
.removeAttr("onclick")
.html(
'<div class="slide-verify-card" role="button" tabindex="0"><div class="slide-verify-mark">↔</div><div class="slide-verify-main"><div class="slide-verify-title">拖动滑块验证</div><div class="slide-verify-desc">完成后继续登录</div></div><div class="slide-verify-action">开始</div></div>',
);
$(".slide-verify-card")
.off("click keydown")
.on("click", openSlideCaptcha)
.on("keydown", function (event) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
openSlideCaptcha();
}
});
}
function validatePhone(show) {
const value = $("#phoneInput").val().trim();
if (!AuthPageUtil.isPhone(value)) {
if (show) showError("phone", "请输入正确的手机号");
return false;
}
clearError("phone");
return true;
}
function validatePassword(show) {
if (!AuthPageUtil.isPassword($("#passwordInput").val())) {
if (show) showError("password", "密码长度应为6-20位");
return false;
}
clearError("password");
return true;
}
function getVerifyParams(show) {
if (currentVerifyType === "slide") {
if (!currentSlideUuid) {
if (show) layerInstance.msg("请先完成滑动验证", { icon: 2 });
return null;
}
return {
verifyType: "slide",
slideUuid: currentSlideUuid,
slideX: currentSlideX,
};
}
const code = $("#captchaInput").val().trim();
if (!code || !currentCaptchaId) {
if (show) showError("captcha", "请填写有效验证码");
return null;
}
clearError("captcha");
return { verifyType: "captcha", code, uuid: currentCaptchaId };
}
function validateForm() {
if (!validatePhone(true) || !validatePassword(true)) return false;
if (!getVerifyParams(true)) return false;
if (!$("#agreementCheckbox").prop("checked")) {
layerInstance.msg("请阅读并同意用户协议和隐私政策", { icon: 2 });
return false;
}
return true;
}
function submitLogin() {
const payload = Object.assign(
{
grantType: "password",
clientId: ApiClient.clientId,
client_id: ApiClient.clientId,
phoneNumber: $("#phoneInput").val().trim(),
password: hex_md5($("#passwordInput").val()),
deviceType: "WEB",
},
getVerifyParams(false) || {},
);
const $button = $("#loginButton");
$("#loadingOverlay").show();
$button.prop("disabled", true).text("登录中...");
ApiClient.post(ApiClient.API.authLogin, payload, {
publicRequest: true,
})
.then(function (res) {
if (ApiClient.isSuccess(res) && res.data) {
localStorage.setItem(
"token",
JSON.stringify(
res.data.token || res.data.access_token || res.data,
),
);
layerInstance.msg(
"登录成功,即将跳转到首页",
{ icon: 1, time: 1200 },
function () {
window.location.href = "index.html";
},
);
return;
}
layerInstance.msg(
(res && res.msg) || "登录失败,请检查手机号或密码",
{ icon: 2 },
);
refreshCaptcha();
})
.catch(function (error) {
layerInstance.msg(
"登录请求失败:" + (error.message || "网络错误"),
{ icon: 2 },
);
refreshCaptcha();
})
.finally(function () {
$("#loadingOverlay").hide();
$button.prop("disabled", false).text("登 录");
});
}
layui.use(["layer", "jquery"], function () {
layerInstance = layui.layer;
loadVerifyConfig().then(refreshCaptcha);
$("#loginForm").on("submit", function (event) {
event.preventDefault();
if (validateForm()) submitLogin();
});
$("#phoneInput").on("blur", function () {
validatePhone(true);
});
$("#passwordInput").on("blur", function () {
validatePassword(true);
});
$("#captchaInput").on("input", function () {
clearError("captcha");
});
});
</script>
</body>
</html>
+619
View File
@@ -0,0 +1,619 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 免费文章详情</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/mianfei.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems"></div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main -->
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<a href="mianfeilist.html?id=1">免费推荐</a><span class="sep">></span>
<span>文章详情</span>
</div>
<!-- Article Card -->
<div class="cxz-article-card">
<div class="cxz-article-header">
<h1 class="cxz-article-title" id="articleTitle">加载中...</h1>
<div class="cxz-article-meta">
<span>作者:<span class="author" id="articleAuthor">--</span></span>
<span id="articleTime">--</span>
</div>
</div>
<div class="cxz-article-body" id="articleContent">
<p style="text-align: center; color: var(--text-muted)">加载中...</p>
</div>
<div class="cxz-article-actions">
<button class="cxz-action-btn" id="btnLike" onclick="toggleLike()">
<i class="layui-icon layui-icon-praise"></i>
<span id="likeText">点赞</span><span id="likeCount">0</span>
</button>
<button
class="cxz-action-btn"
id="btnCollect"
onclick="toggleCollect()"
>
<i class="layui-icon layui-icon-star-fill"></i>
<span id="collectText">收藏</span>
</button>
</div>
</div>
<!-- Prev/Next Navigation -->
<div class="cxz-article-nav">
<a class="cxz-nav-item" id="prevArticle" href="javascript:void(0)">
<div class="cxz-nav-label">上一篇</div>
<div class="cxz-nav-title" id="prevTitle">没有了</div>
</a>
<a class="cxz-nav-item" id="nextArticle" href="javascript:void(0)">
<div class="cxz-nav-label">下一篇</div>
<div class="cxz-nav-title" id="nextTitle">没有了</div>
</a>
</div>
<!-- Comment Section -->
<div class="cxz-comment-section">
<div class="cxz-comment-header">
<i class="layui-icon layui-icon-dialogue"></i>
<span>评论(<span id="commentCount">0</span></span>
</div>
<!-- Comment Input -->
<div class="cxz-comment-input-box">
<textarea
class="cxz-comment-textarea"
id="commentInput"
placeholder="请输入您的评论..."
></textarea>
<button class="cxz-comment-submit-btn" onclick="submitComment()">
发表评论
</button>
</div>
<!-- Comment List -->
<div class="cxz-comment-list" id="commentList">
<div class="cxz-comment-empty">
<i class="layui-icon layui-icon-face-surprised"></i>
<span>暂无评论,快来抢沙发吧~</span>
</div>
</div>
<div class="cxz-pagination" id="commentPager"></div>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
const urlParams = new URLSearchParams(window.location.search);
const pathMatch = window.location.pathname.match(/\/mianfei\/([^/]+)\.html$/);
const articleId = urlParams.get("id") || (pathMatch ? decodeURIComponent(pathMatch[1]) : "");
let isLiked = false;
let isCollected = false;
let articleData = null;
let commentPage = 1;
const commentPageSize = 10;
let likeBusy = false;
let collectBusy = false;
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.get(ApiClient.API.homeNav).then(function (res) {
if (!ApiClient.isSuccess(res)) return;
var list = ApiClient.asArray(ApiClient.pickData(res, []));
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(list, function (i, item) {
if (item.type && item.type !== "lottery") return;
var code = item.code || item.suoxie || "";
$nav.append(
'<a href="cai.html?id=' +
code +
'" class="topbar__nav-link">' +
escapeHtml(item.name || "") +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
// Prevent prev/next navigation when no valid href
$("#prevArticle, #nextArticle").on("click", function (e) {
var href = $(this).attr("href");
if (!href || href === "javascript:void(0)" || href === "") {
e.preventDefault();
}
});
// === Load Article ===
function loadArticle() {
var token = localStorage.getItem("token");
var opts = token ? { headers: CommonUtil.authHeaders() } : {};
ApiClient.get(ApiClient.API.freeArticleDetail(articleId), null, opts)
.then(function (res) {
if (ApiClient.isSuccess(res) && ApiClient.pickData(res, null)) {
articleData = ApiClient.pickData(res, {});
renderArticle(articleData);
loadRelatedArticles();
} else {
layui.layer.msg("文章不存在或已删除", { icon: 2 });
$("#articleTitle").text("文章不存在");
$("#articleContent").html(
'<p style="text-align:center;color:var(--text-muted);">文章不存在或已删除</p>',
);
}
})
.catch(function () {
layui.layer.msg("加载失败,请稍后重试", { icon: 2 });
});
}
function renderArticle(data) {
$("#articleTitle").text(data.title || "无标题");
$("#articleAuthor").text(data.issuer || data.nickName || data.author || data.createUserName || "佚名");
$("#articleTime").text(data.updateTime || data.createTime || data.publishTime || "--");
$("#articleContent").html(scopeArticleContent(data.content || ""));
document.title = "彩先知 - " + (data.title || "免费文章详情");
renderArticleNav(data.prev || data.previous || data.prevArticle, data.next || data.nextArticle);
isLiked = isTruthyState(data.isLike ?? data.liked ?? data.isLiked);
updateLikeButton(data);
isCollected = isTruthyState(data.isCollect ?? data.collected ?? data.isCollected);
updateCollectButton();
}
function scopeArticleContent(content) {
var html = String(content || "");
if (!html.trim()) {
return '<p class="cxz-content-empty">暂无内容</p>';
}
var scopedStyles = [];
html = html.replace(/<style[^>]*>([\s\S]*?)<\/style>/gi, function (_, css) {
scopedStyles.push(prefixContentCss(css));
return "";
});
var styleHtml = scopedStyles.length
? '<style data-cxz-content-style>' + scopedStyles.join("\n") + "</style>"
: "";
return styleHtml + '<div class="cxz-content-inner">' + html + "</div>";
}
function prefixContentCss(css) {
return String(css || "").replace(/(^|})(\s*)([^@{}][^{}]*)\{/g, function (match, close, space, selectorText) {
var selectors = selectorText
.split(",")
.map(function (selector) {
selector = selector.trim();
if (!selector) return "";
if (
selector.indexOf("#articleContent") === 0 ||
selector.indexOf(".cxz-article-body") === 0
) {
return selector;
}
if (/^(html|body)$/i.test(selector)) return "#articleContent";
if (/^(html|body)\b/i.test(selector)) {
return selector.replace(/^(html|body)\b/i, "#articleContent");
}
return "#articleContent " + selector;
})
.filter(Boolean)
.join(", ");
return close + space + selectors + " {";
});
}
function isTruthyState(value) {
return (
value === true ||
value === 1 ||
value === "1" ||
value === 0 ||
value === "0"
);
}
function articleLinkOf(item) {
var id = item && (item.id || item.articleId || item.freeArticleId);
return CommonUtil.freeArticleHref(id);
}
function applyNavItem($item, $title, item, emptyText) {
if (item && (item.id || item.articleId || item.freeArticleId)) {
$item.attr("href", articleLinkOf(item)).removeClass("is-disabled").css("opacity", "");
$title.text(item.title || emptyText);
return;
}
$item.attr("href", "javascript:void(0)").addClass("is-disabled").css("opacity", "");
$title.text("没有了");
}
function renderArticleNav(prev, next) {
applyNavItem($("#prevArticle"), $("#prevTitle"), prev, "上一篇");
applyNavItem($("#nextArticle"), $("#nextTitle"), next, "下一篇");
}
function loadRelatedArticles() {
ApiClient.get(ApiClient.API.freeArticleRelated(articleId))
.then(function (res) {
if (!ApiClient.isSuccess(res)) return;
var data = ApiClient.pickData(res, {}) || {};
var prev = data.prev || data.previous || data.prevArticle;
var next = data.next || data.nextArticle;
var rows = ApiClient.asArray(data);
if (!prev && rows[0]) prev = rows[0];
if (!next && rows[1]) next = rows[1];
if (prev || next) renderArticleNav(prev, next);
})
.catch(function () {});
}
function updateLikeButton(data) {
var count = data.likeCount || 0;
$("#likeCount").text(count);
if (isLiked) {
$("#btnLike").addClass("liked");
$("#likeText").text("已点赞");
} else {
$("#btnLike").removeClass("liked");
$("#likeText").text("点赞");
}
}
function updateCollectButton() {
if (isCollected) {
$("#btnCollect").addClass("collected");
$("#collectText").text("已收藏");
} else {
$("#btnCollect").removeClass("collected");
$("#collectText").text("收藏");
}
}
// === Like ===
function toggleLike() {
if (likeBusy) return;
var token = localStorage.getItem("token");
if (!token) {
layui.layer.confirm(
"点赞需要先登录,是否前往登录?",
{ icon: 3, title: "提示" },
function (index) {
layui.layer.close(index);
window.location.href = "login.html";
},
);
return;
}
var wasLiked = isLiked;
likeBusy = true;
$("#btnLike").prop("disabled", true);
ApiClient.post(ApiClient.API.freeArticleLike(articleId), {}, { headers: CommonUtil.authHeaders() })
.then(function (res) {
if (ApiClient.isSuccess(res)) {
loadArticle();
layui.layer.msg(wasLiked ? "已取消点赞" : "点赞成功", {
icon: 1,
time: 1500,
});
} else {
layui.layer.msg(res.msg || "操作失败", { icon: 2 });
}
})
.finally(function () {
likeBusy = false;
$("#btnLike").prop("disabled", false);
});
}
// === Collect ===
function toggleCollect() {
if (collectBusy) return;
var token = localStorage.getItem("token");
if (!token) {
layui.layer.confirm(
"收藏需要先登录,是否前往登录?",
{ icon: 3, title: "提示" },
function (index) {
layui.layer.close(index);
window.location.href = "login.html";
},
);
return;
}
var wasCollected = isCollected;
collectBusy = true;
$("#btnCollect").prop("disabled", true);
ApiClient.post(ApiClient.API.freeArticleCollect(articleId), {}, { headers: CommonUtil.authHeaders() })
.then(function (res) {
if (ApiClient.isSuccess(res)) {
loadArticle();
layui.layer.msg(wasCollected ? "已取消收藏" : "收藏成功", {
icon: 1,
time: 1500,
});
} else {
layui.layer.msg(res.msg || "操作失败", { icon: 2 });
}
})
.finally(function () {
collectBusy = false;
$("#btnCollect").prop("disabled", false);
});
}
// === Comments ===
function loadComments(pageNum) {
if (pageNum) commentPage = pageNum;
ApiClient.get(ApiClient.API.freeArticleComments(articleId), {
pageNum: commentPage,
pageSize: commentPageSize,
})
.then(function (res) {
if (ApiClient.isSuccess(res) && ApiClient.pickData(res, null)) {
var data = ApiClient.pickData(res, {}) || {};
var list = ApiClient.asArray(data);
var total = data.totalRow || data.total || data.count || list.length || 0;
renderComments(list);
$("#commentCount").text(total);
renderCommentPager(total);
} else {
renderCommentEmpty();
}
})
.catch(function () {
renderCommentEmpty("加载失败");
});
}
function renderComments(list) {
var $container = $("#commentList");
$container.empty();
if (!list || list.length === 0) {
renderCommentEmpty();
return;
}
$.each(list, function (i, item) {
var avatarHtml;
if (item.avatar) {
avatarHtml =
'<img class="cxz-comment-avatar" src="' +
escapeHtml(item.avatar) +
'" />';
} else {
var initial = (item.nickName || item.nickname || item.userName || "匿").charAt(0);
avatarHtml =
'<div class="cxz-comment-avatar-placeholder">' +
escapeHtml(initial) +
"</div>";
}
var name = escapeHtml(item.nickName || item.nickname || item.userName || "匿名用户");
var text = escapeHtml(item.comment || item.content || "");
var time = item.createTime || item.commentTime || item.updateTime || "--";
if (time && time.length > 16) time = time.substring(0, 16);
$container.append(
'<div class="cxz-comment-item">' +
avatarHtml +
'<div class="cxz-comment-body">' +
'<div class="cxz-comment-name">' +
name +
"</div>" +
'<div class="cxz-comment-text">' +
text +
"</div>" +
'<div class="cxz-comment-time">' +
time +
"</div>" +
"</div>" +
"</div>",
);
});
}
function renderCommentEmpty(msg) {
$("#commentList").html(
'<div class="cxz-comment-empty">' +
'<i class="layui-icon layui-icon-face-surprised"></i>' +
"<span>" +
(msg || "暂无评论,快来抢沙发吧~") +
"</span>" +
"</div>",
);
}
function renderCommentPager(total) {
if (total <= 0) {
$("#commentPager").empty();
return;
}
layui.laypage.render({
elem: "commentPager",
count: total,
limit: commentPageSize,
curr: commentPage,
jump: function (obj, first) {
if (!first) {
commentPage = obj.curr;
loadComments();
}
},
});
}
function submitComment() {
var content = $("#commentInput").val().trim();
if (!content) {
layui.layer.msg("请输入评论内容", { icon: 2 });
return;
}
var token = localStorage.getItem("token");
if (!token) {
layui.layer.confirm(
"发表评论需要先登录,是否前往登录?",
{ icon: 3, title: "提示" },
function (index) {
layui.layer.close(index);
window.location.href = "login.html";
},
);
return;
}
var $btn = $(".cxz-comment-submit-btn");
$btn.text("提交中...").prop("disabled", true);
ApiClient.post(ApiClient.API.freeArticleComment(articleId), { comment: content }, { headers: CommonUtil.authHeaders() })
.then(function (res) {
if (ApiClient.isSuccess(res)) {
layui.layer.msg("评论成功", { icon: 1, time: 1500 });
$("#commentInput").val("");
commentPage = 1;
loadComments();
} else {
layui.layer.msg(res.msg || "评论失败", { icon: 2 });
}
$btn.text("发表评论").prop("disabled", false);
})
.catch(function () {
layui.layer.msg("网络错误,请重试", { icon: 2 });
$btn.text("发表评论").prop("disabled", false);
});
}
function escapeHtml(str) {
if (!str) return "";
return String(str)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
// === Init ===
$(function () {
checkLogin();
renderNav();
CommonUtil.loadPageConfig();
if (articleId) {
loadArticle();
loadComments();
} else {
layui.layer.msg("文章ID不存在", { icon: 2 });
}
});
</script>
</body>
</html>
+284
View File
@@ -0,0 +1,284 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 免费推荐</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/mianfei.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main -->
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<span>免费推荐</span>
</div>
<!-- Lottery Tabs -->
<div class="cxz-lottery-tabs" id="lotteryTabs"></div>
<div class="cxz-section-header">
<div class="cxz-section-title" id="listTitle">免费推荐</div>
</div>
<div class="cxz-article-list" id="articleList">
<div class="cxz-article-item">
<span style="color: var(--text-muted)">加载中...</span>
</div>
</div>
<div class="cxz-pagination" id="pagination"></div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
const urlParams = new URLSearchParams(window.location.search);
let menuId = urlParams.get("id") || "";
let currentPage = 1;
const pageSize = 15;
let lotteryMenus = [];
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
const token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(function (data) {
$("#userName").text(data.nickName || "用户");
$("#userAvatar").attr(
"src",
data.avatar || "../public/img/userimg.png",
);
});
}
}
function escapeHtml(str) {
if (!str) return "";
return String(str)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function normalizeLotteryMenus(list) {
return ApiClient.asArray(list).filter(function (item) {
return item && (!item.type || item.type === "lottery");
});
}
function resolveCurrentMenuId() {
if (!lotteryMenus.length) return;
if (
menuId &&
lotteryMenus.some(function (item) {
return String(item.id) === String(menuId);
})
) {
return;
}
menuId = String((lotteryMenus[0] && lotteryMenus[0].id) || "");
}
function renderLotteryTabs() {
return ApiClient.get(ApiClient.API.homeNav)
.then(function (res) {
lotteryMenus = normalizeLotteryMenus(ApiClient.pickData(res, []));
resolveCurrentMenuId();
const $tabs = $("#lotteryTabs");
$tabs.empty();
$.each(lotteryMenus, function (_, item) {
const id = item.id || "";
$tabs.append(
'<a href="mianfeilist.html?id=' +
encodeURIComponent(id) +
'" class="cxz-lottery-tab" data-id="' +
escapeHtml(id) +
'">' +
escapeHtml(item.name || "") +
"</a>",
);
});
highlightTab();
})
.catch(function () {
lotteryMenus = [];
$("#lotteryTabs").empty();
$("#listTitle").text("免费推荐");
document.title = "彩先知 - 免费推荐";
});
}
// Highlight active tab
function highlightTab() {
$(".cxz-lottery-tab").removeClass("active");
$('.cxz-lottery-tab[data-id="' + menuId + '"]').addClass("active");
const currentMenu = lotteryMenus.find(function (item) {
return String(item.id) === String(menuId);
});
const name = (currentMenu && currentMenu.name) || "免费推荐";
$("#listTitle").text(name + " - 免费推荐");
document.title = "彩先知 - " + name + "免费推荐";
}
// Load articles
function loadArticles() {
if (!menuId) {
$("#articleList").html(
'<div class="cxz-article-item"><span style="color:var(--text-muted);">暂无数据</span></div>',
);
renderPagination(0);
return;
}
ApiClient.get("/api/web/free-article/list", {
menuId: menuId,
pageNum: currentPage,
pageSize: pageSize,
}).then(function (res) {
if (ApiClient.isSuccess(res) && ApiClient.pickData(res, null)) {
const data = ApiClient.pickData(res, {}) || {};
const list = data.records || data.rows || data.list || ApiClient.asArray(data);
renderArticles(list);
renderPagination(data.totalRow || data.total || data.count || list.length || 0);
} else {
$("#articleList").html(
'<div class="cxz-article-item"><span style="color:var(--text-muted);">暂无数据</span></div>',
);
}
});
}
function renderArticles(list) {
const $container = $("#articleList");
$container.empty();
if (!list || list.length === 0) {
$container.html(
'<div class="cxz-article-item"><span style="color:var(--text-muted);">暂无数据</span></div>',
);
return;
}
$.each(list, function (i, item) {
let displayDate = "";
if (item.createTime) displayDate = item.createTime.substring(0, 10);
$container.append(
'<div class="cxz-article-item">' +
'<a href="' +
CommonUtil.freeArticleHref(item.id || "") +
'" target="_blank">' +
escapeHtml(item.title || "无标题") +
"</a>" +
'<span class="cxz-article-date">' +
escapeHtml(displayDate) +
"</span>" +
"</div>",
);
});
}
function renderPagination(total) {
layui.laypage.render({
elem: "pagination",
count: total,
limit: pageSize,
curr: currentPage,
jump: function (obj, first) {
if (!first) {
currentPage = obj.curr;
loadArticles();
}
},
});
}
$(function () {
checkLogin();
renderLotteryTabs().then(loadArticles);
CommonUtil.loadPageConfig();
});
</script>
</body>
</html>
+750
View File
@@ -0,0 +1,750 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 我的收藏</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/mianfei.css" />
<link rel="stylesheet" href="../public/css/publish.css" />
<link rel="stylesheet" href="../public/css/mianfeishoucang.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems"></div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged sc-hidden">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main -->
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<a href="usercenter.html">个人中心</a><span class="sep">></span>
<span>我的收藏</span>
</div>
<!-- Stats Summary Cards (also act as tab switcher) -->
<div class="sc-stats">
<div
class="sc-stat-card active"
data-tab="free"
onclick="switchTab('free')"
>
<div class="sc-stat-icon gold">
<i class="layui-icon layui-icon-star-fill"></i>
</div>
<div class="sc-stat-info">
<div class="sc-stat-value" id="statFree">0</div>
<div class="sc-stat-label">免费文章收藏</div>
</div>
</div>
<div class="sc-stat-card" data-tab="paid" onclick="switchTab('paid')">
<div class="sc-stat-icon orange">
<i class="layui-icon layui-icon-rmb"></i>
</div>
<div class="sc-stat-info">
<div class="sc-stat-value" id="statPaid">0</div>
<div class="sc-stat-label">付费文章收藏</div>
</div>
</div>
<div
class="sc-stat-card"
data-tab="expert"
onclick="switchTab('expert')"
>
<div class="sc-stat-icon blue">
<i class="layui-icon layui-icon-friends"></i>
</div>
<div class="sc-stat-info">
<div class="sc-stat-value" id="statExpert">0</div>
<div class="sc-stat-label">专家收藏</div>
</div>
</div>
</div>
<!-- Panel: Free Article Collection -->
<div class="sc-panel active" id="panelFree">
<div class="cxz-collect-grid" id="freeGrid"></div>
<div class="cxz-empty sc-hidden" id="freeEmpty">
<div class="cxz-empty-icon">
<i class="layui-icon layui-icon-star-fill sc-empty-icon-symbol"></i>
</div>
<div class="cxz-empty-text">暂无免费文章收藏</div>
<a href="mianfeilist.html" class="cxz-empty-action">去浏览文章</a>
</div>
<div class="cxz-pagination" id="freePager"></div>
</div>
<!-- Panel: Paid Article Collection -->
<div class="sc-panel" id="panelPaid">
<div class="sc-paid-table" id="paidList"></div>
<div class="cxz-empty sc-hidden" id="paidEmpty">
<div class="cxz-empty-icon">
<i class="layui-icon layui-icon-rmb sc-empty-icon-symbol"></i>
</div>
<div class="cxz-empty-text">暂无付费文章收藏</div>
<a href="cai.html" class="cxz-empty-action">去浏览预测</a>
</div>
<div class="cxz-pagination" id="paidPager"></div>
</div>
<!-- Panel: Expert Collection -->
<div class="sc-panel" id="panelExpert">
<div class="sc-expert-grid" id="expertGrid"></div>
<div class="cxz-empty sc-hidden" id="expertEmpty">
<div class="cxz-empty-icon">
<i class="layui-icon layui-icon-friends sc-empty-icon-symbol"></i>
</div>
<div class="cxz-empty-text">暂无专家收藏</div>
<a href="cai.html" class="cxz-empty-action">去浏览专家</a>
</div>
<div class="cxz-pagination" id="expertPager"></div>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
class="site-footer__record"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
const pageSize = 12;
let currentTab = getInitialTab();
let freePage = 1;
let paidPage = 1;
let expertPage = 1;
const collectTabs = {
free: {
stat: "#statFree",
panel: "#panelFree",
grid: "#freeGrid",
empty: "#freeEmpty",
pager: "freePager",
endpoint: getApiPath("mineFreeCollectList", "/api/web/mine/free-collect/list"),
cancelText: "确定要取消收藏该免费文章吗?",
},
paid: {
stat: "#statPaid",
panel: "#panelPaid",
grid: "#paidList",
empty: "#paidEmpty",
pager: "paidPager",
endpoint: getApiPath("minePayCollectList", "/api/web/mine/pay-collect/list"),
cancelText: "确定要取消收藏该付费文章吗?",
},
expert: {
stat: "#statExpert",
panel: "#panelExpert",
grid: "#expertGrid",
empty: "#expertEmpty",
pager: "expertPager",
endpoint: getApiPath("mineExpertCollectList", "/api/web/mine/expert-collect/list"),
cancelText: "确定要取消收藏该专家吗?",
},
};
function getApiPath(key, fallback) {
return ApiClient.API && ApiClient.API[key] ? ApiClient.API[key] : fallback;
}
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
if (!CommonUtil.getAuthToken()) {
window.location.href = "login.html";
return;
}
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || data.nickname || data.userName || "用户");
$("#userLevel").text(data.isExpert === "1" || data.expertStatus ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (!ApiClient.isSuccess(res) || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
encodeURIComponent(item.suoxie || item.code || item.id || "") +
'" class="topbar__nav-link">' +
escapeHtml(item.name || item.menuName || "彩种") +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
function switchTab(tab) {
if (!collectTabs[tab]) return;
currentTab = tab;
syncTabToUrl(tab);
$(".sc-stat-card").removeClass("active");
$('.sc-stat-card[data-tab="' + tab + '"]').addClass("active");
$(".sc-panel").removeClass("active");
$(collectTabs[tab].panel).addClass("active");
loadCurrentTab();
}
function getInitialTab() {
const tab = new URLSearchParams(window.location.search).get("tab");
return tab === "paid" || tab === "expert" ? tab : "free";
}
function syncTabToUrl(tab) {
const url = new URL(window.location.href);
if (tab === "free") {
url.searchParams.delete("tab");
} else {
url.searchParams.set("tab", tab);
}
window.history.replaceState(null, "", url.pathname + url.search + url.hash);
}
function applyActiveTab() {
$(".sc-stat-card").removeClass("active");
$('.sc-stat-card[data-tab="' + currentTab + '"]').addClass("active");
$(".sc-panel").removeClass("active");
$(collectTabs[currentTab].panel).addClass("active");
}
function loadCurrentTab() {
if (currentTab === "free") loadFreeArticles();
else if (currentTab === "paid") loadPaidArticles();
else loadExperts();
}
function refreshCollectStats() {
Object.keys(collectTabs).forEach(function (tab) {
const config = collectTabs[tab];
safeGet(config.endpoint, {
pageNum: 1,
pageSize: 1,
})
.then(function (res) {
if (!ApiClient.isSuccess(res)) return;
const pageData = normalizePageData(res);
$(config.stat).text(pageData.total);
})
.catch(function (error) {
console.warn("获取收藏数量失败:", tab, error);
});
});
}
function loadCollectPage(tab, page) {
const config = collectTabs[tab];
setPanelLoading(tab);
return safeGet(config.endpoint, {
pageNum: page,
pageSize: pageSize,
})
.then(function (res) {
if (!ApiClient.isSuccess(res)) {
throw new Error(res && res.msg ? res.msg : "获取收藏列表失败");
}
const pageData = normalizePageData(res);
$(config.stat).text(pageData.total);
return pageData;
})
.catch(function (error) {
console.error("获取收藏列表失败:", error);
$(config.stat).text("0");
showEmpty(tab, true);
layui.layer.msg(error.message || "获取收藏列表失败", { icon: 2 });
return { records: [], total: 0 };
});
}
function safeGet(url, params) {
if (!url) {
return Promise.reject(new Error("缺少接口地址"));
}
try {
return ApiClient.get(url, params);
} catch (error) {
return Promise.reject(error);
}
}
function normalizePageData(res) {
const data = res.data && typeof res.data === "object" ? res.data : res;
const records = ApiClient.asArray(
data.records || data.rows || data.list || res.rows || data,
);
const total = Number(data.total || data.totalRow || res.total || records.length || 0);
return { records: records, total: total };
}
function loadFreeArticles() {
loadCollectPage("free", freePage).then(function (pageData) {
renderFreeArticles(pageData.records);
renderPager("free", pageData.total, freePage, function (page) {
freePage = page;
loadFreeArticles();
});
});
}
function renderFreeArticles(list) {
const $grid = $("#freeGrid");
$grid.empty();
showEmpty("free", !list.length);
if (!list.length) return;
$.each(list, function (i, item) {
const articleId = getArticleId(item);
const title = getField(item, ["title", "articleTitle", "name"], "未命名文章");
const author = CommonUtil.getFreeArticleIssuer
? CommonUtil.getFreeArticleIssuer(item)
: getField(item, ["issuer", "author", "authorName", "nickName", "nickname"], "--");
const displayDate = formatTime(getField(item, ["collectTime", "createTime", "createdTime"], ""));
const href = buildFreeArticleHref(item, articleId);
$grid.append(
'<div class="cxz-collect-card" data-article-id="' +
escapeAttr(articleId) +
'">' +
'<div class="cxz-card-body">' +
'<div class="cxz-card-title"><a href="' +
escapeAttr(href) +
'">' +
escapeHtml(title) +
"</a></div>" +
'<div class="cxz-card-meta">' +
"<span>" +
escapeHtml(author || "--") +
"</span>" +
"<span>" +
escapeHtml(displayDate || "--") +
"</span>" +
'<button class="cxz-card-remove" type="button" onclick="removeCollect(this, \'free\', \''
+ escapeJs(articleId) +
'\')">取消收藏</button>' +
"</div>" +
"</div>" +
"</div>",
);
});
}
function loadPaidArticles() {
loadCollectPage("paid", paidPage).then(function (pageData) {
renderPaidArticles(pageData.records);
renderPager("paid", pageData.total, paidPage, function (page) {
paidPage = page;
loadPaidArticles();
});
});
}
function renderPaidArticles(list) {
const $list = $("#paidList");
$list.empty();
showEmpty("paid", !list.length);
if (!list.length) return;
$.each(list, function (i, item) {
const articleId = getArticleId(item);
const expertId = getField(item, ["expertId", "createBy", "userId"], "");
const expertName = getField(item, ["expertName", "nickName", "nickname", "authorName"], "专家");
const avatar = getField(item, ["avatar", "headImg", "headImage"], "");
const title = getField(item, ["title", "articleTitle", "name"], "未命名方案");
const issue = getField(item, ["issue", "expect", "period"], "--");
const price = getField(item, ["price", "amount", "points", "score"], 0);
const collectTime = formatTime(getField(item, ["collectTime", "createTime", "createdTime"], ""));
const href = buildPayArticleHref(item);
const avatarHtml = avatar
? '<img class="sc-expert-avatar" src="' + escapeAttr(avatar) + '" />'
: '<div class="sc-expert-avatar-fallback">' + escapeHtml(String(expertName).charAt(0) || "专") + "</div>";
$list.append(
'<div class="sc-paid-row" data-article-id="' +
escapeAttr(articleId) +
'">' +
'<a class="sc-expert-cell" href="' +
escapeAttr(buildExpertHref(item, expertId, getField(item, ["accountType"], 1))) +
'">' +
avatarHtml +
'<span class="sc-expert-name">' +
escapeHtml(expertName) +
"</span></a>" +
'<span class="sc-paid-period">第' +
escapeHtml(issue) +
"期</span>" +
'<span class="sc-paid-title"><a href="' +
escapeAttr(href) +
'">' +
escapeHtml(title) +
"</a></span>" +
'<span class="sc-paid-price">' +
escapeHtml(price) +
"积分</span>" +
'<span class="sc-paid-time">' +
escapeHtml(collectTime || "--") +
"</span>" +
'<a class="sc-paid-view" href="' +
escapeAttr(href) +
'">查看</a>' +
'<button class="sc-paid-remove" type="button" onclick="removeCollect(this, \'paid\', \''
+ escapeJs(articleId) +
'\')">取消收藏</button>' +
"</div>",
);
});
}
function loadExperts() {
loadCollectPage("expert", expertPage).then(function (pageData) {
renderExperts(pageData.records);
renderPager("expert", pageData.total, expertPage, function (page) {
expertPage = page;
loadExperts();
});
});
}
function renderExperts(list) {
const $grid = $("#expertGrid");
$grid.empty();
showEmpty("expert", !list.length);
if (!list.length) return;
$.each(list, function (i, item) {
const expertId = getField(item, ["expertId", "id", "userId", "createBy"], "");
const accountType = getField(item, ["accountType"], 1);
const name = getField(item, ["expertName", "name", "nickName", "nickname"], "未知专家");
const avatar = getField(item, ["avatar", "headImg", "headImage"], "");
const collectTime = formatTime(getField(item, ["collectTime", "createTime", "createdTime"], ""));
const typeText = isTruthy(getField(item, ["regularExpert", "isRegularExpert"], false))
? "正式专家"
: "实习专家";
const href = buildExpertHref(item, expertId, accountType);
const avatarHtml = avatar
? '<img class="sc-expert-card-avatar" src="' + escapeAttr(avatar) + '" />'
: '<div class="sc-expert-card-avatar-fallback">' + escapeHtml(String(name).charAt(0) || "专") + "</div>";
$grid.append(
'<div class="sc-expert-card" data-expert-id="' +
escapeAttr(expertId) +
'">' +
avatarHtml +
'<div class="sc-expert-card-name">' +
escapeHtml(name) +
"</div>" +
'<div class="sc-expert-card-type">' +
escapeHtml(typeText) +
"</div>" +
'<div class="sc-expert-card-stats">' +
'<div class="sc-expert-card-stat"><div class="val">' +
escapeHtml(collectTime ? collectTime.slice(0, 10) : "--") +
'</div><div class="lbl">收藏时间</div></div>' +
"</div>" +
'<div class="sc-expert-card-actions">' +
'<a class="sc-paid-view" href="' +
escapeAttr(href) +
'">查看</a>' +
'<button class="sc-paid-remove" type="button" onclick="removeCollect(this, \'expert\', \''
+ escapeJs(expertId) +
"\', \'" +
escapeJs(accountType) +
'\')">取消收藏</button>' +
"</div>" +
"</div>",
);
});
}
function removeCollect(btn, type, id, accountType) {
if (!id) {
layui.layer.msg("缺少收藏对象ID,暂不能操作", { icon: 2 });
return;
}
const config = collectTabs[type] || collectTabs[currentTab];
layui.layer.confirm(
config.cancelText,
{ icon: 3, title: "提示" },
function (index) {
layui.layer.close(index);
getCancelRequest(type, id, accountType).then(function (res) {
if (ApiClient.isSuccess(res)) {
layui.layer.msg("已取消收藏", { icon: 1 });
removeCard(btn, type);
return;
}
layui.layer.msg(res.msg || "取消收藏失败", { icon: 2 });
}).catch(function (error) {
console.error("取消收藏失败:", error);
layui.layer.msg("网络异常,请稍后重试", { icon: 2 });
});
},
);
}
function getCancelRequest(type, id, accountType) {
if (type === "expert") {
const url = ApiClient.API && ApiClient.API.expertCollect
? ApiClient.API.expertCollect(id, accountType || 1)
: "/api/web/pay-article/expert-collect?" +
new URLSearchParams({
expertId: String(id || ""),
accountType: String(accountType || 1),
}).toString();
return ApiClient.post(url, {});
}
if (type === "paid") {
const url = ApiClient.API && ApiClient.API.payArticleCollect
? ApiClient.API.payArticleCollect(id)
: "/api/web/pay-article/article-collect/" + encodeURIComponent(id);
return ApiClient.post(url, {});
}
const url = ApiClient.API && ApiClient.API.freeArticleCollect
? ApiClient.API.freeArticleCollect(id)
: "/api/web/free-article/" + encodeURIComponent(id) + "/collect";
return ApiClient.post(url, {});
}
function removeCard(btn, type) {
const selector = type === "expert" ? ".sc-expert-card" : type === "paid" ? ".sc-paid-row" : ".cxz-collect-card";
const $card = $(btn).closest(selector);
$card.addClass("removing");
setTimeout(function () {
$card.remove();
const config = collectTabs[type];
const left = $(config.grid).find(selector).length;
const total = Math.max(0, Number($(config.stat).text()) - 1);
$(config.stat).text(total);
if (left === 0) loadCurrentTab();
}, 300);
}
function setPanelLoading(tab) {
const config = collectTabs[tab];
$(config.grid)
.empty()
.html('<div class="sc-loading">正在加载收藏数据...</div>');
$(config.empty).addClass("sc-hidden").hide();
$("#" + config.pager).empty().hide();
}
function showEmpty(tab, isEmpty) {
const config = collectTabs[tab];
if (isEmpty) {
$(config.grid).empty();
$(config.empty).removeClass("sc-hidden").css("display", "flex");
} else {
$(config.empty).addClass("sc-hidden").hide();
}
$("#" + config.pager).toggle(!isEmpty);
}
function renderPager(tab, total, curr, onJump) {
const config = collectTabs[tab];
const $pager = $("#" + config.pager);
if (!total || total <= pageSize) {
$pager.empty().hide();
return;
}
$pager.show();
layui.laypage.render({
elem: config.pager,
count: total,
limit: pageSize,
curr: curr,
layout: ["prev", "page", "next"],
jump: function (obj, first) {
if (!first) onJump(obj.curr);
},
});
}
function getArticleId(item) {
return getField(item, ["articleId", "payArticleId", "freeArticleId", "id", "collectArticleId"], "");
}
function buildFreeArticleHref(item, articleId) {
return safeHref(
getField(item, ["url", "link"], "") ||
CommonUtil.freeArticleHref(articleId),
);
}
function buildPayArticleHref(item) {
const expertId = getField(item, ["expertId", "createBy", "userId"], "");
const articleId = getArticleId(item);
const code = getField(item, ["code", "lotteryCode", "menuCode"], "");
const menuId = getField(item, ["menuId", "parentId", "lotteryId"], "");
const accountType = getField(item, ["accountType"], 1);
const params = new URLSearchParams();
if (expertId) params.set("expertId", expertId);
if (articleId) params.set("articleId", articleId);
if (code) params.set("code", code);
if (menuId) params.set("menuId", menuId);
params.set("accountType", accountType || 1);
return params.toString() ? siteHref("/caiinfo.html?" + params.toString()) : "#";
}
function buildExpertHref(item, expertId, accountType) {
const code = getField(item, ["code", "lotteryCode", "menuCode"], "");
const menuId = getField(item, ["menuId", "parentId", "lotteryId"], "");
const params = new URLSearchParams();
if (expertId) params.set("expertId", expertId);
if (code) params.set("code", code);
if (menuId) params.set("menuId", menuId);
params.set("accountType", accountType || 1);
return siteHref("/caiinfo.html?" + params.toString());
}
function siteHref(path) {
if (CommonUtil.siteHref) return CommonUtil.siteHref(path);
const cleanPath = String(path || "");
if (/^https?:\/\//i.test(cleanPath) || cleanPath.indexOf("../") === 0) return cleanPath;
return cleanPath.charAt(0) === "/" ? "." + cleanPath : cleanPath;
}
function safeHref(value) {
if (CommonUtil.safeHref) return CommonUtil.safeHref(value);
const href = String(value || "#").trim();
if (!href || /^javascript:/i.test(href)) return "#";
return href;
}
function getField(item, keys, fallback) {
for (let i = 0; i < keys.length; i += 1) {
const value = item && item[keys[i]];
if (value !== undefined && value !== null && value !== "") return value;
}
return fallback || "";
}
function formatTime(value) {
if (!value) return "";
if (typeof DateUtil !== "undefined" && DateUtil.formatDateTimeToYMDHM) {
return DateUtil.formatDateTimeToYMDHM(value);
}
return String(value).slice(0, 16);
}
function isTruthy(value) {
return value === true || value === 1 || value === "1" || String(value).toLowerCase() === "true";
}
function escapeHtml(value) {
return CommonUtil.escapeHtml(String(value == null ? "" : value));
}
function escapeAttr(value) {
return escapeHtml(value).replace(/"/g, "&quot;");
}
function escapeJs(value) {
return String(value == null ? "" : value).replace(/\\/g, "\\\\").replace(/'/g, "\\'");
}
$(function () {
currentTab = getInitialTab();
checkLogin();
CommonUtil.loadPageConfig();
renderNav();
applyActiveTab();
refreshCollectStats();
loadCurrentTab();
});
</script>
</body>
</html>
+409
View File
@@ -0,0 +1,409 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>彩先知 - 排列三走势图</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/trend.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<div class="breadcrumb-bar">
<a href="javascript:history.back();"
><i class="layui-icon layui-icon-left"></i> 返回</a
>
<span>/</span>
<span>排列三走势图</span>
</div>
<div class="page-title-bar">
<div class="page-title">排列三走势图</div>
<div class="qishu">
<div class="qishu-but active" data-count="30">30期</div>
<div class="qishu-but" data-count="60">60期</div>
<div class="qishu-but" data-count="100">100期</div>
</div>
</div>
<div class="table-wrapper">
<table class="trend-table" border="1" id="trend-table">
<thead>
<tr>
<td rowspan="2" class="col-issue">期号</td>
<td rowspan="2" class="col-code">开奖号</td>
<td colspan="3" rowspan="2" class="zone-type">类型</td>
<td colspan="10" class="zone-hundred">百位</td>
<td colspan="10" class="zone-ten">十位</td>
<td colspan="10" class="zone-unit">个位</td>
</tr>
<tr id="header-row"></tr>
</thead>
<tbody id="data-body"></tbody>
</table>
</div>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
CommonUtil.loadPageConfig();
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
checkLogin();
renderNav();
(function () {
var currentCount = 30;
var allData = [];
function buildHeader() {
var html = "";
for (var g = 0; g < 3; g++) {
for (var i = 0; i <= 9; i++) html += "<td>" + i + "</td>";
}
$("#header-row").html(html);
}
function parseCode(code) {
var nums = code.split(",").map(function (n) {
return parseInt(n);
});
return {
hundred: nums[0] || 0,
ten: nums[1] || 0,
unit: nums[2] || 0,
nums: nums,
};
}
function getType(h, t, u) {
if (h === t && t === u) return "baozi";
if (h === t || t === u || h === u) return "zu3";
return "zu6";
}
function renderData(count) {
var data = allData.slice(0, count);
var html = "";
for (var r = 0; r < data.length; r++) {
var item = data[r];
var parsed = parseCode(item.openCode);
var type = getType(parsed.hundred, parsed.ten, parsed.unit);
html += "<tr>";
html += '<td class="col-issue">' + item.issue + "</td>";
html += '<td class="col-code">' + item.openCode + "</td>";
var typeNames = ["baozi", "zu3", "zu6"];
var typeLabels = ["豹子", "组三", "组六"];
for (var ti = 0; ti < 3; ti++) {
if (type === typeNames[ti]) {
html +=
'<td><span class="type-' +
typeNames[ti] +
'">' +
typeLabels[ti] +
"</span></td>";
} else {
html +=
'<td><span class="type-empty">' +
typeLabels[ti] +
"</span></td>";
}
}
for (var i = 0; i <= 9; i++) {
if (parsed.hundred === i)
html += '<td><span class="hit-hundred">' + i + "</span></td>";
else html += '<td><span class="miss-num">' + i + "</span></td>";
}
for (var i = 0; i <= 9; i++) {
if (parsed.ten === i)
html += '<td><span class="hit-ten">' + i + "</span></td>";
else html += '<td><span class="miss-num">' + i + "</span></td>";
}
for (var i = 0; i <= 9; i++) {
if (parsed.unit === i)
html += '<td><span class="hit-unit">' + i + "</span></td>";
else html += '<td><span class="miss-num">' + i + "</span></td>";
}
html += "</tr>";
}
html += buildStats(data);
$("#data-body").html(html);
}
function calcPosStats(data, getVal) {
var count = {},
maxOmit = {},
currOmit = {},
maxConsec = {},
avgOmit = {};
for (var i = 0; i <= 9; i++) {
count[i] = 0;
maxOmit[i] = 0;
currOmit[i] = 0;
maxConsec[i] = 0;
avgOmit[i] = 0;
}
for (var i = 0; i <= 9; i++) {
var omit = 0,
mo = 0,
consec = 0,
mc = 0,
totalOmit = 0,
oc = 0;
for (var r = 0; r < data.length; r++) {
if (getVal(data[r]) === i) {
count[i]++;
if (omit > mo) mo = omit;
totalOmit += omit;
oc++;
omit = 0;
consec++;
if (consec > mc) mc = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > mo) mo = omit;
currOmit[i] = omit;
maxOmit[i] = mo;
maxConsec[i] = mc;
avgOmit[i] = oc > 0 ? (totalOmit / oc).toFixed(1) : data.length;
}
return {
count: count,
maxOmit: maxOmit,
currOmit: currOmit,
maxConsec: maxConsec,
avgOmit: avgOmit,
};
}
function calcTypeStats(data, typeName) {
var count = 0,
maxOmit = 0,
currOmit = 0,
maxConsec = 0,
avgOmit = 0;
var omit = 0,
mo = 0,
consec = 0,
mc = 0,
totalOmit = 0,
oc = 0;
for (var r = 0; r < data.length; r++) {
var p = parseCode(data[r].openCode);
var t = getType(p.hundred, p.ten, p.unit);
if (t === typeName) {
count++;
if (omit > mo) mo = omit;
totalOmit += omit;
oc++;
omit = 0;
consec++;
if (consec > mc) mc = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > mo) mo = omit;
return {
count: count,
maxOmit: mo,
currOmit: omit,
maxConsec: mc,
avgOmit: oc > 0 ? (totalOmit / oc).toFixed(1) : data.length,
};
}
function buildStats(data) {
var hs = calcPosStats(data, function (d) {
return parseCode(d.openCode).hundred;
});
var ts = calcPosStats(data, function (d) {
return parseCode(d.openCode).ten;
});
var us = calcPosStats(data, function (d) {
return parseCode(d.openCode).unit;
});
var bz = calcTypeStats(data, "baozi");
var z3 = calcTypeStats(data, "zu3");
var z6 = calcTypeStats(data, "zu6");
var labels = [
"出现次数",
"最大遗漏",
"平均遗漏",
"最大连出",
"当前遗漏",
];
var keys = ["count", "maxOmit", "avgOmit", "maxConsec", "currOmit"];
var typeStats = [bz, z3, z6];
var stats = "";
for (var li = 0; li < labels.length; li++) {
var cls = li % 2 === 0 ? "stat-row" : "stat-row alt";
stats +=
'<tr class="' +
cls +
'"><td colspan="2" class="stat-label">' +
labels[li] +
"</td>";
for (var ti = 0; ti < 3; ti++)
stats += "<td>" + typeStats[ti][keys[li]] + "</td>";
for (var i = 0; i <= 9; i++)
stats += "<td>" + hs[keys[li]][i] + "</td>";
for (var i = 0; i <= 9; i++)
stats += "<td>" + ts[keys[li]][i] + "</td>";
for (var i = 0; i <= 9; i++)
stats += "<td>" + us[keys[li]][i] + "</td>";
stats += "</tr>";
}
return stats;
}
function loadData() {
ApiClient.post(
"/api/web/lotteryResult/getList",
{ suoxie: "pl3" },
function (res) {
if (res && res.data) {
allData = res.data;
renderData(currentCount);
}
},
);
}
$(document).on("click", ".qishu-but", function () {
$(".qishu-but").removeClass("active");
$(this).addClass("active");
currentCount = parseInt($(this).data("count"));
renderData(currentCount);
});
buildHeader();
loadData();
})();
</script>
</body>
</html>
+338
View File
@@ -0,0 +1,338 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>彩先知 - 排列五走势图</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/trend.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<div class="breadcrumb-bar">
<a href="javascript:history.back();"
><i class="layui-icon layui-icon-left"></i> 返回</a
>
<span>/</span>
<span>排列五走势图</span>
</div>
<div class="page-title-bar">
<div class="page-title">排列五走势图</div>
<div class="qishu">
<div class="qishu-but active" data-count="30">30期</div>
<div class="qishu-but" data-count="60">60期</div>
<div class="qishu-but" data-count="100">100期</div>
</div>
</div>
<div class="table-wrapper">
<table class="trend-table" border="1" id="trend-table">
<thead>
<tr>
<td rowspan="2" class="col-issue">期号</td>
<td rowspan="2" class="col-code">开奖号</td>
<td colspan="10" class="zone-p1">万位</td>
<td colspan="10" class="zone-p2">千位</td>
<td colspan="10" class="zone-p3">百位</td>
<td colspan="10" class="zone-p4">十位</td>
<td colspan="10" class="zone-p5">个位</td>
</tr>
<tr id="header-row"></tr>
</thead>
<tbody id="data-body"></tbody>
</table>
</div>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
CommonUtil.loadPageConfig();
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
checkLogin();
renderNav();
(function () {
var POS_COUNT = 5;
var currentCount = 30;
var allData = [];
var posNames = ["万位", "千位", "百位", "十位", "个位"];
var hitClasses = [
"hit-pos1",
"hit-pos2",
"hit-pos3",
"hit-pos4",
"hit-pos5",
];
function buildHeader() {
var html = "";
for (var g = 0; g < POS_COUNT; g++) {
for (var i = 0; i <= 9; i++) html += "<td>" + i + "</td>";
}
$("#header-row").html(html);
}
function parseCode(code) {
return code.split(",").map(function (n) {
return parseInt(n);
});
}
function renderData(count) {
var data = allData.slice(0, count);
var html = "";
for (var r = 0; r < data.length; r++) {
var item = data[r];
var nums = parseCode(item.openCode);
html += "<tr>";
html += '<td class="col-issue">' + item.issue + "</td>";
html += '<td class="col-code">' + item.openCode + "</td>";
for (var p = 0; p < POS_COUNT; p++) {
var val = nums[p] !== undefined ? nums[p] : -1;
for (var i = 0; i <= 9; i++) {
if (val === i) {
html +=
'<td><span class="' +
hitClasses[p] +
'">' +
i +
"</span></td>";
} else {
html += '<td><span class="miss-num">' + i + "</span></td>";
}
}
}
html += "</tr>";
}
html += buildStats(data);
$("#data-body").html(html);
}
function calcPosStats(data, posIdx) {
var count = {},
maxOmit = {},
currOmit = {},
maxConsec = {},
avgOmit = {};
for (var i = 0; i <= 9; i++) {
count[i] = 0;
maxOmit[i] = 0;
currOmit[i] = 0;
maxConsec[i] = 0;
avgOmit[i] = 0;
}
for (var i = 0; i <= 9; i++) {
var omit = 0,
mo = 0,
consec = 0,
mc = 0,
totalOmit = 0,
oc = 0;
for (var r = 0; r < data.length; r++) {
var nums = parseCode(data[r].openCode);
if (nums[posIdx] === i) {
count[i]++;
if (omit > mo) mo = omit;
totalOmit += omit;
oc++;
omit = 0;
consec++;
if (consec > mc) mc = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > mo) mo = omit;
currOmit[i] = omit;
maxOmit[i] = mo;
maxConsec[i] = mc;
avgOmit[i] = oc > 0 ? (totalOmit / oc).toFixed(1) : data.length;
}
return {
count: count,
maxOmit: maxOmit,
currOmit: currOmit,
maxConsec: maxConsec,
avgOmit: avgOmit,
};
}
function buildStats(data) {
var posStats = [];
for (var p = 0; p < POS_COUNT; p++)
posStats.push(calcPosStats(data, p));
var labels = [
"出现次数",
"最大遗漏",
"平均遗漏",
"最大连出",
"当前遗漏",
];
var keys = ["count", "maxOmit", "avgOmit", "maxConsec", "currOmit"];
var stats = "";
for (var li = 0; li < labels.length; li++) {
var cls = li % 2 === 0 ? "stat-row" : "stat-row alt";
stats +=
'<tr class="' +
cls +
'"><td colspan="2" class="stat-label">' +
labels[li] +
"</td>";
for (var p = 0; p < POS_COUNT; p++) {
for (var i = 0; i <= 9; i++)
stats += "<td>" + posStats[p][keys[li]][i] + "</td>";
}
stats += "</tr>";
}
return stats;
}
function loadData() {
ApiClient.post(
"/api/web/lotteryResult/getList",
{ suoxie: "pl5" },
function (res) {
if (res && res.data) {
allData = res.data;
renderData(currentCount);
}
},
);
}
$(document).on("click", ".qishu-but", function () {
$(".qishu-but").removeClass("active");
$(this).addClass("active");
currentCount = parseInt($(this).data("count"));
renderData(currentCount);
});
buildHeader();
loadData();
})();
</script>
</body>
</html>
+360
View File
@@ -0,0 +1,360 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>彩先知 - 七乐彩走势图</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/trend.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<div class="breadcrumb-bar">
<a href="javascript:history.back();"
><i class="layui-icon layui-icon-left"></i> 返回</a
>
<span>/</span>
<span>七乐彩走势图</span>
</div>
<div class="page-title-bar">
<div class="page-title">七乐彩走势图</div>
<div class="qishu">
<div class="qishu-but active" data-count="30">30期</div>
<div class="qishu-but" data-count="60">60期</div>
<div class="qishu-but" data-count="100">100期</div>
</div>
</div>
<div class="table-wrapper">
<table class="trend-table" border="1" id="trend-table">
<thead>
<tr>
<td rowspan="2" class="col-issue">期号</td>
<td rowspan="2" class="col-code">开奖号</td>
<td colspan="10" class="zone-z1">一区(01-10)</td>
<td colspan="10" class="zone-z2">二区(11-20)</td>
<td colspan="10" class="zone-z3">三区(21-30)</td>
<td colspan="1" class="zone-sp">特别号</td>
</tr>
<tr id="header-row"></tr>
</thead>
<tbody id="data-body"></tbody>
</table>
</div>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
CommonUtil.loadPageConfig();
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
checkLogin();
renderNav();
(function () {
var MAIN_MAX = 30;
var currentCount = 30;
var allData = [];
function buildHeader() {
var html = "";
for (var i = 1; i <= MAIN_MAX; i++) {
html += "<td>" + (i < 10 ? "0" + i : i) + "</td>";
}
// Special number column
html += "<td></td>";
$("#header-row").html(html);
}
// Parse openCode like "01,05,12,18,23,27,30+08"
function parseCode(code) {
var parts = code.split("+");
var mains = parts[0].split(",").map(function (n) {
return parseInt(n);
});
var specials = parts[1]
? parts[1].split(",").map(function (n) {
return parseInt(n);
})
: [];
return { mains: mains, specials: specials };
}
function renderData(count) {
var data = allData.slice(0, count);
var html = "";
for (var r = 0; r < data.length; r++) {
var item = data[r];
var parsed = parseCode(item.openCode);
var mainSet = {},
specialSet = {};
parsed.mains.forEach(function (n) {
mainSet[n] = true;
});
parsed.specials.forEach(function (n) {
specialSet[n] = true;
});
html += "<tr>";
html += '<td class="col-issue">' + item.issue + "</td>";
html += '<td class="col-code">' + item.openCode + "</td>";
for (var i = 1; i <= MAIN_MAX; i++) {
if (mainSet[i]) {
html +=
'<td><span class="hit-main">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
} else {
html +=
'<td><span class="miss-num">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
}
}
// Show special number
if (parsed.specials.length > 0) {
html +=
'<td><span class="hit-special">' +
(parsed.specials[0] < 10
? "0" + parsed.specials[0]
: parsed.specials[0]) +
"</span></td>";
} else {
html += "<td>-</td>";
}
html += "</tr>";
}
html += buildStats(data);
$("#data-body").html(html);
}
function calcStats(data, maxNum, getNums) {
var count = {},
maxOmit = {},
currOmit = {},
maxConsec = {},
avgOmit = {};
for (var i = 1; i <= maxNum; i++) {
count[i] = 0;
maxOmit[i] = 0;
currOmit[i] = 0;
maxConsec[i] = 0;
avgOmit[i] = 0;
}
for (var i = 1; i <= maxNum; i++) {
var omit = 0,
mo = 0,
consec = 0,
mc = 0,
totalOmit = 0,
oc = 0;
for (var r = 0; r < data.length; r++) {
var nums = getNums(data[r]);
if (nums.indexOf(i) >= 0) {
count[i]++;
if (omit > mo) mo = omit;
totalOmit += omit;
oc++;
omit = 0;
consec++;
if (consec > mc) mc = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > mo) mo = omit;
currOmit[i] = omit;
maxOmit[i] = mo;
maxConsec[i] = mc;
avgOmit[i] = oc > 0 ? (totalOmit / oc).toFixed(1) : data.length;
}
return {
count: count,
maxOmit: maxOmit,
currOmit: currOmit,
maxConsec: maxConsec,
avgOmit: avgOmit,
};
}
function buildStats(data) {
var ms = calcStats(data, MAIN_MAX, function (d) {
return parseCode(d.openCode).mains;
});
// Special number stats (1-30 range for special)
var ss = calcStats(data, MAIN_MAX, function (d) {
return parseCode(d.openCode).specials;
});
var labels = [
"出现次数",
"最大遗漏",
"平均遗漏",
"最大连出",
"当前遗漏",
];
var keys = ["count", "maxOmit", "avgOmit", "maxConsec", "currOmit"];
var stats = "";
for (var li = 0; li < labels.length; li++) {
var cls = li % 2 === 0 ? "stat-row" : "stat-row alt";
stats +=
'<tr class="' +
cls +
'"><td colspan="2" class="stat-label">' +
labels[li] +
"</td>";
for (var i = 1; i <= MAIN_MAX; i++)
stats += "<td>" + ms[keys[li]][i] + "</td>";
// Special column stats
stats += "<td>-</td>";
stats += "</tr>";
}
return stats;
}
function loadData() {
ApiClient.post(
"/api/web/lotteryResult/getList",
{ suoxie: "qlc" },
function (res) {
if (res && res.data) {
allData = res.data;
renderData(currentCount);
}
},
);
}
$(document).on("click", ".qishu-but", function () {
$(".qishu-but").removeClass("active");
$(this).addClass("active");
currentCount = parseInt($(this).data("count"));
renderData(currentCount);
});
buildHeader();
loadData();
})();
</script>
</body>
</html>
+348
View File
@@ -0,0 +1,348 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>彩先知 - 七星彩走势图</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/trend.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<div class="breadcrumb-bar">
<a href="javascript:history.back();"
><i class="layui-icon layui-icon-left"></i> 返回</a
>
<span>/</span>
<span>七星彩走势图</span>
</div>
<div class="page-title-bar">
<div class="page-title">七星彩走势图</div>
<div class="qishu">
<div class="qishu-but active" data-count="30">30期</div>
<div class="qishu-but" data-count="60">60期</div>
<div class="qishu-but" data-count="100">100期</div>
</div>
</div>
<div class="table-wrapper">
<table class="trend-table" border="1" id="trend-table">
<thead>
<tr>
<td rowspan="2" class="col-issue">期号</td>
<td rowspan="2" class="col-code">开奖号</td>
<td colspan="10" class="zone-0to9">第一位</td>
<td colspan="10" class="zone-0to9">第二位</td>
<td colspan="10" class="zone-0to9">第三位</td>
<td colspan="10" class="zone-0to9">第四位</td>
<td colspan="10" class="zone-0to9">第五位</td>
<td colspan="10" class="zone-0to9">第六位</td>
<td colspan="15" class="zone-last">第七位</td>
</tr>
<tr id="header-row"></tr>
</thead>
<tbody id="data-body"></tbody>
</table>
</div>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
CommonUtil.loadPageConfig();
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
checkLogin();
renderNav();
(function () {
var POS_COUNT = 7;
var LAST_MAX = 14; // Last position 0-14
var currentCount = 30;
var allData = [];
var hitClasses = [
"hit-p1",
"hit-p2",
"hit-p3",
"hit-p4",
"hit-p5",
"hit-p6",
"hit-p7",
];
var posMaxes = [9, 9, 9, 9, 9, 9, LAST_MAX]; // max value for each position
function buildHeader() {
var html = "";
for (var p = 0; p < POS_COUNT; p++) {
var max = posMaxes[p];
for (var i = 0; i <= max; i++) html += "<td>" + i + "</td>";
}
$("#header-row").html(html);
}
function parseCode(code) {
var nums = code.split(",").map(function (n) {
return parseInt(n);
});
return nums;
}
function renderData(count) {
var data = allData.slice(0, count);
var html = "";
for (var r = 0; r < data.length; r++) {
var item = data[r];
var nums = parseCode(item.openCode);
html += "<tr>";
html += '<td class="col-issue">' + item.issue + "</td>";
html += '<td class="col-code">' + item.openCode + "</td>";
for (var p = 0; p < POS_COUNT; p++) {
var max = posMaxes[p];
var val = nums[p] !== undefined ? nums[p] : -1;
for (var i = 0; i <= max; i++) {
if (val === i) {
html +=
'<td><span class="' +
hitClasses[p] +
'">' +
i +
"</span></td>";
} else {
html += '<td><span class="miss-num">' + i + "</span></td>";
}
}
}
html += "</tr>";
}
html += buildStats(data);
$("#data-body").html(html);
}
function calcPosStats(data, posIdx) {
var max = posMaxes[posIdx];
var count = {},
maxOmit = {},
currOmit = {},
maxConsec = {},
avgOmit = {};
for (var i = 0; i <= max; i++) {
count[i] = 0;
maxOmit[i] = 0;
currOmit[i] = 0;
maxConsec[i] = 0;
avgOmit[i] = 0;
}
for (var i = 0; i <= max; i++) {
var omit = 0,
mo = 0,
consec = 0,
mc = 0,
totalOmit = 0,
oc = 0;
for (var r = 0; r < data.length; r++) {
var nums = parseCode(data[r].openCode);
if (nums[posIdx] === i) {
count[i]++;
if (omit > mo) mo = omit;
totalOmit += omit;
oc++;
omit = 0;
consec++;
if (consec > mc) mc = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > mo) mo = omit;
currOmit[i] = omit;
maxOmit[i] = mo;
maxConsec[i] = mc;
avgOmit[i] = oc > 0 ? (totalOmit / oc).toFixed(1) : data.length;
}
return {
count: count,
maxOmit: maxOmit,
currOmit: currOmit,
maxConsec: maxConsec,
avgOmit: avgOmit,
max: max,
};
}
function buildStats(data) {
var posStats = [];
for (var p = 0; p < POS_COUNT; p++)
posStats.push(calcPosStats(data, p));
var labels = [
"出现次数",
"最大遗漏",
"平均遗漏",
"最大连出",
"当前遗漏",
];
var keys = ["count", "maxOmit", "avgOmit", "maxConsec", "currOmit"];
var stats = "";
for (var li = 0; li < labels.length; li++) {
var cls = li % 2 === 0 ? "stat-row" : "stat-row alt";
stats +=
'<tr class="' +
cls +
'"><td colspan="2" class="stat-label">' +
labels[li] +
"</td>";
for (var p = 0; p < POS_COUNT; p++) {
for (var i = 0; i <= posStats[p].max; i++)
stats += "<td>" + posStats[p][keys[li]][i] + "</td>";
}
stats += "</tr>";
}
return stats;
}
function loadData() {
ApiClient.post(
"/api/web/lotteryResult/getList",
{ suoxie: "qxc" },
function (res) {
if (res && res.data) {
allData = res.data;
renderData(currentCount);
}
},
);
}
$(document).on("click", ".qishu-but", function () {
$(".qishu-but").removeClass("active");
$(this).addClass("active");
currentCount = parseInt($(this).data("count"));
renderData(currentCount);
});
buildHeader();
loadData();
})();
</script>
</body>
</html>
+520
View File
@@ -0,0 +1,520 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 注册</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<script src="../utils/AuthPageUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/auth.css" />
</head>
<body>
<main class="auth-container">
<div class="auth-card">
<div class="auth-header">
<a class="auth-logo" href="index.html" aria-label="返回首页">
<img src="../images/cxz-logo.png" alt="彩先知" />
</a>
<div class="auth-subtitle">CaiXianZhi - 新用户注册</div>
</div>
<div class="auth-body">
<form id="registerForm" autocomplete="off">
<div class="form-group" id="nicknameContainer">
<label for="nickname">昵称</label>
<input
type="text"
id="nickname"
class="form-input"
placeholder="请输入昵称(2-20个字符)"
maxlength="20"
/>
<div class="error-message" id="nicknameError"></div>
</div>
<div class="form-group" id="phoneContainer">
<label for="phone">手机号</label>
<input
type="text"
id="phone"
class="form-input"
placeholder="请输入手机号"
maxlength="11"
/>
<div class="error-message" id="phoneError"></div>
</div>
<div class="form-group" id="captchaGroup">
<label for="captchaInput">滑动验证</label>
<div class="captcha-row">
<input
type="text"
id="captchaInput"
class="form-input"
placeholder="请输入验证码"
maxlength="6"
/>
<div
class="captcha-img-box"
id="captchaContainer"
onclick="refreshCaptcha()"
>
<img id="captchaImage" src="" alt="验证码" />
</div>
</div>
<div
class="captcha-refresh-link"
id="captchaRefreshLink"
onclick="refreshCaptcha()"
>
看不清?换一张
</div>
<div class="error-message" id="captchaError"></div>
<input type="hidden" id="captchaId" value="" />
</div>
<div class="form-group" id="smsCodeContainer">
<label for="smsCode">短信验证码</label>
<div class="auth-sms-row">
<input
type="text"
id="smsCode"
class="form-input"
placeholder="请输入短信验证码"
maxlength="6"
/>
<button type="button" class="auth-sms-button" id="sendSmsBtn">
获取验证码
</button>
</div>
<div class="error-message" id="smsCodeError"></div>
</div>
<div class="form-group" id="passwordContainer">
<label for="password">密码</label>
<input
type="password"
id="password"
class="form-input"
placeholder="请输入密码(6-20位)"
maxlength="20"
/>
<div class="error-message" id="passwordError"></div>
</div>
<div class="form-group" id="confirmPasswordContainer">
<label for="confirmPassword">确认密码</label>
<input
type="password"
id="confirmPassword"
class="form-input"
placeholder="请再次输入密码"
maxlength="20"
/>
<div class="error-message" id="confirmPasswordError"></div>
</div>
<button type="submit" class="auth-submit-btn" id="registerBtn">
确认注册
</button>
<div class="auth-agreement">
<input type="checkbox" id="agreementCheckbox" />
<span>已阅读并同意</span>
<a href="yonghuxieyi.html" target="_blank">《彩先知用户协议》</a>
<a href="yinsixieyi.html" target="_blank">《彩先知隐私政策》</a>
</div>
<div class="auth-links">
<a href="login.html">已有账号?立即登录</a>
</div>
</form>
</div>
</div>
</main>
<script src="../public/js/jquery.js"></script>
<script src="../public/js/md5.js"></script>
<script>
CommonUtil.loadPageConfig({ ads: false, links: false });
const $ = layui.$;
let layerInstance = null;
let currentCaptchaId = "";
let currentVerifyType = "captcha";
let currentSlideUuid = "";
let currentSlideX = 0;
let pendingSmsAfterVerify = false;
let referralPreviewData = null;
function showError(field, message) {
const containerId =
field === "captcha" ? "captchaGroup" : field + "Container";
$("#" + field + "Error")
.text(message)
.show();
$("#" + containerId).addClass("error");
}
function clearError(field) {
const containerId =
field === "captcha" ? "captchaGroup" : field + "Container";
$("#" + field + "Error")
.hide()
.text("");
$("#" + containerId).removeClass("error");
}
function loadVerifyConfig() {
return AuthPageUtil.loadVerifyType().then(function (type) {
currentVerifyType = type;
});
}
function refreshCaptcha() {
currentCaptchaId = "";
currentSlideUuid = "";
currentSlideX = 0;
$("#captchaId").val("");
$("#captchaInput").val("");
$("#captchaGroup").removeClass("is-slide-mode");
$("#sendSmsBtn").prop("disabled", currentVerifyType === "slide");
if (currentVerifyType === "slide") {
renderSlideCaptchaEntry();
return;
}
$("#captchaInput").show();
$("#captchaRefreshLink").show().text("看不清?换一张");
$("#captchaContainer")
.attr("onclick", "refreshCaptcha()")
.html('<img id="captchaImage" src="" alt="验证码" />');
$("#captchaImage").attr("src", ApiClient.transparentGif());
ApiClient.get(ApiClient.API.authCode, {}, { publicRequest: true })
.then(function (res) {
if (ApiClient.isSuccess(res) && res.data) {
currentCaptchaId = res.data.uuid || res.data.id || "";
$("#captchaId").val(currentCaptchaId);
$("#captchaImage").attr(
"src",
res.data.img || res.data.image || "",
);
clearError("captcha");
return;
}
layerInstance.msg(
"获取验证码失败:" + ((res && res.msg) || "未知错误"),
{ icon: 2 },
);
})
.catch(function () {
layerInstance.msg("网络错误,无法获取验证码", { icon: 2 });
});
}
function completeSlideCaptcha(result) {
const data = result || {};
const nested = data.data || {};
currentSlideUuid =
data.slideUuid || data.uuid || nested.slideUuid || nested.uuid || "";
currentSlideX =
Number(data.slideX || data.x || nested.slideX || nested.x || 0) || 0;
if (!currentSlideUuid) {
layerInstance.msg("滑动验证未返回凭证,请重试", { icon: 2 });
renderSlideCaptchaEntry();
return;
}
$("#captchaContainer").html(
'<div class="slide-verify-card is-success"><div class="slide-verify-mark"></div><div class="slide-verify-main"><div class="slide-verify-title">验证已通过</div><div class="slide-verify-desc">可以获取短信或注册</div></div><div class="slide-verify-action">完成</div></div>',
);
$("#sendSmsBtn").prop("disabled", false);
if (pendingSmsAfterVerify) {
pendingSmsAfterVerify = false;
sendSmsCode();
}
}
function openSlideCaptcha() {
AuthPageUtil.loadSlideCaptchaScript()
.then(function (SlideCaptcha) {
SlideCaptcha.open({
title: "注册安全验证",
subtitle: "拖动滑块完成验证后继续注册",
apiBase: ApiClient.baseUrl || "",
onSuccess: completeSlideCaptcha,
onCancel: function () {
currentSlideUuid = "";
currentSlideX = 0;
},
onError: function (error) {
layerInstance.msg(
(error && error.message) || "滑动验证失败,请重试",
{ icon: 2 },
);
renderSlideCaptchaEntry();
},
});
})
.catch(function (error) {
layerInstance.msg(error.message || "滑动验证组件加载失败", {
icon: 2,
});
});
}
function renderSlideCaptchaEntry() {
$("#captchaInput").hide();
$("#captchaRefreshLink").hide();
$("#captchaGroup").addClass("is-slide-mode");
$("#sendSmsBtn").prop("disabled", true);
$("#captchaContainer")
.removeAttr("onclick")
.html(
'<div class="slide-verify-card" role="button" tabindex="0"><div class="slide-verify-mark"></div><div class="slide-verify-main"><div class="slide-verify-title">拖动滑块验证</div><div class="slide-verify-desc">完成后获取短信</div></div><div class="slide-verify-action">开始</div></div>',
);
$(".slide-verify-card")
.off("click keydown")
.on("click", openSlideCaptcha)
.on("keydown", function (event) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
openSlideCaptcha();
}
});
}
function validateNickname(show) {
const value = $("#nickname").val().trim();
if (value.length < 2 || value.length > 20) {
if (show) showError("nickname", "昵称长度应为2-20个字符");
return false;
}
clearError("nickname");
return true;
}
function validatePhone(show) {
const value = $("#phone").val().trim();
if (!AuthPageUtil.isPhone(value)) {
if (show) showError("phone", "请输入正确的手机号");
return false;
}
clearError("phone");
return true;
}
function validateSmsCode(show) {
const value = $("#smsCode").val().trim();
if (!/^\d{4,6}$/.test(value)) {
if (show) showError("smsCode", "请输入4-6位短信验证码");
return false;
}
clearError("smsCode");
return true;
}
function validatePassword(show) {
if (!AuthPageUtil.isPassword($("#password").val())) {
if (show) showError("password", "密码长度应为6-20位");
return false;
}
clearError("password");
return true;
}
function validateConfirmPassword(show) {
if ($("#confirmPassword").val() !== $("#password").val()) {
if (show) showError("confirmPassword", "两次输入的密码不一致");
return false;
}
clearError("confirmPassword");
return true;
}
function getVerifyParams(show) {
if (currentVerifyType === "slide") {
if (!currentSlideUuid) {
if (show) layerInstance.msg("请先完成滑动验证", { icon: 2 });
return null;
}
return {
verifyType: "slide",
slideUuid: currentSlideUuid,
slideX: currentSlideX,
};
}
const code = $("#captchaInput").val().trim();
if (!code || !currentCaptchaId) {
if (show) showError("captcha", "请填写有效验证码");
return null;
}
clearError("captcha");
return { verifyType: "captcha", code, uuid: currentCaptchaId };
}
function sendSmsCode() {
if (!validatePhone(true)) return;
if (currentVerifyType === "slide" && !currentSlideUuid) {
layerInstance.msg("请先完成滑动验证后获取短信验证码", { icon: 2 });
return;
}
const verifyParams = getVerifyParams(true);
if (!verifyParams) return;
$("#sendSmsBtn").prop("disabled", true);
AuthPageUtil.sendSmsCode(
Object.assign(
{ phoneNumber: $("#phone").val().trim() },
verifyParams,
),
)
.then(function (res) {
if (ApiClient.isSuccess(res)) {
layerInstance.msg("短信验证码已发送", { icon: 1 });
AuthPageUtil.startSmsCountdown("#sendSmsBtn");
return;
}
$("#sendSmsBtn").prop("disabled", false);
layerInstance.msg((res && res.msg) || "短信验证码发送失败", {
icon: 2,
});
refreshCaptcha();
})
.catch(function () {
$("#sendSmsBtn").prop("disabled", false);
layerInstance.msg("网络异常,短信发送失败", { icon: 2 });
refreshCaptcha();
});
}
function validateForm() {
if (
!validateNickname(true) ||
!validatePhone(true) ||
!validateSmsCode(true) ||
!validatePassword(true) ||
!validateConfirmPassword(true)
)
return false;
if (!getVerifyParams(true)) return false;
if (!$("#agreementCheckbox").prop("checked")) {
layerInstance.msg("请阅读并同意用户协议和隐私政策", { icon: 2 });
return false;
}
return true;
}
function buildRegisterData() {
const params = new URLSearchParams(window.location.search);
const referrerId =
(referralPreviewData &&
(referralPreviewData.referrerId ||
referralPreviewData.userId ||
referralPreviewData.id)) ||
params.get("referrerId") ||
params.get("pid") ||
0;
return Object.assign(
{
phoneNumber: $("#phone").val().trim(),
password: hex_md5($("#password").val()),
smsCode: $("#smsCode").val().trim(),
nickname: $("#nickname").val().trim(),
referrerId: referrerId,
},
getVerifyParams(false) || {},
);
}
function loadReferralBindPreview() {
const params = new URLSearchParams(window.location.search);
const inviteCode =
params.get("inviteCode") || params.get("referralCode") || "";
if (!inviteCode) return;
ApiClient.get(
ApiClient.API.referralBindPreview,
{ inviteCode: inviteCode },
{ publicRequest: true },
)
.then(function (res) {
if (!ApiClient.isSuccess(res)) return;
referralPreviewData = ApiClient.pickData(res, {}) || {};
const name =
referralPreviewData.nickname ||
referralPreviewData.nickName ||
referralPreviewData.userName ||
"";
if (name)
layerInstance.msg("已识别推荐人:" + name, {
icon: 1,
time: 1500,
});
})
.catch(function () {
referralPreviewData = null;
});
}
function register() {
$("#registerBtn").prop("disabled", true).text("注册中...");
ApiClient.post(ApiClient.API.authRegister, buildRegisterData(), {
publicRequest: true,
})
.then(function (res) {
if (ApiClient.isSuccess(res)) {
layerInstance.msg(
"注册成功,即将前往登录",
{ icon: 1, time: 1200 },
function () {
window.location.href = "login.html";
},
);
return;
}
layerInstance.msg((res && res.msg) || "注册失败,请稍后重试", {
icon: 2,
});
refreshCaptcha();
})
.catch(function () {
layerInstance.msg("网络异常,注册失败", { icon: 2 });
refreshCaptcha();
})
.finally(function () {
$("#registerBtn").prop("disabled", false).text("确认注册");
});
}
layui.use(["layer", "jquery"], function () {
layerInstance = layui.layer;
loadVerifyConfig().then(refreshCaptcha);
loadReferralBindPreview();
$("#sendSmsBtn").on("click", sendSmsCode);
$("#registerForm").on("submit", function (event) {
event.preventDefault();
if (validateForm()) register();
});
$("#nickname").on("blur", function () {
validateNickname(true);
});
$("#phone").on("blur", function () {
validatePhone(true);
});
$("#smsCode").on("blur", function () {
validateSmsCode(true);
});
$("#password").on("blur", function () {
validatePassword(true);
});
$("#confirmPassword").on("blur", function () {
validateConfirmPassword(true);
});
$("#captchaInput").on("input", function () {
clearError("captcha");
});
});
</script>
</body>
</html>
+423
View File
@@ -0,0 +1,423 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 找回密码</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<script src="../utils/AuthPageUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/auth.css" />
</head>
<body>
<main class="auth-container">
<div class="auth-card">
<div class="auth-header">
<a class="auth-logo" href="index.html" aria-label="返回首页">
<img src="../images/cxz-logo.png" alt="彩先知" />
</a>
<div class="auth-subtitle">CaiXianZhi - 找回密码</div>
</div>
<div class="auth-body">
<form id="resetPasswordForm" autocomplete="off">
<div class="form-group" id="phoneContainer">
<label for="phone">手机号</label>
<input
type="text"
id="phone"
class="form-input"
placeholder="请输入手机号"
maxlength="11"
/>
<div class="error-message" id="phoneError"></div>
</div>
<div class="form-group" id="captchaGroup">
<label for="captchaInput">滑动验证</label>
<div class="captcha-row">
<input
type="text"
id="captchaInput"
class="form-input"
placeholder="请输入验证码"
maxlength="6"
/>
<div
class="captcha-img-box"
id="captchaContainer"
onclick="refreshCaptcha()"
>
<img id="captchaImage" src="" alt="验证码" />
</div>
</div>
<div
class="captcha-refresh-link"
id="captchaRefreshLink"
onclick="refreshCaptcha()"
>
看不清?换一张
</div>
<div class="error-message" id="captchaError"></div>
<input type="hidden" id="captchaId" value="" />
</div>
<div class="form-group" id="smsCodeContainer">
<label for="smsCode">短信验证码</label>
<div class="auth-sms-row">
<input
type="text"
id="smsCode"
class="form-input"
placeholder="请输入短信验证码"
maxlength="6"
/>
<button type="button" class="auth-sms-button" id="sendSmsBtn">
获取验证码
</button>
</div>
<div class="error-message" id="smsCodeError"></div>
</div>
<div class="form-group" id="passwordContainer">
<label for="password">新密码</label>
<input
type="password"
id="password"
class="form-input"
placeholder="请输入新密码(6-20位)"
maxlength="20"
/>
<div class="error-message" id="passwordError"></div>
</div>
<div class="form-group" id="confirmPasswordContainer">
<label for="confirmPassword">确认密码</label>
<input
type="password"
id="confirmPassword"
class="form-input"
placeholder="请再次输入新密码"
maxlength="20"
/>
<div class="error-message" id="confirmPasswordError"></div>
</div>
<button type="submit" class="auth-submit-btn" id="resetBtn">
确认重置
</button>
<div class="auth-links">
<a href="login.html">想起密码?返回登录</a>
</div>
</form>
</div>
</div>
</main>
<script src="../public/js/jquery.js"></script>
<script src="../public/js/md5.js"></script>
<script>
CommonUtil.loadPageConfig({ ads: false, links: false });
const $ = layui.$;
let layerInstance = null;
let currentCaptchaId = "";
let currentVerifyType = "captcha";
let currentSlideUuid = "";
let currentSlideX = 0;
let pendingSmsAfterVerify = false;
function showError(field, message) {
const containerId =
field === "captcha" ? "captchaGroup" : field + "Container";
$("#" + field + "Error")
.text(message)
.show();
$("#" + containerId).addClass("error");
}
function clearError(field) {
const containerId =
field === "captcha" ? "captchaGroup" : field + "Container";
$("#" + field + "Error")
.hide()
.text("");
$("#" + containerId).removeClass("error");
}
function loadVerifyConfig() {
return AuthPageUtil.loadVerifyType().then(function (type) {
currentVerifyType = type;
});
}
function refreshCaptcha() {
currentCaptchaId = "";
currentSlideUuid = "";
currentSlideX = 0;
$("#captchaId").val("");
$("#captchaInput").val("");
$("#captchaGroup").removeClass("is-slide-mode");
$("#sendSmsBtn").prop("disabled", currentVerifyType === "slide");
if (currentVerifyType === "slide") {
renderSlideCaptchaEntry();
return;
}
$("#captchaInput").show();
$("#captchaRefreshLink").show().text("看不清?换一张");
$("#captchaContainer")
.attr("onclick", "refreshCaptcha()")
.html('<img id="captchaImage" src="" alt="验证码" />');
$("#captchaImage").attr("src", ApiClient.transparentGif());
ApiClient.get(ApiClient.API.authCode, {}, { publicRequest: true })
.then(function (res) {
if (ApiClient.isSuccess(res) && res.data) {
currentCaptchaId = res.data.uuid || res.data.id || "";
$("#captchaId").val(currentCaptchaId);
$("#captchaImage").attr(
"src",
res.data.img || res.data.image || "",
);
clearError("captcha");
return;
}
layerInstance.msg(
"获取验证码失败:" + ((res && res.msg) || "未知错误"),
{ icon: 2 },
);
})
.catch(function () {
layerInstance.msg("网络错误,无法获取验证码", { icon: 2 });
});
}
function completeSlideCaptcha(result) {
const data = result || {};
const nested = data.data || {};
currentSlideUuid =
data.slideUuid || data.uuid || nested.slideUuid || nested.uuid || "";
currentSlideX =
Number(data.slideX || data.x || nested.slideX || nested.x || 0) || 0;
if (!currentSlideUuid) {
layerInstance.msg("滑动验证未返回凭证,请重试", { icon: 2 });
renderSlideCaptchaEntry();
return;
}
$("#captchaContainer").html(
'<div class="slide-verify-card is-success"><div class="slide-verify-mark"></div><div class="slide-verify-main"><div class="slide-verify-title">验证已通过</div><div class="slide-verify-desc">可以获取短信或重置</div></div><div class="slide-verify-action">完成</div></div>',
);
$("#sendSmsBtn").prop("disabled", false);
if (pendingSmsAfterVerify) {
pendingSmsAfterVerify = false;
sendSmsCode();
}
}
function openSlideCaptcha() {
AuthPageUtil.loadSlideCaptchaScript()
.then(function (SlideCaptcha) {
SlideCaptcha.open({
title: "找回密码安全验证",
subtitle: "拖动滑块完成验证后继续重置",
apiBase: ApiClient.baseUrl || "",
onSuccess: completeSlideCaptcha,
onCancel: function () {
currentSlideUuid = "";
currentSlideX = 0;
},
onError: function (error) {
layerInstance.msg(
(error && error.message) || "滑动验证失败,请重试",
{ icon: 2 },
);
renderSlideCaptchaEntry();
},
});
})
.catch(function (error) {
layerInstance.msg(error.message || "滑动验证组件加载失败", {
icon: 2,
});
});
}
function renderSlideCaptchaEntry() {
$("#captchaInput").hide();
$("#captchaRefreshLink").hide();
$("#captchaGroup").addClass("is-slide-mode");
$("#sendSmsBtn").prop("disabled", true);
$("#captchaContainer")
.removeAttr("onclick")
.html(
'<div class="slide-verify-card" role="button" tabindex="0"><div class="slide-verify-mark"></div><div class="slide-verify-main"><div class="slide-verify-title">拖动滑块验证</div><div class="slide-verify-desc">完成后获取短信</div></div><div class="slide-verify-action">开始</div></div>',
);
$(".slide-verify-card")
.off("click keydown")
.on("click", openSlideCaptcha)
.on("keydown", function (event) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
openSlideCaptcha();
}
});
}
function validatePhone(show) {
const value = $("#phone").val().trim();
if (!AuthPageUtil.isPhone(value)) {
if (show) showError("phone", "请输入正确的手机号");
return false;
}
clearError("phone");
return true;
}
function validateSmsCode(show) {
const value = $("#smsCode").val().trim();
if (!/^\d{4,6}$/.test(value)) {
if (show) showError("smsCode", "请输入4-6位短信验证码");
return false;
}
clearError("smsCode");
return true;
}
function validatePassword(show) {
if (!AuthPageUtil.isPassword($("#password").val())) {
if (show) showError("password", "密码长度应为6-20位");
return false;
}
clearError("password");
return true;
}
function validateConfirmPassword(show) {
if ($("#confirmPassword").val() !== $("#password").val()) {
if (show) showError("confirmPassword", "两次输入的密码不一致");
return false;
}
clearError("confirmPassword");
return true;
}
function getVerifyParams(show) {
if (currentVerifyType === "slide") {
if (!currentSlideUuid) {
if (show) layerInstance.msg("请先完成滑动验证", { icon: 2 });
return null;
}
return {
verifyType: "slide",
slideUuid: currentSlideUuid,
slideX: currentSlideX,
};
}
const code = $("#captchaInput").val().trim();
if (!code || !currentCaptchaId) {
if (show) showError("captcha", "请填写有效验证码");
return null;
}
clearError("captcha");
return { verifyType: "captcha", code, uuid: currentCaptchaId };
}
function sendSmsCode() {
if (!validatePhone(true)) return;
if (currentVerifyType === "slide" && !currentSlideUuid) {
layerInstance.msg("请先完成滑动验证后获取短信验证码", { icon: 2 });
return;
}
const verifyParams = getVerifyParams(true);
if (!verifyParams) return;
$("#sendSmsBtn").prop("disabled", true);
AuthPageUtil.sendSmsCode(
Object.assign(
{ phoneNumber: $("#phone").val().trim() },
verifyParams,
),
)
.then(function (res) {
if (ApiClient.isSuccess(res)) {
layerInstance.msg("短信验证码已发送", { icon: 1 });
AuthPageUtil.startSmsCountdown("#sendSmsBtn");
return;
}
$("#sendSmsBtn").prop("disabled", false);
layerInstance.msg((res && res.msg) || "短信验证码发送失败", {
icon: 2,
});
refreshCaptcha();
})
.catch(function () {
$("#sendSmsBtn").prop("disabled", false);
layerInstance.msg("网络异常,短信发送失败", { icon: 2 });
refreshCaptcha();
});
}
function validateForm() {
if (
!validatePhone(true) ||
!validateSmsCode(true) ||
!validatePassword(true) ||
!validateConfirmPassword(true)
)
return false;
if (!getVerifyParams(true)) return false;
return true;
}
function buildResetData() {
return Object.assign(
{
phoneNumber: $("#phone").val().trim(),
smsCode: $("#smsCode").val().trim(),
password: hex_md5($("#password").val()),
},
getVerifyParams(false) || {},
);
}
function submitResetPassword() {
const payload = buildResetData();
console.warn(
"找回密码提交参数已准备好,但最新接口文档暂未提供提交接口:",
payload,
);
layerInstance.alert(
"最新接口文档暂未提供找回密码提交接口。页面流程、短信验证码和验证参数已按参考项目准备好,等后端补接口后即可接入提交。",
{ title: "暂不能提交" },
);
}
layui.use(["layer", "jquery"], function () {
layerInstance = layui.layer;
loadVerifyConfig().then(refreshCaptcha);
$("#sendSmsBtn").on("click", sendSmsCode);
$("#resetPasswordForm").on("submit", function (event) {
event.preventDefault();
if (validateForm()) submitResetPassword();
});
$("#phone").on("blur", function () {
validatePhone(true);
});
$("#smsCode").on("blur", function () {
validateSmsCode(true);
});
$("#password").on("blur", function () {
validatePassword(true);
});
$("#confirmPassword").on("blur", function () {
validateConfirmPassword(true);
});
$("#captchaInput").on("input", function () {
clearError("captcha");
});
});
</script>
</body>
</html>
+1192
View File
File diff suppressed because it is too large Load Diff
+424
View File
@@ -0,0 +1,424 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>彩先知 - 双色球走势图</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/trend.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<div class="breadcrumb-bar">
<a href="javascript:history.back();"
><i class="layui-icon layui-icon-left"></i> 返回</a
>
<span>/</span>
<span>双色球走势图</span>
</div>
<div class="page-title-bar">
<div class="page-title">双色球走势图</div>
<div class="qishu">
<div class="qishu-but active" data-count="30">30期</div>
<div class="qishu-but" data-count="60">60期</div>
<div class="qishu-but" data-count="100">100期</div>
</div>
</div>
<div class="table-wrapper">
<table class="trend-table" border="1" id="trend-table">
<thead>
<tr>
<td rowspan="2" class="col-issue">期号</td>
<td rowspan="2" class="col-code">开奖号</td>
<td colspan="33" class="zone-red-header">红球</td>
<td colspan="16" class="zone-blue-header">蓝球</td>
</tr>
<tr id="header-row"></tr>
</thead>
<tbody id="data-body"></tbody>
</table>
</div>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
CommonUtil.loadPageConfig();
function checkLogin() {
var token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
checkLogin();
renderNav();
(function () {
var RED_MAX = 33,
BLUE_MAX = 16;
var currentCount = 30;
var allData = [];
// Build header row
function buildHeader() {
var html = "";
for (var i = 1; i <= RED_MAX; i++) {
html += "<td>" + (i < 10 ? "0" + i : i) + "</td>";
}
for (var i = 1; i <= BLUE_MAX; i++) {
html += "<td>" + (i < 10 ? "0" + i : i) + "</td>";
}
$("#header-row").html(html);
}
// Parse openCode like "01,10,11,16,24,26+03"
function parseCode(code) {
var parts = code.split("+");
var reds = parts[0].split(",").map(function (n) {
return parseInt(n);
});
var blues = parts[1]
? parts[1].split(",").map(function (n) {
return parseInt(n);
})
: [];
return { reds: reds, blues: blues };
}
// Render data rows
function renderData(count) {
var data = allData.slice(0, count);
var html = "";
for (var r = 0; r < data.length; r++) {
var item = data[r];
var parsed = parseCode(item.openCode);
var redSet = {};
var blueSet = {};
parsed.reds.forEach(function (n) {
redSet[n] = true;
});
parsed.blues.forEach(function (n) {
blueSet[n] = true;
});
html += "<tr>";
html += '<td class="col-issue">' + item.issue + "</td>";
html +=
'<td class="col-code">' +
item.openCode.replace(/\+/g, "+") +
"</td>";
for (var i = 1; i <= RED_MAX; i++) {
if (redSet[i]) {
html +=
'<td><span class="hit-red">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
} else {
html +=
'<td><span class="miss-num">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
}
}
for (var i = 1; i <= BLUE_MAX; i++) {
if (blueSet[i]) {
html +=
'<td><span class="hit-blue">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
} else {
html +=
'<td><span class="miss-num">' +
(i < 10 ? "0" + i : i) +
"</span></td>";
}
}
html += "</tr>";
}
// Statistics
html += buildStats(data);
$("#data-body").html(html);
}
// Build statistics rows
function buildStats(data) {
var redCount = {},
blueCount = {};
var redMaxOmit = {},
blueMaxOmit = {};
var redCurrOmit = {},
blueCurrOmit = {};
var redMaxConsec = {},
blueMaxConsec = {};
var redAvgOmit = {},
blueAvgOmit = {};
for (var i = 1; i <= RED_MAX; i++) {
redCount[i] = 0;
redMaxOmit[i] = 0;
redCurrOmit[i] = 0;
redMaxConsec[i] = 0;
redAvgOmit[i] = 0;
}
for (var i = 1; i <= BLUE_MAX; i++) {
blueCount[i] = 0;
blueMaxOmit[i] = 0;
blueCurrOmit[i] = 0;
blueMaxConsec[i] = 0;
blueAvgOmit[i] = 0;
}
// Calculate occurrence count, max omission, current omission, max consecutive, avg omission
for (var i = 1; i <= RED_MAX; i++) {
var omit = 0,
maxOmit = 0,
consec = 0,
maxConsec = 0,
totalOmit = 0,
omitCount = 0;
for (var r = 0; r < data.length; r++) {
var parsed = parseCode(data[r].openCode);
if (parsed.reds.indexOf(i) >= 0) {
redCount[i]++;
if (omit > maxOmit) maxOmit = omit;
totalOmit += omit;
omitCount++;
omit = 0;
consec++;
if (consec > maxConsec) maxConsec = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > maxOmit) maxOmit = omit;
redCurrOmit[i] = omit;
redMaxOmit[i] = maxOmit;
redMaxConsec[i] = maxConsec;
redAvgOmit[i] =
omitCount > 0 ? (totalOmit / omitCount).toFixed(1) : data.length;
}
for (var i = 1; i <= BLUE_MAX; i++) {
var omit = 0,
maxOmit = 0,
consec = 0,
maxConsec = 0,
totalOmit = 0,
omitCount = 0;
for (var r = 0; r < data.length; r++) {
var parsed = parseCode(data[r].openCode);
if (parsed.blues.indexOf(i) >= 0) {
blueCount[i]++;
if (omit > maxOmit) maxOmit = omit;
totalOmit += omit;
omitCount++;
omit = 0;
consec++;
if (consec > maxConsec) maxConsec = consec;
} else {
omit++;
consec = 0;
}
}
if (omit > maxOmit) maxOmit = omit;
blueCurrOmit[i] = omit;
blueMaxOmit[i] = maxOmit;
blueMaxConsec[i] = maxConsec;
blueAvgOmit[i] =
omitCount > 0 ? (totalOmit / omitCount).toFixed(1) : data.length;
}
var stats = "";
// Occurrence count row
stats +=
'<tr class="stat-row"><td colspan="2" class="stat-label">出现次数</td>';
for (var i = 1; i <= RED_MAX; i++)
stats += "<td>" + redCount[i] + "</td>";
for (var i = 1; i <= BLUE_MAX; i++)
stats += "<td>" + blueCount[i] + "</td>";
stats += "</tr>";
// Max omission row
stats +=
'<tr class="stat-row alt"><td colspan="2" class="stat-label">最大遗漏</td>';
for (var i = 1; i <= RED_MAX; i++)
stats += "<td>" + redMaxOmit[i] + "</td>";
for (var i = 1; i <= BLUE_MAX; i++)
stats += "<td>" + blueMaxOmit[i] + "</td>";
stats += "</tr>";
// Avg omission row
stats +=
'<tr class="stat-row"><td colspan="2" class="stat-label">平均遗漏</td>';
for (var i = 1; i <= RED_MAX; i++)
stats += "<td>" + redAvgOmit[i] + "</td>";
for (var i = 1; i <= BLUE_MAX; i++)
stats += "<td>" + blueAvgOmit[i] + "</td>";
stats += "</tr>";
// Max consecutive row
stats +=
'<tr class="stat-row alt"><td colspan="2" class="stat-label">最大连出</td>';
for (var i = 1; i <= RED_MAX; i++)
stats += "<td>" + redMaxConsec[i] + "</td>";
for (var i = 1; i <= BLUE_MAX; i++)
stats += "<td>" + blueMaxConsec[i] + "</td>";
stats += "</tr>";
// Current omission row
stats +=
'<tr class="stat-row"><td colspan="2" class="stat-label">当前遗漏</td>';
for (var i = 1; i <= RED_MAX; i++)
stats += "<td>" + redCurrOmit[i] + "</td>";
for (var i = 1; i <= BLUE_MAX; i++)
stats += "<td>" + blueCurrOmit[i] + "</td>";
stats += "</tr>";
return stats;
}
// Load data from API
function loadData() {
ApiClient.post(
"/api/web/lotteryResult/getList",
{ suoxie: "ssq" },
function (res) {
if (res && res.data) {
allData = res.data;
renderData(currentCount);
}
},
);
}
// Period count buttons
$(document).on("click", ".qishu-but", function () {
$(".qishu-but").removeClass("active");
$(this).addClass("active");
currentCount = parseInt($(this).data("count"));
renderData(currentCount);
});
buildHeader();
loadData();
})();
</script>
</body>
</html>
+818
View File
@@ -0,0 +1,818 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 提现</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/publish.css" />
<link rel="stylesheet" href="../public/css/tixian.css" />
</head>
<body>
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img src="../images/home-logo.png" alt="彩先知" class="topbar__logo" />
</a>
<div class="topbar__nav" id="navLotteryItems"></div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary">免费注册</a>
</div>
<div id="loggedInBox" class="topbar__logged tx-hidden">
<a href="usercenter.html" class="topbar__profile">
<img id="userAvatar" src="../public/img/userimg.png" alt="" class="topbar__avatar" />
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">退出登录</button>
</div>
</div>
</div>
</nav>
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<a href="usercenter.html">个人中心</a><span class="sep">></span>
<span>提现</span>
</div>
<div class="tx-balance-grid">
<div class="tx-balance-card">
<div class="tx-balance-icon account">
<i class="layui-icon layui-icon-rmb"></i>
</div>
<div class="tx-balance-info">
<div class="tx-balance-label">账户余额</div>
<div class="tx-balance-value" id="accountBalance">0.00<span class="unit"></span></div>
</div>
</div>
<div class="tx-balance-card">
<div class="tx-balance-icon sales">
<i class="layui-icon layui-icon-group"></i>
</div>
<div class="tx-balance-info">
<div class="tx-balance-label">销售余额</div>
<div class="tx-balance-value" id="salesBalance">0.00<span class="unit"></span></div>
</div>
</div>
<div class="tx-balance-card">
<div class="tx-balance-icon expert">
<i class="layui-icon layui-icon-star-fill"></i>
</div>
<div class="tx-balance-info">
<div class="tx-balance-label">专家余额</div>
<div class="tx-balance-value" id="expertBalance">0.00<span class="unit"></span></div>
</div>
</div>
<div class="tx-balance-card">
<div class="tx-balance-icon withdrawable">
<i class="layui-icon layui-icon-ok-circle"></i>
</div>
<div class="tx-balance-info">
<div class="tx-balance-label">可提现金额</div>
<div class="tx-balance-value" id="withdrawableAmount">0.00<span class="unit"></span></div>
</div>
</div>
<div class="tx-balance-card">
<div class="tx-balance-icon frozen">
<i class="layui-icon layui-icon-password"></i>
</div>
<div class="tx-balance-info">
<div class="tx-balance-label">冻结金额</div>
<div class="tx-balance-value" id="frozenAmount">0.00<span class="unit"></span></div>
</div>
</div>
</div>
<div class="publish-tab-bar tx-tab-bar">
<div class="publish-tab-item active" data-tab="withdraw" onclick="switchTab('withdraw')">提现</div>
<div class="publish-tab-item" data-tab="transfer" onclick="switchTab('transfer')">余额转入</div>
<a href="tixianlist.html" class="publish-tab-item">提现记录</a>
</div>
<div class="tx-panel active" id="panelWithdraw">
<div class="cxz-form-card">
<div class="cxz-form-title">申请提现</div>
<div class="cxz-form-group">
<label class="cxz-form-label">提现来源 <span class="required">*</span></label>
<input id="withdrawSource" type="hidden" value="sales" />
<div class="tx-choice-grid" id="withdrawSourceCards"></div>
</div>
<div class="cxz-form-group">
<label class="cxz-form-label">可提现金额</label>
<div class="tx-available" id="withdrawAvailableAmount">0.00 元</div>
</div>
<div class="cxz-form-group">
<label class="cxz-form-label">提现金额 <span class="required">*</span></label>
<div class="tx-amount-row">
<input class="cxz-form-input" id="withdrawAmount" type="number" min="1" step="0.01" placeholder="请输入提现金额" />
<button class="tx-all-btn" id="withdrawAll" type="button">全部</button>
</div>
</div>
<div class="cxz-form-group">
<label class="cxz-form-label">提现方式 <span class="required">*</span></label>
<input id="withdrawMethod" type="hidden" value="alipay" />
<input id="receiveAccountId" type="hidden" />
<div class="tx-method-grid" id="withdrawMethodCards"></div>
</div>
<div class="cxz-form-group">
<div class="tx-account-head">
<label class="cxz-form-label tx-label-tight">收款账户 <span class="required">*</span></label>
<button class="tx-link-btn" type="button" onclick="openAddAccount()">新增收款账户</button>
</div>
<div class="tx-account-grid" id="receiveAccountCards"></div>
<div class="tx-add-panel" id="addAccountPanel"></div>
<div class="tx-note" id="withdrawHint">请选择或新增对应提现方式的收款账户。</div>
</div>
<button class="cxz-submit-btn" id="withdrawSubmitBtn" onclick="submitWithdraw()">确认提现</button>
</div>
</div>
<div class="tx-panel" id="panelTransfer">
<div class="cxz-form-card">
<div class="cxz-form-title">余额转入</div>
<div class="cxz-form-group">
<label class="cxz-form-label">转出来源 <span class="required">*</span></label>
<input id="transferSource" type="hidden" value="sales" />
<div class="tx-choice-grid" id="transferSourceCards"></div>
</div>
<div class="cxz-form-group">
<label class="cxz-form-label">可转余额</label>
<div class="tx-available" id="transferAvailableAmount">0.00 元</div>
</div>
<div class="cxz-form-group">
<label class="cxz-form-label">转入金额 <span class="required">*</span></label>
<div class="tx-amount-row">
<input class="cxz-form-input" id="transferAmount" type="number" min="1" step="0.01" placeholder="请输入转入账户余额的金额" />
<button class="tx-all-btn" id="transferAll" type="button">全部</button>
</div>
<div class="tx-note">转入账户余额后,可用于购买付费文章、专家预测或其他站内服务。</div>
</div>
<button class="cxz-submit-btn" id="transferSubmitBtn" onclick="submitTransfer()">确认转入</button>
</div>
</div>
</main>
<div class="tx-modal-mask" id="receiveAccountEditMask">
<div class="tx-modal">
<div class="tx-modal-head">
<div class="tx-modal-title">编辑收款账户</div>
<button class="tx-modal-close" type="button" onclick="closeReceiveAccountEdit()">×</button>
</div>
<div class="tx-modal-body">
<div id="receiveAccountEditBody"></div>
<div class="tx-modal-actions">
<button class="tx-secondary-btn" type="button" onclick="closeReceiveAccountEdit()">取消</button>
<button class="tx-small-btn" type="button" onclick="saveReceiveAccountEdit()">保存修改</button>
</div>
</div>
</div>
</div>
<footer class="site-footer">
<div class="site-footer__inner">
<img src="../images/home-logo.png" alt="彩先知" class="site-footer__logo" />
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a id="beian" class="tx-beian-link" href="https://beian.miit.gov.cn/" target="_blank">工信部备案号</a>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button class="gotop" id="backTop" type="button" onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"></button>
<script>
const $ = layui.$;
let receiveAccounts = [];
let editingReceiveAccountId = "";
let userBalances = { account: 0, sales: 0, expert: 0, withdrawable: 0, frozen: 0 };
const walletSourceOptions = [
{ value: "sales", title: "销售余额", desc: "销售返佣可提现或转入账户余额" },
{ value: "expert", title: "专家余额", desc: "专家收益可提现或转入账户余额" },
];
const withdrawMethodOptions = [
{ value: "alipay", title: "支付宝", desc: "支持支付宝账号或收款二维码" },
{ value: "wechat_qr", title: "微信", desc: "使用微信收款二维码" },
{ value: "bank", title: "银行卡", desc: "绑定银行卡收款" },
];
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function isSuccess(res) {
return ApiClient.isSuccess ? ApiClient.isSuccess(res) : res && (res.code === 0 || res.code === 200);
}
function pickData(res) {
return ApiClient.pickData ? ApiClient.pickData(res) : res && res.data;
}
function asArray(value) {
return ApiClient.asArray ? ApiClient.asArray(value) : Array.isArray(value) ? value : value ? [value] : [];
}
function escapeHtml(value) {
return String(value == null ? "" : value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function fieldValue(item, keys) {
for (let i = 0; i < keys.length; i += 1) {
const value = item && item[keys[i]];
if (value !== undefined && value !== null && value !== "") return value;
}
return "";
}
function money(value) {
const number = Number(value || 0);
return Number.isFinite(number) ? number.toFixed(2) : "0.00";
}
function normalizeReceiveType(type) {
const value = String(type || "").toLowerCase();
if (value === "wechat") return "wechat_qr";
if (value === "alipay_qr" || value === "alipay_qrcode") return "alipay";
return value;
}
function getAccountId(item) {
return fieldValue(item, ["id", "receiveAccountId", "accountId"]);
}
function getAccountById(id) {
return receiveAccounts.find((item) => String(getAccountId(item)) === String(id)) || null;
}
function getSourceAmount(source) {
return Number(userBalances[source] || 0);
}
function getQrcodeUrl(item) {
return fieldValue(item, ["qrcodeUrl", "qrCodeUrl", "ossUrl", "url", "fileUrl"]);
}
function getQrcodeFileId(item) {
return fieldValue(item, ["qrCodeFileId", "qrcodeFileId", "qrcodeOssId", "ossId", "fileId"]);
}
function hasQrcode(item) {
return Boolean(getQrcodeUrl(item) || getQrcodeFileId(item));
}
function getReceiveTypeText(type) {
const value = normalizeReceiveType(type);
if (value === "bank") return "银行卡";
if (value === "wechat_qr") return "微信";
return "支付宝";
}
function getReceiveAccountText(item) {
const type = normalizeReceiveType(item.receiveType || item.withdrawType);
if (type === "bank") {
return [item.bankName, fieldValue(item, ["bankCardNo", "bankAccount", "accountNo"])].filter(Boolean).join(" ");
}
if (type === "wechat_qr") {
return fieldValue(item, ["wechatAccount", "account", "accountNo", "receiveAccount"]) || (hasQrcode(item) ? "已保存微信收款码" : "微信收款账户");
}
return fieldValue(item, ["alipayAccount", "account", "accountNo", "receiveAccount"]) || (hasQrcode(item) ? "已保存支付宝收款码" : "支付宝收款账户");
}
function checkLogin() {
const token = CommonUtil.getAuthToken ? CommonUtil.getAuthToken() : localStorage.getItem("token");
if (!token) {
window.location.href = "login.html";
return;
}
$("#notLoggedInBox").addClass("tx-hidden");
$("#loggedInBox").removeClass("tx-hidden");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || data.nickname || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").removeClass("tx-hidden");
$("#loggedInBox").addClass("tx-hidden");
},
);
loadWalletOverview();
}
function renderNav() {
ApiClient.get(ApiClient.API.homeNav).then(function (res) {
if (!isSuccess(res) || !res.data) return;
const $nav = $("#navLotteryItems").empty();
$.each(asArray(pickData(res)), function (_, item) {
if (item.type && item.type !== "lottery") return;
const code = item.code || item.suoxie || "";
if (!code) return;
$nav.append('<a href="cai.html?id=' + encodeURIComponent(code) + '" class="topbar__nav-link">' + escapeHtml(item.name || item.lotteryName || code) + "</a>");
});
});
}
function loadWalletOverview() {
ApiClient.get(ApiClient.API.walletOverview).then(function (res) {
if (!isSuccess(res)) return;
const data = pickData(res) || {};
userBalances.account = Number(fieldValue(data, ["accountBalance", "balance", "accountAmount"]));
userBalances.sales = Number(fieldValue(data, ["salesBalance", "saleBalance", "distributionBalance", "commissionBalance"]));
userBalances.expert = Number(fieldValue(data, ["expertBalance", "expertCommission", "expertAmount"]));
userBalances.withdrawable = Number(fieldValue(data, ["withdrawableAmount", "availableWithdrawAmount", "availableAmount"])) || userBalances.sales + userBalances.expert;
userBalances.frozen = Number(fieldValue(data, ["frozenAmount", "freezeAmount", "frozenBalance"]));
$("#accountBalance").html(money(userBalances.account) + '<span class="unit"></span>');
$("#salesBalance").html(money(userBalances.sales) + '<span class="unit"></span>');
$("#expertBalance").html(money(userBalances.expert) + '<span class="unit"></span>');
$("#withdrawableAmount").html(money(userBalances.withdrawable) + '<span class="unit"></span>');
$("#frozenAmount").html(money(userBalances.frozen) + '<span class="unit"></span>');
updateAvailableAmounts();
renderWalletSourceCards();
});
}
function loadReceiveAccounts(selectedId) {
return ApiClient.get(ApiClient.API.walletReceiveAccountList)
.then(function (res) {
const data = pickData(res);
receiveAccounts = isSuccess(res) ? asArray((data && (data.records || data.rows || data.list)) || data) : [];
renderReceiveAccounts(selectedId);
})
.catch(function () {
receiveAccounts = [];
renderReceiveAccounts();
});
}
function switchTab(tab) {
$(".publish-tab-item[data-tab]").removeClass("active");
$('.publish-tab-item[data-tab="' + tab + '"]').addClass("active");
$(".tx-panel").removeClass("active");
if (tab === "withdraw") $("#panelWithdraw").addClass("active");
if (tab === "transfer") $("#panelTransfer").addClass("active");
}
function renderWalletSourceCards() {
renderSourceCards("#withdrawSourceCards", "#withdrawSource", function () {
updateAvailableAmounts();
});
renderSourceCards("#transferSourceCards", "#transferSource", function () {
updateAvailableAmounts();
});
}
function renderSourceCards(containerSelector, inputSelector, onChange) {
const $container = $(containerSelector).empty();
const selectedValue = $(inputSelector).val();
$.each(walletSourceOptions, function (_, item) {
const selected = selectedValue === item.value ? " selected" : "";
const $card = $(
'<div class="tx-choice-card' + selected + '" data-value="' + item.value + '">' +
'<div class="tx-choice-title">' + escapeHtml(item.title) + '</div>' +
'<div class="tx-choice-desc">' + escapeHtml(item.desc) + "" + money(getSourceAmount(item.value)) + " 元</div>" +
"</div>",
);
$card.on("click", function () {
$(inputSelector).val(item.value);
renderSourceCards(containerSelector, inputSelector, onChange);
if (onChange) onChange();
});
$container.append($card);
});
}
function updateAvailableAmounts() {
$("#withdrawAvailableAmount").text(money(getSourceAmount($("#withdrawSource").val())) + " 元");
$("#transferAvailableAmount").text(money(getSourceAmount($("#transferSource").val())) + " 元");
}
function renderMethodCards() {
const $container = $("#withdrawMethodCards").empty();
const selectedMethod = $("#withdrawMethod").val();
$.each(withdrawMethodOptions, function (_, item) {
const selected = selectedMethod === item.value ? " selected" : "";
const $card = $(
'<div class="tx-method-card' + selected + '" data-method="' + item.value + '">' +
'<div class="tx-method-title">' + escapeHtml(item.title) + '</div>' +
'<div class="tx-method-desc">' + escapeHtml(item.desc) + "</div>" +
"</div>",
);
$card.on("click", function () {
$("#withdrawMethod").val(item.value);
$("#receiveAccountId").val("");
$("#addAccountPanel").removeClass("show").empty();
renderMethodCards();
renderReceiveAccounts();
});
$container.append($card);
});
}
function renderReceiveAccounts(selectedId) {
const method = $("#withdrawMethod").val();
const $wrap = $("#receiveAccountCards").empty();
const list = receiveAccounts.filter((item) => normalizeReceiveType(item.receiveType || item.withdrawType) === method);
if (selectedId) $("#receiveAccountId").val(selectedId);
if (!list.length) {
$wrap.html('<div class="tx-empty">当前提现方式暂无收款账户,请先新增。</div>');
$("#withdrawHint").text("新增收款账户并保存后,就可以选择它提交提现。");
return;
}
$.each(list, function (_, item) {
const id = getAccountId(item);
const current = $("#receiveAccountId").val();
const selected = String(current) === String(id) || (!current && item.isDefault == 1);
if (selected) $("#receiveAccountId").val(id);
const defaultTag = item.isDefault == 1 ? "<em>默认</em>" : "";
const $card = $(
'<div class="tx-account-card' + (selected ? " selected" : "") + '" data-id="' + escapeHtml(id) + '">' +
'<div class="tx-account-title">' + escapeHtml(item.realName || item.name || getReceiveTypeText(method)) + defaultTag + '</div>' +
'<div class="tx-account-desc">' + escapeHtml(getReceiveTypeText(method) + "" + getReceiveAccountText(item)) + '</div>' +
'<div class="tx-account-actions">' +
'<span data-action="edit">编辑</span>' +
(item.isDefault == 1 ? "" : '<span data-action="default">设默认</span>') +
'<span data-action="delete">删除</span>' +
"</div>" +
"</div>",
);
$card.on("click", function () {
$("#receiveAccountId").val(id);
renderReceiveAccounts();
});
$card.find("[data-action]").on("click", function (event) {
event.stopPropagation();
const action = $(this).data("action");
if (action === "edit") openReceiveAccountEdit(id);
if (action === "default") setDefaultReceiveAccount(id);
if (action === "delete") deleteReceiveAccount(id);
});
$wrap.append($card);
});
$("#withdrawHint").text("请选择已维护的收款账户;需要变更信息时可直接编辑。");
}
function renderUploadHtml(scope, label, existingUrl) {
return (
'<div class="tx-upload-box">' +
'<input id="' + scope + 'Qrcode" class="tx-qrcode-file tx-hidden" type="file" accept="image/*" />' +
'<button class="tx-secondary-btn" type="button" data-upload-trigger="' + scope + '">' + escapeHtml(label) + '</button>' +
'<span class="tx-upload-name" id="' + scope + 'UploadName">' + (existingUrl ? "已保存收款码,可重新选择图片" : "未选择图片") + '</span>' +
'<img id="' + scope + 'QrcodePreview" class="tx-qrcode-preview' + (existingUrl ? " is-visible" : "") + '" ' + (existingUrl ? 'src="' + escapeHtml(existingUrl) + '"' : "") + " />" +
"</div>"
);
}
function openAddAccount() {
const method = $("#withdrawMethod").val();
const $currentPanel = $("#addAccountPanel");
if ($currentPanel.hasClass("show") && $currentPanel.attr("data-method") === method) {
$currentPanel.removeClass("show").empty().removeAttr("data-method").removeData("method");
return;
}
const $panel = $("#addAccountPanel").addClass("show").attr("data-method", method).data("method", method);
let html = "";
if (method === "alipay") {
html += '<div class="tx-bind-mode" data-bind-type="account"><button class="active" type="button" data-bind-type="account">支付宝账号</button><button type="button" data-bind-type="qrcode">收款二维码</button></div>';
}
html += '<div class="tx-add-grid">';
html += '<input class="cxz-form-input" id="addRealName" placeholder="请输入真实姓名" />';
if (method === "bank") {
html += '<input class="cxz-form-input" id="addBankName" placeholder="请输入开户银行" />';
html += '<input class="cxz-form-input full" id="addAccountNo" placeholder="请输入银行卡号" />';
} else if (method === "wechat_qr") {
html += renderUploadHtml("add", "选择微信收款码", "");
} else {
html += '<input class="cxz-form-input" id="addAccountNo" placeholder="请输入支付宝账号" />';
html += '<div class="tx-alipay-qrcode full tx-hidden">' + renderUploadHtml("add", "选择支付宝收款码", "") + '</div>';
}
html += '<label class="tx-check-row full"><input id="addIsDefault" type="checkbox" /> 设为默认收款账户</label>';
html += "</div>";
html += '<div class="tx-note">保存成功后会自动加入收款账户列表,并用于本次提现选择。</div>';
html += '<button class="cxz-submit-btn tx-save-account-btn" type="button" onclick="saveReceiveAccountAdd()">保存收款账户</button>';
$panel.html(html);
}
function applyAlipayBindMode($panel, bindType) {
$panel.find(".tx-bind-mode").attr("data-bind-type", bindType);
$panel.find(".tx-bind-mode button").removeClass("active");
$panel.find('.tx-bind-mode button[data-bind-type="' + bindType + '"]').addClass("active");
$panel.find("#addAccountNo").toggleClass("tx-hidden", bindType !== "account");
$panel.find(".tx-alipay-qrcode").toggleClass("tx-hidden", bindType !== "qrcode");
}
function uploadQrcode(scope, method) {
const input = $("#" + scope + "Qrcode")[0];
const file = input && input.files && input.files[0];
if (!file) return Promise.resolve({});
const formData = new FormData();
formData.append("file", file);
formData.append("receiveType", method);
return ApiClient.post(ApiClient.API.walletReceiveAccountUploadQrcode, formData).then(function (res) {
if (!isSuccess(res)) throw new Error(res.msg || "收款码上传失败");
const data = pickData(res) || {};
const qrCodeFileId = fieldValue(data, ["qrCodeFileId", "qrcodeFileId", "fileId", "ossId", "id"]) || fieldValue(res, ["qrCodeFileId", "qrcodeFileId", "fileId", "ossId", "id"]);
if (!qrCodeFileId) throw new Error("收款码上传成功,但未返回二维码文件ID");
return { qrCodeFileId: qrCodeFileId };
});
}
function appendQrcodePayload(payload, uploadData) {
if (uploadData && uploadData.qrCodeFileId) payload.qrCodeFileId = uploadData.qrCodeFileId;
return payload;
}
function buildAddPayload(uploadData) {
const method = $("#addAccountPanel").attr("data-method") || $("#withdrawMethod").val();
const realName = $("#addRealName").val().trim();
const account = String($("#addAccountNo").val() || "").trim();
if (!realName) throw new Error("请输入真实姓名");
const payload = {
receiveType: method,
realName: realName,
isDefault: $("#addIsDefault").prop("checked") || receiveAccounts.length === 0 ? 1 : 0,
};
if (method === "bank") {
const bankName = $("#addBankName").val().trim();
if (!bankName) throw new Error("请输入开户银行");
if (!account) throw new Error("请输入银行卡号");
payload.bankName = bankName;
payload.bankCardNo = account;
} else if (method === "wechat_qr") {
appendQrcodePayload(payload, uploadData);
if (!payload.qrCodeFileId) throw new Error("请上传微信收款码");
} else {
const bindType = $("#addAccountPanel .tx-bind-mode").attr("data-bind-type") || "account";
if (bindType === "qrcode") {
appendQrcodePayload(payload, uploadData);
if (!payload.qrCodeFileId) throw new Error("请上传支付宝收款码");
} else {
if (!account) throw new Error("请输入支付宝账号");
payload.alipayAccount = account;
}
}
return payload;
}
function saveReceiveAccountAdd() {
const method = $("#addAccountPanel").attr("data-method") || $("#withdrawMethod").val();
uploadQrcode("add", method)
.then(function (uploadData) {
return ApiClient.post(ApiClient.API.walletReceiveAccount, buildAddPayload(uploadData));
})
.then(function (res) {
if (!isSuccess(res)) throw new Error(res.msg || "保存失败");
const data = pickData(res) || {};
const createdId = fieldValue(data, ["id", "receiveAccountId", "accountId"]);
layui.layer.msg("收款账户已保存", { icon: 1 });
$("#addAccountPanel").removeClass("show").empty();
return loadReceiveAccounts(createdId);
})
.catch(function (err) {
layui.layer.msg(err.message || "保存失败", { icon: 2 });
});
}
function openReceiveAccountEdit(id) {
const item = getAccountById(id);
if (!item) return;
editingReceiveAccountId = id;
const method = normalizeReceiveType(item.receiveType || item.withdrawType);
let html = '<div class="tx-add-grid" data-edit-method="' + method + '">';
html += '<input class="cxz-form-input" id="editRealName" placeholder="请输入真实姓名" value="' + escapeHtml(item.realName || item.name || "") + '" />';
if (method === "bank") {
html += '<input class="cxz-form-input" id="editBankName" placeholder="请输入开户银行" value="' + escapeHtml(item.bankName || "") + '" />';
html += '<input class="cxz-form-input full" id="editAccountNo" placeholder="请输入银行卡号" value="' + escapeHtml(fieldValue(item, ["bankCardNo", "bankAccount", "accountNo"])) + '" />';
} else if (method === "wechat_qr") {
html += renderUploadHtml("edit", "更换微信收款码", getQrcodeUrl(item));
} else {
const isQrcode = hasQrcode(item) && !fieldValue(item, ["alipayAccount", "account", "accountNo", "receiveAccount"]);
html += '<div class="full"><div class="tx-bind-mode" data-bind-type="' + (isQrcode ? "qrcode" : "account") + '"><button class="' + (isQrcode ? "" : "active") + '" type="button" data-bind-type="account">支付宝账号</button><button class="' + (isQrcode ? "active" : "") + '" type="button" data-bind-type="qrcode">收款二维码</button></div></div>';
html += '<input class="cxz-form-input' + (isQrcode ? " tx-hidden" : "") + '" id="editAccountNo" placeholder="请输入支付宝账号" value="' + escapeHtml(fieldValue(item, ["alipayAccount", "account", "accountNo", "receiveAccount"])) + '" />';
html += '<div class="tx-alipay-qrcode full' + (isQrcode ? "" : " tx-hidden") + '">' + renderUploadHtml("edit", "更换支付宝收款码", getQrcodeUrl(item)) + '</div>';
}
html += '<label class="tx-check-row full"><input id="editIsDefault" type="checkbox" ' + (item.isDefault == 1 ? "checked" : "") + " /> 设为默认收款账户</label>";
html += "</div>";
$("#receiveAccountEditBody").html(html);
$("#receiveAccountEditMask").addClass("show");
}
function closeReceiveAccountEdit() {
editingReceiveAccountId = "";
$("#receiveAccountEditBody").empty();
$("#receiveAccountEditMask").removeClass("show");
}
function buildEditPayload(uploadData) {
const item = getAccountById(editingReceiveAccountId);
const method = normalizeReceiveType(item.receiveType || item.withdrawType);
const realName = $("#editRealName").val().trim();
const account = String($("#editAccountNo").val() || "").trim();
if (!realName) throw new Error("请输入真实姓名");
const payload = {
receiveType: method,
realName: realName,
isDefault: $("#editIsDefault").prop("checked") ? 1 : 0,
};
if (method === "bank") {
const bankName = $("#editBankName").val().trim();
if (!bankName) throw new Error("请输入开户银行");
if (!account) throw new Error("请输入银行卡号");
payload.bankName = bankName;
payload.bankCardNo = account;
} else if (method === "wechat_qr") {
appendQrcodePayload(payload, uploadData);
if (!payload.qrCodeFileId && getQrcodeFileId(item)) payload.qrCodeFileId = getQrcodeFileId(item);
} else {
const bindType = $("#receiveAccountEditBody .tx-bind-mode").attr("data-bind-type") || "account";
if (bindType === "qrcode") {
appendQrcodePayload(payload, uploadData);
if (!payload.qrCodeFileId && getQrcodeFileId(item)) payload.qrCodeFileId = getQrcodeFileId(item);
if (!payload.qrCodeFileId) throw new Error("请上传支付宝收款码");
} else {
if (!account) throw new Error("请输入支付宝账号");
payload.alipayAccount = account;
}
}
return payload;
}
function saveReceiveAccountEdit() {
if (!editingReceiveAccountId) return;
const item = getAccountById(editingReceiveAccountId);
const method = normalizeReceiveType(item.receiveType || item.withdrawType);
uploadQrcode("edit", method)
.then(function (uploadData) {
return ApiClient.put(ApiClient.API.walletReceiveAccountUpdate(editingReceiveAccountId), buildEditPayload(uploadData));
})
.then(function (res) {
if (!isSuccess(res)) throw new Error(res.msg || "保存失败");
layui.layer.msg("收款账户已更新", { icon: 1 });
const selectedId = editingReceiveAccountId;
closeReceiveAccountEdit();
return loadReceiveAccounts(selectedId);
})
.catch(function (err) {
layui.layer.msg(err.message || "保存失败", { icon: 2 });
});
}
function setDefaultReceiveAccount(id) {
ApiClient.put(ApiClient.API.walletReceiveAccountDefault(id)).then(function (res) {
if (!isSuccess(res)) return layui.layer.msg(res.msg || "设置失败", { icon: 2 });
layui.layer.msg("默认收款账户已更新", { icon: 1 });
loadReceiveAccounts(id);
});
}
function deleteReceiveAccount(id) {
layui.layer.confirm("确认删除这个收款账户?", function (index) {
layui.layer.close(index);
ApiClient.delete(ApiClient.API.walletReceiveAccountDelete(id)).then(function (res) {
if (!isSuccess(res)) return layui.layer.msg(res.msg || "删除失败", { icon: 2 });
layui.layer.msg("已删除", { icon: 1 });
if (String($("#receiveAccountId").val()) === String(id)) $("#receiveAccountId").val("");
loadReceiveAccounts();
});
});
}
function submitWithdraw() {
const amount = Number($("#withdrawAmount").val());
const sourceWallet = $("#withdrawSource").val();
const method = $("#withdrawMethod").val();
const receiveAccountId = $("#receiveAccountId").val();
if (!amount || amount <= 0) return layui.layer.msg("请输入有效的提现金额", { icon: 2 });
if (amount > getSourceAmount(sourceWallet)) return layui.layer.msg("提现金额不能超过可提现余额", { icon: 2 });
if (!receiveAccountId) return layui.layer.msg("请选择收款账户", { icon: 2 });
const $btn = $("#withdrawSubmitBtn").text("提交中...").prop("disabled", true);
ApiClient.post(ApiClient.API.walletWithdraw, {
sourceWallet: sourceWallet,
withdrawType: method,
amount: amount,
receiveAccountId: receiveAccountId,
})
.then(function (res) {
if (!isSuccess(res)) throw new Error(res.msg || "提现失败");
layui.layer.msg("提现申请已提交,请等待审核", { icon: 1 });
$("#withdrawAmount").val("");
loadWalletOverview();
})
.catch(function (err) {
layui.layer.msg(err.message || "提现失败", { icon: 2 });
})
.finally(function () {
$btn.text("确认提现").prop("disabled", false);
});
}
function submitTransfer() {
const amount = Number($("#transferAmount").val());
const sourceWallet = $("#transferSource").val();
if (!amount || amount <= 0) return layui.layer.msg("请输入有效的转入金额", { icon: 2 });
if (amount > getSourceAmount(sourceWallet)) return layui.layer.msg("转入金额不能超过可转余额", { icon: 2 });
const $btn = $("#transferSubmitBtn").text("转入中...").prop("disabled", true);
ApiClient.post(ApiClient.API.walletTransferToAccount, {
sourceWallet: sourceWallet,
amount: amount,
})
.then(function (res) {
if (!isSuccess(res)) throw new Error(res.msg || "操作失败");
layui.layer.msg("余额转入成功", { icon: 1 });
$("#transferAmount").val("");
loadWalletOverview();
})
.catch(function (err) {
layui.layer.msg(err.message || "操作失败", { icon: 2 });
})
.finally(function () {
$btn.text("确认转入").prop("disabled", false);
});
}
$(document).on("click", "[data-upload-trigger]", function () {
$("#" + $(this).data("upload-trigger") + "Qrcode").trigger("click");
});
$(document).on("change", ".tx-qrcode-file", function () {
const scope = this.id.replace("Qrcode", "");
const file = this.files && this.files[0];
$("#" + scope + "UploadName").text(file ? file.name : "未选择图片");
if (file) {
const reader = new FileReader();
reader.onload = function (event) {
$("#" + scope + "QrcodePreview").attr("src", event.target.result).addClass("is-visible");
};
reader.readAsDataURL(file);
}
});
$(document).on("click", ".tx-bind-mode button", function () {
const $panel = $(this).closest("#addAccountPanel, #receiveAccountEditBody");
const bindType = $(this).data("bind-type");
$panel.find(".tx-bind-mode").attr("data-bind-type", bindType);
$panel.find(".tx-bind-mode button").removeClass("active");
$(this).addClass("active");
$panel.find("#addAccountNo, #editAccountNo").toggleClass("tx-hidden", bindType !== "account");
$panel.find(".tx-alipay-qrcode").toggleClass("tx-hidden", bindType !== "qrcode");
});
$("#withdrawAll").on("click", function () {
$("#withdrawAmount").val(money(getSourceAmount($("#withdrawSource").val())));
});
$("#transferAll").on("click", function () {
$("#transferAmount").val(money(getSourceAmount($("#transferSource").val())));
});
$("#receiveAccountEditMask").on("click", function (event) {
if (event.target === this) closeReceiveAccountEdit();
});
$(window).on("scroll", function () {
$(window).scrollTop() > 320 ? $("#backTop").addClass("is-visible") : $("#backTop").removeClass("is-visible");
});
$(function () {
checkLogin();
renderNav();
renderWalletSourceCards();
renderMethodCards();
CommonUtil.loadPageConfig({ ads: false, links: false });
CommonUtil.requireAuth(function () {
loadReceiveAccounts();
});
});
</script>
</body>
</html>
+450
View File
@@ -0,0 +1,450 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 提现记录</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/publish.css" />
<link rel="stylesheet" href="../public/css/pagination.css" />
<link rel="stylesheet" href="../public/css/tixianlist.css" />
</head>
<body>
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img src="../images/home-logo.png" alt="彩先知" class="topbar__logo" />
</a>
<div class="topbar__nav" id="navLotteryItems"></div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary">免费注册</a>
</div>
<div id="loggedInBox" class="topbar__logged tl-hidden">
<a href="usercenter.html" class="topbar__profile">
<img id="userAvatar" src="../public/img/userimg.png" alt="" class="topbar__avatar" />
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">退出登录</button>
</div>
</div>
</div>
</nav>
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<a href="usercenter.html">个人中心</a><span class="sep">></span>
<a href="tixian.html">提现</a><span class="sep">></span>
<span>提现记录</span>
</div>
<section class="tl-card">
<div class="tl-header">
<div>
<div class="tl-title">提现记录</div>
<div class="tl-desc">查看销售余额、专家余额的提现申请进度和收款方式。</div>
</div>
<a class="tl-back-link" href="tixian.html">返回提现</a>
</div>
<div class="tl-filter-bar">
<div class="tl-filter-group">
<span>状态</span>
<div class="tl-segment" id="statusFilters"></div>
</div>
<div class="tl-filter-group">
<span>来源</span>
<div class="tl-segment" id="sourceFilters"></div>
</div>
</div>
<div class="tl-summary">
<div class="tl-summary-item">
<span>记录总数</span>
<strong id="recordTotal">0</strong>
</div>
<div class="tl-summary-item">
<span>本页金额</span>
<strong id="pageAmount">0.00 元</strong>
</div>
<div class="tl-summary-item">
<span>当前筛选</span>
<strong id="filterSummary">全部状态 / 全部来源</strong>
</div>
</div>
<div class="tl-table-wrap">
<table class="tl-table" id="withdrawTable">
<thead>
<tr>
<th>申请单号</th>
<th>提现来源</th>
<th>提现方式</th>
<th>金额</th>
<th>收款账户</th>
<th>状态</th>
<th>申请时间</th>
</tr>
</thead>
<tbody></tbody>
</table>
<div class="tl-empty" id="emptyState">
<div class="tl-empty-icon">¥</div>
<div class="tl-empty-title">暂无提现记录</div>
<div class="tl-empty-desc">当前筛选条件下还没有提现申请。</div>
</div>
</div>
<div class="tl-pagination cxz-pagination" id="withdrawPagination"></div>
</section>
</main>
<footer class="site-footer">
<div class="site-footer__inner">
<img src="../images/home-logo.png" alt="彩先知" class="site-footer__logo" />
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a id="beian" class="tl-beian-link" href="https://beian.miit.gov.cn/" target="_blank">工信部备案号</a>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button class="gotop" id="backTop" type="button" onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"></button>
<script>
const $ = layui.$;
const statusOptions = [
{ value: "", label: "全部" },
{ value: "reviewing", label: "审核中" },
{ value: "success", label: "成功" },
{ value: "failed", label: "失败" },
{ value: "cancelled", label: "取消" },
];
const sourceOptions = [
{ value: "", label: "全部" },
{ value: "sales", label: "销售余额" },
{ value: "expert", label: "专家余额" },
];
const statusTextMap = {
0: "审核中",
1: "成功",
2: "失败",
3: "取消",
reviewing: "审核中",
success: "成功",
failed: "失败",
cancelled: "取消",
};
const statusClassMap = {
0: "reviewing",
1: "success",
2: "failed",
3: "cancelled",
reviewing: "reviewing",
success: "success",
failed: "failed",
cancelled: "cancelled",
};
const sourceTextMap = {
sales: "销售余额",
expert: "专家余额",
};
const methodTextMap = {
alipay: "支付宝",
wechat: "微信收款码",
wechat_qr: "微信收款码",
bank: "银行卡",
bank_card: "银行卡",
};
let currentPage = 1;
let pageSize = 10;
let totalRow = 0;
let currentStatus = "";
let currentSource = "";
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function isSuccess(res) {
return ApiClient.isSuccess ? ApiClient.isSuccess(res) : res && (res.code === 0 || res.code === 200);
}
function pickData(res) {
return ApiClient.pickData ? ApiClient.pickData(res) : res && res.data;
}
function asArray(value) {
return ApiClient.asArray ? ApiClient.asArray(value) : Array.isArray(value) ? value : value ? [value] : [];
}
function escapeHtml(value) {
return String(value == null ? "" : value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function fieldValue(item, keys) {
for (let i = 0; i < keys.length; i += 1) {
const value = item && item[keys[i]];
if (value !== undefined && value !== null && value !== "") return value;
}
return "";
}
function money(value) {
const number = Number(value || 0);
return Number.isFinite(number) ? number.toFixed(2) : "0.00";
}
function checkLogin() {
const token = CommonUtil.getAuthToken ? CommonUtil.getAuthToken() : localStorage.getItem("token");
if (!token) {
window.location.href = "login.html";
return;
}
$("#notLoggedInBox").addClass("tl-hidden");
$("#loggedInBox").removeClass("tl-hidden");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || data.nickname || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").removeClass("tl-hidden");
$("#loggedInBox").addClass("tl-hidden");
},
);
}
function renderNav() {
ApiClient.get(ApiClient.API.homeNav).then(function (res) {
if (!isSuccess(res) || !res.data) return;
const $nav = $("#navLotteryItems").empty();
$.each(asArray(pickData(res)), function (_, item) {
if (item.type && item.type !== "lottery") return;
const code = item.code || item.suoxie || "";
if (!code) return;
$nav.append('<a href="cai.html?id=' + encodeURIComponent(code) + '" class="topbar__nav-link">' + escapeHtml(item.name || item.lotteryName || code) + "</a>");
});
});
}
function renderFilters() {
renderSegment("#statusFilters", statusOptions, currentStatus, function (value) {
currentStatus = value;
currentPage = 1;
loadWithdrawList();
});
renderSegment("#sourceFilters", sourceOptions, currentSource, function (value) {
currentSource = value;
currentPage = 1;
loadWithdrawList();
});
updateFilterSummary();
}
function renderSegment(selector, options, selectedValue, onChange) {
const $wrap = $(selector).empty();
$.each(options, function (_, item) {
const $button = $('<button class="tl-segment-btn" type="button"></button>');
$button.text(item.label);
$button.toggleClass("active", item.value === selectedValue);
$button.on("click", function () {
onChange(item.value);
renderFilters();
});
$wrap.append($button);
});
}
function updateFilterSummary() {
const statusLabel = statusOptions.find((item) => item.value === currentStatus)?.label || "全部";
const sourceLabel = sourceOptions.find((item) => item.value === currentSource)?.label || "全部";
$("#filterSummary").text(statusLabel + "状态 / " + sourceLabel + "来源");
}
function getRecords(data, res) {
return asArray(data.records || data.rows || data.list || res.records || res.rows || res.list || data);
}
function getTotal(data, records, res) {
return Number(data.total || data.totalRow || data.totalCount || data.count || res.total || res.totalRow || res.totalCount || res.count || records.length || 0);
}
function getRecordNo(item) {
return fieldValue(item, ["withdrawNo", "orderNo", "flowNo", "serialNo", "tradeNo", "id"]) || "--";
}
function getAmount(item) {
return Number(fieldValue(item, ["amount", "withdrawAmount", "money", "applyAmount", "flowAmount"]) || 0);
}
function getStatus(item) {
return fieldValue(item, ["status", "withdrawStatus", "auditStatus"]);
}
function getStatusText(item) {
const status = getStatus(item);
return fieldValue(item, ["statusText", "withdrawStatusText", "auditStatusText"]) || statusTextMap[status] || statusTextMap[String(status)] || "--";
}
function getStatusClass(item) {
const status = getStatus(item);
return statusClassMap[status] || statusClassMap[String(status)] || "normal";
}
function getSourceText(item) {
const source = fieldValue(item, ["sourceWallet", "walletType"]);
return fieldValue(item, ["sourceWalletText", "walletTypeText"]) || sourceTextMap[source] || source || "--";
}
function getMethodText(item) {
const method = fieldValue(item, ["withdrawType", "receiveType", "paymentType"]);
return fieldValue(item, ["withdrawTypeText", "receiveTypeText", "paymentTypeText"]) || methodTextMap[method] || method || "--";
}
function getReceiveAccountText(item) {
const direct = fieldValue(item, ["receiveAccountText", "receiveAccount", "accountNo", "alipayAccount", "bankCardNo"]);
if (direct && typeof direct !== "object") return direct;
const receiveAccount = item.receiveAccountInfo || item.receiveAccountVO || (typeof item.receiveAccount === "object" ? item.receiveAccount : {}) || {};
const method = fieldValue(item, ["withdrawType", "receiveType"]);
if (method === "bank") {
return [receiveAccount.bankName, receiveAccount.bankCardNo].filter(Boolean).join(" ") || "--";
}
return fieldValue(receiveAccount, ["alipayAccount", "wechatAccount", "accountNo", "realName"]) || "--";
}
function getCreateTime(item) {
const time = fieldValue(item, ["createTime", "createdTime", "applyTime", "submitTime", "updateTime"]);
return DateUtil.formatDateTimeToYMDHM(time) || time || "--";
}
function loadWithdrawList() {
const params = {
pageNum: currentPage,
pageSize: pageSize,
};
if (currentStatus) params.status = currentStatus;
if (currentSource) params.sourceWallet = currentSource;
$("#withdrawTable").addClass("loading");
ApiClient.get(ApiClient.API.walletWithdrawList, params)
.then(function (res) {
if (!isSuccess(res)) throw new Error(res.msg || "获取提现记录失败");
const data = pickData(res) || res || {};
const records = getRecords(data, res);
totalRow = getTotal(data, records, res);
renderTable(records);
renderPagination();
})
.catch(function (err) {
layui.layer.msg(err.message || "获取提现记录失败", { icon: 2 });
totalRow = 0;
renderTable([]);
renderPagination();
})
.finally(function () {
$("#withdrawTable").removeClass("loading");
});
}
function renderTable(records) {
const $tbody = $("#withdrawTable tbody").empty();
let pageAmount = 0;
if (!records.length) {
$("#withdrawTable").addClass("tl-table-hidden");
$("#emptyState").addClass("show");
$(".tl-summary").addClass("is-hidden");
} else {
$("#withdrawTable").removeClass("tl-table-hidden");
$("#emptyState").removeClass("show");
$(".tl-summary").removeClass("is-hidden");
}
$.each(records, function (_, item) {
const amount = getAmount(item);
pageAmount += Math.abs(amount);
const $row = $("<tr></tr>");
$row.html(
"<td>" + escapeHtml(getRecordNo(item)) + "</td>" +
"<td>" + escapeHtml(getSourceText(item)) + "</td>" +
"<td>" + escapeHtml(getMethodText(item)) + "</td>" +
'<td class="tl-amount">-' + escapeHtml(money(Math.abs(amount))) + " 元</td>" +
"<td>" + escapeHtml(getReceiveAccountText(item)) + "</td>" +
'<td><span class="tl-status ' + getStatusClass(item) + '">' + escapeHtml(getStatusText(item)) + "</span></td>" +
"<td>" + escapeHtml(getCreateTime(item)) + "</td>",
);
$tbody.append($row);
});
$("#recordTotal").text(totalRow);
$("#pageAmount").text(money(pageAmount) + " 元");
updateFilterSummary();
}
function renderPagination() {
if (totalRow <= 0) {
$("#withdrawPagination").empty().addClass("is-hidden");
return;
}
$("#withdrawPagination").removeClass("is-hidden");
layui.use("laypage", function () {
layui.laypage.render({
elem: "withdrawPagination",
count: totalRow,
limit: pageSize,
curr: currentPage,
layout: ["prev", "page", "next"],
jump: function (obj, first) {
if (first) return;
currentPage = obj.curr;
loadWithdrawList();
},
});
});
}
function initFromUrl() {
const query = new URLSearchParams(window.location.search);
const status = query.get("status") || "";
const sourceWallet = query.get("sourceWallet") || "";
if (statusOptions.some((item) => item.value === status)) currentStatus = status;
if (sourceOptions.some((item) => item.value === sourceWallet)) currentSource = sourceWallet;
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320 ? $("#backTop").addClass("is-visible") : $("#backTop").removeClass("is-visible");
});
$(function () {
checkLogin();
renderNav();
initFromUrl();
renderFilters();
CommonUtil.loadPageConfig({ ads: false, links: false });
CommonUtil.requireAuth(function () {
loadWithdrawList();
});
});
</script>
</body>
</html>
+873
View File
@@ -0,0 +1,873 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 推广赚钱</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<script src="../public/js/qrcode.min.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/distribution.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems"></div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="cxz-main">
<div class="cxz-breadcrumb">
<a href="index.html">首页</a><span class="sep">></span>
<a href="usercenter.html">个人中心</a><span class="sep">></span>
<span>推广赚钱</span>
</div>
<!-- Banner -->
<div class="promo-banner">
<h2>推广赚<span>佣金</span></h2>
<p>
复制分享链接,邀请好友注册即可获得分销收益。一级分销返佣更高,多级返佣轻松赚钱!
</p>
</div>
<!-- Stats -->
<div class="stats-row">
<div class="stat-card">
<div class="stat-label">分销余额</div>
<div class="stat-value">
<span id="salesBalance">0.00</span><span class="unit"></span>
</div>
</div>
<div class="stat-card">
<div class="stat-label">累计返利</div>
<div class="stat-value">
<span id="distIncome">0.00</span><span class="unit"></span>
</div>
</div>
<div class="stat-card">
<div class="stat-label">邀请人数</div>
<div class="stat-value">
<span id="totalSubordinates">0</span><span class="unit"></span>
</div>
</div>
<div class="stat-card">
<div class="stat-label">待结算</div>
<div class="stat-value">
<span id="pendingRebate">0.00</span><span class="unit"></span>
</div>
</div>
<div class="stat-card">
<div class="stat-label">返利比例</div>
<div class="stat-value">
<span id="rebateRate">--</span>
</div>
</div>
</div>
<!-- QR Code & Link -->
<div class="promo-content">
<div class="qr-card">
<div class="qr-card-title">推广二维码</div>
<div class="qr-code-box">
<img id="promoQrImg" src="" alt="推广二维码" />
</div>
<div class="qr-card-tip">扫描二维码即可注册</div>
</div>
<div class="link-card">
<div class="link-card-title">推广链接</div>
<div class="link-input-wrap">
<input
type="text"
class="link-input"
id="promoLink"
readonly
value=""
/>
<button class="copy-btn" id="copyLinkBtn" onclick="copyPromoLink()">
复制链接
</button>
</div>
<div class="share-tips">
<h4>推广说明</h4>
<ul>
<li>
通过您的推广链接注册的用户将成为您的<strong>一级下级</strong>
</li>
<li>一级下级消费您可获得<strong>高比例</strong>返佣</li>
<li>
一级下级的下级为您的<strong>二级下级</strong>,也可获得返佣
</li>
<li>推广收益可随时申请<strong>提现</strong></li>
</ul>
</div>
</div>
</div>
<!-- Invite List Table -->
<div class="team-section">
<div class="team-section-title team-section-title--filter">
<span><i class="layui-icon layui-icon-group"></i> 邀请用户</span>
<div class="promo-filter" id="inviteFilter">
<button class="active" type="button" data-filter="all">全部</button>
<button type="button" data-filter="active">已消费</button>
<button type="button" data-filter="new">未消费</button>
</div>
</div>
<table class="team-table">
<thead>
<tr>
<th>头像</th>
<th>用户名称</th>
<th>注册时间</th>
<th>消费金额</th>
<th>贡献返利</th>
<th>状态</th>
</tr>
</thead>
<tbody id="inviteTableBody">
<tr>
<td
colspan="6"
style="
text-align: center;
color: var(--text-muted);
padding: 40px;
"
>
加载中...
</td>
</tr>
</tbody>
</table>
<div class="cxz-pagination" id="invitePager"></div>
</div>
<!-- Rebate Flow Table -->
<div class="team-section">
<div class="team-section-title team-section-title--filter">
<span><i class="layui-icon layui-icon-rmb"></i> 返利流水</span>
<div class="promo-filter" id="rebateFilter">
<button class="active" type="button" data-filter="all">全部</button>
<button type="button" data-filter="pending">待结算</button>
<button type="button" data-filter="success">已结算</button>
</div>
</div>
<table class="team-table">
<thead>
<tr>
<th>流水号</th>
<th>来源用户</th>
<th>返利金额</th>
<th>比例</th>
<th>状态</th>
<th>时间</th>
</tr>
</thead>
<tbody id="rebateTableBody">
<tr>
<td
colspan="6"
style="
text-align: center;
color: var(--text-muted);
padding: 40px;
"
>
加载中...
</td>
</tr>
</tbody>
</table>
<div class="cxz-pagination" id="rebatePager"></div>
</div>
<div class="promo-info-grid">
<div class="team-section promo-info-card">
<div class="team-section-title">
<i class="layui-icon layui-icon-about"></i> 绑定规则
</div>
<div class="promo-rule-list">
<div class="promo-rule-item">
<span>1</span>
<p>好友通过您的推广链接注册后,会自动绑定为邀请用户。</p>
</div>
<div class="promo-rule-item">
<span>2</span>
<p>好友购买付费内容后,会生成返利记录并等待结算。</p>
</div>
<div class="promo-rule-item">
<span>3</span>
<p>返利比例以当前平台配置为准,页面会展示接口返回的比例。</p>
</div>
<div class="promo-rule-item">
<span>4</span>
<p>已结算返利可进入提现页面处理,具体以钱包余额为准。</p>
</div>
</div>
</div>
<div class="team-section promo-info-card">
<div class="team-section-title">
<i class="layui-icon layui-icon-chart-screen"></i> 返利流程
</div>
<div class="promo-flow-list">
<div class="promo-flow-step"><strong>01</strong><span>分享链接</span></div>
<div class="promo-flow-step"><strong>02</strong><span>好友注册</span></div>
<div class="promo-flow-step"><strong>03</strong><span>购买付费文章</span></div>
<div class="promo-flow-step"><strong>04</strong><span>返利结算</span></div>
</div>
</div>
</div>
</main>
<!-- QR Code Modal -->
<div class="cxz-dialog-overlay" id="qrDialog" style="display: none">
<div class="cxz-dialog-box">
<div class="cxz-dialog-header">
<div class="cxz-dialog-title">推广二维码</div>
<button class="cxz-dialog-close" onclick="closeQrDialog()">
&times;
</button>
</div>
<div class="cxz-dialog-body" style="text-align: center; padding: 24px">
<div
id="qrCodeContainer"
style="
display: inline-block;
padding: 16px;
border: 2px dashed var(--border-light);
border-radius: var(--radius-lg);
"
></div>
<p
style="margin-top: 12px; font-size: 13px; color: var(--text-muted)"
>
长按或截图分享给好友
</p>
</div>
</div>
</div>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
const $ = layui.$;
let invitePage = 1;
let rebatePage = 1;
let inviteFilter = "all";
let rebateFilter = "all";
const pageSize = 10;
let promoSummary = {
salesBalance: 0,
totalSubordinates: 0,
distributionIncome: 0,
pendingRebate: 0,
rebateRate: "",
};
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
const token = localStorage.getItem("token");
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
} else {
window.location.href = "login.html";
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
function isSuccess(res) {
return ApiClient.isSuccess
? ApiClient.isSuccess(res)
: res && (res.code === 0 || res.code === 200);
}
function pickData(res, fallback) {
if (ApiClient.pickData) return ApiClient.pickData(res, fallback);
return res && res.data !== undefined && res.data !== null
? res.data
: fallback;
}
function asRows(data) {
if (ApiClient.asArray) return ApiClient.asArray(data);
if (Array.isArray(data)) return data;
if (!data || typeof data !== "object") return [];
return data.records || data.rows || data.list || data.items || [];
}
function firstDefined() {
for (var i = 0; i < arguments.length; i++) {
var value = arguments[i];
if (value !== undefined && value !== null && value !== "") return value;
}
return "";
}
function numberValue(value, fallback) {
var n = Number(value);
return isNaN(n) ? fallback || 0 : n;
}
function updateStats(data) {
data = data || {};
promoSummary.salesBalance = numberValue(
firstDefined(
data.salesBalance,
data.rebateBalance,
data.availableRebate,
data.availableAmount,
data.canWithdrawAmount,
promoSummary.salesBalance,
),
);
promoSummary.totalSubordinates = numberValue(
firstDefined(
data.totalSubordinates,
data.inviteTotal,
data.inviteCount,
data.totalInvite,
promoSummary.totalSubordinates,
),
);
promoSummary.distributionIncome = numberValue(
firstDefined(
data.distributionIncome,
data.rebateTotal,
data.totalRebate,
data.totalAmount,
promoSummary.distributionIncome,
),
);
promoSummary.pendingRebate = numberValue(
firstDefined(
data.pendingRebate,
data.pendingAmount,
data.waitAmount,
data.unsettledAmount,
promoSummary.pendingRebate,
),
);
promoSummary.rebateRate = firstDefined(
data.rebateRate,
data.rate,
data.rebateRatio,
promoSummary.rebateRate,
);
$("#salesBalance").text((promoSummary.salesBalance || 0).toFixed(2));
$("#totalSubordinates").text(promoSummary.totalSubordinates || 0);
$("#distIncome").text(
(promoSummary.distributionIncome || 0).toFixed(2),
);
$("#pendingRebate").text((promoSummary.pendingRebate || 0).toFixed(2));
$("#rebateRate").text(rateText(promoSummary.rebateRate));
}
function rateText(value) {
if (value === undefined || value === null || value === "") return "--";
var n = Number(value);
if (isNaN(n)) return String(value);
return n > 0 && n < 1 ? Math.round(n * 100) + "%" : n + "%";
}
function totalOf(data, rows) {
return (
numberValue(
firstDefined(data.totalRow, data.total, data.totalCount, data.count),
0,
) || (rows ? rows.length : 0)
);
}
function fullShareLink(url, code) {
if (url && /^https?:\/\//.test(url)) return url;
if (url) {
if (url.indexOf("/html/reg.html") >= 0) {
return ApiClient.baseUrl.replace(/\/$/, "") + "/" + url.replace(/^\//, "");
}
return (
ApiClient.baseUrl.replace(/\/$/, "") +
"/html/reg.html?affUrl=" +
encodeURIComponent(url.replace(/^\//, ""))
);
}
return (
location.origin +
"/html/reg.html" +
(code ? "?inviteCode=" + encodeURIComponent(code) : "")
);
}
// Load promotion info (link + QR code)
function loadPromoInfo() {
ApiClient.get(ApiClient.API.referralShareInfo)
.then(function (res) {
if (isSuccess(res)) {
var data = pickData(res, {}) || {};
// Build share link
var shareLink = fullShareLink(
firstDefined(
data.affUrl,
data.shareUrl,
data.shareLink,
data.inviteUrl,
data.promoteUrl,
data.referralUrl,
data.url,
),
firstDefined(
data.inviteCode,
data.referralCode,
data.affCode,
data.code,
),
);
$("#promoLink").val(shareLink);
// Generate QR code
if (shareLink) {
CommonUtil.generateQRCode(shareLink, "promoQrImg", {
width: 200,
height: 200,
colorDark: "#102b6a",
colorLight: "#ffffff",
});
}
updateStats(data);
}
})
.catch(function (err) {
console.error("获取推广信息失败:", err);
});
}
function loadPromoSummary() {
if (!ApiClient.API.referralSummary) return;
ApiClient.get(ApiClient.API.referralSummary)
.then(function (res) {
if (isSuccess(res)) updateStats(pickData(res, {}) || {});
})
.catch(function (err) {
console.error("获取推广统计失败:", err);
});
}
// Copy promo link
function copyPromoLink() {
var link = $("#promoLink").val();
if (!link) {
layui.layer.msg("推广链接为空", { icon: 2 });
return;
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard
.writeText(link)
.then(function () {
layui.layer.msg("链接已复制到剪贴板!", { icon: 1, time: 1500 });
var $btn = $("#copyLinkBtn");
$btn
.text("已复制")
.css(
"background",
"linear-gradient(135deg, var(--green), #2ecc71)",
);
setTimeout(function () {
$btn.text("复制链接").css("background", "");
}, 2000);
})
.catch(function () {
fallbackCopy(link);
});
} else {
fallbackCopy(link);
}
}
function fallbackCopy(text) {
var input = document.createElement("input");
input.value = text;
document.body.appendChild(input);
input.select();
try {
document.execCommand("copy");
layui.layer.msg("链接已复制到剪贴板!", { icon: 1, time: 1500 });
} catch (e) {
layui.layer.msg("复制失败,请手动复制", { icon: 2 });
}
document.body.removeChild(input);
}
function closeQrDialog() {
$("#qrDialog").hide();
}
function avatarCell(item, fallbackName) {
var avatar = firstDefined(item.avatar, item.avatarUrl, item.headImg);
if (avatar) {
return (
'<img src="' +
escapeHtml(avatar) +
'" style="width:32px;height:32px;border-radius:50%;object-fit:cover;border:2px solid rgba(253,185,51,0.4);" />'
);
}
return (
'<div style="width:32px;height:32px;border-radius:50%;background:linear-gradient(135deg,var(--primary),var(--primary-light));color:#fff;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;">' +
escapeHtml((fallbackName || "用").charAt(0)) +
"</div>"
);
}
function statusLabel(label, type) {
return (
'<span class="promo-status ' +
type +
'">' +
escapeHtml(label) +
"</span>"
);
}
function loadInviteList() {
var params = { pageNum: invitePage, pageSize: pageSize };
if (inviteFilter === "active") params.consumed = true;
if (inviteFilter === "new") params.consumed = false;
ApiClient.get(ApiClient.API.referralInviteList, params)
.then(function (res) {
if (isSuccess(res)) {
var data = pickData(res, {}) || {};
var list = asRows(data);
renderInviteList(list);
renderPager("invitePager", totalOf(data, list), invitePage, function (page) {
invitePage = page;
loadInviteList();
});
} else {
renderInviteList([]);
renderPager("invitePager", 0, 1, function () {});
}
})
.catch(function (err) {
console.error("请求邀请用户失败:", err);
$("#inviteTableBody").html(
'<tr><td colspan="6" style="text-align:center;color:var(--text-muted);padding:40px;">加载失败,请重试</td></tr>',
);
});
}
function renderInviteList(list) {
var $tbody = $("#inviteTableBody");
$tbody.empty();
if (!list || !list.length) {
$tbody.html(
'<tr><td colspan="6" style="text-align:center;color:var(--text-muted);padding:40px;">暂无邀请用户</td></tr>',
);
return;
}
$.each(list, function (i, item) {
var nickName = firstDefined(
item.nickName,
item.nickname,
item.userName,
item.username,
item.mobile,
item.phone,
"未知用户",
);
var registerTime = firstDefined(
item.registerTime,
item.regTime,
item.createTime,
item.createdAt,
);
var consumeAmount = numberValue(
firstDefined(item.consumeAmount, item.amount, item.totalAmount),
).toFixed(2);
var rebateAmount = numberValue(
firstDefined(
item.rebateMoney,
item.rebateAmount,
item.commissionAmount,
item.rewardAmount,
),
).toFixed(2);
var consumed =
item.hasConsume === true ||
item.isConsumed === true ||
item.consumeFlag === 1 ||
item.consumeFlag === "1" ||
Number(consumeAmount) > 0;
$tbody.append(
"<tr>" +
"<td>" +
avatarCell(item, nickName) +
"</td>" +
"<td>" +
escapeHtml(nickName) +
"</td>" +
"<td>" +
escapeHtml(registerTime ? String(registerTime).substring(0, 10) : "--") +
"</td>" +
"<td>" +
consumeAmount +
"元</td>" +
'<td style="color:var(--orange);font-weight:600;">' +
rebateAmount +
"元</td>" +
"<td>" +
(consumed
? statusLabel("已消费", "success")
: statusLabel("未消费", "pending")) +
"</td>" +
"</tr>",
);
});
}
function loadRebateList() {
var params = { pageNum: rebatePage, pageSize: pageSize };
if (rebateFilter === "pending") params.status = 0;
if (rebateFilter === "success") params.status = 1;
ApiClient.get(ApiClient.API.referralRebateList, params)
.then(function (res) {
if (isSuccess(res)) {
var data = pickData(res, {}) || {};
var list = asRows(data);
renderRebateList(list);
renderPager("rebatePager", totalOf(data, list), rebatePage, function (page) {
rebatePage = page;
loadRebateList();
});
} else {
renderRebateList([]);
renderPager("rebatePager", 0, 1, function () {});
}
})
.catch(function (err) {
console.error("请求返利流水失败:", err);
$("#rebateTableBody").html(
'<tr><td colspan="6" style="text-align:center;color:var(--text-muted);padding:40px;">加载失败,请重试</td></tr>',
);
});
}
function renderRebateList(list) {
var $tbody = $("#rebateTableBody");
$tbody.empty();
if (!list || !list.length) {
$tbody.html(
'<tr><td colspan="6" style="text-align:center;color:var(--text-muted);padding:40px;">暂无返利流水</td></tr>',
);
return;
}
$.each(list, function (i, item) {
var flowNo = firstDefined(item.flowNo, item.orderNo, item.id, item.rebateNo, "--");
var sourceUser = firstDefined(
item.sourceUserName,
item.inviteUserName,
item.nickName,
item.nickname,
item.mobile,
"未知用户",
);
var amount = numberValue(
firstDefined(
item.rebateMoney,
item.rebateAmount,
item.commissionAmount,
item.rewardAmount,
item.amount,
),
).toFixed(2);
var rate = rateText(firstDefined(item.rebateRate, item.rate, item.ratio));
var status = firstDefined(item.status, item.state, item.settleStatus, 0);
var statusHtml =
status === 1 ||
status === "1" ||
status === "success" ||
status === "settled"
? statusLabel("已结算", "success")
: statusLabel("待结算", "pending");
var time = firstDefined(item.createTime, item.settleTime, item.time, item.createdAt);
$tbody.append(
"<tr>" +
"<td>" +
escapeHtml(flowNo) +
"</td>" +
"<td>" +
escapeHtml(sourceUser) +
"</td>" +
'<td style="color:var(--orange);font-weight:600;">' +
amount +
"元</td>" +
"<td>" +
escapeHtml(rate) +
"</td>" +
"<td>" +
statusHtml +
"</td>" +
"<td>" +
escapeHtml(time ? String(time).substring(0, 19).replace("T", " ") : "--") +
"</td>" +
"</tr>",
);
});
}
function renderPager(elem, total, curr, onJump) {
layui.laypage.render({
elem: elem,
count: total,
limit: pageSize,
curr: curr,
layout: ["prev", "page", "next"],
jump: function (obj, first) {
if (!first && obj.curr !== curr) onJump(obj.curr);
},
});
}
function escapeHtml(str) {
if (str === undefined || str === null) return "";
return String(str)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
// Init
$(function () {
checkLogin();
renderNav();
CommonUtil.loadPageConfig({ ads: false, links: false });
$("#inviteFilter button").on("click", function () {
$("#inviteFilter button").removeClass("active");
$(this).addClass("active");
inviteFilter = $(this).data("filter");
invitePage = 1;
loadInviteList();
});
$("#rebateFilter button").on("click", function () {
$("#rebateFilter button").removeClass("active");
$(this).addClass("active");
rebateFilter = $(this).data("filter");
rebatePage = 1;
loadRebateList();
});
CommonUtil.requireAuth(function () {
loadPromoInfo();
loadPromoSummary();
loadInviteList();
loadRebateList();
});
});
</script>
</body>
</html>
+1277
View File
File diff suppressed because it is too large Load Diff
+339
View File
@@ -0,0 +1,339 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 我的分销</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/distribution.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="page-main">
<!-- Stats -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">下级总数</div>
<div class="stat-value">
<span id="totalSubordinates">0</span><span class="unit"></span>
</div>
</div>
<div class="stat-card">
<div class="stat-label">一级下级</div>
<div class="stat-value">
<span id="levelOneCount">0</span><span class="unit"></span>
</div>
</div>
<div class="stat-card">
<div class="stat-label">二级下级</div>
<div class="stat-value">
<span id="levelTwoCount">0</span><span class="unit"></span>
</div>
</div>
<div class="stat-card">
<div class="stat-label">分销收益</div>
<div class="stat-value">
<span id="distIncome">0.00</span><span class="unit"></span>
</div>
</div>
</div>
<!-- Table -->
<div class="table-section">
<div class="table-section-title">
<i class="layui-icon layui-icon-group"></i> 下级用户列表
</div>
<table id="distTable" lay-filter="distTable" lay-even></table>
<div id="distPage"></div>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
layui.use(["layer", "jquery", "table", "laypage"], function () {
var layer = layui.layer;
var $ = layui.$;
var table = layui.table;
var laypage = layui.laypage;
// Load page config
CommonUtil.loadPageConfig({ ads: false, links: false });
// Check login state for topbar
checkLogin();
renderNav();
// Check auth and load data
CommonUtil.requireAuth(function () {
loadUserDetail();
loadDistList(1);
});
function checkLogin() {
var token = CommonUtil.getToken();
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
if (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) {
$("#userAvatar").attr("src", data.avatar);
}
}
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(
function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
},
);
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
function loadUserDetail() {
CommonUtil.getCurrentUserDetail(function (data) {
if (!data) return;
$("#totalSubordinates").text(data.totalSubordinates || 0);
$("#levelOneCount").text(data.levelOneCount || 0);
$("#levelTwoCount").text(data.levelTwoCount || 0);
$("#distIncome").text((data.distributionIncome || 0).toFixed(2));
});
}
function loadDistList(pageNum) {
currentPage = pageNum;
ApiClient.post(
"/api/web/distribution/getPageList",
{
pageNum: pageNum,
pageSize: pageSize,
},
{
headers: CommonUtil.authHeaders(),
},
)
.then(function (res) {
if (res.code === 0 && res.data) {
renderTable(res.data.records || []);
renderPagination(res.data.total || 0);
} else {
layer.msg(res.msg || "获取数据失败", { icon: 2 });
}
})
.catch(function (err) {
layer.msg("网络错误,请稍后重试", { icon: 2 });
});
}
function renderTable(records) {
if (records.length === 0) {
$("#distTable").replaceWith(
'<div class="empty-state"><i class="layui-icon layui-icon-group"></i><p>暂无下级用户</p></div>',
);
return;
}
table.render({
elem: "#distTable",
cols: [
[
{
field: "nickName",
title: "昵称",
width: 160,
align: "center",
},
{
field: "phoneNumber",
title: "手机号",
width: 160,
align: "center",
templet: function (d) {
if (d.phoneNumber) {
return d.phoneNumber.replace(
/(\d{3})\d{4}(\d{4})/,
"$1****$2",
);
}
return "--";
},
},
{
field: "level",
title: "层级",
width: 100,
align: "center",
templet: function (d) {
return d.level === 1
? '<span style="color:#102b6a;font-weight:600;">一级</span>'
: '<span style="color:#5b2c8e;font-weight:600;">二级</span>';
},
},
{
field: "createTime",
title: "注册时间",
width: 180,
align: "center",
templet: function (d) {
return d.createTime
? d.createTime.substring(0, 19).replace("T", " ")
: "--";
},
},
{
field: "totalRecharge",
title: "累计充值",
width: 130,
align: "center",
templet: function (d) {
return (
'<span style="color:#f15a22;font-weight:600;">' +
(d.totalRecharge || 0).toFixed(2) +
"</span>"
);
},
},
{
field: "distributionIncome",
title: "分销收益",
width: 130,
align: "center",
templet: function (d) {
return (
'<span style="color:#fdb933;font-weight:600;">' +
(d.distributionIncome || 0).toFixed(2) +
"</span>"
);
},
},
],
],
data: records,
even: true,
skin: "line",
size: "lg",
});
}
function renderPagination(total) {
if (total <= pageSize) return;
laypage.render({
elem: "distPage",
count: total,
limit: pageSize,
curr: currentPage,
theme: "#102b6a",
jump: function (obj, first) {
if (!first) {
loadDistList(obj.curr);
}
},
});
}
});
</script>
</body>
</html>
+257
View File
@@ -0,0 +1,257 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 隐私政策</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/static.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="page-main">
<div class="policy-card">
<div class="policy-card-header">
<h1 id="articleTitle">隐私政策</h1>
<p>彩先知隐私政策说明</p>
</div>
<div class="policy-card-body" id="articleContent">
<h2>一、信息收集</h2>
<p>我们收集您主动提供的信息,包括但不限于:</p>
<ul>
<li>注册信息:手机号码、昵称、密码等;</li>
<li>身份信息:实名认证所需的姓名、身份证号码等;</li>
<li>交易信息:充值、提现等交易记录;</li>
<li>内容信息:您发布的文章、评论等用户生成内容;</li>
<li>设备信息:设备型号、操作系统版本、唯一设备标识符等;</li>
<li>日志信息:访问时间、访问页面、IP地址等。</li>
</ul>
<h2>二、信息使用</h2>
<p>我们将收集的信息用于以下目的:</p>
<ul>
<li>提供、维护和改进我们的服务;</li>
<li>创建和管理您的账户;</li>
<li>处理交易和发送相关通知;</li>
<li>向您推送服务相关通知和营销信息(经您同意);</li>
<li>进行数据分析以改善用户体验;</li>
<li>遵守法律法规的要求。</li>
</ul>
<h2>三、信息共享</h2>
<p>
我们不会将您的个人信息出售给任何第三方。在以下情况下,我们可能会共享您的信息:
</p>
<ul>
<li>经您事先同意;</li>
<li>与关联公司共享:仅为本政策声明的目的;</li>
<li>与授权合作伙伴共享:仅为实现本政策中声明的目的;</li>
<li>应法律法规要求:依据法律法规或政府部门的强制性要求。</li>
</ul>
<h2>四、信息存储</h2>
<p>
您的个人信息将存储在中华人民共和国境内。在您注销账户后,我们将在法律法规规定的期限内保留您的相关信息,超过保留期限后将对您的个人信息进行删除或匿名化处理。
</p>
<h2>五、信息安全</h2>
<p>我们采取合理的安全措施保护您的个人信息,包括但不限于:</p>
<ul>
<li>使用加密技术保护数据传输安全;</li>
<li>对敏感信息进行加密存储;</li>
<li>建立严格的数据访问权限控制机制;</li>
<li>定期进行安全评估和漏洞修复。</li>
</ul>
<h2>六、您的权利</h2>
<p>您对您的个人信息享有以下权利:</p>
<ul>
<li>访问权:您有权访问您的个人信息;</li>
<li>更正权:您有权更正不准确的个人信息;</li>
<li>删除权:在特定情况下,您有权要求删除您的个人信息;</li>
<li>撤回同意权:您有权撤回之前给予的同意;</li>
<li>注销权:您有权注销您的账户。</li>
</ul>
<h2>七、未成年人保护</h2>
<p>
我们的产品和服务主要面向成年人。如果您是未满18周岁的未成年人,请在法定监护人的陪同下阅读本政策,并在取得监护人对本政策及使用我们服务的同意后使用我们的服务。
</p>
<h2>八、隐私政策的更新</h2>
<p>
我们可能会不时更新本隐私政策。更新后的政策将在本页面上发布,并通过适当方式通知您。如您在政策更新后继续使用我们的服务,即表示您同意受修订后的政策约束。
</p>
<h2>九、联系我们</h2>
<p>
如您对本隐私政策有任何疑问、意见或建议,请通过以下方式与我们联系:
</p>
<ul>
<li>邮箱:support@caixianzhi.com</li>
<li>客服电话:400-XXX-XXXX</li>
</ul>
<div class="update-date">最后更新日期:2026年4月22日</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
layui.use(["layer", "jquery"], function () {
var $ = layui.$;
// Load page config
CommonUtil.loadPageConfig({ ads: false, links: false });
loadAgreementDetail();
function loadAgreementDetail() {
var $contentEl = $("#articleContent");
$contentEl.html('<p style="text-align:center;color:var(--text-muted);padding:40px 0;">加载中...</p>');
ApiClient.post("/api/web/agreement/getByType?type=2").then(function (res) {
if (res.code === 0 && res.data) {
var article = res.data;
if (article.title) {
$("#articleTitle").text(article.title);
}
$contentEl.html(article.content || "内容为空");
} else {
$contentEl.html('<p style="text-align:center;color:var(--text-muted);padding:40px 0;">暂无隐私政策内容</p>');
}
}).catch(function () {
$contentEl.html('<p style="text-align:center;color:var(--text-muted);padding:40px 0;">隐私政策加载失败</p>');
});
}
// Check login state for topbar
checkLogin();
renderNav();
function checkLogin() {
var token = CommonUtil.getToken();
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
if (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) {
$("#userAvatar").attr("src", data.avatar);
}
}
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(
function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
},
);
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
});
</script>
</body>
</html>
+301
View File
@@ -0,0 +1,301 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="page-title">彩先知 - 用户协议</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/static.css" />
</head>
<body>
<!-- Header -->
<nav class="topbar" id="navbar">
<div class="topbar__inner">
<a href="index.html" class="topbar__brand">
<img
src="../images/home-logo.png"
alt="彩先知"
class="topbar__logo"
/>
</a>
<div class="topbar__nav" id="navLotteryItems">
<!-- <a href="index.html" class="topbar__nav-link">首页</a> -->
</div>
<div class="topbar__user">
<div id="notLoggedInBox" class="topbar__guest">
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
<a href="reg.html" class="topbar__btn topbar__btn--primary"
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
src="../public/img/userimg.png"
alt=""
class="topbar__avatar"
/>
<div class="topbar__meta">
<span id="userName" class="topbar__uname">用户</span>
<span id="userLevel" class="topbar__ubadge">会员</span>
</div>
</a>
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
退出登录
</button>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="page-main">
<div class="policy-card">
<div class="policy-card-header">
<h1 id="articleTitle">用户协议</h1>
<p>彩先知用户服务协议</p>
</div>
<div class="policy-card-body" id="articleContent">
<h2>一、协议的接受与修改</h2>
<p>
欢迎使用彩先知(以下简称"本平台")。在使用本平台服务之前,请您仔细阅读本用户协议(以下简称"本协议")。您一旦使用本平台服务,即视为您已充分理解并同意接受本协议的全部内容。
</p>
<p>
本平台有权根据法律法规变化及业务发展需要,对本协议进行修改。修改后的协议将在本页面上公布,自公布之日起生效。如您不同意修改后的协议内容,您有权停止使用本平台服务。
</p>
<h2>二、账户注册与管理</h2>
<p>
您在注册账户时,应提供真实、准确、完整的个人资料,并在资料发生变更时及时更新。
</p>
<ul>
<li>
您应妥善保管账户信息和密码,因您的原因导致账户被盗用的,由您自行承担相关责任;
</li>
<li>您不得将账户出借、赠与、出租、转让或售卖给他人;</li>
<li>如发现账户被非法使用,您应立即通知本平台;</li>
<li>每个手机号码仅可注册一个账户。</li>
</ul>
<h2>三、服务内容</h2>
<p>本平台提供的服务包括但不限于:</p>
<ul>
<li>彩票数据分析和预测信息展示;</li>
<li>专家预测文章发布与阅读;</li>
<li>分销推广及收益管理;</li>
<li>社区交流与互动功能;</li>
<li>其他本平台提供的相关服务。</li>
</ul>
<p>本平台有权根据运营需要,随时变更、中断或终止部分或全部服务。</p>
<h2>四、用户行为规范</h2>
<p>您在使用本平台服务时,应遵守以下规范:</p>
<ul>
<li>遵守中华人民共和国相关法律法规;</li>
<li>不得利用本平台从事任何违法违规活动;</li>
<li>不得发布虚假、诈骗、淫秽、暴力、恐怖或违反公序良俗的内容;</li>
<li>
不得恶意攻击本平台系统或网络,不得利用技术手段非法获取数据;
</li>
<li>不得冒充他人或伪造身份信息;</li>
<li>不得对其他用户进行骚扰、侮辱、威胁或其他侵害行为;</li>
<li>不得进行任何破坏或试图破坏网络安全的行为。</li>
</ul>
<h2>五、内容规范</h2>
<p>您在本平台发布的所有内容,应确保:</p>
<ul>
<li>内容真实、合法,不侵犯任何第三方的合法权益;</li>
<li>预测分析仅供参考,不构成任何购买建议;</li>
<li>不得发布任何诱导他人进行非法赌博的内容;</li>
<li>本平台有权对违规内容进行删除,并对违规用户进行处罚。</li>
</ul>
<h2>六、知识产权</h2>
<p>
本平台所有内容,包括但不限于文字、图片、音频、视频、软件、程序及页面设计等,均属于本平台的知识产权,受法律保护。未经本平台书面许可,任何人不得以任何形式使用。
</p>
<p>
您在本平台发布的原创内容,您保留相应的知识产权。但您授予本平台在全球范围内免费的、非独家的使用许可,本平台有权使用、复制、修改、展示和分发您发布的内容。
</p>
<h2>七、免责声明</h2>
<p>
本平台提供的所有预测分析信息仅供娱乐参考,不构成任何形式的投注建议或承诺。彩票开奖结果具有随机性,过往数据不代表未来表现。
</p>
<p>因以下原因导致的服务中断或损失,本平台不承担责任:</p>
<ul>
<li>不可抗力因素;</li>
<li>系统维护或升级;</li>
<li>网络故障或第三方服务故障;</li>
<li>用户自身原因导致的信息泄露或损失。</li>
</ul>
<h2>八、交易与退款</h2>
<p>您在本平台进行的充值、购买等交易行为,应遵守以下规定:</p>
<ul>
<li>充值成功后,虚拟商品不支持退款;</li>
<li>因系统故障导致交易异常的,请联系客服处理;</li>
<li>提现申请将在审核通过后按规定时间到账;</li>
<li>如有疑问,请在交易完成后7日内联系客服。</li>
</ul>
<h2>九、违约处理</h2>
<p>如您违反本协议的规定,本平台有权视情节轻重采取以下措施:</p>
<ol>
<li>警告并要求改正;</li>
<li>删除违规内容;</li>
<li>限制或暂停账户功能;</li>
<li>永久封禁账户;</li>
<li>追究法律责任。</li>
</ol>
<h2>十、协议的终止</h2>
<p>
您有权随时注销您的账户。账户注销后,本平台将在法律法规规定的期限内保留您的相关信息。
</p>
<p>
如您严重违反本协议,本平台有权单方面终止向您提供服务,并永久封禁您的账户。
</p>
<h2>十一、法律适用与争议解决</h2>
<p>
本协议的订立、执行和解释均适用中华人民共和国法律。因本协议引起的或与本协议有关的任何争议,双方应友好协商解决;协商不成的,任何一方均可向本平台所在地有管辖权的人民法院提起诉讼。
</p>
<h2>十二、其他</h2>
<p>
本协议构成您与本平台之间关于使用本平台服务的完整协议,取代此前的任何口头或书面沟通。
</p>
<p>
如本协议的任何条款被认定为无效或不可执行,该条款应在可执行的最大范围内执行,其余条款仍然有效。
</p>
<p>如您对本协议有任何疑问,请通过以下方式联系我们:</p>
<ul>
<li>邮箱:support@caixianzhi.com</li>
<li>客服电话:400-XXX-XXXX</li>
</ul>
<div class="update-date">最后更新日期:2026年4月22日</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="site-footer__inner">
<img
src="../images/home-logo.png"
alt="彩先知"
class="site-footer__logo"
/>
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
<div class="site-footer__meta">
<a
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
</div>
</div>
</footer>
<button
class="gotop"
id="backTop"
type="button"
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
>
</button>
<script>
layui.use(["layer", "jquery"], function () {
var $ = layui.$;
// Load page config
CommonUtil.loadPageConfig({ ads: false, links: false });
loadAgreementDetail();
function loadAgreementDetail() {
var $contentEl = $("#articleContent");
$contentEl.html('<p style="text-align:center;color:var(--text-muted);padding:40px 0;">加载中...</p>');
ApiClient.post("/api/web/agreement/getByType?type=1").then(function (res) {
if (res.code === 0 && res.data) {
var article = res.data;
if (article.title) {
$("#articleTitle").text(article.title);
}
$contentEl.html(article.content || "内容为空");
} else {
$contentEl.html('<p style="text-align:center;color:var(--text-muted);padding:40px 0;">暂无协议内容</p>');
}
}).catch(function () {
$contentEl.html('<p style="text-align:center;color:var(--text-muted);padding:40px 0;">协议内容加载失败</p>');
});
}
// Check login state for topbar
checkLogin();
renderNav();
function checkLogin() {
var token = CommonUtil.getToken();
if (token) {
$("#notLoggedInBox").hide();
$("#loggedInBox").css("display", "flex");
CommonUtil.getCurrentUserDetail(
function (data) {
if (data) {
$("#userName").text(data.nickName || "用户");
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
if (data.avatar) {
$("#userAvatar").attr("src", data.avatar);
}
}
},
function () {
$("#notLoggedInBox").show();
$("#loggedInBox").hide();
},
);
}
}
function renderNav() {
ApiClient.post("/api/web/menu/getFormatList", {}).then(
function (res) {
if (res.code !== 0 || !res.data) return;
var $nav = $("#navLotteryItems");
$nav.find(".topbar__nav-link").not(":first").remove();
$.each(res.data, function (i, item) {
$nav.append(
'<a href="cai.html?id=' +
item.suoxie +
'" class="topbar__nav-link">' +
item.name +
"</a>",
);
});
},
);
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
});
</script>
</body>
</html>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Some files were not shown because too many files have changed in this diff Show More