1313 <el-button
1414 type =" primary"
1515 size =" large"
16- :loading =" generating"
16+ :disabled =" generating"
1717 @click =" generateLevel"
1818 >
1919 <el-icon ><MagicStick /></el-icon >
2020 生成关卡
2121 </el-button >
22+
23+ <!-- 自定义加载区域 -->
24+ <div v-if =" generating" class =" custom-loading-area" >
25+ <img
26+ :src =" loadingIcon"
27+ alt =" 生成中..."
28+ class =" custom-loading-icon"
29+ :style =" { left: loadingPosition.x + 'px', top: loadingPosition.y + 'px' }"
30+ />
31+ <div class =" loading-text" >正在生成关卡中...</div >
32+ </div >
2233 </div >
2334 </el-card >
2435 </div >
94105 </div >
95106
96107 <div class =" submit-area" >
97- <el-button
98- type =" primary"
99- size =" large"
100- :loading =" submitting"
101- :disabled =" selectedOptions.length === 0"
102- @click =" submitAnswer"
103- >
104- <el-icon ><Check /></el-icon >
105- 提交答案
106- </el-button >
108+ <div class =" button-row" >
109+ <el-button
110+ type =" primary"
111+ size =" large"
112+ :disabled =" submitting || selectedOptions.length === 0"
113+ @click =" submitAnswer"
114+ >
115+ <el-icon ><Check /></el-icon >
116+ 提交答案
117+ </el-button >
118+
119+ <el-button
120+ size =" large"
121+ :disabled =" submitting"
122+ @click =" resetLevel"
123+ >
124+ <el-icon ><Refresh /></el-icon >
125+ 重新生成
126+ </el-button >
127+ </div >
107128
108- <el-button
109- size =" large"
110- @click =" resetLevel"
111- >
112- <el-icon ><Refresh /></el-icon >
113- 重新生成
114- </el-button >
129+ <!-- 提交答案加载区域 -->
130+ <div v-if =" submitting" class =" custom-loading-area submit-loading" >
131+ <img
132+ :src =" loadingIcon"
133+ alt =" 提交中..."
134+ class =" custom-loading-icon"
135+ :style =" { left: submitLoadingPosition.x + 'px', top: submitLoadingPosition.y + 'px' }"
136+ />
137+ <div class =" loading-text" >正在提交答案...</div >
138+ </div >
115139 </div >
116140 </div >
117141 </div >
121145</template >
122146
123147<script setup>
124- import { ref , computed , onMounted } from ' vue'
148+ import { ref , computed , onMounted , watch , onUnmounted } from ' vue'
125149import { useRouter } from ' vue-router'
126150import { useUserStore } from ' ../stores/user'
127151import { generateLevel as generateLevelAPI } from ' ../api/level'
@@ -134,6 +158,7 @@ import {
134158 Close
135159} from ' @element-plus/icons-vue'
136160import GlobalNavbar from ' ../components/GlobalNavbar.vue'
161+ import loadingIcon from ' ../assets/loading.png'
137162
138163const router = useRouter ()
139164const userStore = useUserStore ()
@@ -145,6 +170,48 @@ const currentLevel = ref(null)
145170const selectedOptions = ref ([])
146171const draggedOption = ref (null )
147172
173+ // 加载图标随机移动相关
174+ const loadingPosition = ref ({ x: 0 , y: 0 })
175+ const submitLoadingPosition = ref ({ x: 0 , y: 0 })
176+ let loadingInterval = null
177+ let submitLoadingInterval = null
178+
179+ // 随机移动函数
180+ const getRandomPosition = (containerWidth = 300 , containerHeight = 80 , iconSize = 40 ) => {
181+ return {
182+ x: Math .random () * (containerWidth - iconSize),
183+ y: Math .random () * (containerHeight - iconSize)
184+ }
185+ }
186+
187+ // 开始随机移动
188+ const startRandomMovement = (positionRef , intervalRef ) => {
189+ if (intervalRef) {
190+ clearInterval (intervalRef)
191+ }
192+
193+ // 根据屏幕大小调整容器尺寸
194+ const isMobile = window .innerWidth <= 768
195+ const containerWidth = isMobile ? 210 : 260
196+ const containerHeight = isMobile ? 30 : 40
197+ const iconSize = isMobile ? 35 : 40
198+
199+ // 初始位置
200+ positionRef .value = getRandomPosition (containerWidth, containerHeight, iconSize)
201+
202+ // 每800ms移动一次(稍微慢一点,更优雅)
203+ return setInterval (() => {
204+ positionRef .value = getRandomPosition (containerWidth, containerHeight, iconSize)
205+ }, 800 )
206+ }
207+
208+ // 停止随机移动
209+ const stopRandomMovement = (intervalRef ) => {
210+ if (intervalRef) {
211+ clearInterval (intervalRef)
212+ }
213+ }
214+
148215// 计算可用选项(排除已选择的)
149216const availableOptions = computed (() => {
150217 if (! currentLevel .value ? .options ) return []
@@ -302,12 +369,38 @@ const resetLevel = () => {
302369 selectedOptions .value = []
303370}
304371
372+ // 监听生成状态变化
373+ watch (generating, (newVal ) => {
374+ if (newVal) {
375+ loadingInterval = startRandomMovement (loadingPosition, loadingInterval)
376+ } else {
377+ stopRandomMovement (loadingInterval)
378+ loadingInterval = null
379+ }
380+ })
381+
382+ // 监听提交状态变化
383+ watch (submitting, (newVal ) => {
384+ if (newVal) {
385+ submitLoadingInterval = startRandomMovement (submitLoadingPosition, submitLoadingInterval)
386+ } else {
387+ stopRandomMovement (submitLoadingInterval)
388+ submitLoadingInterval = null
389+ }
390+ })
391+
305392onMounted (() => {
306393 // 页面加载时检查用户登录状态
307394 if (! user .value ) {
308395 router .push (' /login' )
309396 }
310397})
398+
399+ onUnmounted (() => {
400+ // 组件销毁时清理定时器
401+ stopRandomMovement (loadingInterval)
402+ stopRandomMovement (submitLoadingInterval)
403+ })
311404< / script>
312405
313406< style scoped>
@@ -496,20 +589,56 @@ onMounted(() => {
496589
497590.submit - area {
498591 margin- top: 30px ;
592+ display: flex;
593+ flex- direction: column;
594+ align- items: center;
595+ }
596+
597+ .submit - area .button - row {
499598 display: flex;
500599 gap: 15px ;
501600 justify- content: center;
601+ margin- bottom: 10px ;
602+ }
603+
604+ /* 自定义加载区域 */
605+ .custom - loading- area {
606+ position: relative;
607+ width: 300px ;
608+ height: 80px ;
609+ margin: 20px auto;
610+ border: 2px dashed var (-- border- medium);
611+ border- radius: 12px ;
612+ background: var (-- bg- secondary);
613+ overflow: hidden;
614+ }
615+
616+ .custom - loading- icon {
617+ position: absolute;
618+ width: 40px ;
619+ height: 40px ;
620+ transition: all 0 .3s ease- in - out;
621+ z- index: 2 ;
622+ }
623+
624+ .loading - text {
625+ position: absolute;
626+ bottom: 5px ;
627+ left: 50 % ;
628+ transform: translateX (- 50 % );
629+ color: var (-- text- secondary);
630+ font- size: 14px ;
631+ font- weight: 500 ;
632+ z- index: 1 ;
633+ }
634+
635+ .submit - loading {
636+ margin- top: 15px ;
502637}
503638
504639@media (max - width : 768px ) {
505- .header - content {
506- flex- direction: column;
507- gap: 15px ;
508- }
509-
510- .user - info {
511- flex- direction: column;
512- gap: 10px ;
640+ .main - content {
641+ padding: 30px 20px ;
513642 }
514643
515644 .answer - section {
@@ -520,8 +649,19 @@ onMounted(() => {
520649 grid- template- columns: 1fr ;
521650 }
522651
523- .submit - area {
652+ .submit - area . button - row {
524653 flex- direction: column;
654+ gap: 10px ;
655+ }
656+
657+ .custom - loading- area {
658+ width: 250px ;
659+ height: 70px ;
660+ }
661+
662+ .custom - loading- icon {
663+ width: 35px ;
664+ height: 35px ;
525665 }
526666}
527667< / style>
0 commit comments