- 修复商店物品名称显示问题,添加堆叠物品出售数量选择 - 自动战斗状态持久化,战斗结束显示"寻找中"状态 - 战斗日志显示经验获取详情(战斗经验、武器经验) - 技能进度条显示当前/最大经验值 - 阅读自动解锁技能并持续获得阅读经验,背包可直接阅读 - 优化训练平衡:时长60秒,经验5点/秒,耐力消耗降低 - 实现自然回复系统:基于体质回复HP/耐力,休息提供3倍加成 - 战斗和训练时不进行自然回复 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
876 lines
22 KiB
Vue
876 lines
22 KiB
Vue
<template>
|
||
<view class="status-panel">
|
||
<!-- 资源进度条 -->
|
||
<view class="status-panel__resources">
|
||
<view class="resource-row">
|
||
<text class="resource-label">HP</text>
|
||
<view class="resource-bar">
|
||
<ProgressBar
|
||
:value="player.currentStats.health"
|
||
:max="player.currentStats.maxHealth"
|
||
color="#ff6b6b"
|
||
:showText="true"
|
||
height="24rpx"
|
||
/>
|
||
</view>
|
||
</view>
|
||
<view class="resource-row">
|
||
<text class="resource-label">耐力</text>
|
||
<view class="resource-bar">
|
||
<ProgressBar
|
||
:value="player.currentStats.stamina"
|
||
:max="player.currentStats.maxStamina"
|
||
color="#ffe66d"
|
||
:showText="true"
|
||
height="24rpx"
|
||
/>
|
||
</view>
|
||
</view>
|
||
<view class="resource-row">
|
||
<text class="resource-label">精神</text>
|
||
<view class="resource-bar">
|
||
<ProgressBar
|
||
:value="player.currentStats.sanity"
|
||
:max="player.currentStats.maxSanity"
|
||
color="#4ecdc4"
|
||
:showText="true"
|
||
height="24rpx"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 主属性摘要 -->
|
||
<view class="status-panel__stats">
|
||
<StatItem label="力量" :value="player.baseStats.strength" icon="💪" />
|
||
<StatItem label="敏捷" :value="player.baseStats.agility" icon="🏃" />
|
||
<StatItem label="灵巧" :value="player.baseStats.dexterity" icon="🎯" />
|
||
<StatItem label="智力" :value="player.baseStats.intuition" icon="🧠" />
|
||
<StatItem label="体质" :value="player.baseStats.vitality" icon="❤️" />
|
||
</view>
|
||
|
||
<!-- 等级信息 -->
|
||
<view class="status-panel__level">
|
||
<text class="level-text">Lv.{{ player.level.current }}</text>
|
||
<ProgressBar
|
||
:value="player.level.exp"
|
||
:max="player.level.maxExp"
|
||
color="#a855f7"
|
||
:showText="true"
|
||
height="16rpx"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 货币 -->
|
||
<view class="status-panel__currency">
|
||
<text class="currency-icon">💰</text>
|
||
<text class="currency-text">{{ currencyText }}</text>
|
||
</view>
|
||
|
||
<!-- 装备栏 -->
|
||
<view class="status-panel__equipment">
|
||
<text class="section-title">🎽 装备</text>
|
||
<view class="equipment-slots">
|
||
<view class="equipment-slot" :class="{ 'equipment-slot--empty': !player.equipment.weapon }">
|
||
<text class="equipment-slot__label">武器</text>
|
||
<text v-if="player.equipment.weapon" class="equipment-slot__item">
|
||
{{ player.equipment.weapon.icon }} {{ player.equipment.weapon.name }}
|
||
</text>
|
||
<text v-else class="equipment-slot__empty">空</text>
|
||
</view>
|
||
<view class="equipment-slot" :class="{ 'equipment-slot--empty': !player.equipment.armor }">
|
||
<text class="equipment-slot__label">防具</text>
|
||
<text v-if="player.equipment.armor" class="equipment-slot__item">
|
||
{{ player.equipment.armor.icon }} {{ player.equipment.armor.name }}
|
||
</text>
|
||
<text v-else class="equipment-slot__empty">空</text>
|
||
</view>
|
||
<view class="equipment-slot" :class="{ 'equipment-slot--empty': !player.equipment.shield }">
|
||
<text class="equipment-slot__label">盾牌</text>
|
||
<text v-if="player.equipment.shield" class="equipment-slot__item">
|
||
{{ player.equipment.shield.icon }} {{ player.equipment.shield.name }}
|
||
</text>
|
||
<text v-else class="equipment-slot__empty">空</text>
|
||
</view>
|
||
<view class="equipment-slot" :class="{ 'equipment-slot--empty': !player.equipment.accessory }">
|
||
<text class="equipment-slot__label">饰品</text>
|
||
<text v-if="player.equipment.accessory" class="equipment-slot__item">
|
||
{{ player.equipment.accessory.icon }} {{ player.equipment.accessory.name }}
|
||
</text>
|
||
<text v-else class="equipment-slot__empty">空</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 更多属性折叠 -->
|
||
<Collapse
|
||
title="更多属性"
|
||
:expanded="showMoreStats"
|
||
@toggle="showMoreStats = $event"
|
||
>
|
||
<view class="status-panel__more-stats">
|
||
<StatItem label="攻击力" :value="finalStats.attack" />
|
||
<StatItem label="防御力" :value="finalStats.defense" />
|
||
<StatItem label="暴击率" :value="finalStats.critRate + '%'" />
|
||
<StatItem label="AP" :value="finalStats.ap" />
|
||
<StatItem label="EP" :value="finalStats.ep" />
|
||
</view>
|
||
</Collapse>
|
||
|
||
<!-- 技能筛选 -->
|
||
<FilterTabs
|
||
:tabs="skillFilterTabs"
|
||
:modelValue="currentSkillFilter"
|
||
@update:modelValue="currentSkillFilter = $event"
|
||
/>
|
||
|
||
<!-- 技能列表 -->
|
||
<scroll-view class="status-panel__skills" scroll-y>
|
||
<view v-for="skill in filteredSkills" :key="skill.id" class="skill-item">
|
||
<text class="skill-item__icon">{{ skill.icon || '⚡' }}</text>
|
||
<view class="skill-item__info">
|
||
<text class="skill-item__name">{{ skill.name }}</text>
|
||
<text class="skill-item__level">Lv.{{ skill.level }}</text>
|
||
</view>
|
||
<view class="skill-item__bar">
|
||
<ProgressBar
|
||
:value="skill.exp"
|
||
:max="skill.maxExp"
|
||
height="8rpx"
|
||
:showText="false"
|
||
/>
|
||
<text class="skill-item__progress">{{ skill.exp }}/{{ skill.maxExp }}</text>
|
||
</view>
|
||
</view>
|
||
<view v-if="filteredSkills.length === 0" class="skill-empty">
|
||
<text class="skill-empty__text">暂无技能</text>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 书籍区域 -->
|
||
<Collapse title="📚 书籍" :expanded="showBooks" @toggle="showBooks = $event">
|
||
<!-- 活动中的阅读任务 -->
|
||
<view v-if="readingTask" class="reading-task">
|
||
<text class="reading-task__title">正在阅读:{{ readingTask.bookName }}</text>
|
||
<ProgressBar
|
||
:value="readingTask.progress"
|
||
:max="readingTask.totalTime"
|
||
color="#4ecdc4"
|
||
:showText="true"
|
||
height="16rpx"
|
||
/>
|
||
<text class="reading-task__time">{{ readingTimeText }}</text>
|
||
</view>
|
||
|
||
<!-- 可阅读的书籍列表 -->
|
||
<view v-if="!readingTask && ownedBooks.length > 0" class="books-list">
|
||
<view
|
||
v-for="book in ownedBooks"
|
||
:key="book.id"
|
||
class="book-item"
|
||
@click="startReading(book)"
|
||
>
|
||
<text class="book-item__icon">{{ book.icon || '📖' }}</text>
|
||
<view class="book-item__info">
|
||
<text class="book-item__name">{{ book.name }}</text>
|
||
<text class="book-item__desc">阅读需 {{ book.readingTime }}秒</text>
|
||
</view>
|
||
<TextButton text="阅读" type="primary" size="small" />
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="!readingTask && ownedBooks.length === 0" class="books-empty">
|
||
<text class="books-empty__text">暂无书籍,去商店看看吧</text>
|
||
</view>
|
||
</Collapse>
|
||
|
||
<!-- 训练区域 -->
|
||
<Collapse title="⚔️ 训练" :expanded="showTraining" @toggle="showTraining = $event">
|
||
<!-- 活动中的训练任务 -->
|
||
<view v-if="trainingTask" class="training-task">
|
||
<text class="training-task__title">正在训练:{{ trainingTask.skillName }}</text>
|
||
<ProgressBar
|
||
:value="trainingTask.progress"
|
||
:max="trainingTask.totalTime || 300"
|
||
color="#ff6b6b"
|
||
:showText="true"
|
||
height="16rpx"
|
||
/>
|
||
<TextButton text="停止训练" type="warning" size="small" @click="stopTraining" />
|
||
</view>
|
||
|
||
<!-- 可训练的技能列表 -->
|
||
<view v-if="!trainingTask && trainableSkills.length > 0" class="trainable-list">
|
||
<view
|
||
v-for="skill in trainableSkills"
|
||
:key="skill.id"
|
||
class="trainable-item"
|
||
>
|
||
<text class="trainable-item__icon">{{ skill.icon || '⚔️' }}</text>
|
||
<view class="trainable-item__info">
|
||
<text class="trainable-item__name">{{ skill.name }}</text>
|
||
<text class="trainable-item__level">Lv.{{ skill.level }}</text>
|
||
</view>
|
||
<TextButton
|
||
text="训练"
|
||
type="primary"
|
||
size="small"
|
||
:disabled="player.currentStats.stamina < 10"
|
||
@click="startTraining(skill)"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="!trainingTask && trainableSkills.length === 0" class="training-empty">
|
||
<text class="training-empty__text">暂无可训练技能</text>
|
||
</view>
|
||
</Collapse>
|
||
|
||
<!-- 活动任务面板 -->
|
||
<Collapse title="📋 活动任务" :expanded="showTasks" @toggle="showTasks = $event">
|
||
<view v-if="activeTasksList.length > 0" class="active-tasks-list">
|
||
<view v-for="task in activeTasksList" :key="task.id" class="active-task-item">
|
||
<view class="active-task-item__header">
|
||
<text class="active-task-item__name">{{ task.name }}</text>
|
||
<TextButton
|
||
text="取消"
|
||
type="warning"
|
||
size="small"
|
||
@click="cancelActiveTask(task)"
|
||
/>
|
||
</view>
|
||
<ProgressBar
|
||
:value="task.progress"
|
||
:max="task.total || 100"
|
||
color="#4ecdc4"
|
||
:showText="true"
|
||
height="12rpx"
|
||
/>
|
||
<text class="active-task-item__time">{{ task.timeText }}</text>
|
||
</view>
|
||
</view>
|
||
<view v-else class="tasks-empty">
|
||
<text class="tasks-empty__text">暂无活动任务</text>
|
||
</view>
|
||
</Collapse>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref } from 'vue'
|
||
import { usePlayerStore } from '@/store/player'
|
||
import { useGameStore } from '@/store/game'
|
||
import { SKILL_CONFIG } from '@/config/skills'
|
||
import { ITEM_CONFIG } from '@/config/items'
|
||
import { startTask, endTask } from '@/utils/taskSystem'
|
||
import { getSkillDisplayInfo } from '@/utils/skillSystem'
|
||
import ProgressBar from '@/components/common/ProgressBar.vue'
|
||
import StatItem from '@/components/common/StatItem.vue'
|
||
import Collapse from '@/components/common/Collapse.vue'
|
||
import FilterTabs from '@/components/common/FilterTabs.vue'
|
||
import TextButton from '@/components/common/TextButton.vue'
|
||
|
||
const player = usePlayerStore()
|
||
const game = useGameStore()
|
||
const showMoreStats = ref(false)
|
||
const currentSkillFilter = ref('all')
|
||
const showBooks = ref(false)
|
||
const showTraining = ref(false)
|
||
const showTasks = ref(true)
|
||
|
||
const skillFilterTabs = [
|
||
{ id: 'all', label: '全部' },
|
||
{ id: 'combat', label: '战斗' },
|
||
{ id: 'life', label: '生活' },
|
||
{ id: 'passive', label: '被动' }
|
||
]
|
||
|
||
const currencyText = computed(() => {
|
||
const c = player.currency.copper
|
||
const gold = Math.floor(c / 10000)
|
||
const silver = Math.floor((c % 10000) / 100)
|
||
const copper = c % 100
|
||
const parts = []
|
||
if (gold > 0) parts.push(`${gold}金`)
|
||
if (silver > 0 || gold > 0) parts.push(`${silver}银`)
|
||
parts.push(`${copper}铜`)
|
||
return parts.join(' ')
|
||
})
|
||
|
||
const filteredSkills = computed(() => {
|
||
const skills = Object.entries(player.skills || {})
|
||
.filter(([_, skill]) => skill.unlocked)
|
||
.map(([id, _]) => getSkillDisplayInfo(player, id))
|
||
.filter(Boolean)
|
||
.sort((a, b) => b.level - a.level || b.exp - a.exp)
|
||
|
||
if (currentSkillFilter.value === 'all') {
|
||
return skills
|
||
}
|
||
return skills.filter(s => s.type === currentSkillFilter.value)
|
||
})
|
||
|
||
const finalStats = computed(() => {
|
||
// 计算最终属性(基础值 + 装备加成 + 全局加成)
|
||
let attack = player.baseStats?.strength || 0
|
||
let defense = 0
|
||
let critRate = (player.baseStats?.dexterity || 0) + (player.globalBonus?.critRate || 0)
|
||
|
||
// 装备属性加成
|
||
if (player.equipmentStats) {
|
||
attack += player.equipmentStats.attack || 0
|
||
defense += player.equipmentStats.defense || 0
|
||
critRate += player.equipmentStats.critRate || 0
|
||
}
|
||
|
||
// 如果没有equipmentStats,直接从装备计算
|
||
if (!player.equipmentStats) {
|
||
// 武器攻击力(使用finalDamage如果存在)
|
||
if (player.equipment.weapon) {
|
||
const weapon = player.equipment.weapon
|
||
attack += weapon.finalDamage || weapon.baseDamage || 0
|
||
}
|
||
|
||
// 防具防御力(使用finalDefense如果存在)
|
||
if (player.equipment.armor) {
|
||
const armor = player.equipment.armor
|
||
defense += armor.finalDefense || armor.baseDefense || 0
|
||
}
|
||
|
||
// 盾牌格挡
|
||
if (player.equipment.shield) {
|
||
const shield = player.equipment.shield
|
||
defense += shield.finalDefense || shield.baseDefense || 0
|
||
}
|
||
}
|
||
|
||
// 计算AP和EP(包含装备加成)
|
||
const ap = (player.baseStats?.dexterity || 0) + (player.baseStats?.intuition || 0) * 0.2
|
||
const ep = (player.baseStats?.agility || 0) + (player.baseStats?.intuition || 0) * 0.2
|
||
|
||
return {
|
||
attack: Math.floor(attack),
|
||
defense: Math.floor(defense),
|
||
critRate: Math.min(80, Math.floor(critRate)),
|
||
ap: Math.floor(ap),
|
||
ep: Math.floor(ep)
|
||
}
|
||
})
|
||
|
||
// 书籍相关
|
||
const ownedBooks = computed(() => {
|
||
return (player.inventory || []).filter(item => item.type === 'book')
|
||
})
|
||
|
||
const readingTask = computed(() => {
|
||
const task = (game.activeTasks || []).find(t => t.type === 'reading')
|
||
if (!task) return null
|
||
|
||
const bookConfig = ITEM_CONFIG[task.data.itemId]
|
||
return {
|
||
bookName: bookConfig?.name || '未知书籍',
|
||
progress: task.progress,
|
||
totalTime: bookConfig?.readingTime || 60
|
||
}
|
||
})
|
||
|
||
const readingTimeText = computed(() => {
|
||
if (!readingTask.value) return ''
|
||
const remaining = Math.max(0, readingTask.value.totalTime - readingTask.value.progress)
|
||
const minutes = Math.floor(remaining / 60)
|
||
const seconds = Math.floor(remaining % 60)
|
||
return `剩余 ${minutes}:${String(seconds).padStart(2, '0')}`
|
||
})
|
||
|
||
function startReading(book) {
|
||
const result = startTask(game, player, 'reading', {
|
||
itemId: book.id,
|
||
duration: book.readingTime || 60
|
||
})
|
||
|
||
if (result.success) {
|
||
game.addLog(`开始阅读《${book.name}》`, 'info')
|
||
} else {
|
||
game.addLog(result.message, 'error')
|
||
}
|
||
}
|
||
|
||
// 训练相关
|
||
const trainableSkills = computed(() => {
|
||
return Object.entries(player.skills || {})
|
||
.filter(([_, skill]) => skill.unlocked && SKILL_CONFIG[_]?.type === 'combat')
|
||
.map(([id, _]) => {
|
||
const info = getSkillDisplayInfo(player, id)
|
||
return {
|
||
id,
|
||
name: info?.name || id,
|
||
icon: info?.icon || '⚔️',
|
||
level: info?.level || 0
|
||
}
|
||
})
|
||
.filter(Boolean)
|
||
})
|
||
|
||
const trainingTask = computed(() => {
|
||
const task = (game.activeTasks || []).find(t => t.type === 'training')
|
||
if (!task) return null
|
||
|
||
const info = getSkillDisplayInfo(player, task.data.skillId)
|
||
return {
|
||
skillName: info?.name || '未知技能',
|
||
progress: task.progress,
|
||
totalTime: task.data.duration || 300,
|
||
taskId: task.id
|
||
}
|
||
})
|
||
|
||
function startTraining(skill) {
|
||
const result = startTask(game, player, 'training', {
|
||
skillId: skill.id,
|
||
duration: 60 // 1分钟
|
||
})
|
||
|
||
if (result.success) {
|
||
game.addLog(`开始训练 ${skill.name}`, 'info')
|
||
} else {
|
||
game.addLog(result.message, 'error')
|
||
}
|
||
}
|
||
|
||
function stopTraining() {
|
||
if (trainingTask.value) {
|
||
endTask(game, player, trainingTask.value.taskId, false)
|
||
game.addLog('停止了训练', 'info')
|
||
}
|
||
}
|
||
|
||
// 活动任务相关
|
||
const activeTasksList = computed(() => {
|
||
const tasks = (game.activeTasks || []).map(task => {
|
||
let name = '未知任务'
|
||
let total = 100
|
||
|
||
if (task.type === 'reading') {
|
||
const book = ITEM_CONFIG[task.data.itemId]
|
||
name = `阅读《${book?.name || '未知书籍'}》`
|
||
total = book?.readingTime || 60
|
||
} else if (task.type === 'training') {
|
||
const skill = SKILL_CONFIG[task.data.skillId]
|
||
name = `训练 ${skill?.name || '技能'}`
|
||
total = task.data.duration || 300
|
||
} else if (task.type === 'resting') {
|
||
name = '休息'
|
||
total = task.data.duration || 60
|
||
}
|
||
|
||
const progress = Math.min(task.progress, total)
|
||
const percentage = Math.floor((progress / total) * 100)
|
||
|
||
// 计算剩余时间
|
||
const remaining = Math.max(0, total - progress)
|
||
const minutes = Math.floor(remaining / 60)
|
||
const seconds = Math.floor(remaining % 60)
|
||
let timeText = ''
|
||
if (minutes > 0) {
|
||
timeText = `剩余 ${minutes}分${seconds}秒`
|
||
} else {
|
||
timeText = `剩余 ${seconds}秒`
|
||
}
|
||
|
||
return {
|
||
id: task.id,
|
||
name,
|
||
progress,
|
||
total,
|
||
percentage,
|
||
timeText
|
||
}
|
||
})
|
||
|
||
return tasks.sort((a, b) => b.progress - a.progress)
|
||
})
|
||
|
||
function cancelActiveTask(task) {
|
||
const { endTask } = require('@/utils/taskSystem')
|
||
endTask(game, player, task.id, false)
|
||
game.addLog(`取消了 ${task.name}`, 'info')
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.status-panel {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
padding: 16rpx;
|
||
background-color: $bg-primary;
|
||
|
||
&__resources {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12rpx;
|
||
padding: 16rpx;
|
||
background-color: $bg-secondary;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
&__stats {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8rpx;
|
||
padding: 16rpx;
|
||
background-color: $bg-secondary;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
&__level {
|
||
padding: 16rpx;
|
||
background-color: $bg-secondary;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
&__currency {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
padding: 16rpx;
|
||
background-color: $bg-secondary;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
&__equipment {
|
||
padding: 16rpx;
|
||
background-color: $bg-secondary;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
&__more-stats {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
&__skills {
|
||
flex: 1;
|
||
margin-top: 16rpx;
|
||
}
|
||
}
|
||
|
||
.resource-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.resource-bar {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.resource-label {
|
||
min-width: 80rpx;
|
||
color: $text-secondary;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.level-text {
|
||
display: block;
|
||
color: $accent;
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.currency-icon {
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.currency-text {
|
||
color: $warning;
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.skill-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
padding: 12rpx;
|
||
background-color: $bg-secondary;
|
||
border-radius: 8rpx;
|
||
margin-bottom: 8rpx;
|
||
|
||
&__icon {
|
||
font-size: 32rpx;
|
||
width: 48rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
&__info {
|
||
flex: 1;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
&__name {
|
||
color: $text-primary;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
&__level {
|
||
color: $accent;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
&__bar {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4rpx;
|
||
}
|
||
|
||
&__progress {
|
||
color: $text-muted;
|
||
font-size: 20rpx;
|
||
text-align: right;
|
||
}
|
||
}
|
||
|
||
.skill-empty {
|
||
padding: 48rpx;
|
||
text-align: center;
|
||
|
||
&__text {
|
||
color: $text-muted;
|
||
font-size: 28rpx;
|
||
}
|
||
}
|
||
|
||
.section-title {
|
||
display: block;
|
||
color: $text-primary;
|
||
font-size: 24rpx;
|
||
font-weight: bold;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.equipment-slots {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.equipment-slot {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 12rpx;
|
||
background-color: $bg-primary;
|
||
border-radius: 8rpx;
|
||
border: 1rpx solid $border-color;
|
||
|
||
&--empty {
|
||
opacity: 0.6;
|
||
}
|
||
|
||
&__label {
|
||
color: $text-secondary;
|
||
font-size: 20rpx;
|
||
margin-bottom: 4rpx;
|
||
}
|
||
|
||
&__item {
|
||
color: $text-primary;
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
&__empty {
|
||
color: $text-muted;
|
||
font-size: 22rpx;
|
||
}
|
||
}
|
||
|
||
// 书籍相关样式
|
||
.reading-task {
|
||
padding: 16rpx;
|
||
background-color: $bg-tertiary;
|
||
border-radius: 8rpx;
|
||
margin-bottom: 12rpx;
|
||
|
||
&__title {
|
||
display: block;
|
||
color: $text-primary;
|
||
font-size: 24rpx;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
&__time {
|
||
display: block;
|
||
color: $text-secondary;
|
||
font-size: 20rpx;
|
||
margin-top: 8rpx;
|
||
text-align: right;
|
||
}
|
||
}
|
||
|
||
.books-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.book-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
padding: 12rpx;
|
||
background-color: $bg-primary;
|
||
border-radius: 8rpx;
|
||
|
||
&:active {
|
||
background-color: $bg-tertiary;
|
||
}
|
||
|
||
&__icon {
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
&__info {
|
||
flex: 1;
|
||
}
|
||
|
||
&__name {
|
||
display: block;
|
||
color: $text-primary;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
&__desc {
|
||
display: block;
|
||
color: $text-secondary;
|
||
font-size: 22rpx;
|
||
margin-top: 4rpx;
|
||
}
|
||
}
|
||
|
||
.books-empty {
|
||
padding: 32rpx;
|
||
text-align: center;
|
||
|
||
&__text {
|
||
color: $text-muted;
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
|
||
// 训练相关样式
|
||
.training-task {
|
||
padding: 16rpx;
|
||
background-color: $bg-tertiary;
|
||
border-radius: 8rpx;
|
||
margin-bottom: 12rpx;
|
||
|
||
&__title {
|
||
display: block;
|
||
color: $text-primary;
|
||
font-size: 24rpx;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
}
|
||
|
||
.trainable-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.trainable-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
padding: 12rpx;
|
||
background-color: $bg-primary;
|
||
border-radius: 8rpx;
|
||
|
||
&__icon {
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
&__info {
|
||
flex: 1;
|
||
}
|
||
|
||
&__name {
|
||
display: block;
|
||
color: $text-primary;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
&__level {
|
||
display: block;
|
||
color: $accent;
|
||
font-size: 22rpx;
|
||
margin-top: 4rpx;
|
||
}
|
||
}
|
||
|
||
.training-empty {
|
||
padding: 32rpx;
|
||
text-align: center;
|
||
|
||
&__text {
|
||
color: $text-muted;
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
|
||
// 活动任务样式
|
||
.active-tasks-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.active-task-item {
|
||
padding: 12rpx;
|
||
background-color: $bg-primary;
|
||
border-radius: 8rpx;
|
||
|
||
&__header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
&__name {
|
||
color: $text-primary;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
&__time {
|
||
display: block;
|
||
color: $text-secondary;
|
||
font-size: 20rpx;
|
||
margin-top: 4rpx;
|
||
}
|
||
}
|
||
|
||
.tasks-empty {
|
||
padding: 32rpx;
|
||
text-align: center;
|
||
|
||
&__text {
|
||
color: $text-muted;
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
</style>
|