文字游戏
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
6.5 KiB

2 months ago
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { useCharacterStore } from '@/stores/character'
2 months ago
import Particles from '@/components/Particles/Particles.vue'
import MeditationIcon from '@/components/MeditationIcon/MeditationIcon.vue'
2 months ago
const router = useRouter()
const characterStore = useCharacterStore()
const message = ref('')
const showMessage = ref(false)
const showMsg = (text: string, type: 'success' | 'error' = 'success') => {
message.value = text
showMessage.value = true
setTimeout(() => {
showMessage.value = false
}, 2000)
}
const trainingStartTime = ref<Date | null>(null)
const elapsedSeconds = ref(0)
const levelId = computed(() => characterStore.currentCharacter?.levelId ?? 1)
const expectedExp = computed(() => {
const level = levelId.value
return Math.floor(0.02 * elapsedSeconds.value * Math.pow(level, 1.5))
})
2 months ago
const isTraining = computed(() => !!trainingStartTime.value)
let timer: number | null = null
const updateElapsedTime = () => {
if (trainingStartTime.value) {
const now = new Date()
const diff = now.getTime() - trainingStartTime.value!.getTime()
elapsedSeconds.value = Math.floor(diff / 1000)
2 months ago
}
}
onMounted(() => {
const trainingOn = characterStore.currentCharacter?.trainingOn
if (trainingOn) {
trainingStartTime.value = new Date(trainingOn)
updateElapsedTime()
timer = window.setInterval(updateElapsedTime, 1000)
}
})
onUnmounted(() => {
if (timer) {
clearInterval(timer)
}
})
const handleGoBack = () => {
router.push('/game')
}
const handleStartTraining = async () => {
const success = await characterStore.startTraining()
if (success) {
trainingStartTime.value = new Date()
elapsedSeconds.value = 0
2 months ago
timer = window.setInterval(updateElapsedTime, 1000)
showMsg('开始打坐')
} else {
showMsg('开始打坐失败', 'error')
}
}
const handleStopTraining = async () => {
const expGained = await characterStore.stopTraining()
if (timer) {
clearInterval(timer)
timer = null
}
trainingStartTime.value = null
elapsedSeconds.value = 0
2 months ago
showMsg(`结算成功,获得 ${expGained} 经验`)
}
</script>
<template>
<div class="training-page">
<Particles :particle-count="50" :particle-colors="['#ffffff', '#aaaaaa']" class="particles-bg" />
2 months ago
2 months ago
<div class="page-container">
<div class="training-header">
<span class="back-btn" @click="handleGoBack"> 返回</span>
<span class="title">打坐</span>
<span class="placeholder"></span>
2 months ago
</div>
2 months ago
<div v-if="showMessage" class="toast-message">
{{ message }}
</div>
<div class="training-content">
<div class="status-section">
<div class="status-icon">
<MeditationIcon :is-training="isTraining" />
2 months ago
</div>
<div class="status-text">
{{ isTraining ? '正在打坐中...' : '未开始打坐' }}
</div>
2 months ago
</div>
2 months ago
<div class="info-section">
<div class="info-item">
<span class="label">已打坐</span>
<span class="value">{{ Math.floor(elapsedSeconds / 60) }}{{ elapsedSeconds % 60 }}</span>
</div>
<div class="info-item">
<span class="label">预计获得</span>
<span class="value highlight">{{ expectedExp }} 经验</span>
</div>
2 months ago
</div>
2 months ago
<div class="action-section">
<button v-if="!isTraining" class="action-btn start-btn" @click="handleStartTraining">
开始打坐
</button>
<button v-else class="action-btn stop-btn" @click="handleStopTraining">
停止打坐
</button>
</div>
2 months ago
</div>
</div>
2 months ago
2 months ago
</div>
</template>
<style scoped>
.training-page {
min-height: 100vh;
background: #000000;
padding: 20px;
position: relative;
overflow: hidden;
}
.training-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 0;
margin-bottom: 40px;
}
2 months ago
.page-container {
max-width: 480px;
margin: 0 auto;
position: relative;
z-index: 10;
}
.particles-bg {
position: fixed !important;
top: 0;
left: 0;
width: 100% !important;
height: 100% !important;
}
2 months ago
.back-btn {
color: #666666;
font-size: 0.9rem;
cursor: pointer;
padding: 8px;
}
.back-btn:hover {
color: #ffffff;
}
.title {
color: #ffffff;
font-size: 1.1rem;
font-weight: 500;
}
.placeholder {
width: 60px;
}
.training-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 40px;
}
.status-section {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.status-icon {
display: flex;
align-items: center;
justify-content: center;
width: 120px;
height: 120px;
2 months ago
}
.status-text {
color: #888888;
font-size: 1rem;
}
.status-section:has(.meditation-icon.training) .status-text {
2 months ago
color: #ff8844;
}
.info-section {
width: 100%;
max-width: 300px;
display: flex;
flex-direction: column;
gap: 16px;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
}
.info-item .label {
color: #666666;
font-size: 0.9rem;
}
.info-item .value {
color: #ffffff;
font-size: 1.1rem;
font-weight: 500;
}
.info-item .value.highlight {
color: #ff8844;
font-size: 1.3rem;
}
.action-section {
margin-top: 20px;
}
.action-btn {
padding: 16px 60px;
border-radius: 12px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
border: none;
}
.start-btn {
background: linear-gradient(135deg, #ff8844 0%, #ff6644 100%);
color: #ffffff;
}
.start-btn:hover {
transform: scale(1.05);
box-shadow: 0 4px 20px rgba(255, 136, 68, 0.4);
}
.stop-btn {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #cccccc;
}
.stop-btn:hover {
background: rgba(255, 255, 255, 0.15);
color: #ffffff;
}
.toast-message {
position: fixed;
top: 80px;
left: 50%;
transform: translateX(-50%);
background: rgba(255, 255, 255, 0.9);
color: #000000;
padding: 12px 24px;
border-radius: 8px;
font-size: 0.9rem;
z-index: 1000;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateX(-50%) translateY(-10px);
}
2 months ago
2 months ago
to {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
}
</style>