Merge branch 'cocos-main'

# Conflicts:
#	docker-conf/conf.d/nginx.conf
#	nginx.conf
#	scratch-blocks/build.py
#	scratch-blocks/cleanup.sh
#	scratch-blocks/i18n/create_messages.py
#	scratch-blocks/i18n/dedup_json.py
#	scratch-blocks/i18n/js_to_json.py
#	scratch-blocks/i18n/json_to_js.py
#	scratch-blocks/i18n/sync_tx_translations.js
#	scratch-blocks/i18n/xliff_to_json.py
#	scratch-blocks/local_build.sh
#	scratch-blocks/media/icons/control_forever.svg
#	scratch-blocks/media/icons/control_repeat.svg
#	scratch-blocks/media/icons/control_wait.svg
#	scratch-blocks/media/icons/event_broadcast_blue.svg
#	scratch-blocks/media/icons/event_broadcast_coral.svg
#	scratch-blocks/media/icons/event_broadcast_green.svg
#	scratch-blocks/media/icons/event_broadcast_magenta.svg
#	scratch-blocks/media/icons/event_broadcast_orange.svg
#	scratch-blocks/media/icons/event_broadcast_purple.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_blue.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_coral.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_green.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_magenta.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_orange.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_purple.svg
#	scratch-blocks/media/icons/event_whenflagclicked.svg
#	scratch-blocks/media/icons/set-led_blue.svg
#	scratch-blocks/media/icons/set-led_coral.svg
#	scratch-blocks/media/icons/set-led_green.svg
#	scratch-blocks/media/icons/set-led_magenta.svg
#	scratch-blocks/media/icons/set-led_mystery.svg
#	scratch-blocks/media/icons/set-led_orange.svg
#	scratch-blocks/media/icons/set-led_purple.svg
#	scratch-blocks/media/icons/set-led_white.svg
#	scratch-blocks/media/icons/set-led_yellow.svg
#	scratch-blocks/media/icons/wedo_motor-clockwise.svg
#	scratch-blocks/media/icons/wedo_motor-counterclockwise.svg
#	scratch-blocks/media/icons/wedo_when-distance_close.svg
#	scratch-blocks/media/icons/wedo_when-tilt-backward.svg
#	scratch-blocks/media/icons/wedo_when-tilt-forward.svg
#	scratch-blocks/media/icons/wedo_when-tilt-left.svg
#	scratch-blocks/media/icons/wedo_when-tilt-right.svg
#	scratch-blocks/media/icons/wedo_when-tilt.svg
#	scratch-blocks/package-lock.json
#	scratch-blocks/pull_from_blockly.sh
#	scratch-gui/competitio_login/index.html
#	scratch-gui/package-lock.json
#	scratch-gui/package.json
#	scratch-gui/prune-gh-pages.sh
#	scratch-gui/src/components/menu-bar/menu-bar.css
#	scratch-gui/src/components/menu-bar/menu-bar.jsx
#	scratch-gui/src/components/nopermission-modal/NoPermissionModal.jsx
#	scratch-gui/src/components/stage-cpp-unity/currentLevelChoose.jsx
#	scratch-gui/src/components/stage-cpp-unity/stage-unity.jsx
#	scratch-gui/src/components/stage-cpp/cpp-parser.js
#	scratch-gui/src/components/stage-cpp/stage-cpp-clang.jsx
#	scratch-gui/src/components/stage-cpp/stage-cpp.css
#	scratch-gui/src/components/stage-python-unity/currentLevelChoose.jsx
#	scratch-gui/src/components/stage-python-unity/stage-unity.jsx
#	scratch-gui/src/components/stage-python/stage-python.css
#	scratch-gui/src/components/stage-python/stage-python.jsx
#	scratch-gui/src/components/stage-unity/currentLevelChoose.jsx
#	scratch-gui/src/components/stage-unity/stage-unity.jsx
#	scratch-gui/src/lib/libraries/decks/steps/add-effects.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-add-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-change-color.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-jump.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-move.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-say-something.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-talk.es.png
#	scratch-gui/src/lib/libraries/decks/steps/change-size.es.png
#	scratch-gui/src/lib/libraries/decks/steps/chase-game-change-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/chase-game-move-randomly.es.png
#	scratch-gui/src/lib/libraries/decks/steps/chase-game-play-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/chase-game-right-left.es.png
#	scratch-gui/src/lib/libraries/decks/steps/chase-game-up-down.es.png
#	scratch-gui/src/lib/libraries/decks/steps/cn-backdrop.es.png
#	scratch-gui/src/lib/libraries/decks/steps/cn-collect.es.png
#	scratch-gui/src/lib/libraries/decks/steps/cn-glide.es.png
#	scratch-gui/src/lib/libraries/decks/steps/cn-say.es.png
#	scratch-gui/src/lib/libraries/decks/steps/cn-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-01-say-something.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-02-animate.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-03-select-different-character.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-04-use-minus-sign.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-05-grow-shrink.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-06-select-another-different-character.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-07-jump.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-08-change-scenes.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-09-glide-around.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-10-change-costumes.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-11-choose-more-characters.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-choose-character.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-flying-heart.es.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-keep-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-make-interactive.es.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-move-scenery.es.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-object-to-collect.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-say-something.es.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-select-flyer.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-switch-costume.es.png
#	scratch-gui/src/lib/libraries/decks/steps/glide-around-back-and-forth.es.png
#	scratch-gui/src/lib/libraries/decks/steps/glide-around-point.es.png
#	scratch-gui/src/lib/libraries/decks/steps/hide-show.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-change-costumes.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-choose-another-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-choose-another-sprite.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-choose-any-sprite.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-choose-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-choose-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-click-green-flag.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-fly-around.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-glide-to-point.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-grow-shrink.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-left-right.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-type-what-you-want.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-up-down.es.png
#	scratch-gui/src/lib/libraries/decks/steps/move-arrow-keys-left-right.es.png
#	scratch-gui/src/lib/libraries/decks/steps/move-arrow-keys-up-down.es.png
#	scratch-gui/src/lib/libraries/decks/steps/music-make-beat.es.png
#	scratch-gui/src/lib/libraries/decks/steps/music-make-beatbox.es.png
#	scratch-gui/src/lib/libraries/decks/steps/music-make-song.es.png
#	scratch-gui/src/lib/libraries/decks/steps/music-play-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/name-change-color.es.png
#	scratch-gui/src/lib/libraries/decks/steps/name-grow.es.png
#	scratch-gui/src/lib/libraries/decks/steps/name-play-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/name-spin.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-add-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-add-ball-sprite.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-add-code-to-ball.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-bounce-around.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-choose-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-game-over.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-insert-change-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-move-the-paddle.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-reset-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-select-ball.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/pop-game-change-color.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pop-game-change-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pop-game-play-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pop-game-random-position.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pop-game-reset-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/record-a-sound-choose-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/record-a-sound-click-record.es.png
#	scratch-gui/src/lib/libraries/decks/steps/record-a-sound-play-your-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/record-a-sound-press-record-button.es.png
#	scratch-gui/src/lib/libraries/decks/steps/record-a-sound-sounds-tab.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-change-color.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-grow-shrink.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-move-around.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-say-something.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-set-voice.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-song.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-spin.es.png
#	scratch-gui/src/lib/libraries/decks/steps/spin-point-in-direction.es.png
#	scratch-gui/src/lib/libraries/decks/steps/spin-turn.es.png
#	scratch-gui/src/lib/libraries/decks/steps/story-conversation.es.png
#	scratch-gui/src/lib/libraries/decks/steps/story-hide-character.es.png
#	scratch-gui/src/lib/libraries/decks/steps/story-say-something.es.png
#	scratch-gui/src/lib/libraries/decks/steps/story-show-character.es.png
#	scratch-gui/src/lib/libraries/decks/steps/story-switch-backdrop.es.png
#	scratch-gui/src/lib/libraries/decks/steps/switch-costumes.es.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-10-choose-third-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-12-dance-moves.es.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-2-choose-sprite.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-4-choose-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-6-choose-another-sprite.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-7-move-around.es.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-8-choose-another-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/video-animate.es.png
#	scratch-gui/src/lib/libraries/decks/steps/video-pet.es.png
#	scratch-gui/src/lib/libraries/decks/steps/video-pop.es.png
#	scratch-gui/src/playground/aboutus.ejs
#	scratch-gui/src/playground/checkStatus.js
#	scratch-gui/src/playground/competition.ejs
#	scratch-gui/src/playground/competition.js
#	scratch-gui/src/playground/competition_list.ejs
#	scratch-gui/src/playground/competition_list.js
#	scratch-gui/src/playground/competition_sum.ejs
#	scratch-gui/src/playground/competition_sum.js
#	scratch-gui/src/playground/config.js
#	scratch-gui/src/playground/home.ejs
#	scratch-gui/src/playground/import-check.js
#	scratch-gui/src/playground/leaderboard.ejs
#	scratch-gui/src/playground/login-new.ejs
#	scratch-gui/src/playground/login.ejs
#	scratch-gui/src/playground/login.js
#	scratch-gui/src/playground/matchsum.js
#	scratch-gui/src/playground/programming-learning.ejs
#	scratch-gui/src/playground/programming-learning.js
#	scratch-gui/src/playground/signup-new.ejs
#	scratch-gui/src/playground/third-part-login.js
#	scratch-gui/src/playground/welcome.ejs
#	scratch-gui/src/playground/welcome.js
#	scratch-gui/static/css/welcome-new.css
#	scratch-gui/static/pyodide/pyodide.asm.wasm.br
#	scratch-gui/static/unity/Build/mstest5.data.br
#	scratch-gui/static/unity/Build/mstest5.framework.js.br
#	scratch-gui/static/unity/Build/mstest5.loader.js
#	scratch-gui/static/unity/Build/mstest5.wasm.br
#	scratch-gui/static/unity/StreamingAssets/aa/AddressablesLink/link.xml
#	scratch-gui/static/unity/StreamingAssets/aa/catalog.json
#	scratch-gui/static/unity/StreamingAssets/aa/settings.json
#	scratch-gui/webpack.config.js
#	scratch-l10n/scripts/build-data.js
#	scratch-l10n/scripts/build-i18n-src.js
#	scratch-l10n/scripts/tw-push.js
#	scratch-l10n/scripts/tx-pull-help-articles.js
#	scratch-l10n/scripts/tx-pull-help-names.js
#	scratch-l10n/scripts/tx-pull-locale-articles.js
#	scratch-l10n/scripts/tx-pull-www.js
#	scratch-l10n/scripts/tx-push-help.js
#	scratch-l10n/scripts/tx-push-src.js
#	scratch-l10n/scripts/update-translations.sh
#	scratch-l10n/scripts/validate-www.js
#	scratch-vm/package-lock.json
#	scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/36.mp3
#	scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/60.mp3
#	scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3
#	scratch-vm/src/extensions/scratch3_music/assets/instruments/5-electric-guitar/60.mp3
#	scratch-vm/test/fixtures/sprite.json
This commit is contained in:
Evan
2026-09-07 11:35:22 +08:00
206 changed files with 21799 additions and 437 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

BIN
docker-conf/.DS_Store vendored Normal file

Binary file not shown.

