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
BIN
docker-conf/.DS_Store
vendored
Normal file
BIN
docker-conf/conf.d/.DS_Store
vendored
Normal 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
|
||||
}
|
||||
|
||||
# 静态资源缓存配置
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
# 静态资源缓存配置
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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={{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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; // 最大关卡数据数量
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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; // 最大关卡数据数量
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
@@ -650,14 +668,64 @@ const StageWrapperPythonCPLT = () => {
|
||||
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',
|
||||
|
||||
@@ -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; // 最大关卡数据数量
|
||||
|
||||
119
scratch-gui/src/lib/unity-build-loader.js
Normal 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);
|
||||
});
|
||||
}
|
||||
@@ -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="公众号小图标" />
|
||||
|
||||
4
scratch-gui/src/playground/ai-config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// 给 chunks: [] 的 AI 静态页注入与 login 相同的 config.js
|
||||
import CONFIG from './config.js';
|
||||
|
||||
window.CONFIG = CONFIG;
|
||||
2104
scratch-gui/src/playground/ai-draw.ejs
Normal file
2124
scratch-gui/src/playground/ai-huiben.ejs
Normal file
1988
scratch-gui/src/playground/ai-music.ejs
Normal file
2178
scratch-gui/src/playground/ai-text.ejs
Normal file
1897
scratch-gui/src/playground/ai-video.ejs
Normal file
2473
scratch-gui/src/playground/ai-voice.ejs
Normal file
2496
scratch-gui/src/playground/ai.ejs
Normal file
9
scratch-gui/src/playground/ai.js
Normal 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 是否为最新构建');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
360
scratch-gui/src/playground/coming-soon.ejs
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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="公众号小图标" />
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
/** 方案 B:nginx / 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',
|
||||
|
||||
// 可以根据需要添加其他配置
|
||||
|
||||
|
||||
1709
scratch-gui/src/playground/course-detail.ejs
Normal file
65
scratch-gui/src/playground/devtool-control.js
Normal 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;
|
||||
}
|
||||
@@ -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="公众号小图标" />
|
||||
|
||||
3
scratch-gui/src/playground/home.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import { stopDev } from './devtool-control.js';
|
||||
|
||||
window.stopDev = stopDev;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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="公众号小图标" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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"
|
||||
};
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/********************************************************
|
||||
|
||||
@@ -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="公众号小图标" />
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
|
||||
1752
scratch-gui/src/playground/works-plaza.ejs
Normal file
294
scratch-gui/static/css/ai-account-modals.css
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
scratch-gui/static/images/about-illustration.svg
Normal file
|
After Width: | Height: | Size: 19 MiB |
19
scratch-gui/static/images/ai-sparkle.svg
Normal 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 |
BIN
scratch-gui/static/images/aigc/ai-banner1.png
Normal file
|
After Width: | Height: | Size: 198 KiB |
BIN
scratch-gui/static/images/aigc/ai-banner2.png
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
scratch-gui/static/images/aigc/ai-banner3.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
scratch-gui/static/images/aigc/ai-fanyi.png
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
scratch-gui/static/images/aigc/ai-home-page1.png
Normal file
|
After Width: | Height: | Size: 503 KiB |
BIN
scratch-gui/static/images/aigc/ai-icon-class.png
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
scratch-gui/static/images/aigc/ai-icon-locked.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
scratch-gui/static/images/aigc/ai-icon-lockopen.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
scratch-gui/static/images/aigc/ai-logo.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
scratch-gui/static/images/aigc/ai-wenben.png
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
scratch-gui/static/images/aigc/ai-zuojia.png
Normal file
|
After Width: | Height: | Size: 195 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_01.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_02.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_03.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_04.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_05.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_06.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_07.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_08.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_09.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_10.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_11.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
scratch-gui/static/images/aigc/ai_programming_avatar_12.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
scratch-gui/static/images/aigc/icon-ai-huiben.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
scratch-gui/static/images/aigc/icon-ai-huitu.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
scratch-gui/static/images/aigc/icon-ai-music.png
Normal file
|
After Width: | Height: | Size: 220 KiB |
BIN
scratch-gui/static/images/aigc/icon-ai-shiping.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
scratch-gui/static/images/aigc/icon-ai-wenben.png
Normal file
|
After Width: | Height: | Size: 165 KiB |
BIN
scratch-gui/static/images/aigc/icon-ai-yuying.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
scratch-gui/static/images/aigc/icon-beat-boom.png
Normal file
|
After Width: | Height: | Size: 813 KiB |
BIN
scratch-gui/static/images/aigc/icon-beat-chinese.png
Normal file
|
After Width: | Height: | Size: 947 KiB |
BIN
scratch-gui/static/images/aigc/icon-beat-edm.png
Normal file
|
After Width: | Height: | Size: 909 KiB |
BIN
scratch-gui/static/images/aigc/icon-beat-jazz.png
Normal file
|
After Width: | Height: | Size: 987 KiB |
BIN
scratch-gui/static/images/aigc/icon-beat-latin.png
Normal file
|
After Width: | Height: | Size: 906 KiB |
BIN
scratch-gui/static/images/aigc/icon-beat-march.png
Normal file
|
After Width: | Height: | Size: 847 KiB |
BIN
scratch-gui/static/images/aigc/icon-beat-rock.png
Normal file
|
After Width: | Height: | Size: 993 KiB |
BIN
scratch-gui/static/images/aigc/icon-beat-waltz.png
Normal file
|
After Width: | Height: | Size: 830 KiB |
BIN
scratch-gui/static/images/aigc/icon-draw-doodle.png
Normal file
|
After Width: | Height: | Size: 881 KiB |
BIN
scratch-gui/static/images/aigc/icon-draw-paint.png
Normal file
|
After Width: | Height: | Size: 896 KiB |
BIN
scratch-gui/static/images/aigc/icon-draw-style.png
Normal file
|
After Width: | Height: | Size: 931 KiB |
BIN
scratch-gui/static/images/aigc/icon-draw-upload.png
Normal file
|
After Width: | Height: | Size: 840 KiB |
BIN
scratch-gui/static/images/aigc/icon-empty.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
scratch-gui/static/images/aigc/icon-inst-drums.png
Normal file
|
After Width: | Height: | Size: 841 KiB |
BIN
scratch-gui/static/images/aigc/icon-inst-guitar.png
Normal file
|
After Width: | Height: | Size: 837 KiB |