mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-08-12 17:10:37 +00:00
Merge branch 'pr/215'
This commit is contained in:
commit
fda7c9e5b3
30
module/middle_play_do_lottery.js
Normal file
30
module/middle_play_do_lottery.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 云小编每日抽奖
|
||||||
|
//
|
||||||
|
// activityId:
|
||||||
|
// 默认 6501202
|
||||||
|
//
|
||||||
|
// drawCount:
|
||||||
|
// 默认 1
|
||||||
|
//
|
||||||
|
// checkToken:
|
||||||
|
// 易盾反作弊 Token
|
||||||
|
|
||||||
|
const createOption = require('../util/option.js')
|
||||||
|
|
||||||
|
module.exports = (query, request) => {
|
||||||
|
const data = {
|
||||||
|
activityId: query.activityId || '6501202',
|
||||||
|
drawCount: query.drawCount || '1',
|
||||||
|
}
|
||||||
|
// TODO: 如何传递 checkToken
|
||||||
|
const optionQuery = { ...query }
|
||||||
|
if (query.checkToken) {
|
||||||
|
data.checkToken = query.checkToken
|
||||||
|
delete query.checkToken
|
||||||
|
}
|
||||||
|
return request(
|
||||||
|
`/api/middle/play/do/lottery`,
|
||||||
|
data,
|
||||||
|
createOption(optionQuery, 'eapi', 'v2'),
|
||||||
|
)
|
||||||
|
}
|
||||||
17
module/middle_play_lottery_remain_chance.js
Normal file
17
module/middle_play_lottery_remain_chance.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// 云小编抽奖剩余次数查询
|
||||||
|
//
|
||||||
|
// activityId:
|
||||||
|
// 默认 6501202
|
||||||
|
|
||||||
|
const createOption = require('../util/option.js')
|
||||||
|
|
||||||
|
module.exports = (query, request) => {
|
||||||
|
const data = {
|
||||||
|
activityId: query.activityId || '6501202',
|
||||||
|
}
|
||||||
|
return request(
|
||||||
|
`/api/middle/play/lottery/remain/chance`,
|
||||||
|
data,
|
||||||
|
createOption(query, 'eapi'),
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -5727,6 +5727,36 @@ let data = encodeURIComponent(
|
|||||||
|
|
||||||
**调用例子 :** `/device/kickoff?key=00ALDFGEXXXXXXXXXXXXXXXXX&captcha=1234`
|
**调用例子 :** `/device/kickoff?key=00ALDFGEXXXXXXXXXXXXXXXXX&captcha=1234`
|
||||||
|
|
||||||
|
### 云小编 - 剩余抽奖次数
|
||||||
|
|
||||||
|
说明: 登录后调用此接口, 获取今日云小编抽奖剩余次数
|
||||||
|
|
||||||
|
**可选参数:**
|
||||||
|
|
||||||
|
`activityId`: 活动 ID, 默认 `6501202`
|
||||||
|
|
||||||
|
**接口地址:** `/middle/play/lottery/remain/chance`
|
||||||
|
|
||||||
|
**调用例子:** `/middle/play/lottery/remain/chance?activityId=6501202`
|
||||||
|
|
||||||
|
### 云小编 - 每日抽奖
|
||||||
|
|
||||||
|
说明: 登录后调用此接口, 消耗 200 积分进行抽奖, 每日最多抽 3 次
|
||||||
|
|
||||||
|
> 注意: 抽奖失败也消耗每日次数, 请先调用 `/rep/ugc/user/get` 查询可用积分
|
||||||
|
|
||||||
|
**可选参数:**
|
||||||
|
|
||||||
|
`activityId`: 活动 ID, 默认 `6501202`
|
||||||
|
|
||||||
|
`drawCount`: 未知, 默认 `1`
|
||||||
|
|
||||||
|
`checkToken`: 易盾反作弊 Token, 默认自动获取
|
||||||
|
|
||||||
|
**接口地址:** `/middle/play/do/lottery`
|
||||||
|
|
||||||
|
**调用例子:** `/middle/play/do/lottery?activityId=6501202&drawCount=1`
|
||||||
|
|
||||||
## 离线访问此文档
|
## 离线访问此文档
|
||||||
|
|
||||||
此文档同时也是 Progressive Web Apps(PWA), 加入了 serviceWorker, 可离线访问
|
此文档同时也是 Progressive Web Apps(PWA), 加入了 serviceWorker, 可离线访问
|
||||||
|
|||||||
@ -99,7 +99,8 @@ curl -s {origin}/search?keywords=网易云</code></pre>
|
|||||||
<a href="/scrobble.html">听歌打卡示例</a> ·
|
<a href="/scrobble.html">听歌打卡示例</a> ·
|
||||||
<a href="/yidun.html">获取 CheckToken</a> ·
|
<a href="/yidun.html">获取 CheckToken</a> ·
|
||||||
<a href="/free_listen.html">免费听权益</a> ·
|
<a href="/free_listen.html">免费听权益</a> ·
|
||||||
<a href="/ugc.html">云小编任务中心</a>
|
<a href="/ugc.html">云小编任务中心</a> ·
|
||||||
|
<a href="/ugc_lottery.html">云小编每日抽奖</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="referrer" content="no-referrer" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
<title>云小编任务中心 - 网易云音乐 API Enhanced</title>
|
<title>云小编任务中心 - 网易云音乐 API Enhanced</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -404,6 +406,8 @@
|
|||||||
<div class="quick-fill">
|
<div class="quick-fill">
|
||||||
<span onclick="loadLocalCookie()">读取本地 Cookie</span>
|
<span onclick="loadLocalCookie()">读取本地 Cookie</span>
|
||||||
<span onclick="clearCookie()">清除 Cookie</span>
|
<span onclick="clearCookie()">清除 Cookie</span>
|
||||||
|
<span onclick="saveCookie()">保存 Cookie</span>
|
||||||
|
<span onclick="loadAll()">一键载入</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -413,7 +417,7 @@
|
|||||||
<button class="btn-sm" id="userGet">获取用户详情</button>
|
<button class="btn-sm" id="userGet">获取用户详情</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-card" id="user">
|
<div class="info-card" id="user">
|
||||||
<img alt="头像" class="avatar" id="userAvatar" />
|
<img src="docs/netease.png" alt="头像" class="avatar" id="userAvatar" />
|
||||||
<span class="info-text" id="userName">未登录</span>
|
<span class="info-text" id="userName">未登录</span>
|
||||||
<button class="btn-sm green" id="userSign" disabled>签到</button>
|
<button class="btn-sm green" id="userSign" disabled>签到</button>
|
||||||
</div>
|
</div>
|
||||||
@ -462,7 +466,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
var k_cookie = 'ncm_cookie';
|
var k_cookie = 'ncm_cookie';
|
||||||
@ -496,6 +499,12 @@
|
|||||||
showResult('已清除本地 Cookie', 'info');
|
showResult('已清除本地 Cookie', 'info');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.saveCookie = function () {
|
||||||
|
var ck = getCookie()
|
||||||
|
if (ck) localStorage.setItem(k_cookie, ck)
|
||||||
|
showResult('已保存本地 Cookie', 'info');
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Result ---- */
|
/* ---- Result ---- */
|
||||||
function hideResult() {
|
function hideResult() {
|
||||||
resultDiv.style.display = 'none';
|
resultDiv.style.display = 'none';
|
||||||
@ -637,8 +646,15 @@
|
|||||||
|
|
||||||
if (d.auditCount >= 50) {
|
if (d.auditCount >= 50) {
|
||||||
request('rep/ugc/activity/collect', { activityId: '5001' }, function (res) {
|
request('rep/ugc/activity/collect', { activityId: '5001' }, function (res) {
|
||||||
showResult('积分领取' + (res.data ? '成功,可以兑换会员了!' : '失败'), res.data ? 'success' : 'error');
|
var d = res.data;
|
||||||
|
showResult('积分领取' + (d ? '成功,可以兑换会员了!' : '失败'), d ? 'success' : 'error');
|
||||||
|
// 刷新任务状态
|
||||||
|
if (d) {
|
||||||
|
vipGet.click();
|
||||||
|
taskGetBtn.click();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
songAudio.pause()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -686,7 +702,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 一键载入 ///
|
/// 一键载入 ///
|
||||||
document.getElementById('loadall').onclick = function () {
|
window.loadAll = function () {
|
||||||
userGet.click();
|
userGet.click();
|
||||||
vipGet.click();
|
vipGet.click();
|
||||||
taskGetBtn.click();
|
taskGetBtn.click();
|
||||||
|
|||||||
545
public/ugc_lottery.html
Normal file
545
public/ugc_lottery.html
Normal file
@ -0,0 +1,545 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes">
|
||||||
|
<meta name="referrer" content="no-referrer" />
|
||||||
|
<link rel="icon" href="docs/netease.png" />
|
||||||
|
<title>云小编每日抽奖 - 网易云音乐 API Enhanced</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg-start: #f3f0ff;
|
||||||
|
--bg-end: #e8f4fd;
|
||||||
|
--primary: #7c6cf0;
|
||||||
|
--primary-light: #ede9fe;
|
||||||
|
--accent: #fd79a8;
|
||||||
|
--gold: #fdcb6e;
|
||||||
|
--text: #2d3436;
|
||||||
|
--text-light: #636e72;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--border: #e8e8f0;
|
||||||
|
--shadow: 0 8px 30px rgba(108, 92, 231, 0.08);
|
||||||
|
--radius: 20px;
|
||||||
|
--radius-sm: 12px;
|
||||||
|
--transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', system-ui, -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||||
|
background: linear-gradient(135deg, var(--bg-start) 0%, var(--bg-end) 50%, #fce4ec 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 520px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标题编辑卡片 */
|
||||||
|
.card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 1.8rem 2rem;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-section {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-input {
|
||||||
|
width: 100%;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
color: var(--text);
|
||||||
|
background: #fafafa;
|
||||||
|
transition: var(--transition);
|
||||||
|
outline: none;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-input:hover {
|
||||||
|
background: #f5f3ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-input:focus {
|
||||||
|
border-color: var(--primary-light);
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 0 0 4px rgba(124, 108, 240, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-hint {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #b2bec3;
|
||||||
|
margin-top: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 抽奖主卡片 */
|
||||||
|
.lottery-card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 2rem 2rem 2.2rem;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rolling-display {
|
||||||
|
font-size: 2.4rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--primary);
|
||||||
|
min-height: 4rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin: 0.5rem 0 1rem;
|
||||||
|
word-break: break-all;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rolling-display.idle {
|
||||||
|
color: #c4b5fd;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 结果展示 */
|
||||||
|
.winner-display {
|
||||||
|
background: linear-gradient(135deg, #fff9e6, #fff3cd);
|
||||||
|
border: 2px solid var(--gold);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 1rem 0 1.5rem;
|
||||||
|
animation: popIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
|
}
|
||||||
|
|
||||||
|
.winner-label {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
color: #b8860b;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.winner-name {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #d63031;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confetti-row {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confetti-emoji {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
animation: bounce 0.6s ease infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confetti-emoji:nth-child(2) {
|
||||||
|
animation-delay: 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confetti-emoji:nth-child(3) {
|
||||||
|
animation-delay: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes popIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
from {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: translateY(-8px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮 */
|
||||||
|
.btn {
|
||||||
|
padding: 0.9rem 2.5rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--transition);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
width: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-lottery {
|
||||||
|
background: linear-gradient(135deg, #fd79a8, #e84393);
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 6px 20px rgba(253, 121, 168, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-lottery:hover:not(:disabled) {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 28px rgba(253, 121, 168, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-lottery:active:not(:disabled) {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-lottery:disabled {
|
||||||
|
background: #b2bec3;
|
||||||
|
box-shadow: none;
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reset {
|
||||||
|
background: transparent;
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
color: var(--text-light);
|
||||||
|
margin-top: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reset:hover {
|
||||||
|
border-color: var(--primary-light);
|
||||||
|
color: var(--primary);
|
||||||
|
background: #f8f7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态提示 */
|
||||||
|
.status-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-light);
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #00b894;
|
||||||
|
animation: pulse 1.5s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot.error {
|
||||||
|
background: #e17055;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式 */
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
|
||||||
|
.card,
|
||||||
|
.lottery-card {
|
||||||
|
padding: 1.4rem 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-input {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rolling-display {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
min-height: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.winner-name {
|
||||||
|
font-size: 1.7rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<!-- 标题编辑区 -->
|
||||||
|
<div class="card title-section">
|
||||||
|
<input type="text" class="title-input" id="titleInput" value="🎉 云小编每日抽奖 🎉" autocomplete="off" disabled>
|
||||||
|
<div class="title-hint">每次抽奖消耗 <b>200 个曲库编辑部积分</b>,每日最多可抽奖 3 次</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 抽奖主区域 -->
|
||||||
|
<div class="lottery-card">
|
||||||
|
<div class="rolling-display idle" id="rollingDisplay">等待抽奖</div>
|
||||||
|
<div id="resultContainer"></div>
|
||||||
|
<button class="btn btn-lottery" id="lotteryBtn">
|
||||||
|
<span>🎲</span> 开始抽奖
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-reset" id="resetBtn">
|
||||||
|
<span>🔄</span> 编辑令牌
|
||||||
|
</button>
|
||||||
|
<div class="status-bar" id="statusBar">
|
||||||
|
<span class="status-dot" id="statusDot"></span>
|
||||||
|
<span id="statusText">就绪,点击按钮开始抽奖</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
// ============ DOM 元素 ============
|
||||||
|
const titleInput = document.getElementById('titleInput');
|
||||||
|
const rollingDisplay = document.getElementById('rollingDisplay');
|
||||||
|
const resultContainer = document.getElementById('resultContainer');
|
||||||
|
const lotteryBtn = document.getElementById('lotteryBtn');
|
||||||
|
const resetBtn = document.getElementById('resetBtn');
|
||||||
|
const statusDot = document.getElementById('statusDot');
|
||||||
|
const statusText = document.getElementById('statusText');
|
||||||
|
|
||||||
|
// ============ 状态 ============
|
||||||
|
let isDrawing = false;
|
||||||
|
let rollingTimer = null;
|
||||||
|
|
||||||
|
// ============ 状态栏更新 ============
|
||||||
|
function setStatus(type, message) {
|
||||||
|
statusText.textContent = message;
|
||||||
|
statusDot.className = 'status-dot';
|
||||||
|
if (type === 'error') {
|
||||||
|
statusDot.classList.add('error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 重置结果 ============
|
||||||
|
function resetResult() {
|
||||||
|
resultContainer.innerHTML = '';
|
||||||
|
rollingDisplay.textContent = '等待抽奖';
|
||||||
|
rollingDisplay.className = 'rolling-display idle';
|
||||||
|
setStatus('info', '就绪,点击按钮开始抽奖');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 本地令牌管理 ============
|
||||||
|
const cookieKey = 'ncm_cookie';
|
||||||
|
let cookie = localStorage.getItem(cookieKey) || '';
|
||||||
|
function setCookie() {
|
||||||
|
const ck = prompt('请输入令牌:', cookie);
|
||||||
|
if (ck !== null) {
|
||||||
|
cookie = ck;
|
||||||
|
localStorage.setItem(cookieKey, cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 调用后端接口 ============
|
||||||
|
async function request(url, params) {
|
||||||
|
try {
|
||||||
|
url += '?timestamp=' + Date.now() + params;
|
||||||
|
if (cookie) url += '&cookie=' + encodeURIComponent(cookie);
|
||||||
|
|
||||||
|
const response = await fetch(url);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var res = await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('数据解析错误:', error);
|
||||||
|
throw new Error('接口请求错误: ' + response.status + ': ' + response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.code !== 200) {
|
||||||
|
throw new Error('接口返回错误: ' + res.code + ': ' + (res.message || '未知错误'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('接口请求失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchLotteryResult() {
|
||||||
|
const user = await request('rep/ugc/user/get', '');
|
||||||
|
|
||||||
|
if ((user.data?.availablePoints ?? 0) < 200) {
|
||||||
|
throw new Error('用户剩余积分不足,请先完成任务获取积分');
|
||||||
|
}
|
||||||
|
|
||||||
|
const chance = await request('middle/play/lottery/remain/chance', '&activityId=6501202');
|
||||||
|
|
||||||
|
if (chance.data === 0) {
|
||||||
|
throw new Error('今日机会已用完,明天再来吧');
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await request('middle/play/do/lottery', '&activityId=6501202&drawCount=1');
|
||||||
|
|
||||||
|
// 根据你的后端返回格式提取中奖者名称
|
||||||
|
const winner = res.data?.prizeDetailInfoMap?.['8186002']?.prizeName;
|
||||||
|
if (!winner) {
|
||||||
|
throw new Error('无法解析响应数据');
|
||||||
|
}
|
||||||
|
|
||||||
|
// [抽奖结果, 剩余机会]
|
||||||
|
return [winner, res.data.restChance];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 滚动动画 ============
|
||||||
|
function startRollingAnimation(finalWinner) {
|
||||||
|
const placeholderNames = ['🎰', '🎲', '🎯', '✨', '💫', '🌟', '🎪', '🎭'];
|
||||||
|
const totalDuration = 2800;
|
||||||
|
const startInterval = 50;
|
||||||
|
const endInterval = 280;
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
rollingDisplay.className = 'rolling-display';
|
||||||
|
resultContainer.innerHTML = '';
|
||||||
|
|
||||||
|
function roll() {
|
||||||
|
const elapsed = Date.now() - startTime;
|
||||||
|
const progress = Math.min(elapsed / totalDuration, 1.0);
|
||||||
|
const easeOutProgress = 1 - Math.pow(1 - progress, 3);
|
||||||
|
const currentInterval = startInterval + (endInterval - startInterval) * easeOutProgress;
|
||||||
|
|
||||||
|
// 随机显示占位符
|
||||||
|
const randomPlaceholder = placeholderNames[Math.floor(Math.random() * placeholderNames.length)];
|
||||||
|
rollingDisplay.textContent = randomPlaceholder;
|
||||||
|
|
||||||
|
if (progress >= 1.0) {
|
||||||
|
// 动画结束,显示真实结果
|
||||||
|
clearTimeout(rollingTimer);
|
||||||
|
rollingDisplay.textContent = finalWinner;
|
||||||
|
showWinnerResult(finalWinner);
|
||||||
|
finishDrawing();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rollingTimer = setTimeout(roll, currentInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
roll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showWinnerResult(winner) {
|
||||||
|
resultContainer.innerHTML = `
|
||||||
|
<div class="winner-display">
|
||||||
|
<div class="winner-label">🏆 恭喜中奖</div>
|
||||||
|
<div class="winner-name">${escapeHTML(winner)}</div>
|
||||||
|
<div class="confetti-row">
|
||||||
|
<span class="confetti-emoji">🎉</span>
|
||||||
|
<span class="confetti-emoji">🎊</span>
|
||||||
|
<span class="confetti-emoji">✨</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHTML(str) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.appendChild(document.createTextNode(str));
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishDrawing() {
|
||||||
|
isDrawing = false;
|
||||||
|
lotteryBtn.disabled = false;
|
||||||
|
resetBtn.disabled = false;
|
||||||
|
// setStatus('info', '抽奖完成!点击重置可再次抽奖');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 主抽奖流程 ============
|
||||||
|
async function startLottery() {
|
||||||
|
if (isDrawing) return;
|
||||||
|
|
||||||
|
// 开始前状态
|
||||||
|
isDrawing = true;
|
||||||
|
lotteryBtn.disabled = true;
|
||||||
|
resetBtn.disabled = true;
|
||||||
|
resultContainer.innerHTML = '';
|
||||||
|
rollingDisplay.className = 'rolling-display';
|
||||||
|
rollingDisplay.textContent = '🎰';
|
||||||
|
setStatus('info', '正在获取抽奖结果...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 调用后端接口获取中奖者
|
||||||
|
const res = await fetchLotteryResult();
|
||||||
|
setStatus('info', '抽奖成功,剩余次数 ' + res[1]);
|
||||||
|
// 启动滚动动画,最终定格在中奖者
|
||||||
|
startRollingAnimation(res[0]);
|
||||||
|
} catch (error) {
|
||||||
|
// 请求失败处理
|
||||||
|
rollingDisplay.textContent = '抽奖失败';
|
||||||
|
rollingDisplay.className = 'rolling-display idle';
|
||||||
|
setStatus('error', '抽奖失败: ' + error.message);
|
||||||
|
isDrawing = false;
|
||||||
|
lotteryBtn.disabled = false;
|
||||||
|
resetBtn.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 事件绑定 ============
|
||||||
|
lotteryBtn.addEventListener('click', startLottery);
|
||||||
|
resetBtn.addEventListener('click', setCookie);
|
||||||
|
|
||||||
|
// 空格键快捷抽奖
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === ' ' && document.activeElement !== titleInput) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!isDrawing && !lotteryBtn.disabled) {
|
||||||
|
startLottery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ============ 初始化 ============
|
||||||
|
resetResult();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user