BIN
docker-conf/conf.d/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -17,6 +17,7 @@ server {
return 301 /competition.html?competition_id=13;
}
<<<<<<< HEAD
# Brotli 压缩 WebAssembly 文件处理
location ~* \.wasm\.br$ {
default_type "";
@@ -32,13 +33,51 @@ server {
add_header Content-Type application/javascript always;
add_header Vary Accept-Encoding;
expires 30d;
=======
# Unity 预压缩文件:禁止再次 gzip避免 Content-Length 与实际内容不一致
location ~* \.wasm\.br$ {
gzip off;
default_type application/wasm;
add_header Content-Encoding br;
add_header Content-Type application/wasm always;
add_header Vary Accept-Encoding;
add_header Accept-Ranges bytes;
expires 1h;
}
location ~* \.js\.br$ {
gzip off;
default_type application/javascript;
add_header Content-Encoding br;
add_header Content-Type application/javascript always;
add_header Vary Accept-Encoding;
add_header Accept-Ranges bytes;
expires 1h;
}
location ~* \.data\.br$ {
gzip off;
default_type application/octet-stream;
add_header Content-Encoding br;
add_header Vary Accept-Encoding;
add_header Accept-Ranges bytes;
expires 1h;
>>>>>>> cocos-main
}
# 通用 Brotli 文件处理
location ~* \.br$ {
<<<<<<< HEAD
add_header Content-Encoding br;
add_header Vary Accept-Encoding;
expires 30d;
=======
gzip off;
add_header Content-Encoding br;
add_header Vary Accept-Encoding;
add_header Accept-Ranges bytes;
expires 1h;
>>>>>>> cocos-main
}
# 静态资源缓存配置

View File

@@ -37,9 +37,12 @@ server {
# 通用 Brotli 文件处理
location ~* \.br$ {
gzip off;
gzip_static off;
add_header Content-Encoding br;
add_header Vary Accept-Encoding;
expires 30d;
add_header Accept-Ranges bytes;
expires 1h;
}
# 静态资源缓存配置
@@ -78,4 +81,4 @@ server {
# 错误日志和访问日志配置
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
}

View File

@@ -383,8 +383,8 @@
<footer>
<div class="footer-line" role="presentation"></div>
<p class="footer-text">ICP备案号<a href=" " target="_blank" style="color:#478ac9 ;">ICP备2022002563号-5</a>
公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a>
<p class="footer-text">ICP备案号<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:#478ac9 ;">ICP备2026042325号</a>
<!-- 公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a> -->
© 灵丌编程001CODE.COM</p>
</footer>
</div>

View File

@@ -235,52 +235,70 @@
}
.startButton {
background-color: white;
color: inherit;
background: linear-gradient(135deg, #22c55e, #16a34a);
color: #fff;
border: none;
padding: 10px 20px;
padding: 10px 22px;
text-align: center;
text-decoration: none;
display: inline-block;
/* font-size: 16px; */
font-weight: 600;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s, box-shadow 0.3s;
border-radius: 4px;
animation: pulse 2s infinite; /* 添加吸引注意的动画 */
transition: all 0.3s ease;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(34, 197, 94, 0.3);
letter-spacing: 0.5px;
}
.startButton:hover {
background-color: #56b9b7; /* 悬停时颜色加深 */
transform: scale(1.05); /* 悬停时略微放大 */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 添加阴影 */
background: linear-gradient(135deg, #16a34a, #15803d);
transform: scale(1.04);
box-shadow: 0 4px 16px rgba(34, 197, 94, 0.45);
}
.startButton:active {
background-color: #279a98; /* 点击时颜色进一步加深 */
transform: scale(0.98); /* 点击时略微缩小 */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); /* 减少阴影 */
}
/* 定义吸引注意的动画 */
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
background: linear-gradient(135deg, #15803d, #166534);
transform: scale(0.97);
box-shadow: 0 2px 6px rgba(34, 197, 94, 0.35);
}
/* 可选:在点击后停止动画并改变样式 */
.startButtonClicked {
background-color: #ff9800; /* 点击后颜色变为橙色 */
transform: scale(1.1); /* 点击后稍微放大 */
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); /* 增加阴影 */
animation: none; /* 停止动画 */
background: linear-gradient(135deg, #f59e0b, #d97706);
transform: scale(1.05);
box-shadow: 0 6px 12px rgba(245, 158, 11, 0.4);
}
.interruptButton {
background: linear-gradient(135deg, #ef4444, #dc2626);
color: #fff;
border: none;
padding: 10px 22px;
text-align: center;
text-decoration: none;
display: inline-block;
font-weight: 600;
font-size: 14px;
margin: 4px 8px;
cursor: pointer;
transition: all 0.3s ease;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3);
letter-spacing: 0.5px;
}
.interruptButton:hover {
background: linear-gradient(135deg, #dc2626, #b91c1c);
transform: scale(1.04);
box-shadow: 0 4px 16px rgba(239, 68, 68, 0.45);
}
.interruptButton:active {
background: linear-gradient(135deg, #b91c1c, #991b1b);
transform: scale(0.97);
box-shadow: 0 2px 6px rgba(239, 68, 68, 0.35);
}
.lvcount{

View File

@@ -246,8 +246,10 @@ class MenuBar extends React.Component {
'restoreOptionMessage',
'handleClickStart',
'handleClickStopAll',
'handleClickInterrupt',
'handleFeedbackClick',
'handleCloseModal',
'handleOpenNoPermissionModal',
'handleCloseVideoModal',
'handlelearnVideoClick',
'handleVideoLoaded',
@@ -262,6 +264,7 @@ class MenuBar extends React.Component {
showAntiAddictionMsg: false,
antiAddictionMsg: '',
showModal: false,
showNoPermissionModal: false,
showVideoModal: false,
currentLevel: window.gamelvdata ? window.gamelvdata.currentlv : '',
currentGameId: window.gamelvdata ? window.gamelvdata.gameid : '',
@@ -582,9 +585,15 @@ class MenuBar extends React.Component {
window.vm.stopAll();
}
handleOpenNoPermissionModal() {
this.setState({ showNoPermissionModal: true });
}
handleCloseModal() {
// 设置 showModal 为 false 以隐藏模态窗口
this.setState({ showModal: false });
this.setState({ showModal: false, showNoPermissionModal: false });
if (window.location.hash === '#carousel_ac25') {
history.replaceState(null, document.title, window.location.pathname + window.location.search);
}
}
// 打开视频模态窗口
@@ -843,7 +852,7 @@ class MenuBar extends React.Component {
let realLvId_tmp = '';
// 发送消息给 Unity 实例,切换关卡
if (localStorage.getItem('platformMode') === 'normal') {
realLvId_tmp = CONFIG.getRealLvId(window.gamelvdataJson.gamelv, window.gamelvdataJson.gameid, window.gamelvdataJson.ismatch, window.gamelvdataJson.gameNumber, window.gamelvdataJson.gamemode) + window.gamelvdata.currentlv;
realLvId_tmp = CONFIG.getRealLvId(window.gamelvdataJson.gamelv, window.gamelvdataJson.gameid, window.gamelvdataJson.ismatch, window.gamelvdataJson.gameNumber) + window.gamelvdata.currentlv;
} else {
realLvId_tmp = CONFIG.getRealLvId(window.gamelvdataJson.gamelv, window.gamelvdataJson.gameid, window.gamelvdataJson.ismatch, window.gamelvdataJson.gameNumber);
}
@@ -875,6 +884,42 @@ class MenuBar extends React.Component {
}
handleClickInterrupt() {
console.log('取消执行按钮点击');
// 1. 终止所有代码执行
try { this.props.vm.stopAll(); } catch (e) { /* ignore */ }
try { if (window.pythonCodeStopRun) window.pythonCodeStopRun(); } catch (e) { /* ignore */ }
try { if (window.cppCodeStopRun) window.cppCodeStopRun(); } catch (e) { /* ignore */ }
// 2. 强制重置执行状态
window.isPythonRun = false;
window.isScriptRunOver = true;
// 3. 重置 Unity 游戏到初始状态(重新加载当前关卡)
if (window.unityInstance && window.gamelvdataJson) {
try {
let realLvId_tmp = '';
if (localStorage.getItem('platformMode') === 'normal') {
realLvId_tmp = CONFIG.getRealLvId(
window.gamelvdataJson.gamelv,
window.gamelvdataJson.gameid,
window.gamelvdataJson.ismatch,
window.gamelvdataJson.gameNumber
) + (window.gamelvdata ? window.gamelvdata.currentlv : '');
} else {
realLvId_tmp = CONFIG.getRealLvId(
window.gamelvdataJson.gamelv,
window.gamelvdataJson.gameid,
window.gamelvdataJson.ismatch,
window.gamelvdataJson.gameNumber
);
}
window.unityInstance.SendMessage("GameController", "SwitchLevel", realLvId_tmp);
console.log('Unity 关卡重置:', realLvId_tmp);
} catch (e) {
console.warn('Unity 重置失败:', e);
}
}
}
handleChangeSkin() {
unityInstance.SendMessage("Player", "CallChangeSkin", 1);
}
@@ -1341,6 +1386,18 @@ class MenuBar extends React.Component {
</a>
</div> */}
<div className={styles.accountInfoGroup}>
<a
className={styles.feedbackLink}
rel="noopener noreferrer"
target="_blank"
>
<Button className={styles.interruptButton} onClick={this.handleClickInterrupt}>
取消执行
</Button>
</a>
</div>
<div className={styles.accountInfoGroup}>
<a
className={styles.feedbackLink}
@@ -1436,9 +1493,16 @@ class MenuBar extends React.Component {
{aboutButton}
<a href="#carousel_ac25" className='u-dialog-link' id='noPermissionModal'></a>
<a
href="#carousel_ac25"
className='u-dialog-link'
id='noPermissionModal'
onClick={this.handleOpenNoPermissionModal}
></a>
<a id='competitionLevelChoose_dialog' className="u-dialog-link" href="#carousel_d668"></a>
<NoPermissionModal onCancel={this.handleCloseModal} />
{this.state.showNoPermissionModal ? (
<NoPermissionModal onCancel={this.handleCloseModal} />
) : null}
{enable_screen_block && (
<div style={{

View File

@@ -126,6 +126,7 @@ const NoPermissionModal = ({ onCancel }) => {
u-container-align-center-xl
u-container-style
u-dialog-block
u-dialog-open
u-opacity
u-opacity-70
u-valign-middle-lg

View File

@@ -148,7 +148,7 @@ const CurrentLevelChoose = ({ onCancel }) => {
console.warn('window.loadCppProject 未定义或不是函数');
}
}
const realLvId_tmp = CONFIG.getRealLvId(window.gamelvdataJson.gamelv, window.gamelvdataJson.gameid, window.gamelvdataJson.ismatch, window.gamelvdataJson.gameNumber, window.gamelvdataJson.gamemode);
const realLvId_tmp = CONFIG.getRealLvId(window.gamelvdataJson.gamelv, window.gamelvdataJson.gameid, window.gamelvdataJson.ismatch, window.gamelvdataJson.gameNumber);
const realLvId = realLvId_tmp + cellIndex
window.unityInstance.SendMessage("GameController", "SwitchLevel", realLvId);
CONFIG.checkGameStyle(realLvId);

View File

@@ -12,6 +12,8 @@ import RankBadgeGenerator from '../../playground/rank-badge-generator.js';
window.RankBadgeGenerator = RankBadgeGenerator;
window.CONFIG = CONFIG;
import disableDevtool from 'disable-devtool';
import { initDisableDevtool } from '../../playground/devtool-control.js';
import { getUnityPlayerConfig, loadUnityLoaderScript } from '../../lib/unity-build-loader.js';
import { debug } from 'scratch-vm/src/util/log.js';
const MAX_LEVELS = 500; // 最大关卡数据数量

View File

@@ -504,9 +504,41 @@
flex-direction: column;
}
.apiDrawer.apiDrawerOpen {
right: 0;
/* 滑出显示 */
.apiDrawerLoading {
position: absolute;
inset: 0;
z-index: 20;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 14px;
background: rgba(18, 18, 18, 0.82);
backdrop-filter: blur(2px);
cursor: not-allowed;
}
.apiDrawerLoadingSpinner {
width: 38px;
height: 38px;
border: 3px solid rgba(255, 255, 255, 0.18);
border-top-color: #ba68c8;
border-radius: 50%;
animation: apiDrawerLoadingSpin 0.9s linear infinite;
}
.apiDrawerLoadingText {
color: #e0e0e0;
font-size: 13px;
line-height: 1.7;
text-align: center;
padding: 0 18px;
}
@keyframes apiDrawerLoadingSpin {
to {
transform: rotate(360deg);
}
}
.apiDrawerHeader {
@@ -526,21 +558,6 @@
font-weight: 600;
}
.apiDrawerClose {
background: none;
border: none;
color: white;
font-size: 20px;
cursor: pointer;
padding: 5px;
border-radius: 3px;
transition: background-color 0.2s ease;
}
.apiDrawerClose:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.apiDrawerContent {
flex: 1;
overflow: hidden;
@@ -710,11 +727,6 @@
@media (max-width: 1200px) {
.apiDrawer {
width: 25vw;
right: -25vw;
}
.apiDrawer.apiDrawerOpen {
right: 0;
}
.apiDrawerCategoryButton {
@@ -730,11 +742,6 @@
@media (max-width: 900px) {
.apiDrawer {
width: 35vw;
right: -35vw;
}
.apiDrawer.apiDrawerOpen {
right: 0;
}
.apiDrawerHeader h3 {

View File

@@ -144,7 +144,7 @@ const CurrentLevelChoose = ({ onCancel }) => {
// 清除所有代码
window.loadPythonProject(`` + `\n`.repeat(50));
}
const realLvId_tmp = CONFIG.getRealLvId(window.gamelvdataJson.gamelv, window.gamelvdataJson.gameid, window.gamelvdataJson.ismatch, window.gamelvdataJson.gameNumber, window.gamelvdataJson.gamemode);
const realLvId_tmp = CONFIG.getRealLvId(window.gamelvdataJson.gamelv, window.gamelvdataJson.gameid, window.gamelvdataJson.ismatch, window.gamelvdataJson.gameNumber);
const realLvId = realLvId_tmp + cellIndex
window.unityInstance.SendMessage("GameController", "SwitchLevel", realLvId);
CONFIG.checkGameStyle(realLvId);

View File

@@ -12,6 +12,8 @@ import RankBadgeGenerator from '../../playground/rank-badge-generator.js';
window.RankBadgeGenerator = RankBadgeGenerator;
window.CONFIG = CONFIG;
import disableDevtool from 'disable-devtool';
import { initDisableDevtool } from '../../playground/devtool-control.js';
import { getUnityPlayerConfig, loadUnityLoaderScript } from '../../lib/unity-build-loader.js';
import { debug } from 'scratch-vm/src/util/log.js';
const MAX_LEVELS = 500; // 最大关卡数据数量

View File

@@ -139,103 +139,71 @@
background-color: #0056b3;
}
/* API抽屉标签样式 */
.apiDrawerTab {
position: fixed;
top: 50%;
right: 0;
transform: translateY(-50%);
width: 45px;
height: 90px;
background: hsla(215, 100%, 65%, 1);
border: 1px solid hsla(215, 100%, 65%, 1);
border-right: none;
border-radius: 12px 0 0 12px;
cursor: pointer;
z-index: 1001;
/* API参考 + 编辑器并排:参考在左,编辑器靠右 */
.editorWithApi {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: -4px 0 12px rgba(0, 0, 0, 0.25);
color: #BDC3C7;
backdrop-filter: blur(10px);
flex-direction: row;
height: 100%;
width: 54vw;
overflow: hidden;
}
.apiDrawerTab:hover {
background: hsla(215, 100%, 65%, 1);
transform: translateY(-50%) translateX(-3px);
box-shadow: -6px 0 18px rgba(0, 0, 0, 0.35);
color: #FFFFFF;
.editorPane {
flex: 1;
min-width: 0;
height: 100%;
position: relative;
overflow: auto;
}
.apiDrawerTab.apiDrawerTabOpen {
background: linear-gradient(135deg, #D35400 0%, #E67E22 100%);
border-color: #E67E22;
right: 20vw;
color: #FFFFFF;
}
.apiDrawerTab.apiDrawerTabOpen:hover {
background: linear-gradient(135deg, #E67E22 0%, #F39C12 100%);
color: #FFFFFF;
}
.apiDrawerTabIcon {
font-size: 14px;
font-weight: bold;
line-height: 1;
color: #FFFFFF;
opacity: 0.9;
transition: all 0.2s ease;
}
.apiDrawerTab:hover .apiDrawerTabIcon {
opacity: 1;
transform: scale(1.1);
}
.apiDrawerTabText {
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.5px;
writing-mode: vertical-lr;
text-orientation: mixed;
line-height: 1;
color: #FFFFFF;
opacity: 0.8;
transition: all 0.2s ease;
}
.apiDrawerTab:hover .apiDrawerTabText {
opacity: 1;
letter-spacing: 2px;
}
/* API抽屉样式 */
.apiDrawer {
position: fixed;
top: 0;
right: -20vw;
/* 初始隐藏在右侧 */
position: relative;
flex-shrink: 0;
width: 20vw;
height: 100vh;
height: 100%;
background: linear-gradient(135deg, #1e1e1e 0%, #2d2d2d 100%);
border-left: 2px solid #444;
box-shadow: -5px 0 20px rgba(0, 0, 0, 0.3);
z-index: 1000;
transition: right 0.3s ease;
border-right: 2px solid #444;
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.2);
overflow: hidden;
display: flex;
flex-direction: column;
}
.apiDrawer.apiDrawerOpen {
right: 0;
/* 滑出显示 */
.apiDrawerLoading {
position: absolute;
inset: 0;
z-index: 20;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 14px;
background: rgba(18, 18, 18, 0.82);
backdrop-filter: blur(2px);
cursor: not-allowed;
}
.apiDrawerLoadingSpinner {
width: 38px;
height: 38px;
border: 3px solid rgba(255, 255, 255, 0.18);
border-top-color: #ba68c8;
border-radius: 50%;
animation: apiDrawerLoadingSpin 0.9s linear infinite;
}
.apiDrawerLoadingText {
color: #e0e0e0;
font-size: 13px;
line-height: 1.7;
text-align: center;
padding: 0 18px;
}
@keyframes apiDrawerLoadingSpin {
to {
transform: rotate(360deg);
}
}
.apiDrawerHeader {
@@ -255,21 +223,6 @@
font-weight: 600;
}
.apiDrawerClose {
background: none;
border: none;
color: white;
font-size: 20px;
cursor: pointer;
padding: 5px;
border-radius: 3px;
transition: background-color 0.2s ease;
}
.apiDrawerClose:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.apiDrawerContent {
flex: 1;
overflow: hidden;
@@ -440,15 +393,6 @@
@media (max-width: 1200px) {
.apiDrawer {
width: 25vw;
right: -25vw;
}
.apiDrawer.apiDrawerOpen {
right: 0;
}
.apiDrawerTab.apiDrawerTabOpen {
right: 25vw;
}
.apiDrawerCategoryButton {
@@ -464,34 +408,6 @@
@media (max-width: 900px) {
.apiDrawer {
width: 35vw;
right: -35vw;
}
.apiDrawer.apiDrawerOpen {
right: 0;
}
.apiDrawerTab.apiDrawerTabOpen {
right: 35vw;
}
.apiDrawerTab {
width: 35px;
height: 75px;
gap: 4px;
}
.apiDrawerTabIcon {
font-size: 12px;
}
.apiDrawerTabText {
font-size: 8px;
letter-spacing: 1px;
}
.apiDrawerTab:hover .apiDrawerTabText {
letter-spacing: 1.5px;
}
.apiDrawerHeader h3 {

View File

@@ -49,9 +49,6 @@ const StageWrapperPythonCPLT = () => {
`
);
// ★★★ 添加抽屉状态管理
const [apiDrawerOpen, setApiDrawerOpen] = useState(false);
// ★★★ 替换 workerRef用 Pool Ref
const poolRef = useRef(null); // 用这个存 Worker Pool
// const workerRef = useRef(null); // <-- 删掉
@@ -60,6 +57,27 @@ const StageWrapperPythonCPLT = () => {
const [errorModalVisible, setErrorModalVisible] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const [overlayActive, setOverlayActive] = useState(false);
const [apiSelectedCategory, setApiSelectedCategory] = useState('player');
const [isApiPanelReady, setIsApiPanelReady] = useState(() => window.iscreate === 1);
// Unity 加载完成前API 面板显示加载态并屏蔽点击5 秒兜底自动解除)
useEffect(() => {
if (isApiPanelReady) return undefined;
const timer = setInterval(() => {
if (window.iscreate === 1) {
setIsApiPanelReady(true);
clearInterval(timer);
}
}, 300);
const fallback = setTimeout(() => {
setIsApiPanelReady(true);
clearInterval(timer);
}, 5000);
return () => {
clearInterval(timer);
clearTimeout(fallback);
};
}, [isApiPanelReady]);
// CodeMirror编辑器引用
const editorRef = useRef(null);
@@ -649,15 +667,65 @@ const StageWrapperPythonCPLT = () => {
if (t) parts.push(t);
return parts;
}
function splitOr(stmt) {
if (!hasFuncCall(stmt)) {
return [stmt];
}
const parts = [];
let cur = '';
let skipChars = 0;
parse(stmt, (ch, depth, parenDepth, i, notInStr) => {
if (skipChars > 0) {
skipChars--;
return;
}
if (notInStr && depth === 0 && ch === 'o' && stmt[i + 1] === 'r') {
const prevChar = i > 0 ? stmt[i - 1] : '';
const nextChar = i + 2 < stmt.length ? stmt[i + 2] : '';
const isPrevWordChar = /[a-zA-Z0-9_]/.test(prevChar);
const isNextWordChar = /[a-zA-Z0-9_]/.test(nextChar);
if (!isPrevWordChar && !isNextWordChar) {
const t = cur.trim();
if (t) parts.push(t);
cur = '';
skipChars = 1;
return;
}
}
cur += ch;
});
const t = cur.trim();
if (t) parts.push(t);
return parts;
}
let count = 0;
for (const stmt of splitStatements(s)) {
const assign = findAssign(stmt);
if (!assign) {
count += splitComma(stmt).length;
let stmtCount = 0;
for (const orPart of splitOr(stmt)) {
stmtCount += splitComma(orPart).length;
}
count += stmtCount;
} else {
const lhs = stmt.slice(0, assign.index).trim();
count += hasComma(lhs) ? 1 : Math.max(1, splitComma(stmt.slice(assign.index + assign.length)).length);
if (hasComma(lhs)) {
count += 1;
} else {
const rhs = stmt.slice(assign.index + assign.length);
let rhsCount = 0;
for (const orPart of splitOr(rhs)) {
rhsCount += splitComma(orPart).length;
}
count += Math.max(1, rhsCount);
}
}
}
return count;
@@ -1168,22 +1236,15 @@ sys.settrace(trace_func)`;
);
};
// API参考抽屉组件
// API参考面板
const ApiDrawer = () => {
const [selectedCategory, setSelectedCategory] = useState('player');
const selectedCategory = apiSelectedCategory;
const setSelectedCategory = setApiSelectedCategory;
return (
<div className={`${styles.apiDrawer} ${apiDrawerOpen ? styles.apiDrawerOpen : ''}`}>
{/* 抽屉头部 */}
<div className={styles.apiDrawer}>
<div className={styles.apiDrawerHeader}>
<h3>🐍 Python API参考</h3>
<button
className={styles.apiDrawerClose}
onClick={() => setApiDrawerOpen(false)}
title="关闭API参考"
>
×
</button>
</div>
{/* 抽屉内容 */}
@@ -1226,6 +1287,12 @@ sys.settrace(trace_func)`;
))}
</div>
</div>
{!isApiPanelReady && (
<div className={styles.apiDrawerLoading}>
<div className={styles.apiDrawerLoadingSpinner}></div>
<div className={styles.apiDrawerLoadingText}>游戏引擎加载中完成后即可插入代码</div>
</div>
)}
</div>
);
};
@@ -1288,9 +1355,6 @@ sys.settrace(trace_func)`;
});
}
// 关闭API参考窗口
setApiDrawerOpen(false);
// 聚焦编辑器
view.focus();
}
@@ -1300,36 +1364,16 @@ sys.settrace(trace_func)`;
return (
<>
<ErrorModal />
<div style={{
position: 'relative',
height: '100%',
width: '100%',
overflow: 'hidden',
}}>
{/* API抽屉标签 */}
<div
className={`${styles.apiDrawerTab} ${apiDrawerOpen ? styles.apiDrawerTabOpen : ''}`}
onClick={() => setApiDrawerOpen(!apiDrawerOpen)}
title={apiDrawerOpen ? "关闭API参考" : "打开API参考"}
>
<div className={styles.apiDrawerTabIcon}>
{apiDrawerOpen ? '▶' : '◀'}
</div>
<div className={styles.apiDrawerTabText}>
{apiDrawerOpen ? '关闭' : 'API'}
</div>
</div>
{/* API抽屉 */}
<div className={styles.editorWithApi}>
<ApiDrawer />
{/* Python代码编辑器 - 现在占满整个高度 */}
<div onWheel={(e) => e.stopPropagation()} style={{ overflow: 'auto', position: 'relative', height: '100%' }}>
<div
className={styles.editorPane}
onWheel={(e) => e.stopPropagation()}
>
<CodeMirror
ref={editorRef}
style={{
width: '54vw',
// height: '92vh',
width: '100%',
height: '100%',
position: 'relative',
backgroundColor: '#010101',

View File

@@ -12,6 +12,8 @@ import RankBadgeGenerator from '../../playground/rank-badge-generator.js';
window.RankBadgeGenerator = RankBadgeGenerator;
window.CONFIG = CONFIG;
import disableDevtool from 'disable-devtool';
import { initDisableDevtool } from '../../playground/devtool-control.js';
import { getUnityPlayerConfig, loadUnityLoaderScript } from '../../lib/unity-build-loader.js';
import { debug } from 'scratch-vm/src/util/log.js';
const MAX_LEVELS = 500; // 最大关卡数据数量

View File

@@ -0,0 +1,119 @@
import CONFIG from '../playground/config.js';
const FILES = {
dataUrl: 'mstest5.data.br',
frameworkUrl: 'mstest5.framework.js.br',
codeUrl: 'mstest5.wasm.br'
};
// 压缩后 wasm 约 4.4MB;用户侧曾出现只下到 ~800KB 的截断包
const MIN_BYTES = {
dataUrl: 1024 * 1024,
frameworkUrl: 20 * 1024,
codeUrl: 2 * 1024 * 1024
};
const WASM_MAGIC = [0x00, 0x61, 0x73, 0x6d];
function withVersion(url) {
const version = CONFIG.version || Date.now();
return `${url}${url.includes('?') ? '&' : '?'}v=${encodeURIComponent(version)}`;
}
function buildCandidates() {
const local = '/unity/Build';
const cdn = CONFIG.unitycdnbuilddir;
if (CONFIG.usecdn) {
return [cdn, local].filter(Boolean);
}
return [local, cdn].filter(Boolean);
}
function isUncompressedWasm(buffer) {
const bytes = new Uint8Array(buffer);
return WASM_MAGIC.every((value, index) => bytes[index] === value);
}
function looksLikeJavascript(buffer) {
const head = new TextDecoder().decode(buffer.slice(0, 80));
return /function|unityFramework|^\s*var |^\s*const |^\s*!function/.test(head);
}
function looksLikeUnityData(buffer) {
const head = new TextDecoder().decode(buffer.slice(0, 16));
return head.startsWith('UnityWebData1.0');
}
function toTrustedUrl(fileName, buffer, sourceUrl) {
if (fileName.endsWith('.wasm.br') && isUncompressedWasm(buffer)) {
return URL.createObjectURL(new Blob([buffer], { type: 'application/wasm' }));
}
if (fileName.endsWith('.js.br') && looksLikeJavascript(buffer)) {
return URL.createObjectURL(new Blob([buffer], { type: 'application/javascript' }));
}
if (fileName.endsWith('.data.br') && looksLikeUnityData(buffer)) {
return URL.createObjectURL(new Blob([buffer]));
}
return sourceUrl;
}
async function fetchCompleteAsset(fileName, minBytes) {
let lastError = null;
for (const base of buildCandidates()) {
const url = withVersion(`${base}/${fileName}`);
try {
const response = await fetch(url, { cache: 'no-store' });
if (!response.ok) {
lastError = new Error(`${url} HTTP ${response.status}`);
continue;
}
const buffer = await response.arrayBuffer();
if (buffer.byteLength < minBytes) {
console.warn(`[Unity] ${fileName} 被截断: ${buffer.byteLength} bytes来自 ${url}),期望至少 ${minBytes}`);
lastError = new Error(`${fileName} truncated: ${buffer.byteLength}`);
continue;
}
console.log(`[Unity] ${fileName} 下载完整: ${buffer.byteLength} bytes from ${url}`);
return toTrustedUrl(fileName, buffer, url);
} catch (error) {
console.warn(`[Unity] 下载失败 ${url}`, error);
lastError = error;
}
}
throw lastError || new Error(`无法完整下载 ${fileName}`);
}
export async function getUnityPlayerConfig() {
const [dataUrl, frameworkUrl, codeUrl] = await Promise.all([
fetchCompleteAsset(FILES.dataUrl, MIN_BYTES.dataUrl),
fetchCompleteAsset(FILES.frameworkUrl, MIN_BYTES.frameworkUrl),
fetchCompleteAsset(FILES.codeUrl, MIN_BYTES.codeUrl)
]);
const streamingAssetsUrl = (CONFIG.usecdn && CONFIG.unitycdndir)
? `${CONFIG.unitycdndir}/StreamingAssets`
: '/unity/StreamingAssets';
return {
dataUrl,
frameworkUrl,
codeUrl,
streamingAssetsUrl,
companyName: 'DefaultCompany',
productName: 'Web A',
productVersion: String(CONFIG.version || '1.0')
};
}
export function loadUnityLoaderScript() {
if (typeof window.createUnityInstance === 'function') {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = withVersion('/unity/Build/mstest5.loader.js');
script.onload = () => resolve();
script.onerror = () => reject(new Error('Unity loader 加载失败'));
document.body.appendChild(script);
});
}

View File

@@ -5,6 +5,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>关于我们 - 灵丌编程001CODE - 你身边的青少年编程学习平台 - 图形化、Python、C++</title>
<script src="js/nav-responsive.js?v=<%= htmlWebpackPlugin.options.version %>" defer></script>
<style>
* {
box-sizing: border-box;
@@ -111,6 +112,138 @@
list-style: none;
}
.nav-toggle {
display: none;
width: 40px;
height: 40px;
margin-left: auto;
padding: 0;
border: 1px solid #e2e8f0;
border-radius: 10px;
background: #f8fafc;
cursor: pointer;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 5px;
flex-shrink: 0;
appearance: none;
-webkit-appearance: none;
}
.nav-toggle:focus-visible {
outline: 2px solid rgba(79, 140, 255, 0.55);
outline-offset: 2px;
}
.nav-toggle span {
display: block;
width: 18px;
height: 2px;
background: #1e1a4c;
border-radius: 1px;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-open .nav-toggle span:nth-child(1) {
transform: translateY(7px) rotate(45deg);
}
.nav-open .nav-toggle span:nth-child(2) {
opacity: 0;
}
.nav-open .nav-toggle span:nth-child(3) {
transform: translateY(-7px) rotate(-45deg);
}
.nav-backdrop {
display: none;
}
@media (max-width: 960px) {
body.nav-open {
overflow: hidden;
}
.nav-inner {
gap: 12px;
padding: 4px 12px;
}
.brand {
padding: 4px 0;
}
.brand-logo {
width: 156px;
height: 44px;
}
.nav-toggle {
display: inline-flex;
}
.nav-list {
display: none;
position: fixed;
top: 64px;
left: 0;
right: 0;
flex-direction: column;
align-items: stretch;
gap: 4px;
max-height: calc(100vh - 64px);
overflow-y: auto;
padding: 12px 16px 20px;
background: #ffffff;
border-top: 1px solid #e2e8f0;
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.12);
z-index: 51;
}
.nav-open .nav-list {
display: flex;
}
.nav-backdrop {
position: fixed;
top: 64px;
left: 0;
right: 0;
bottom: 0;
background: rgba(15, 23, 42, 0.32);
z-index: 49;
}
.nav-open .nav-backdrop {
display: block;
}
.nav-list li {
width: 100%;
}
.nav-list .nav-item,
.nav-list .nav__pillAI {
display: flex;
width: 100%;
min-height: 44px;
padding: 10px 12px;
border-radius: 10px;
align-items: center;
box-sizing: border-box;
}
.nav-list .nav-item:hover {
background: #f8fafc;
}
.nav-list .nav__pillAI:hover {
transform: none;
}
}
.nav-item {
display: flex;
align-items: center;
@@ -130,7 +263,19 @@
}
.nav-item.about {
color: #0790d9;
color: #1e1a4c;
}
.nav-item--active,
.nav-item[aria-current="page"] {
display: inline-flex;
align-items: center;
padding: 5px 12px;
height: 32px;
border-radius: 3px;
background: #f2f3ff;
color: #4F8CFF;
box-sizing: border-box;
}
.nav__pill {
@@ -169,6 +314,87 @@
color: #ffffff;
}
/* 灵丌AI 导航胶囊 */
.nav__pillAI {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 18px 5px 14px;
height: 32px;
border-radius: 20px;
text-decoration: none;
position: relative;
transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.nav__pillAI:hover {
transform: scale(1.05);
}
.nav__pillAI:focus {
outline: none;
}
.nav__pillAI:focus-visible {
outline: 2px solid rgba(0, 3, 255, 0.45);
outline-offset: 3px;
}
.nav__pillAIText {
font-family: "PingFang SC", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
font-weight: 600;
font-size: 14px;
line-height: 1.5714285714;
font-style: italic;
letter-spacing: 0.5px;
background-image: linear-gradient(
105deg,
rgba(0, 3, 255, 0.55) 0%,
rgba(0, 3, 255, 0.55) 38%,
#7dd3fc 46%,
#ffffff 50%,
#60a5fa 54%,
rgba(0, 3, 255, 0.55) 62%,
rgba(0, 3, 255, 0.55) 100%
);
background-size: 220% 100%;
background-position: 100% 50%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
transition: letter-spacing 0.35s ease;
}
.nav__pillAI:hover .nav__pillAIText {
letter-spacing: 1.2px;
animation: navAIShimmer 1.35s ease-in-out infinite;
}
@keyframes navAIShimmer {
0% { background-position: 100% 50%; }
100% { background-position: -120% 50%; }
}
.nav__pillAIIcon {
width: 18px;
height: 18px;
display: block;
flex-shrink: 0;
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), filter 0.35s ease;
}
.nav__pillAI:hover .nav__pillAIIcon {
transform: rotate(18deg) scale(1.18);
filter: brightness(1.35) saturate(1.25);
animation: navAISpark 1.1s ease-in-out infinite;
}
@keyframes navAISpark {
0%, 100% { transform: rotate(18deg) scale(1.18); }
50% { transform: rotate(-8deg) scale(1.28); }
}
main {
position: relative;
height: 1660px;
@@ -953,16 +1179,24 @@
<a class="brand" href="/home.html" aria-label="001CODE">
<img class="brand-logo" src="../images/logo.svg" alt="001CODE" />
</a>
<button type="button" class="nav-toggle" aria-label="打开菜单" aria-expanded="false">
<span></span>
<span></span>
<span></span>
</button>
<ul class="nav-list" aria-label="主导航">
<li><a class="nav-item home" href="/home.html">主页</a></li>
<li>
<a class="nav__pill" href="/">
<span class="nav__pillText">学习入口</span>
<span class="nav__pillNew">new</span>
<a class="nav-item" href="/welcome.html">灵丌编程</a>
</li>
<li>
<a class="nav__pillAI" href="/ai.html">
<img class="nav__pillAIIcon" src="../images/ai-sparkle.svg" alt="" />
<span class="nav__pillAIText">灵丌AI</span>
</a>
</li>
<li><a class="nav-item event" href="/competition_list.html">赛事详情</a></li>
<li><a class="nav-item about" href="/aboutus.html">关于我们</a></li>
<li><a class="nav-item about nav-item--active" href="/aboutus.html" aria-current="page">关于我们</a></li>
</ul>
</div>
</header>
@@ -1123,8 +1357,8 @@
<footer class="footer" aria-label="页脚">
<div class="footer-line"></div>
<div class="footer-text">ICP备案号<a href=" " target="_blank" style="color:#478ac9 ;">ICP备2022002563号-5</a>
公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a>
<div class="footer-text">ICP备案号<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:#478ac9 ;">ICP备2026042325号</a>
<!-- 公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a> -->
© 灵丌编程001CODE.COM
<span class="qr" tabindex="0">
<img class="qr-icon" src="../images/qrcode_gery.svg" alt="公众号小图标" />

View File

@@ -0,0 +1,4 @@
// 给 chunks: [] 的 AI 静态页注入与 login 相同的 config.js
import CONFIG from './config.js';
window.CONFIG = CONFIG;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
// ai.html 的 CONFIG 由 head 中的 aiConfig chunk 注入(与 login 同一份 config.js
if (typeof window.__AI_PAGE_BOOTED__ === 'undefined') {
document.addEventListener('DOMContentLoaded', function () {
if (!window.__AI_PAGE_BOOTED__) {
console.warn('[ai.js] 内联 boot 未执行,请检查 ai.html 是否为最新构建');
}
});
}

View File

@@ -6,6 +6,9 @@ let timerId = localStorage.getItem('loginStatusCheckTimerId');
async function checkLoginStatus() {
const token = localStorage.getItem('access_token');
if (!token) {
try {
localStorage.setItem('returnUrl', window.location.pathname + window.location.search);
} catch (e) {}
alert('您尚未登录或登录已过期,请重新登录');
window.location.href = window.location.origin + '/login.html';
return;

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>即将上线 - 灵丌AI - 001CODE</title>
<style>
:root {
--bg: #f0f4f9;
--ink: #0f172a;
--ai-cyan: #00CCFF;
--ai-accent: #00CCFF;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
background: #0f172a;
color: #fff;
font-family: "PingFang SC", "Noto Sans SC", system-ui, -apple-system, sans-serif;
overflow: hidden;
display: flex; align-items: center; justify-content: center;
}
/* ===== 开幕动画层 ===== */
.reveal-overlay {
position: fixed; inset: 0; z-index: 100;
background: radial-gradient(circle at center, rgba(0,3,255,0.15) 0%, #0f172a 70%);
display: flex; align-items: center; justify-content: center;
animation: overlayFade 0.8s 0.6s cubic-bezier(0.4,0,0.2,1) forwards;
}
@keyframes overlayFade {
0% { opacity: 1; }
100% { opacity: 0; pointer-events: none; }
}
/* 中心光圈扩散 */
.reveal-ring {
position: absolute; width: 20px; height: 20px; border-radius: 50%;
border: 1px solid rgba(0,204,255,0.6);
animation: ringExpand 1.2s 0.1s cubic-bezier(0.25,0.8,0.25,1) forwards;
}
.reveal-ring:nth-child(2) { animation-delay: 0.25s; border-color: rgba(0,3,255,0.4); }
.reveal-ring:nth-child(3) { animation-delay: 0.4s; border-color: rgba(0,3,255,0.3); }
@keyframes ringExpand {
0% { width: 20px; height: 20px; opacity: 1; }
100% { width: 800px; height: 800px; opacity: 0; }
}
/* 中心粒子 */
.reveal-particle {
position: absolute; width: 4px; height: 4px; border-radius: 50%;
background: #00CCFF; opacity: 0;
animation: particleBurst 0.9s 0.15s ease-out forwards;
}
@keyframes particleBurst {
0% { opacity: 1; transform: translate(0, 0) scale(1); }
100% { opacity: 0; transform: translate(var(--tx), var(--ty)) scale(0); }
}
/* 中心 Logo 缩放出现 */
.reveal-logo {
position: relative; z-index: 2; text-align: center;
animation: logoScaleIn 0.7s 0.2s cubic-bezier(0.34,1.56,0.64,1) both;
}
@keyframes logoScaleIn {
0% { transform: scale(0); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
.reveal-logo__icon { font-size: 72px; margin-bottom: 16px; filter: drop-shadow(0 0 30px rgba(0,204,255,0.5)); }
.reveal-logo__text {
font-size: 32px; font-weight: 700; letter-spacing: 2px;
background: linear-gradient(90deg, rgba(0, 204, 255, 1) 0%, rgba(0, 3, 255, 1) 100%);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
/* ===== 主页面 ===== */
.page {
width: 100%; max-width: 800px; padding: 48px 32px;
text-align: center; z-index: 1;
animation: contentFadeIn 0.8s 1s ease both;
}
@keyframes contentFadeIn {
0% { opacity: 0; transform: translateY(20px); }
100% { opacity: 1; transform: translateY(0); }
}
.badge {
display: inline-flex; align-items: center; gap: 8px;
padding: 6px 18px; border-radius: 50px;
background: rgba(0,3,255,0.12); border: 1px solid rgba(0,3,255,0.2);
color: #00CCFF; font-size: 13px; font-weight: 600; margin-bottom: 28px;
}
.badge__dot {
width: 8px; height: 8px; border-radius: 50%; background: #00CCFF;
animation: dotPulse 1.5s ease-in-out infinite;
}
@keyframes dotPulse {
0%, 100% { opacity: 1; box-shadow: 0 0 6px #00CCFF; }
50% { opacity: 0.4; box-shadow: 0 0 12px #00CCFF; }
}
h1 {
font-size: 42px; font-weight: 700; margin: 0 0 16px; color: #fff;
letter-spacing: 1px;
}
h1 span {
background: linear-gradient(90deg, rgba(0, 204, 255, 1) 0%, rgba(0, 3, 255, 1) 100%);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
.subtitle { font-size: 17px; color: #94a3b8; margin: 0 0 48px; line-height: 1.7; }
/* ===== 骨架屏卡片 ===== */
.skeleton-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 70px; position: relative; }
.skeleton-card {
background: rgba(255,255,255,0.04); border-radius: 14px;
padding: 24px 20px; border: 1px solid rgba(255,255,255,0.06);
overflow: hidden; position: relative;
transition: opacity 0.5s ease, transform 0.5s ease;
}
.skeleton-card.fade-out {
opacity: 0; transform: scale(0.95); pointer-events: none;
}
.skeleton-card::after {
content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.03), transparent);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% { left: -100%; }
100% { left: 100%; }
}
.sk-circle {
width: 40px; height: 40px; border-radius: 10px; margin: 0 auto 16px;
background: rgba(0,204,255,0.1);
}
.sk-line { height: 12px; border-radius: 6px; margin: 0 auto 8px; background: rgba(255,255,255,0.06); }
.sk-line--lg { width: 80%; }
.sk-line--md { width: 60%; }
.sk-line--sm { width: 40%; }
/* ===== 真实功能卡片 ===== */
.feature-grid {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;
position: absolute; top: 0; left: 0; width: 100%;
pointer-events: none; opacity: 0;
transition: opacity 0.6s 0.3s ease;
}
.feature-grid.show { opacity: 1; pointer-events: auto; }
.feature-card {
background: rgba(255,255,255,0.04); border-radius: 14px;
padding: 28px 20px; border: 1px solid rgba(255,255,255,0.06);
text-align: center; transition: all 0.35s ease;
animation: cardReveal 0.6s ease both;
}
.feature-card:nth-child(1) { animation-delay: 0.1s; }
.feature-card:nth-child(2) { animation-delay: 0.25s; }
.feature-card:nth-child(3) { animation-delay: 0.4s; }
@keyframes cardReveal {
0% { opacity: 0; transform: translateY(16px) scale(0.95); }
100% { opacity: 1; transform: translateY(0) scale(1); }
}
.feature-card:hover {
border-color: rgba(0,204,255,0.3); background: rgba(0,204,255,0.06);
transform: translateY(-3px);
box-shadow: 0 8px 30px rgba(0,3,255,0.12);
}
.feature-card__icon {
width: 80px; height: 80px; border-radius: 14px; margin: 0 auto 16px;
display: flex; align-items: center; justify-content: center; font-size: 26px;
}
.feature-card__icon--green { background: rgba(34,197,94,0.12); }
.feature-card__icon--blue { background: rgba(59,130,246,0.12); }
.feature-card h3 {
font-size: 18px; font-weight: 700; margin: 0 0 8px; color: #fff;
}
.feature-card p {
font-size: 13px; color: #94a3b8; margin: 0 0 16px; line-height: 1.6;
}
.feature-card__link {
display: inline-flex; align-items: center; gap: 5px;
padding: 6px 16px; border-radius: 20px; font-size: 12px; font-weight: 600;
text-decoration: none; transition: all 0.3s;
border: 1px solid rgba(0,204,255,0.25);
color: #00CCFF; background: transparent;
}
.feature-card__link:hover {
background: rgba(0,204,255,0.15); border-color: #00CCFF;
}
/* ===== 通知订阅区 ===== */
.notify-box {
display: inline-flex; align-items: center; gap: 10px;
padding: 14px 28px; border-radius: 50px;
background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08);
transition: all 0.3s ease;
}
.notify-box:hover { border-color: rgba(0,204,255,0.3); background: rgba(0,204,255,0.05); }
.notify-box__icon { font-size: 18px; }
.notify-box__text { font-size: 14px; color: #94a3b8; }
.notify-box__text strong { color: #00CCFF; font-weight: 600; }
.back-link {
display: inline-flex; align-items: center; gap: 8px;
margin-top: 36px; color: #00CCFF; text-decoration: none;
font-size: 14px; font-weight: 600; transition: all 0.3s;
padding: 10px 28px; border-radius: 30px;
border: 1px solid rgba(0,204,255,0.3);
background: rgba(0,204,255,0.08);
animation: backGlow 2s ease-in-out infinite;
}
@keyframes backGlow {
0%, 100% { box-shadow: 0 0 8px rgba(0,204,255,0.2); border-color: rgba(0,204,255,0.3); }
50% { box-shadow: 0 0 22px rgba(0,204,255,0.5); border-color: rgba(0,204,255,0.7); }
}
.back-link:hover {
background: rgba(0,204,255,0.18); border-color: #00CCFF;
box-shadow: 0 0 30px rgba(0,204,255,0.6);
animation: none;
}
/* ===== 二维码区 ===== */
.qr-section { margin-top: 28px; }
.qr-section img {
width: 130px; height: 130px; border-radius: 12px;
border: 2px solid rgba(255,255,255,0.08);
transition: all 0.3s ease;
}
.qr-section img:hover { border-color: rgba(0,204,255,0.4); box-shadow: 0 0 24px rgba(0,204,255,0.2); }
.qr-section p { font-size: 12px; color: #64748b; margin-top: 8px; }
@media (max-width: 640px) {
.skeleton-grid { grid-template-columns: 1fr; }
h1 { font-size: 28px; }
.reveal-logo__icon { font-size: 48px; }
}
</style>
</head>
<body>
<!-- ===== 开幕动画层 ===== -->
<div class="reveal-overlay">
<div class="reveal-ring"></div>
<div class="reveal-ring"></div>
<div class="reveal-ring"></div>
<div class="reveal-particle" style="--tx:60px;--ty:-80px;"></div>
<div class="reveal-particle" style="--tx:-70px;--ty:-50px;"></div>
<div class="reveal-particle" style="--tx:80px;--ty:40px;"></div>
<div class="reveal-particle" style="--tx:-50px;--ty:70px;"></div>
<div class="reveal-particle" style="--tx:20px;--ty:-100px;"></div>
<div class="reveal-particle" style="--tx:-90px;--ty:20px;"></div>
<div class="reveal-logo">
<div class="reveal-logo__icon">✨</div>
<div class="reveal-logo__text">灵丌AI</div>
</div>
</div>
<!-- ===== 主内容 ===== -->
<div class="page">
<div class="badge">
<span class="badge__dot"></span>
COMING SOON
</div>
<h1>灵丌AI<span> 即将上线</span></h1>
<p class="subtitle">贯穿青少年科创教育始终全新的AI学习工具即将精彩呈现敬请期待。</p>
<!-- 骨架屏 + 真实卡片容器 -->
<div class="skeleton-grid" id="cardContainer" style="min-height: 220px;">
<!-- 骨架占位 -->
<div class="skeleton-card" id="sk1">
<div class="sk-circle"></div>
<div class="sk-line sk-line--lg"></div>
<div class="sk-line sk-line--md"></div>
<div class="sk-line sk-line--sm"></div>
</div>
<div class="skeleton-card" id="sk2">
<div class="sk-circle"></div>
<div class="sk-line sk-line--lg"></div>
<div class="sk-line sk-line--md"></div>
<div class="sk-line sk-line--sm"></div>
</div>
<div class="skeleton-card" id="sk3">
<div class="sk-circle"></div>
<div class="sk-line sk-line--lg"></div>
<div class="sk-line sk-line--md"></div>
<div class="sk-line sk-line--sm"></div>
</div>
<!-- 真实卡片 -->
<div class="feature-grid" id="featureGrid">
<div class="feature-card">
<div class="feature-card__icon">
<img src="../images/aigc/icon3.png" alt="AI创意课程" style="width: 100%; height: 100%;" />
</div>
<h3>AI创意课程体系</h3>
<p>采用全新创意式项目制学习及评价体系赋能青少年AI素养培养。</p>
</div>
<div class="feature-card">
<div class="feature-card__icon">
<img src="../images/aigc/icon2.png" alt="AI实践平台" style="width: 100%; height: 100%;" />
</div>
<h3>AI实践平台</h3>
<p>一站式满足青少年AI素养学习需求多模态工具助力探究式AI学习。</p>
</div>
<div class="feature-card">
<div class="feature-card__icon">
<img src="../images/aigc/iocn1.png" alt="赛事体系" style="width: 100%; height: 100%;" />
</div>
<h3>赛事体系</h3>
<p>每一级都有出口,每一步都有回响。</p>
</div>
</div>
</div>
<!-- 二维码 -->
<div class="qr-section">
<img src="../images/qrcode.jpg" alt="公众号二维码" />
<p>扫码关注“灵丌编程”,了解更多资讯</p>
</div>
<a href="/home.html" class="back-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
返回首页
</a>
</div>
<script>
// 骨架屏 → 真实内容 过渡
(function() {
var delay = 2500; // 2.5秒后切换
function revealContent() {
var sk1 = document.getElementById('sk1');
var sk2 = document.getElementById('sk2');
var sk3 = document.getElementById('sk3');
var grid = document.getElementById('featureGrid');
// 骨架渐隐
if (sk1) sk1.classList.add('fade-out');
if (sk2) { sk2.classList.add('fade-out'); sk2.style.transitionDelay = '0.1s'; }
if (sk3) { sk3.classList.add('fade-out'); sk3.style.transitionDelay = '0.2s'; }
// 延迟展示真实卡片
setTimeout(function() {
if (grid) grid.classList.add('show');
// 隐藏骨架 DOM
setTimeout(function() {
if (sk1) sk1.style.display = 'none';
if (sk2) sk2.style.display = 'none';
if (sk3) sk3.style.display = 'none';
}, 500);
}, 300);
}
// 开幕动画结束后开始计时
setTimeout(revealContent, delay);
})();
</script>
</body>
</html>

View File

@@ -87,8 +87,8 @@
<footer>
<div class="footer-line"></div>
<p class="footer-text">ICP备案号<a style="color:#478ac9 ;" href=" " target="_blank">蜀ICP备2022002563号-5</a>
公安备案号:<a style="color:#478ac9 ;" href="https://beian.mps.gov.cn/" target="_blank">川公网安备51011202000980号</a>
<p class="footer-text">ICP备案号<a style="color:#478ac9 ;" href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">沪ICP备2026042325号</a>
<!-- 公安备案号:<a style="color:#478ac9 ;" href="https://beian.mps.gov.cn/" target="_blank">川公网安备51011202000980号</a> -->
© 灵丌编程001CODE.COM</p>
</footer>
</div>

View File

@@ -5,6 +5,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>赛事详情 - 灵丌编程001CODE - 你身边的青少年编程学习平台 - 图形化、Python、C++</title>
<script src="js/nav-responsive.js?v=<%= htmlWebpackPlugin.options.version %>" defer></script>
</head>
<style>
:root {
@@ -123,6 +124,131 @@
gap: 32px;
}
.nav-toggle {
display: none;
width: 40px;
height: 40px;
margin-left: auto;
padding: 0;
border: 1px solid #e2e8f0;
border-radius: 10px;
background: #f8fafc;
cursor: pointer;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 5px;
flex-shrink: 0;
appearance: none;
-webkit-appearance: none;
}
.nav-toggle:focus-visible {
outline: 2px solid rgba(79, 140, 255, 0.55);
outline-offset: 2px;
}
.nav-toggle span {
display: block;
width: 18px;
height: 2px;
background: #1e1a4c;
border-radius: 1px;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-open .nav-toggle span:nth-child(1) {
transform: translateY(7px) rotate(45deg);
}
.nav-open .nav-toggle span:nth-child(2) {
opacity: 0;
}
.nav-open .nav-toggle span:nth-child(3) {
transform: translateY(-7px) rotate(-45deg);
}
.nav-backdrop {
display: none;
}
@media (max-width: 960px) {
body.nav-open {
overflow: hidden;
}
.nav-inner {
gap: 12px;
padding: 4px 12px;
}
.nav-logo {
position: static;
width: 156px;
height: 44px;
}
.nav-toggle {
display: inline-flex;
}
.nav-list {
display: none;
position: fixed;
top: 64px;
left: 0;
right: 0;
flex-direction: column;
align-items: stretch;
gap: 4px;
max-height: calc(100vh - 64px);
overflow-y: auto;
padding: 12px 16px 20px;
background: #ffffff;
border-top: 1px solid #e2e8f0;
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.12);
z-index: 51;
}
.nav-open .nav-list {
display: flex;
}
.nav-backdrop {
position: fixed;
top: 64px;
left: 0;
right: 0;
bottom: 0;
background: rgba(15, 23, 42, 0.32);
z-index: 49;
}
.nav-open .nav-backdrop {
display: block;
}
.nav-list .nav-link,
.nav-list .nav__pillAI {
display: flex;
width: 100%;
min-height: 44px;
padding: 10px 12px;
border-radius: 10px;
align-items: center;
box-sizing: border-box;
}
.nav-list .nav-link:hover {
background: #f8fafc;
}
.nav-list .nav__pillAI:hover {
transform: none;
}
}
.nav-link {
text-decoration: none;
color: #020f30;
@@ -138,7 +264,14 @@
}
.nav-link-current {
color: #0790d9;
display: inline-flex;
align-items: center;
padding: 5px 12px;
height: 32px;
border-radius: 3px;
background: #f2f3ff;
color: #4F8CFF;
box-sizing: border-box;
}
.nav__pill {
@@ -177,6 +310,86 @@
color: #ffffff;
}
/* 灵丌AI 导航胶囊 */
.nav__pillAI {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 18px 5px 14px;
height: 32px;
border-radius: 20px;
text-decoration: none;
position: relative;
transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.nav__pillAI:hover {
transform: scale(1.05);
}
.nav__pillAI:focus {
outline: none;
}
.nav__pillAI:focus-visible {
outline: 2px solid rgba(0, 3, 255, 0.45);
outline-offset: 3px;
}
.nav__pillAIText {
font-family: "PingFang SC", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
font-weight: 600;
font-size: 14px;
line-height: 1.5714285714;
font-style: italic;
letter-spacing: 0.5px;
background-image: linear-gradient(
105deg,
rgba(0, 3, 255, 0.55) 0%,
rgba(0, 3, 255, 0.55) 38%,
#7dd3fc 46%,
#ffffff 50%,
#60a5fa 54%,
rgba(0, 3, 255, 0.55) 62%,
rgba(0, 3, 255, 0.55) 100%
);
background-size: 220% 100%;
background-position: 100% 50%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
transition: letter-spacing 0.35s ease;
}
.nav__pillAI:hover .nav__pillAIText {
letter-spacing: 1.2px;
animation: navAIShimmer 1.35s ease-in-out infinite;
}
@keyframes navAIShimmer {
0% { background-position: 100% 50%; }
100% { background-position: -120% 50%; }
}
.nav__pillAIIcon {
width: 18px;
height: 18px;
display: block;
flex-shrink: 0;
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), filter 0.35s ease;
}
.nav__pillAI:hover .nav__pillAIIcon {
transform: rotate(18deg) scale(1.18);
filter: brightness(1.35) saturate(1.25);
animation: navAISpark 1.1s ease-in-out infinite;
}
@keyframes navAISpark {
0%, 100% { transform: rotate(18deg) scale(1.18); }
50% { transform: rotate(-8deg) scale(1.28); }
}
.hero {
@@ -345,10 +558,10 @@
}
.card-cover {
width: 400px;
width: 300px;
height: 230px;
display: block;
object-fit: cover;
object-fit: contain;
border-radius: 16px;
/* 所有四个角都是 16px 的圆角 */
}
@@ -630,13 +843,19 @@
<a class="brand" href="/home.html" aria-label="001CODE">
<img class="nav-logo" src="../images/logo.svg" alt="001CODE" />
</a>
<button type="button" class="nav-toggle" aria-label="打开菜单" aria-expanded="false">
<span></span>
<span></span>
<span></span>
</button>
<nav class="nav-list" aria-label="主导航">
<a class="nav-link nav-link-home" href="/home.html">主页</a>
<a class="nav__pill" href="/">
<span class="nav__pillText">学习入口</span>
<span class="nav__pillNew">new</span>
<a class="nav-link" href="/welcome.html">灵丌编程</a>
<a class="nav__pillAI" href="/ai.html">
<img class="nav__pillAIIcon" src="../images/ai-sparkle.svg" alt="" />
<span class="nav__pillAIText">灵丌AI</span>
</a>
<a class="nav-link nav-link-current" href="/competition_list.html">赛事详情</a>
<a class="nav-link nav-link-current" href="/competition_list.html" aria-current="page">赛事详情</a>
<a class="nav-link" href="/aboutus.html">关于我们</a>
</nav>
</div>
@@ -683,7 +902,7 @@
</div>
</div>
<section class="cta" aria-label="学习入口">
<section class="cta" aria-label="灵丌编程">
<div class="cta-bg"></div>
<div class="cta-inner">
<img class="cta-mask" src="../images/competition-cta-mask.svg" alt="" />
@@ -697,8 +916,8 @@
<footer class="footer" aria-label="页脚">
<div class="footer-line"></div>
<div class="footer-text">ICP备案号<a href=" " target="_blank" style="color:#478ac9 ;">ICP备2022002563号-5</a>
公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a>
<div class="footer-text">ICP备案号<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:#478ac9 ;">ICP备2026042325号</a>
<!-- 公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a> -->
© 灵丌编程001CODE.COM
<span class="qr" tabindex="0">
<img class="qr-icon" src="../images/qrcode_gery.svg" alt="公众号小图标" />

View File

@@ -79,7 +79,7 @@ const renderCompetitions = (competitions, currentServerTime) => {
const endTimeObj = new Date(endTime * 1000);
const timeStr = `${formatDate(startTimeObj)}-${formatDate(endTimeObj)}`;
const coverSrc = item.cover_image_url || '../images/image-119.png';
const coverSrc = item.cover_image_url || '../images/default_compertion.png';
/*
If item.is_competition is true, prefix the URL with https://coding.scxqn.com/

View File

@@ -159,8 +159,8 @@
<!-- Footer -->
<footer>
<div class="footer-line"></div>
<p class="footer-text">ICP备案号<a href=" " target="_blank" style="color:#478ac9 ;">ICP备2022002563号-5</a>
公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a>
<p class="footer-text">ICP备案号<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:#478ac9 ;">ICP备2026042325号</a>
<!-- 公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a> -->
© 灵丌编程001CODE.COM</p>
</footer>
</div>

View File

@@ -165,7 +165,6 @@ const CONFIG = {
// DataServerBaseUrl: "https://test-service.001code.cn",
version: "2026071701",
/** OSS 真实地址(生产环境浏览器直连) */
unityCdnRemote: 'https://oss.eanic.cn/001_code_cocos_res_2026071701',
/** 方案 Bnginx / webpack 同源代理前缀(方案 A 直连 OSS 请保持 false */
@@ -177,6 +176,8 @@ const CONFIG = {
unitycdndir: 'https://oss.eanic.cn/001_code_cocos_res_2026071701',
unitycdnbuilddir: 'https://oss.eanic.cn/001_code_cocos_res_2026071701/Build',
python_pyodide_cdn_dir: 'https://oss.eanic.cn/001_code_python_res_20241213/pyodide/',
/** AI 课程课件 HTML 资源(原 static/html 已迁到 OSS */
aiCourseOssBase: 'https://oss.001code.cn/ai_courses',
// 可以根据需要添加其他配置

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
import disableDevtool from 'disable-devtool';
import CONFIG from './config.js';
const STOP_DEV_KEY = 'stopDev';
function isStopped() {
try {
return sessionStorage.getItem(STOP_DEV_KEY) === '1';
} catch (e) {
return false;
}
}
/**
* 关闭开发者工具检测,之后可以正常打开 F12。
* 可在控制台直接执行stopDev()
*/
export function stopDev() {
try {
sessionStorage.setItem(STOP_DEV_KEY, '1');
} catch (e) {
// ignore quota / private mode
}
disableDevtool.isSuspend = true;
}
export function initDisableDevtool(options = {}) {
window.stopDev = stopDev;
if (isStopped()) {
disableDevtool.isSuspend = true;
}
if (!CONFIG.disabledev) {
return;
}
const userIgnore = options.ignore;
disableDevtool({
ondevtoolopen: () => {
window.location.href = localStorage.getItem('returnUrl');
},
interval: 500,
clearLog: true,
disableMenu: true,
...options,
ignore: () => {
if (isStopped()) {
return true;
}
if (typeof userIgnore === 'function') {
return userIgnore();
}
return false;
}
});
if (isStopped()) {
disableDevtool.isSuspend = true;
}
}
if (typeof window !== 'undefined') {
window.stopDev = stopDev;
}

View File

@@ -5,6 +5,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title> 首页 - 灵丌编程001CODE - 你身边的青少年编程学习平台 - 图形化、Python、C++</title>
<script src="js/nav-responsive.js?v=<%= htmlWebpackPlugin.options.version %>" defer></script>
</head>
<style>
:root {
@@ -73,6 +74,55 @@
gap: 32px;
}
.nav-toggle {
display: none;
width: 40px;
height: 40px;
margin-left: auto;
padding: 0;
border: 1px solid #e2e8f0;
border-radius: 10px;
background: #f8fafc;
cursor: pointer;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 5px;
flex-shrink: 0;
appearance: none;
-webkit-appearance: none;
}
.nav-toggle:focus-visible {
outline: 2px solid rgba(79, 140, 255, 0.55);
outline-offset: 2px;
}
.nav-toggle span {
display: block;
width: 18px;
height: 2px;
background: #1e1a4c;
border-radius: 1px;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-open .nav-toggle span:nth-child(1) {
transform: translateY(7px) rotate(45deg);
}
.nav-open .nav-toggle span:nth-child(2) {
opacity: 0;
}
.nav-open .nav-toggle span:nth-child(3) {
transform: translateY(-7px) rotate(-45deg);
}
.nav-backdrop {
display: none;
}
@@ -86,7 +136,14 @@
}
.nav__link--active {
color: var(--brand);
display: inline-flex;
align-items: center;
padding: 5px 12px;
height: 32px;
border-radius: 3px;
background: #f2f3ff;
color: #4F8CFF;
box-sizing: border-box;
}
.nav__pill {
@@ -124,6 +181,87 @@
color: #ffffff;
}
/* 灵丌AI 导航胶囊 */
.nav__pillAI {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 18px 5px 14px;
height: 32px;
border-radius: 20px;
text-decoration: none;
position: relative;
transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.nav__pillAI:hover {
transform: scale(1.05);
}
.nav__pillAI:focus {
outline: none;
}
.nav__pillAI:focus-visible {
outline: 2px solid rgba(0, 3, 255, 0.45);
outline-offset: 3px;
}
.nav__pillAIText {
font-family: "PingFang SC", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
font-weight: 600;
font-size: 14px;
line-height: 1.5714285714;
font-style: italic;
letter-spacing: 0.5px;
background-image: linear-gradient(
105deg,
rgba(0, 3, 255, 0.55) 0%,
rgba(0, 3, 255, 0.55) 38%,
#7dd3fc 46%,
#ffffff 50%,
#60a5fa 54%,
rgba(0, 3, 255, 0.55) 62%,
rgba(0, 3, 255, 0.55) 100%
);
background-size: 220% 100%;
background-position: 100% 50%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
transition: letter-spacing 0.35s ease;
}
.nav__pillAI:hover .nav__pillAIText {
letter-spacing: 1.2px;
animation: navAIShimmer 1.35s ease-in-out infinite;
}
@keyframes navAIShimmer {
0% { background-position: 100% 50%; }
100% { background-position: -120% 50%; }
}
.nav__pillAIIcon {
width: 18px;
height: 18px;
display: block;
flex-shrink: 0;
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), filter 0.35s ease;
}
.nav__pillAI:hover .nav__pillAIIcon {
transform: rotate(18deg) scale(1.18);
filter: brightness(1.35) saturate(1.25);
animation: navAISpark 1.1s ease-in-out infinite;
}
@keyframes navAISpark {
0%, 100% { transform: rotate(18deg) scale(1.18); }
50% { transform: rotate(-8deg) scale(1.28); }
}
.hero {
position: relative;
height: 791px;
@@ -1111,6 +1249,81 @@
text-decoration: none;
}
@media (max-width: 960px) {
body.nav-open {
overflow: hidden;
}
.nav__inner {
gap: 12px;
padding: 4px 12px;
}
.nav__logo {
width: 156px;
height: 44px;
}
.nav-toggle {
display: inline-flex;
}
.nav__links {
display: none;
position: fixed;
top: 64px;
left: 0;
right: 0;
flex-direction: column;
align-items: stretch;
gap: 4px;
max-height: calc(100vh - 64px);
overflow-y: auto;
padding: 12px 16px 20px;
background: #ffffff;
border-top: 1px solid #e2e8f0;
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.12);
z-index: 51;
}
.nav-open .nav__links {
display: flex;
}
.nav-backdrop {
position: fixed;
top: 64px;
left: 0;
right: 0;
bottom: 0;
background: rgba(15, 23, 42, 0.32);
z-index: 49;
}
.nav-open .nav-backdrop {
display: block;
}
.nav__links .nav__link,
.nav__links .nav__pillAI {
display: flex;
width: 100%;
min-height: 44px;
padding: 10px 12px;
border-radius: 10px;
align-items: center;
box-sizing: border-box;
}
.nav__links .nav__link:hover {
background: #f8fafc;
}
.nav__links .nav__pillAI:hover {
transform: none;
}
}
@media (max-width: 1280px) {
.feature__inner {
padding: 56px 24px;
@@ -1181,13 +1394,7 @@
.nav__logo {
width: 180px;
}
.nav__links {
gap: 16px;
overflow-x: auto;
padding-bottom: 6px;
width: 132px;
}
.progress {
@@ -1286,14 +1493,27 @@ top: 2032px; */
</style>
<body>
<script>
// 首页默认清除登录态,避免访客页残留 token
try {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
} catch (e) {}
</script>
<header class="nav">
<div class="nav__inner">
<img class="nav__logo" src="../images/logo.svg" alt="001CODE" />
<button type="button" class="nav-toggle" aria-label="打开菜单" aria-expanded="false">
<span></span>
<span></span>
<span></span>
</button>
<nav class="nav__links" aria-label="主导航">
<a class="nav__link nav__link--active" href="/home.html">主页</a>
<a class="nav__pill" href="/">
<span class="nav__pillText">学习入口</span>
<span class="nav__pillNew">new</span>
<a class="nav__link" href="/welcome.html">灵丌编程</a>
<a class="nav__pillAI" href="/ai.html">
<img class="nav__pillAIIcon" src="../images/ai-sparkle.svg" alt="" />
<span class="nav__pillAIText">灵丌AI</span>
</a>
<a class="nav__link" href="/competition_list.html">赛事详情</a>
<a class="nav__link" href="/aboutus.html">关于我们</a>
@@ -1545,8 +1765,8 @@ top: 2032px; */
<footer class="footer" aria-label="页脚">
<div class="footer-line"></div>
<div class="footer-text">ICP备案号<a href=" " target="_blank" style="color:#478ac9 ;">ICP备2022002563号-5</a>
公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a>
<div class="footer-text">ICP备案号<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:#478ac9 ;">ICP备2026042325号</a>
<!-- 公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a> -->
© 灵丌编程001CODE.COM
<span class="qr" tabindex="0">
<img class="qr-icon" src="../images/qrcode_gery.svg" alt="公众号小图标" />

View File

@@ -0,0 +1,3 @@
import { stopDev } from './devtool-control.js';
window.stopDev = stopDev;

View File

@@ -6,6 +6,9 @@ let timerId = localStorage.getItem('loginStatusCheckTimerId');
async function checkLoginStatus() {
const token = localStorage.getItem('access_token');
if (!token) {
try {
localStorage.setItem('returnUrl', window.location.pathname + window.location.search);
} catch (e) {}
window.location.href = window.location.origin + '/login.html';
return;
}

View File

@@ -14,6 +14,77 @@
<link rel="stylesheet" href="css/bootstrap.min.css?v=<%= htmlWebpackPlugin.options.version %>">
<link rel="stylesheet" href="css/bootstrap-icons.css?v=<%= htmlWebpackPlugin.options.version %>">
<link rel="stylesheet" href="css/leaderboard.css?v=<%= htmlWebpackPlugin.options.version %>">
<style>
/* 灵丌AI 导航链接 */
.nav-ai-link {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 6px 18px 6px 14px;
border-radius: 20px;
text-decoration: none;
white-space: nowrap;
position: relative;
transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.nav-ai-link:hover {
transform: scale(1.05);
}
.nav-ai-link:focus {
outline: none;
}
.nav-ai-link:focus-visible {
outline: 2px solid rgba(0, 3, 255, 0.45);
outline-offset: 3px;
}
.nav-ai-link__text {
font-size: 13px;
font-weight: 600;
font-style: italic;
letter-spacing: 0.5px;
/* 深蓝顶栏上使用浅色流光,保证可读 */
background-image: linear-gradient(
105deg,
rgba(255, 255, 255, 0.92) 0%,
rgba(255, 255, 255, 0.92) 38%,
#bae6fd 46%,
#ffffff 50%,
#7dd3fc 54%,
rgba(255, 255, 255, 0.92) 62%,
rgba(255, 255, 255, 0.92) 100%
);
background-size: 220% 100%;
background-position: 100% 50%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
transition: letter-spacing 0.35s ease;
}
.nav-ai-link:hover .nav-ai-link__text {
letter-spacing: 1.2px;
animation: navAIShimmer 1.35s ease-in-out infinite;
}
@keyframes navAIShimmer {
0% { background-position: 100% 50%; }
100% { background-position: -120% 50%; }
}
.nav-ai-link img {
width: 16px;
height: 16px;
flex-shrink: 0;
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), filter 0.35s ease;
}
.nav-ai-link:hover img {
transform: rotate(18deg) scale(1.18);
filter: brightness(1.35) saturate(1.25);
animation: navAISpark 1.1s ease-in-out infinite;
}
@keyframes navAISpark {
0%, 100% { transform: rotate(18deg) scale(1.18); }
50% { transform: rotate(-8deg) scale(1.28); }
}
</style>
</head>
<body class="leaderboard-page">
@@ -33,6 +104,10 @@
<div class="col-md-6">
<div class="header-info">
<div class="display-mode-controls">
<a class="nav-ai-link" href="/ai.html">
<img src="images/ai-sparkle.svg" alt="" />
<span class="nav-ai-link__text">灵丌AI</span>
</a>
<button id="fullscreen-btn" class="btn btn-outline-light" data-bs-toggle="tooltip"
title="大屏展示模式">
<i class="bi bi-fullscreen"></i>

View File

@@ -72,8 +72,25 @@
text-decoration: none;
}
/* 覆盖 nicepage 的 a:focus 黑色描边;仅键盘聚焦时保留可见提示 */
.nav a:focus {
outline: none;
}
.nav a:focus-visible {
outline: 2px solid rgba(0, 3, 255, 0.45);
outline-offset: 3px;
}
.nav__link--active {
color: var(--brand);
display: inline-flex;
align-items: center;
padding: 5px 12px;
height: 32px;
border-radius: 3px;
background: #f2f3ff;
color: #4F8CFF;
box-sizing: border-box;
}
.nav__pill {
@@ -111,6 +128,78 @@
color: #ffffff;
}
/* 灵丌AI 导航胶囊 */
.nav__pillAI {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 18px 5px 14px;
height: 32px;
border-radius: 20px;
text-decoration: none;
position: relative;
transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.nav__pillAI:hover {
transform: scale(1.05);
}
.nav__pillAIText {
font-family: "PingFang SC", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
font-weight: 600;
font-size: 14px;
line-height: 1.5714285714;
font-style: italic;
letter-spacing: 0.5px;
background-image: linear-gradient(
105deg,
rgba(0, 3, 255, 0.55) 0%,
rgba(0, 3, 255, 0.55) 38%,
#7dd3fc 46%,
#ffffff 50%,
#60a5fa 54%,
rgba(0, 3, 255, 0.55) 62%,
rgba(0, 3, 255, 0.55) 100%
);
background-size: 220% 100%;
background-position: 100% 50%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
transition: letter-spacing 0.35s ease;
}
.nav__pillAI:hover .nav__pillAIText {
letter-spacing: 1.2px;
animation: navAIShimmer 1.35s ease-in-out infinite;
}
@keyframes navAIShimmer {
0% { background-position: 100% 50%; }
100% { background-position: -120% 50%; }
}
.nav__pillAIIcon {
width: 18px;
height: 18px;
display: block;
flex-shrink: 0;
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), filter 0.35s ease;
}
.nav__pillAI:hover .nav__pillAIIcon {
transform: rotate(18deg) scale(1.18);
filter: brightness(1.35) saturate(1.25);
animation: navAISpark 1.1s ease-in-out infinite;
}
@keyframes navAISpark {
0%, 100% { transform: rotate(18deg) scale(1.18); }
50% { transform: rotate(-8deg) scale(1.28); }
}
/* End Copied Styles */
/* Mobile adjustments for nav (<=768px) */
@@ -185,9 +274,10 @@
</a>
<nav class="nav__links" aria-label="主导航">
<a class="nav__link" href="/home.html">主页</a>
<a class="nav__pill" href="/">
<span class="nav__pillText">学习入口</span>
<span class="nav__pillNew">new</span>
<a class="nav__link" href="/welcome.html">灵丌编程</a>
<a class="nav__pillAI" href="/ai.html">
<img class="nav__pillAIIcon" src="images/ai-sparkle.svg" alt="" />
<span class="nav__pillAIText">灵丌AI</span>
</a>
<a class="nav__link" href="/competition_list.html">赛事详情</a>
<a class="nav__link" href="/aboutus.html">关于我们</a>
@@ -256,8 +346,8 @@
<footer class="footer" aria-label="页脚">
<div class="footer-line"></div>
<div class="footer-text">ICP备案号<a href=" " target="_blank" style="color:#478ac9 ;">ICP备2022002563号-5</a>
公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a>
<div class="footer-text">ICP备案号<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:#478ac9 ;">ICP备2026042325号</a>
<!-- 公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a> -->
© 灵丌编程001CODE.COM
<span class="qr" tabindex="0">
<img class="qr-icon" src="../images/qrcode_gery.svg" alt="公众号小图标" />

View File

@@ -52,7 +52,7 @@
<div class="login-card">
<h1 class="card-title">账号登录</h1>
<form id="login-form" action="" class="login-form" name="form">
<form id="login-form" action="javascript:void(0)" class="login-form" name="form">
<div class="form-group">
<label for="name-4c18" class="form-label">手机号码 *</label>
<div class="input-wrapper">
@@ -95,8 +95,8 @@
<footer class="site-footer">
<div class="footer-divider"></div>
<p class="footer-text"> <p class="footer-text">ICP备案号<a href=" " target="_blank" style="color:#478ac9 ;">ICP备2022002563号-5</a>
公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a>
<p class="footer-text">ICP备案号<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:#478ac9 ;">ICP备2026042325号</a>
<!-- 公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a> -->
© 灵丌编程001CODE.COM</p></p>
</footer>

View File

@@ -1,23 +1,12 @@
// 引入配置文件
import CONFIG from './config.js';
import disableDevtool from 'disable-devtool';
import { initDisableDevtool } from './devtool-control.js';
let is_permissionOKUser = false;
document.addEventListener('DOMContentLoaded', async function () {
// 初始化禁用开发者工具
if (CONFIG.disabledev) {
disableDevtool({
ondevtoolopen: () => {
// window.location.href = '/index.html';
window.location.href = localStorage.getItem('returnUrl');
},
interval: 500,
clearLog: true,
disableMenu: true,
});
}
initDisableDevtool();
// 登录相关常量
const MAX_ATTEMPTS = 5;
@@ -202,7 +191,7 @@ document.addEventListener('DOMContentLoaded', async function () {
imageUrl = '/images/anhui.png';
}
else if (result.qrid === 240877 && result.cityid === 164) {
else if (result.qrid === 240877 && result.cityid === 164 && result.districtid === 1320) {
imageUrl = '/images/liaocheng.png';
}
@@ -213,11 +202,14 @@ document.addEventListener('DOMContentLoaded', async function () {
else if (result.qrid === 240877 && result.cityid === 152) {
imageUrl = '/images/shandong-zaozhaung.jpg';
}
else if (result.qrid === 240870) {//河北省全区域
imageUrl = '/images/wang.jpg';
}
else {
const group = {
240875: "ran.png", 240877: "ran.png", 240889: "ran.png", 240873: "ran.png", 240893: "ran.png", 240870: "ran.png", 240878: "ran.png", 240874: "ran.png", 240876: "ran.png", 240891: "ran.png",
240875: "ran.png", 240877: "wang.jpg", 240870: 'wang.jpg', 240889: "ran.png", 240873: "ran.png", 240893: "ran.png", 240878: "ran.png", 240874: "ran.png", 240876: "ran.png", 240891: "ran.png",
240897: "zhushou.png", 240882: "sichuan.png", 240895: "zhushou.png", 240881: "zhushou.png", 240894: "zhushou.png", 240879: "zhushou.png", 240885: "zhushou.png", 240883: "zhushou.png", 240884: "zhushou.png", 240899: "zhushou.png",
240892: "zhoushou2.png", 240871: "zhoushou2.png", 240887: "zhoushou2.png", 240872: "zhoushou2.png", 240888: "zhoushou2.png", 240886: "zhoushou2.png", 240880: "zhoushou2.png", 240900: "zhoushou2.png", 240898: "zhoushou2.png", 240890: "zhoushou2.png", 240896: "zhoushou2.png"
};
@@ -232,8 +224,20 @@ document.addEventListener('DOMContentLoaded', async function () {
}
sessionStorage.setItem('qrcode_url', imageUrl);
await api.playerOnline(result.player_id, result.access);
localStorage.setItem('returnUrl', `${window.location.origin}/login.html`);
window.location.href = `${window.location.origin}/welcome.html`;
// 登录成功跳回来源页(各页面跳转登录前会写 returnUrl默认回 welcome.html
let target = '/welcome.html';
try {
const returnUrl = localStorage.getItem('returnUrl');
if (returnUrl) {
localStorage.removeItem('returnUrl');
// 仅接受站内相对路径,防止开放重定向
if (typeof returnUrl === 'string' && returnUrl.startsWith('/') &&
!returnUrl.startsWith('//') && !returnUrl.startsWith('/login')) {
target = returnUrl;
}
}
} catch (e) {}
window.location.href = `${window.location.origin}${target}`;
};
// 处理登录失败
@@ -242,7 +246,7 @@ document.addEventListener('DOMContentLoaded', async function () {
// uiUtils.triggerModalLink("carousel_ac25");
const modalImageElement = document.querySelector('#carousel_ac25 .modal-qr');
if (modalImageElement) {
console.error(result, '111aaa');
console.error(result, '代理商数据');
let imageUrl = '/images/default_qrcode.png';
if (result.qrid === 240896) {
@@ -264,11 +268,13 @@ document.addEventListener('DOMContentLoaded', async function () {
}
else if (result.qrid === 240877 && result.cityid === 152) {
imageUrl = '/images/shandong-zaozhaung.jpg';
}else if (result.qrid === 240870) { //河北省全区域
imageUrl = '/images/wang.jpg';
}
else {
const group = {
240875: "ran.png", 240877: "ran.png", 240889: "ran.png", 240873: "ran.png", 240893: "ran.png", 240870: "ran.png", 240878: "ran.png", 240874: "ran.png", 240876: "ran.png", 240891: "ran.png",
240875: "ran.png", 240877: "wang.jpg", 240889: "ran.png", 240873: "ran.png", 240893: "ran.png", 240870: "wang.jpg", 240878: "ran.png", 240874: "ran.png", 240876: "ran.png", 240891: "ran.png",
240897: "zhushou.png", 240882: "sichuan.png", 240895: "zhushou.png", 240881: "zhushou.png", 240894: "zhushou.png", 240879: "zhushou.png", 240885: "zhushou.png", 240883: "zhushou.png", 240884: "zhushou.png", 240899: "zhushou.png",
240892: "zhoushou2.png", 240871: "zhoushou2.png", 240887: "zhoushou2.png", 240872: "zhoushou2.png", 240888: "zhoushou2.png", 240886: "zhoushou2.png", 240880: "zhoushou2.png", 240900: "zhoushou2.png", 240898: "zhoushou2.png", 240890: "zhoushou2.png", 240896: "zhoushou2.png"
};

View File

@@ -1,22 +1,11 @@
// matchsum.js
import CONFIG from './config.js';
import disableDevtool from 'disable-devtool';
import { initDisableDevtool } from './devtool-control.js';
sessionStorage.setItem('lastpage', 'matchsum.html');
let gamelvdata = {};
document.addEventListener('DOMContentLoaded', function () {
if (CONFIG.disabledev) {
disableDevtool({
ondevtoolopen: () => {
// window.location.href = '/index.html';
window.location.href = localStorage.getItem('returnUrl');
},
interval: 500,
clearLog: true,
disableMenu: true,
});
}
initDisableDevtool();
// 初始化 window.gamelvdata
if (!window.gamelvdatas) {
window.gamelvdatas = {"gamemode": 1, "gamelv": 1, "gameid": 1, "currentlv": 1};

View File

@@ -92,10 +92,10 @@
<footer class="footer">
<div class="footer-inner">
<div class="footer-text">
ICP备案号<a style=" color: #387cbd;" href=" " target="_blank">蜀ICP备2022002563号-5</a>
<span> 公安备案号:<a style=" color: #387cbd;" href="https://beian.mps.gov.cn/"
target="_blank">川公网安备51011202000980号</a></span>
<div class="footer-text" ">
ICP备案号<a style=" color: #387cbd;" href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">沪ICP备2026042325号</a>
<!-- <span> 公安备案号:<a style=" color: #387cbd;" href="https://beian.mps.gov.cn/"
target="_blank">川公网安备51011202000980号</a></span> -->
<span>© 灵丌编程001CODE.COM</span>
</div>
</div>

View File

@@ -1,5 +1,5 @@
import CONFIG from './config.js';
import disableDevtool from 'disable-devtool';
import { initDisableDevtool } from './devtool-control.js';
// ============= 业务参数 =============
let gamelvid = '1'; // 具体关卡等级,初始为 1
@@ -51,16 +51,11 @@ document.addEventListener('DOMContentLoaded', function () {
updateLanguageDisplay();
// 初始化禁用开发者工具
if (CONFIG.disabledev) {
disableDevtool({
ondevtoolopen: () => {
window.location.href = localStorage.getItem('returnUrl') || '/welcome.html';
},
interval: 500,
clearLog: true,
disableMenu: true,
});
}
initDisableDevtool({
ondevtoolopen: () => {
window.location.href = localStorage.getItem('returnUrl') || '/welcome.html';
}
});
@@ -134,6 +129,11 @@ document.addEventListener('DOMContentLoaded', function () {
gamelvid = (index + 1).toString();
gamelvdata.gamelv = index + 1;
// 记住当前 Level从关卡返回后恢复
try {
sessionStorage.setItem('gamelvdata', JSON.stringify(gamelvdata));
sessionStorage.setItem('programmingLearningSelectedLevel', String(lv));
} catch (e) {}
console.log('当前 gamelvid:', gamelvid);
if (levelDataCache[gamelvid]) {
@@ -145,10 +145,21 @@ document.addEventListener('DOMContentLoaded', function () {
});
});
// Trigger default click
// 恢复上次选中的 Level默认 Level A
if (listItemsModes.length > 0) {
// "触发默认点击... 让用户一进页面就能看到默认关卡"
listItemsModes[0].click();
let savedLv = parseInt(
(gamelvdata && gamelvdata.gamelv) ||
sessionStorage.getItem('programmingLearningSelectedLevel') ||
'1',
10
);
if (!isFinite(savedLv) || savedLv < 1) savedLv = 1;
if (savedLv > listItemsModes.length) savedLv = listItemsModes.length;
// 无权限时只能回 Level A
if (savedLv !== 1 && (!permission || parseInt(permission) === 0)) {
savedLv = 1;
}
listItemsModes[savedLv - 1].click();
}
/********************************************************

View File

@@ -66,7 +66,14 @@
}
.nav__link--active {
color: var(--brand);
display: inline-flex;
align-items: center;
padding: 5px 12px;
height: 32px;
border-radius: 3px;
background: #f2f3ff;
color: #4F8CFF;
box-sizing: border-box;
}
.nav__pill {
@@ -104,34 +111,89 @@
color: #ffffff;
}
/* Mobile adjustments for nav (<=768px) */
@media (max-width: 768px) {
.nav__inner {
padding: 6px 12px;
gap: 12px;
align-items: center;
}
/* 灵丌AI 导航胶囊 */
.nav__pillAI {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 18px 5px 14px;
height: 32px;
border-radius: 20px;
text-decoration: none;
position: relative;
transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.nav__logo {
width: 140px;
height: auto;
}
.nav__pillAI:hover {
transform: scale(1.05);
}
.nav__pillAI:focus {
outline: none;
}
.nav__pillAI:focus-visible {
outline: 2px solid rgba(0, 3, 255, 0.45);
outline-offset: 3px;
}
.nav__pillAIText {
font-family: "PingFang SC", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
font-weight: 600;
font-size: 14px;
line-height: 1.5714285714;
font-style: italic;
letter-spacing: 0.5px;
background-image: linear-gradient(
105deg,
rgba(0, 3, 255, 0.55) 0%,
rgba(0, 3, 255, 0.55) 38%,
#7dd3fc 46%,
#ffffff 50%,
#60a5fa 54%,
rgba(0, 3, 255, 0.55) 62%,
rgba(0, 3, 255, 0.55) 100%
);
background-size: 220% 100%;
background-position: 100% 50%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
transition: letter-spacing 0.35s ease;
}
.nav__pillAI:hover .nav__pillAIText {
letter-spacing: 1.2px;
animation: navAIShimmer 1.35s ease-in-out infinite;
}
@keyframes navAIShimmer {
0% { background-position: 100% 50%; }
100% { background-position: -120% 50%; }
}
.nav__pillAIIcon {
width: 18px;
height: 18px;
display: block;
flex-shrink: 0;
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), filter 0.35s ease;
}
.nav__pillAI:hover .nav__pillAIIcon {
transform: rotate(18deg) scale(1.18);
filter: brightness(1.35) saturate(1.25);
animation: navAISpark 1.1s ease-in-out infinite;
}
@keyframes navAISpark {
0%, 100% { transform: rotate(18deg) scale(1.18); }
50% { transform: rotate(-8deg) scale(1.28); }
}
.nav__links {
gap: 12px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.nav__link,
.nav__pillText {
font-size: 11px;
}
.nav__pillNew {
display: none;
}
}
</style>
</head>
@@ -143,9 +205,10 @@
</a>
<nav class="nav__links" aria-label="主导航">
<a class="nav__link" href="/home.html">主页</a>
<a class="nav__pill" href="/">
<span class="nav__pillText">学习入口</span>
<span class="nav__pillNew">new</span>
<a class="nav__link" href="/welcome.html">灵丌编程</a>
<a class="nav__pillAI" href="/ai.html">
<img class="nav__pillAIIcon" src="images/ai-sparkle.svg" alt="" />
<span class="nav__pillAIText">灵丌AI</span>
</a>
<a class="nav__link" href="/competition_list.html">赛事详情</a>
<a class="nav__link" href="/aboutus.html">关于我们</a>
@@ -297,8 +360,8 @@
<footer class="footer" aria-label="页脚">
<div class="footer-line"></div>
<div class="footer-text">ICP备案号<a href=" " target="_blank" style="color:#478ac9 ;">ICP备2022002563号-5</a>
公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a>
<div class="footer-text">ICP备案号<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:#478ac9 ;">ICP备2026042325号</a>
<!-- 公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank" style="color:#478ac9 ;">川公网安备51011202000980号</a> -->
© 灵丌编程001CODE.COM
<span class="qr" tabindex="0">
<img class="qr-icon" src="../images/qrcode_gery.svg" alt="公众号小图标" />

View File

@@ -1,5 +1,5 @@
import CONFIG from './config.js';
import disableDevtool from 'disable-devtool';
import { initDisableDevtool } from './devtool-control.js';
// 获取URL参数的函数
function getUrlParams() {
const params = new URLSearchParams(window.location.search);
@@ -88,18 +88,7 @@ function showError(message) {
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', () => {
if (CONFIG.disabledev) {
disableDevtool({
ondevtoolopen: () => {
// window.location.href = '/index.html';
window.location.href = localStorage.getItem('returnUrl');
},
interval: 500,
clearLog: true,
disableMenu: true,
});
}
initDisableDevtool();
const {thirdToken, returnUrl} = getUrlParams();

View File

@@ -13,6 +13,101 @@
defer></script>
<!-- <script class="u-script" type="text/javascript"
src="rank-badge-generator.js?v=<%= htmlWebpackPlugin.options.version %>" defer></script> -->
<style>
/* 灵丌编程 — 与 ai.html 一致 */
.nav__pill {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 5px 12px;
height: 32px;
border-radius: 3px;
background: #f2f3ff;
text-decoration: none;
flex-shrink: 0;
}
.nav__pillText {
font-family: "PingFang SC", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
font-weight: 400;
font-size: 14px;
line-height: 1.5714285714;
color: #4F8CFF;
white-space: nowrap;
}
.nav__pillNew {
display: none;
}
/* 灵丌AI 导航胶囊 */
.nav-ai-pill {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 18px 5px 14px;
height: 32px;
border-radius: 20px;
text-decoration: none;
flex-shrink: 0;
position: relative;
transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.nav-ai-pill:hover {
transform: scale(1.05);
}
.nav-ai-pill:focus {
outline: none;
}
.nav-ai-pill:focus-visible {
outline: 2px solid rgba(0, 3, 255, 0.45);
outline-offset: 3px;
}
.nav-ai-pill span {
font-weight: 600;
font-size: 13px;
font-style: italic;
letter-spacing: 0.5px;
background-image: linear-gradient(
105deg,
rgba(0, 3, 255, 0.55) 0%,
rgba(0, 3, 255, 0.55) 38%,
#7dd3fc 46%,
#ffffff 50%,
#60a5fa 54%,
rgba(0, 3, 255, 0.55) 62%,
rgba(0, 3, 255, 0.55) 100%
);
background-size: 220% 100%;
background-position: 100% 50%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
transition: letter-spacing 0.35s ease;
}
.nav-ai-pill:hover span {
letter-spacing: 1.2px;
animation: navAIShimmer 1.35s ease-in-out infinite;
}
@keyframes navAIShimmer {
0% { background-position: 100% 50%; }
100% { background-position: -120% 50%; }
}
.nav-ai-pill img {
width: 18px;
height: 18px;
flex-shrink: 0;
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), filter 0.35s ease;
}
.nav-ai-pill:hover img {
transform: rotate(18deg) scale(1.18);
filter: brightness(1.35) saturate(1.25);
animation: navAISpark 1.1s ease-in-out infinite;
}
@keyframes navAISpark {
0%, 100% { transform: rotate(18deg) scale(1.18); }
50% { transform: rotate(-8deg) scale(1.28); }
}
</style>
</head>
<body data-path-to-root="./" data-include-products="false" class="u-body u-xl-mode" data-lang="en">
@@ -23,33 +118,70 @@
<div class="navbar-logo" onclick="window.location.href='/home.html'" style="cursor:pointer;"></div>
<!-- <div class="navbar-title">001CODE</div> -->
</div>
<div class="navbar-middle">
<div class="nav-link-group">
<a class="nav-link" href="home.html">首页</a>
</div>
<div class="nav-link-group" id="user-rank-badge" title="Rank Badge">
<span class="nav-link nav-label">当前段位:</span>
<span class="nav-rank-icon"></span>
<span class="nav-link nav-rank-text">加载中...</span>
</div>
</div>
<div class="navbar-right">
<a class="navbar-link" id="open-personal-info" href="javascript:void(0);">个人信息</a>
<a class="navbar-link u-dialog-link" href="#carousel_c286">修改密码</a>
<a class="navbar-link navbar-btn-activate u-dialog-link" href="#carousel_ac25" style="text-decoration:none;">
<div style="display:flex;align-items:center;gap:4px;">
<span>开通权益</span>
<span class="navbar-link-vip" style="display:none;">VIP</span>
<button type="button" class="navbar-toggle" id="navbarToggle" aria-label="打开菜单" aria-expanded="false" aria-controls="navbarCollapse">
<span></span>
<span></span>
<span></span>
</button>
<div class="navbar-collapse" id="navbarCollapse">
<div class="navbar-middle">
<div class="nav-link-group">
<a class="nav-link" href="home.html">首页</a>
</div>
</a>
<div class="navbar-account">
<!-- <div class="navbar-avatar"></div> -->
<div class="navbar-logout" id="logout-button">退出</div>
<div class="nav-link-group">
<span class="nav__pill" aria-current="page" style="cursor:default;">
<span class="nav__pillText">灵丌编程</span>
</span>
</div>
<div class="nav-link-group">
<a class="nav-ai-pill" href="/ai.html">
<img src="../images/ai-sparkle.svg" alt="" />
<span>灵丌AI</span>
</a>
</div>
<div class="nav-link-group" id="user-rank-badge" title="Rank Badge">
<span class="nav-link nav-label">当前段位:</span>
<span class="nav-rank-icon"></span>
<span class="nav-link nav-rank-text">加载中...</span>
</div>
</div>
<div class="navbar-right">
<a class="navbar-link" id="open-personal-info" href="javascript:void(0);">个人信息</a>
<a class="navbar-link u-dialog-link" href="#carousel_c286">修改密码</a>
<a class="navbar-link navbar-btn-activate u-dialog-link" href="#carousel_ac25" style="text-decoration:none;">
<div style="display:flex;align-items:center;gap:4px;">
<span>开通权益</span>
<span class="navbar-link-vip" style="display:none;">VIP</span>
</div>
</a>
<div class="navbar-account">
<!-- <div class="navbar-avatar"></div> -->
<div class="navbar-logout" id="logout-button">退出</div>
</div>
<a class="navbar-login" href="/login.html" id="navbarLoginBtn">去登录</a>
</div>
</div>
</div>
<div class="navbar-backdrop" id="navbarBackdrop"></div>
</header>
<script>
(function () {
try {
if (!localStorage.getItem('access_token')) {
var nav = document.querySelector('.navbar-wrapper');
if (nav) nav.classList.add('is-guest');
var loginBtn = document.getElementById('navbarLoginBtn');
if (loginBtn) {
loginBtn.addEventListener('click', function () {
try { localStorage.setItem('returnUrl', '/welcome.html'); } catch (e) {}
});
}
}
} catch (e) {}
})();
</script>
<div class="glass-container">
<main class="main">
@@ -199,8 +331,8 @@
<footer class="footer">
<div class="footer-inner">
<div class="footer-text">
ICP备案号<a href=" " target="_blank">蜀ICP备2022002563号-5</a>
公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank">川公网安备51011202000980号</a> © 灵丌编程001CODE.COM
ICP备案号<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">沪ICP备2026042325号</a>
<!-- 公安备案号:<a href="https://beian.mps.gov.cn/" target="_blank">川公网安备51011202000980号</a> --> © 灵丌编程001CODE.COM
</div>
</div>
</footer>
@@ -778,8 +910,11 @@
} catch (error) { console.error(error); }
}
await fetchUserDetails();
await fetchUserRank();
const hasAccessToken = !!localStorage.getItem('access_token');
if (hasAccessToken) {
await fetchUserDetails();
await fetchUserRank();
}
// Edit Profile Logic
let isEditing = false;

View File

@@ -1,6 +1,6 @@
import CONFIG from './config.js';
import RankBadgeGenerator from './rank-badge-generator.js';
import disableDevtool from 'disable-devtool';
import { initDisableDevtool } from './devtool-control.js';
// 好友按钮逻辑已移除
@@ -83,18 +83,7 @@ window.addEventListener('DOMContentLoaded', () => {
// 检查登录状态
// checkLoginStatus();
// 初始化禁用开发者工具
if (CONFIG.disabledev) {
disableDevtool({
ondevtoolopen: () => {
// window.location.href = '/index.html';
window.location.href = localStorage.getItem('returnUrl');
},
interval: 500,
clearLog: true,
disableMenu: true,
});
}
initDisableDevtool();
// 设置二维码图片
setQRCodeImage();
@@ -105,6 +94,9 @@ window.addEventListener('DOMContentLoaded', () => {
// 初始化权限控制
initPermissionControl();
// 小屏导航折叠
initNavbarToggle();
// 绑定按钮事件
bindButtonEvents();
@@ -129,6 +121,44 @@ if (logoutBtn) {
});
}
function initNavbarToggle() {
const wrapper = document.querySelector('.navbar-wrapper');
const toggle = document.getElementById('navbarToggle');
const collapse = document.getElementById('navbarCollapse');
const backdrop = document.getElementById('navbarBackdrop');
if (!wrapper || !toggle || !collapse) return;
const setOpen = (open) => {
wrapper.classList.toggle('is-open', open);
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
toggle.setAttribute('aria-label', open ? '关闭菜单' : '打开菜单');
document.body.classList.toggle('nav-open', open);
};
toggle.addEventListener('click', (event) => {
event.stopPropagation();
setOpen(!wrapper.classList.contains('is-open'));
});
if (backdrop) {
backdrop.addEventListener('click', () => setOpen(false));
}
collapse.addEventListener('click', (event) => {
if (event.target.closest('a, .navbar-logout, #open-personal-info')) {
setOpen(false);
}
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') setOpen(false);
});
window.addEventListener('resize', () => {
if (window.innerWidth > 960) setOpen(false);
});
}
// 初始化轮播区域
function initCarouselSection() {
const carouselSection = document.getElementById('sec-df5f');

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,294 @@
/* AI 页:个人信息 / 修改密码弹窗(从 welcome-new.css 迁移) */
.modal-overlay {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
background: rgba(0, 0, 0, 0.45);
z-index: 9999;
}
.modal-basic-info {
width: 720px;
max-width: calc(100vw - 48px);
background: #FFFFFF;
border-radius: 10px;
box-shadow: 0px 34px 44px -20px rgba(185, 206, 234, 0.25);
padding: 32px;
display: flex;
flex-direction: column;
gap: 16px;
}
.modal-header {
display: flex;
align-items: center;
gap: 16px;
width: 100%;
}
.modal-title {
flex: 1;
font-family: Nunito, system-ui, sans-serif;
font-weight: 800;
font-size: 28px;
line-height: 1.2;
color: #1E293B;
text-align: left;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.modal-subtitle {
font-family: "PingFang SC", system-ui, sans-serif;
font-size: 14px;
font-weight: 400;
color: #94A3B8;
letter-spacing: 0.5px;
text-transform: uppercase;
}
.modal-close {
width: 32px;
height: 32px;
border-radius: 3px;
padding: 2px;
border: 0;
background: transparent;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.modal-body {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
.modal-actions {
display: flex;
justify-content: center;
}
.modal-primary-btn {
border: 0;
cursor: pointer;
border-radius: 32px;
background: linear-gradient(90deg, rgba(3, 161, 234, 1) 0%, rgba(32, 91, 175, 1) 100%);
color: #FFFFFF;
padding: 10px 18px;
height: 44px;
font-family: "Noto Sans SC", "PingFang SC", system-ui, sans-serif;
font-weight: 400;
font-size: 14px;
line-height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
}
.modal-password {
width: 450px;
max-width: calc(100vw - 48px);
background: #FFFFFF;
border-radius: 10px;
box-shadow: 0px 34px 44px -20px rgba(185, 206, 234, 0.25);
padding: 32px;
display: flex;
flex-direction: column;
align-items: center;
gap: 32px;
}
.modal-header-spacer {
width: 32px;
height: 32px;
flex-shrink: 0;
}
.modal-password .field {
width: 100%;
display: flex;
flex-direction: column;
gap: 15px;
}
.modal-password .field-label {
font-family: Inter, system-ui, sans-serif;
font-weight: 500;
font-size: 16px;
line-height: 19.36px;
color: #000000;
}
.input-gradient {
width: 100%;
border-radius: 8px;
padding: 1px;
background: linear-gradient(164deg, rgba(226, 230, 255, 1) 24%, rgba(255, 234, 227, 1) 95%);
}
.modal-password .field-input {
width: 100%;
height: 44px;
border-radius: 7px;
border: 0;
padding: 0 12px;
background: #F5F6F9;
font-family: "PingFang SC", system-ui, sans-serif;
font-weight: 400;
font-size: 14px;
line-height: 22px;
color: #0F172A;
outline: none;
box-sizing: border-box;
}
.modal-password .field-input::placeholder {
color: #7D91A1;
}
.primary-btn {
width: 180px;
height: 48px;
border: 0;
border-radius: 32px;
background: linear-gradient(90deg, rgba(3, 161, 234, 1) 0%, rgba(32, 91, 175, 1) 100%);
color: #FFFFFF;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
font-family: "Noto Sans SC", system-ui, sans-serif;
font-weight: 400;
font-size: 14px;
line-height: 20px;
white-space: nowrap;
}
.profile-body {
width: 100%;
display: flex;
flex-direction: column;
gap: 32px;
}
.profile-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
width: 100%;
}
@media (max-width: 640px) {
.profile-grid {
grid-template-columns: 1fr;
}
}
.profile-item {
display: flex;
flex-direction: column;
gap: 8px;
}
.profile-label {
font-family: "PingFang SC", system-ui, sans-serif;
font-size: 14px;
color: #64748B;
font-weight: 500;
}
.profile-input-wrap {
display: flex;
align-items: center;
background-color: #F8FAFC;
border: 1px solid #E2E8F0;
border-radius: 8px;
height: 48px;
padding: 0 16px;
transition: all 0.2s ease;
}
.profile-input-wrap:focus-within {
background-color: #FFFFFF;
border-color: #3B82F6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.profile-icon {
width: 20px;
height: 20px;
color: #94A3B8;
margin-right: 12px;
flex-shrink: 0;
}
.profile-input-wrap:focus-within .profile-icon {
color: #3B82F6;
}
.profile-input {
flex: 1;
border: none;
background: transparent;
height: 100%;
font-family: inherit;
font-size: 14px;
color: #1E293B;
outline: none;
width: 100%;
}
.profile-value {
flex: 1;
font-size: 14px;
color: #1E293B;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
select.profile-input {
appearance: none;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0 center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
}
.profile-input-wrap.is-vip {
background: linear-gradient(to right, #FFFBF0, #FFF7E6);
border-color: #FDE68A;
}
.profile-input-wrap.is-vip .profile-icon {
color: #D97706;
}
.profile-value-btn {
background: transparent;
border: none;
color: #D97706;
font-weight: 600;
font-size: 14px;
padding: 0;
cursor: default;
}
.profile-input:disabled {
background-color: transparent;
color: #64748B;
cursor: not-allowed;
}

View File

@@ -60,6 +60,63 @@ body {
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
}
.navbar-collapse {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
}
.navbar-toggle {
display: none;
width: 40px;
height: 40px;
margin-left: 12px;
padding: 0;
border: 1px solid #e2e8f0;
border-radius: 10px;
background: #f8fafc;
cursor: pointer;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 5px;
flex-shrink: 0;
appearance: none;
-webkit-appearance: none;
}
.navbar-toggle:focus-visible {
outline: 2px solid rgba(79, 140, 255, 0.55);
outline-offset: 2px;
}
.navbar-toggle span {
display: block;
width: 18px;
height: 2px;
background: #1e1a4c;
border-radius: 1px;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.navbar-wrapper.is-open .navbar-toggle span:nth-child(1) {
transform: translateY(7px) rotate(45deg);
}
.navbar-wrapper.is-open .navbar-toggle span:nth-child(2) {
opacity: 0;
}
.navbar-wrapper.is-open .navbar-toggle span:nth-child(3) {
transform: translateY(-7px) rotate(-45deg);
}
.navbar-backdrop {
display: none;
}
.navbar-brand {
@@ -110,12 +167,24 @@ body {
font-weight: 400;
font-size: 14px;
line-height: 1.43;
color: #0790D9;
color: #1E1A4C;
text-decoration: none;
cursor: pointer;
white-space: nowrap;
}
.nav-link--active,
.nav-link[aria-current="page"] {
display: inline-flex;
align-items: center;
padding: 5px 12px;
height: 32px;
border-radius: 3px;
background: #f2f3ff;
color: #4F8CFF;
box-sizing: border-box;
}
.nav-label {
color: #1E1A4C;
}
@@ -232,6 +301,38 @@ body {
white-space: nowrap;
}
.navbar-login {
display: none;
align-items: center;
justify-content: center;
padding: 8px 24px;
border-radius: 20px;
background: linear-gradient(90deg, rgba(3, 161, 234, 1) 0%, rgba(32, 91, 175, 1) 100%);
color: #ffffff;
font-size: 14px;
font-weight: 600;
line-height: 1.43;
text-decoration: none;
white-space: nowrap;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.navbar-login:hover {
transform: translateY(-1px);
box-shadow: 0 6px 16px rgba(3, 161, 234, 0.35);
}
/* 未登录游客态:不显示段位与右侧入口,只显示「去登录」 */
.navbar-wrapper.is-guest #user-rank-badge,
.navbar-wrapper.is-guest .navbar-right .navbar-link,
.navbar-wrapper.is-guest .navbar-right .navbar-account {
display: none;
}
.navbar-wrapper.is-guest .navbar-login {
display: inline-flex;
}
.glass-container {
flex: 1;
display: flex;
@@ -411,7 +512,7 @@ body {
}
/* .course-card-image.course-1 {
background-image: url("../images/image-119.png");
background-image: url("../images/default_compertion.png");
}
.course-card-image.course-2 {
@@ -555,13 +656,149 @@ body {
display: inline-block;
}
@media (max-width: 960px) {
@media (max-width: 1200px) {
.navbar-logo {
width: 180px;
}
.navbar-middle {
display: none;
gap: 16px;
margin-left: 16px;
}
.navbar-right {
gap: 10px;
}
}
@media (max-width: 960px) {
body.nav-open {
overflow: hidden;
}
.navbar {
gap: 12px;
gap: 8px;
}
.navbar-logo {
width: 156px;
height: 44px;
}
.navbar-toggle {
display: inline-flex;
margin-left: auto;
}
.navbar-collapse {
display: none;
position: fixed;
top: 64px;
left: 0;
right: 0;
flex: none;
flex-direction: column;
align-items: stretch;
gap: 8px;
max-height: calc(100vh - 64px);
overflow-y: auto;
padding: 12px 16px 20px;
background: #ffffff;
border-top: 1px solid #e2e8f0;
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.12);
z-index: 1001;
}
.navbar-wrapper.is-open .navbar-collapse {
display: flex;
}
.navbar-backdrop {
display: none;
position: fixed;
top: 64px;
left: 0;
right: 0;
bottom: 0;
background: rgba(15, 23, 42, 0.32);
z-index: 1000;
}
.navbar-wrapper.is-open .navbar-backdrop {
display: block;
}
.navbar-middle,
.navbar-right {
flex-direction: column;
align-items: stretch;
width: 100%;
margin: 0;
gap: 4px;
}
.navbar-middle {
padding-bottom: 8px;
border-bottom: 1px solid #eef2f7;
}
.navbar-right {
padding-top: 4px;
justify-content: flex-start;
}
.nav-link-group,
.navbar-link,
.navbar-account {
width: 100%;
min-height: 44px;
padding: 10px 12px;
border-radius: 10px;
justify-content: flex-start;
}
.nav-link-group:hover,
.navbar-link:hover,
.navbar-account:hover {
background: #f8fafc;
}
.navbar-wrapper.is-guest .navbar-login {
width: 100%;
min-height: 44px;
margin-top: 4px;
}
.navbar-collapse .nav__pill,
.navbar-collapse .nav-ai-pill {
width: 100%;
height: 40px;
justify-content: flex-start;
}
.navbar-collapse .nav-ai-pill:hover {
transform: none;
}
.navbar-btn-activate {
justify-content: center;
margin-top: 4px;
}
.navbar-btn-activate:hover {
background: linear-gradient(135deg, #ffc107 0%, #ff8f00 100%) !important;
transform: none;
box-shadow: 0 4px 12px rgba(255, 143, 0, 0.3);
}
}
@media (max-width: 480px) {
.container {
padding: 0 12px;
}
.navbar-logo {
width: 132px;
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 MiB

View File

@@ -0,0 +1,19 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="ai-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#00CCFF"/>
<stop offset="50%" stop-color="#0066FF"/>
<stop offset="100%" stop-color="#0003FF"/>
</linearGradient>
</defs>
<!-- 中心大星 -->
<path d="M10 0.5L11.8 6.5L10 8.2L8.2 6.5L10 0.5Z" fill="url(#ai-grad)"/>
<path d="M10 19.5L8.2 13.5L10 11.8L11.8 13.5L10 19.5Z" fill="url(#ai-grad)"/>
<path d="M0.5 10L6.5 8.2L8.2 10L6.5 11.8L0.5 10Z" fill="url(#ai-grad)"/>
<path d="M19.5 10L13.5 11.8L11.8 10L13.5 8.2L19.5 10Z" fill="url(#ai-grad)"/>
<!-- 对角小星 -->
<path d="M3.5 3.5L6.5 5.5L5.5 6.5L3.5 5.5L2.5 3.5L3.5 3.5Z" fill="url(#ai-grad)" opacity="0.7"/>
<path d="M16.5 3.5L13.5 5.5L14.5 6.5L16.5 5.5L17.5 3.5L16.5 3.5Z" fill="url(#ai-grad)" opacity="0.7"/>
<path d="M3.5 16.5L6.5 14.5L5.5 13.5L3.5 14.5L2.5 16.5L3.5 16.5Z" fill="url(#ai-grad)" opacity="0.7"/>
<path d="M16.5 16.5L13.5 14.5L14.5 13.5L16.5 14.5L17.5 16.5L16.5 16.5Z" fill="url(#ai-grad)" opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 947 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 987 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 KiB

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