|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { onMounted, computed } from 'vue'
|
|
|
|
|
import { useRouter } from 'vue-router'
|
|
|
|
|
import { useBagStore } from '@/stores/bag'
|
|
|
|
|
import StarBorder from '@/components/StarBorder/StarBorder.vue'
|
|
|
|
|
import Particles from '@/components/Particles/Particles.vue'
|
|
|
|
|
import { ArrowLeft } from '@element-plus/icons-vue'
|
|
|
|
|
import bagIcon from '@/assets/images/bag.svg'
|
|
|
|
|
import itemDefaultIcon from '@/assets/images/item-default.svg'
|
|
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
const bagStore = useBagStore()
|
|
|
|
|
|
|
|
|
|
const bagName = computed(() => bagStore.characterBag?.bagName || '背包')
|
|
|
|
|
const bagCapacity = computed(() => bagStore.characterBag?.bagCapacity || 0)
|
|
|
|
|
const usedCapacity = computed(() => bagStore.usedCapacity)
|
|
|
|
|
|
|
|
|
|
const itemTypeMap: Record<number, string> = {
|
|
|
|
|
1: '装备',
|
|
|
|
|
2: '丹药',
|
|
|
|
|
3: '垃圾'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const rarityMap: Record<number, string> = {
|
|
|
|
|
1: '普通',
|
|
|
|
|
2: '稀有',
|
|
|
|
|
3: '史诗',
|
|
|
|
|
4: '传说'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const scrapLevelMap: Record<number, string> = {
|
|
|
|
|
1: '普通',
|
|
|
|
|
2: '优秀',
|
|
|
|
|
3: '精良',
|
|
|
|
|
4: '史诗',
|
|
|
|
|
5: '传说'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const scrapLevelColorMap: Record<number, string> = {
|
|
|
|
|
1: '#FFFFFF',
|
|
|
|
|
2: '#00FF00',
|
|
|
|
|
3: '#0077FF',
|
|
|
|
|
4: '#9932CC',
|
|
|
|
|
5: '#FF8C00'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getItemIcon = (item: { icon: string | null },itemType:string) => {
|
|
|
|
|
if (item.icon) {
|
|
|
|
|
return new URL(`../assets/icons/${itemType}/${item.icon}`, import.meta.url).href
|
|
|
|
|
}
|
|
|
|
|
return itemDefaultIcon
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getEquipmentTooltip = (item: { itemName: string | null; itemRarity: number | null; quantity: number }) => {
|
|
|
|
|
const rarity = item.itemRarity ? rarityMap[item.itemRarity] || '' : ''
|
|
|
|
|
return `${item.itemName}\n类型: 装备${rarity ? ` | 稀有度: ${rarity}` : ''}\n数量: ${item.quantity}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getPillTooltip = (item: { itemName: string | null; quantity: number }) => {
|
|
|
|
|
return `${item.itemName}\n类型: 丹药\n数量: ${item.quantity}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getScrapTooltip = (item: any) => {
|
|
|
|
|
const level = item.scrapLevel ? scrapLevelMap[item.scrapLevel] || '' : ''
|
|
|
|
|
let stats = ''
|
|
|
|
|
if (item.attackBonus) stats += `\n攻击+${item.attackBonus}`
|
|
|
|
|
if (item.defenseBonus) stats += `\n防御+${item.defenseBonus}`
|
|
|
|
|
if (item.hpBonus) stats += `\n生命+${item.hpBonus}`
|
|
|
|
|
if (item.magicBonus) stats += `\n魔力+${item.magicBonus}`
|
|
|
|
|
return `${item.itemName}\n类型: 垃圾 | 稀有度: ${level}\n数量: ${item.quantity}${stats}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleBack = () => {
|
|
|
|
|
router.push('/game')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
bagStore.loadBag()
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div class="bag-page">
|
|
|
|
|
<Particles :particle-count="30" :particle-colors="['#ffffff', '#cccccc']" class="particles-bg" />
|
|
|
|
|
|
|
|
|
|
<div class="bag-container">
|
|
|
|
|
<StarBorder as="div" color="#8b5cf6" speed="5s" :thickness="2" class="back-btn" @click="handleBack">
|
|
|
|
|
<div class="back-content">
|
|
|
|
|
<el-icon>
|
|
|
|
|
<ArrowLeft />
|
|
|
|
|
</el-icon>
|
|
|
|
|
<span>返回</span>
|
|
|
|
|
</div>
|
|
|
|
|
</StarBorder>
|
|
|
|
|
|
|
|
|
|
<div class="bag-header">
|
|
|
|
|
<div class="header-left">
|
|
|
|
|
<img :src="bagIcon" class="header-icon" />
|
|
|
|
|
<h2>{{ bagName }}</h2>
|
|
|
|
|
</div>
|
|
|
|
|
<span class="capacity">{{ usedCapacity }} / {{ bagCapacity }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-if="bagStore.loading" class="loading">
|
|
|
|
|
加载中...
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-else-if="bagStore.error" class="error">
|
|
|
|
|
{{ bagStore.error }}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-else-if="bagStore.bagItems.length === 0" class="empty">
|
|
|
|
|
背包空空如也,快去获取一些物品吧!
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-else class="bag-sections">
|
|
|
|
|
<!-- 装备区域 -->
|
|
|
|
|
<div class="section" v-if="bagStore.paginatedEquipment.length > 0 || bagStore.totalEquipmentPages > 0">
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
<span class="section-title">装备</span>
|
|
|
|
|
<span class="section-count">({{ bagStore.paginatedEquipment.length }} / {{ bagStore.equipmentItems.length
|
|
|
|
|
}})</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="items-grid">
|
|
|
|
|
<el-tooltip v-for="item in bagStore.paginatedEquipment" :key="item.id" :content="getEquipmentTooltip(item)"
|
|
|
|
|
placement="top" :show-after="300">
|
|
|
|
|
<div class="item-cell">
|
|
|
|
|
<img v-if="item.icon" :src="getItemIcon(item,'equipment')" class="item-icon-img"
|
|
|
|
|
:class="'rarity-' + (item.itemRarity || 1)" />
|
|
|
|
|
<div v-else class="item-icon">⚔️</div>
|
|
|
|
|
<span class="item-name">{{ item.itemName }}</span>
|
|
|
|
|
<span v-if="item.quantity > 1" class="item-count">{{ item.quantity }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="bagStore.totalEquipmentPages > 1" class="pagination">
|
|
|
|
|
<button class="page-btn" :disabled="bagStore.equipmentPage === 1" @click="bagStore.prevEquipmentPage()">
|
|
|
|
|
上一页
|
|
|
|
|
</button>
|
|
|
|
|
<span class="page-info">{{ bagStore.equipmentPage }} / {{ bagStore.totalEquipmentPages }}</span>
|
|
|
|
|
<button class="page-btn" :disabled="bagStore.equipmentPage >= bagStore.totalEquipmentPages"
|
|
|
|
|
@click="bagStore.nextEquipmentPage()">
|
|
|
|
|
下一页
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 丹药区域 -->
|
|
|
|
|
<div class="section" v-if="bagStore.paginatedPills.length > 0 || bagStore.totalPillPages > 0">
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
<span class="section-title">丹药</span>
|
|
|
|
|
<span class="section-count">({{ bagStore.paginatedPills.length }} / {{ bagStore.pillItems.length }})</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="items-grid">
|
|
|
|
|
<el-tooltip v-for="item in bagStore.paginatedPills" :key="item.id" :content="getPillTooltip(item)"
|
|
|
|
|
placement="top" :show-after="300">
|
|
|
|
|
<div class="item-cell">
|
|
|
|
|
<img v-if="item.icon" :src="getItemIcon(item,'pill')" class="item-icon-img" />
|
|
|
|
|
<div v-else class="item-icon">💊</div>
|
|
|
|
|
<span class="item-name">{{ item.itemName }}</span>
|
|
|
|
|
<span v-if="item.quantity > 1" class="item-count">{{ item.quantity }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="bagStore.totalPillPages > 1" class="pagination">
|
|
|
|
|
<button class="page-btn" :disabled="bagStore.pillPage === 1" @click="bagStore.prevPillPage()">
|
|
|
|
|
上一页
|
|
|
|
|
</button>
|
|
|
|
|
<span class="page-info">{{ bagStore.pillPage }} / {{ bagStore.totalPillPages }}</span>
|
|
|
|
|
<button class="page-btn" :disabled="bagStore.pillPage >= bagStore.totalPillPages"
|
|
|
|
|
@click="bagStore.nextPillPage()">
|
|
|
|
|
下一页
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 垃圾区域 -->
|
|
|
|
|
<div class="section" v-if="bagStore.paginatedScraps.length > 0 || bagStore.totalScrapPages > 0">
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
<span class="section-title">垃圾</span>
|
|
|
|
|
<span class="section-count">({{ bagStore.paginatedScraps.length }} / {{ bagStore.scrapItems.length
|
|
|
|
|
}})</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="items-grid">
|
|
|
|
|
<el-tooltip v-for="item in bagStore.paginatedScraps" :key="item.id" :content="getScrapTooltip(item)"
|
|
|
|
|
placement="top" :show-after="300">
|
|
|
|
|
<div class="item-cell scrap-cell">
|
|
|
|
|
<img v-if="item.icon" :src="getItemIcon(item,'scrap')" class="item-icon-img"
|
|
|
|
|
:style="{ borderColor: scrapLevelColorMap[item.scrapLevel || 1] }" />
|
|
|
|
|
<div v-else class="item-icon" :style="{ color: scrapLevelColorMap[item.scrapLevel || 1] }">📦</div>
|
|
|
|
|
<span class="item-name">{{ item.itemName }}</span>
|
|
|
|
|
<span v-if="item.quantity > 1" class="item-count">{{ item.quantity }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="bagStore.totalScrapPages > 1" class="pagination">
|
|
|
|
|
<button class="page-btn" :disabled="bagStore.scrapPage === 1" @click="bagStore.prevScrapPage()">
|
|
|
|
|
上一页
|
|
|
|
|
</button>
|
|
|
|
|
<span class="page-info">{{ bagStore.scrapPage }} / {{ bagStore.totalScrapPages }}</span>
|
|
|
|
|
<button class="page-btn" :disabled="bagStore.scrapPage >= bagStore.totalScrapPages"
|
|
|
|
|
@click="bagStore.nextScrapPage()">
|
|
|
|
|
下一页
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.bag-page {
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
background: #000000;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
position: relative;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.particles-bg {
|
|
|
|
|
position: fixed !important;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 100% !important;
|
|
|
|
|
height: 100% !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bag-container {
|
|
|
|
|
max-width: 480px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.back-btn {
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.back-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: #cccccc;
|
|
|
|
|
background: rgba(255, 255, 255, 0.03);
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bag-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
padding: 16px 20px;
|
|
|
|
|
background: rgba(255, 255, 255, 0.03);
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-left {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-icon {
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bag-header h2 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.capacity {
|
|
|
|
|
color: #888888;
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.loading,
|
|
|
|
|
.error,
|
|
|
|
|
.empty {
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding: 40px;
|
|
|
|
|
color: #888888;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.error {
|
|
|
|
|
color: #ff4444;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bag-sections {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section {
|
|
|
|
|
background: rgba(255, 255, 255, 0.02);
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.06);
|
|
|
|
|
border-radius: 16px;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
padding-bottom: 12px;
|
|
|
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-count {
|
|
|
|
|
color: #666666;
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.items-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(4, 1fr);
|
|
|
|
|
gap: 12px;
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item-cell {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 16px 8px;
|
|
|
|
|
background: rgba(255, 255, 255, 0.03);
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item-cell:hover {
|
|
|
|
|
background: rgba(255, 255, 255, 0.06);
|
|
|
|
|
border-color: rgba(255, 255, 255, 0.15);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.scrap-cell {
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item-icon {
|
|
|
|
|
font-size: 2rem;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item-icon-img {
|
|
|
|
|
width: 2.5rem;
|
|
|
|
|
height: 2.5rem;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
border: 2px solid transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item-name {
|
|
|
|
|
color: #cccccc;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
text-align: center;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item-count {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 4px;
|
|
|
|
|
right: 4px;
|
|
|
|
|
background: rgba(139, 92, 246, 0.8);
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
min-width: 18px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rarity-1 {
|
|
|
|
|
filter: grayscale(0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rarity-2 {
|
|
|
|
|
filter: hue-rotate(80deg) saturate(1.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rarity-3 {
|
|
|
|
|
filter: hue-rotate(200deg) saturate(2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rarity-4 {
|
|
|
|
|
filter: hue-rotate(-30deg) saturate(2) brightness(1.2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pagination {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-btn {
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
background: rgba(255, 255, 255, 0.05);
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
color: #cccccc;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-btn:hover:not(:disabled) {
|
|
|
|
|
background: rgba(255, 255, 255, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-btn:disabled {
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-info {
|
|
|
|
|
color: #888888;
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
}
|
|
|
|
|
</style>
|