469 lines
9.7 KiB
Vue
469 lines
9.7 KiB
Vue
<template>
|
||
<div v-if="isOpen" class="inventory-modal" @click.self="close">
|
||
<div class="inventory-container">
|
||
<!-- 標題欄 -->
|
||
<div class="inventory-header">
|
||
<h3>🎒 Inventory</h3>
|
||
<button class="close-btn" @click="close">✕</button>
|
||
</div>
|
||
|
||
<!-- 主要內容 -->
|
||
<div class="inventory-content">
|
||
<!-- 左側:角色裝備 -->
|
||
<div class="equipment-panel">
|
||
<div class="character-display">
|
||
<div class="pet-preview"></div>
|
||
</div>
|
||
<div class="equipment-slots">
|
||
<div class="equip-slot head" title="Head">
|
||
<span class="slot-icon">🎩</span>
|
||
</div>
|
||
<div class="equip-slot body" title="Body">
|
||
<span class="slot-icon">👕</span>
|
||
</div>
|
||
<div class="equip-slot accessory" title="Accessory">
|
||
<span class="slot-icon">💍</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 快速資訊 -->
|
||
<div class="quick-stats">
|
||
<div class="stat-row">
|
||
<span>💰</span>
|
||
<span>{{ coins }}</span>
|
||
</div>
|
||
<div class="stat-row">
|
||
<span>⚔️</span>
|
||
<span>{{ attack }}</span>
|
||
</div>
|
||
<div class="stat-row">
|
||
<span>🛡️</span>
|
||
<span>{{ defense }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右側:物品格子 -->
|
||
<div class="items-panel">
|
||
<!-- 分類標籤 -->
|
||
<div class="category-tabs">
|
||
<button
|
||
v-for="category in categories"
|
||
:key="category.id"
|
||
:class="['tab', { active: activeCategory === category.id }]"
|
||
@click="activeCategory = category.id"
|
||
>
|
||
{{ category.icon }} {{ category.name }}
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 物品網格 -->
|
||
<div class="item-grid">
|
||
<div
|
||
v-for="i in 24"
|
||
:key="i"
|
||
class="item-slot"
|
||
:class="{ filled: items[i - 1] }"
|
||
@click="selectItem(i - 1)"
|
||
>
|
||
<span v-if="items[i - 1]" class="item-icon">
|
||
{{ items[i - 1].icon }}
|
||
</span>
|
||
<span v-if="items[i - 1] && items[i - 1].count > 1" class="item-count">
|
||
{{ items[i - 1].count }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 底部操作欄 -->
|
||
<div class="action-bar">
|
||
<button class="action-btn" @click="useItem">Use</button>
|
||
<button class="action-btn" @click="dropItem">Drop</button>
|
||
<button class="action-btn sort" @click="sortItems">Sort</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue'
|
||
|
||
const props = defineProps({
|
||
isOpen: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
coins: {
|
||
type: Number,
|
||
default: 0
|
||
},
|
||
attack: {
|
||
type: Number,
|
||
default: 10
|
||
},
|
||
defense: {
|
||
type: Number,
|
||
default: 5
|
||
}
|
||
})
|
||
|
||
const emit = defineEmits(['close', 'use-item', 'drop-item'])
|
||
|
||
const activeCategory = ref('all')
|
||
const selectedSlot = ref(null)
|
||
|
||
const categories = [
|
||
{ id: 'all', name: 'All', icon: '📦' },
|
||
{ id: 'equipment', name: 'Gear', icon: '⚔️' },
|
||
{ id: 'consumable', name: 'Food', icon: '🍖' },
|
||
{ id: 'material', name: 'Items', icon: '💎' }
|
||
]
|
||
|
||
// 示例物品數據
|
||
const items = ref([
|
||
{ icon: '🍖', count: 5 },
|
||
{ icon: '💊', count: 3 },
|
||
{ icon: '🗡️', count: 1 },
|
||
{ icon: '🛡️', count: 1 },
|
||
null, null, null, null,
|
||
{ icon: '💎', count: 10 },
|
||
null, null, null,
|
||
null, null, null, null,
|
||
null, null, null, null,
|
||
null, null, null, null
|
||
])
|
||
|
||
const close = () => {
|
||
emit('close')
|
||
}
|
||
|
||
const selectItem = (index) => {
|
||
selectedSlot.value = index
|
||
}
|
||
|
||
const useItem = () => {
|
||
if (selectedSlot.value !== null && items.value[selectedSlot.value]) {
|
||
emit('use-item', items.value[selectedSlot.value])
|
||
}
|
||
}
|
||
|
||
const dropItem = () => {
|
||
if (selectedSlot.value !== null && items.value[selectedSlot.value]) {
|
||
emit('drop-item', items.value[selectedSlot.value])
|
||
items.value[selectedSlot.value] = null
|
||
}
|
||
}
|
||
|
||
const sortItems = () => {
|
||
// 簡單排序邏輯
|
||
const nonNullItems = items.value.filter(item => item !== null)
|
||
items.value = [...nonNullItems, ...Array(24 - nonNullItems.length).fill(null)]
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.inventory-modal {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.inventory-container {
|
||
width: 90%;
|
||
max-width: 800px;
|
||
max-height: 90vh;
|
||
background: var(--color-panel);
|
||
border: 3px solid var(--color-accent);
|
||
border-radius: 16px;
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.8);
|
||
overflow-y: auto;
|
||
}
|
||
|
||
/* 手機直向 */
|
||
@media (max-width: 480px) {
|
||
.inventory-container {
|
||
width: 95%;
|
||
max-width: none;
|
||
max-height: 95vh;
|
||
border-radius: 12px;
|
||
}
|
||
}
|
||
|
||
/* 平板 */
|
||
@media (min-width: 481px) and (max-width: 768px) {
|
||
.inventory-container {
|
||
width: 92%;
|
||
}
|
||
}
|
||
|
||
.inventory-header {
|
||
background: linear-gradient(135deg, #a29bfe 0%, #6c5ce7 100%);
|
||
padding: 16px 20px;
|
||
border-radius: 12px 12px 0 0;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
border-bottom: 3px solid #5a4a3a;
|
||
}
|
||
|
||
.inventory-header h3 {
|
||
margin: 0;
|
||
color: white;
|
||
font-size: 18px;
|
||
text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.close-btn {
|
||
width: 32px;
|
||
height: 32px;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border: 2px solid white;
|
||
border-radius: 8px;
|
||
color: white;
|
||
font-size: 18px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.close-btn:hover {
|
||
background: rgba(255, 255, 255, 0.3);
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.inventory-content {
|
||
display: grid;
|
||
grid-template-columns: 200px 1fr;
|
||
gap: 16px;
|
||
padding: 16px;
|
||
min-height: 500px;
|
||
}
|
||
|
||
/* 手機直向:垂直堆疊 */
|
||
@media (max-width: 480px) {
|
||
.inventory-content {
|
||
grid-template-columns: 1fr;
|
||
gap: 12px;
|
||
padding: 12px;
|
||
min-height: auto;
|
||
}
|
||
|
||
.equipment-panel {
|
||
max-height: 200px;
|
||
}
|
||
}
|
||
|
||
/* 手機橫向 */
|
||
@media (max-width: 768px) and (orientation: landscape) {
|
||
.inventory-content {
|
||
min-height: 300px;
|
||
}
|
||
}
|
||
|
||
/* 左側裝備欄 */
|
||
.equipment-panel {
|
||
background: rgba(255, 255, 255, 0.6);
|
||
border: 3px solid #5a4a3a;
|
||
border-radius: 12px;
|
||
padding: 12px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.character-display {
|
||
width: 100%;
|
||
height: 150px;
|
||
background: linear-gradient(135deg, #ffeaa7 0%, #fdcb6e 100%);
|
||
border: 3px solid #5a4a3a;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.pet-preview {
|
||
width: 80px;
|
||
height: 80px;
|
||
background: #ff6b9d;
|
||
border: 3px solid #5a4a3a;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.equipment-slots {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.equip-slot {
|
||
height: 48px;
|
||
background: rgba(255, 255, 255, 0.8);
|
||
border: 3px solid #5a4a3a;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.equip-slot:hover {
|
||
background: #ffeaa7;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.quick-stats {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
padding-top: 8px;
|
||
border-top: 2px solid #5a4a3a;
|
||
}
|
||
|
||
.stat-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 4px 8px;
|
||
background: rgba(255, 255, 255, 0.5);
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 右側物品欄 */
|
||
.items-panel {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.category-tabs {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
|
||
.tab {
|
||
padding: 8px 16px;
|
||
background: rgba(255, 255, 255, 0.6);
|
||
border: 3px solid #5a4a3a;
|
||
border-radius: 8px;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.tab:hover {
|
||
background: rgba(255, 255, 255, 0.8);
|
||
}
|
||
|
||
.tab.active {
|
||
background: linear-gradient(135deg, #74b9ff 0%, #a29bfe 100%);
|
||
color: white;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.item-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(6, 1fr);
|
||
gap: 8px;
|
||
background: var(--color-panel-light);
|
||
border: 2px solid var(--color-border);
|
||
border-radius: 12px;
|
||
padding: 12px;
|
||
flex: 1;
|
||
}
|
||
|
||
/* 手機直向:4列 */
|
||
@media (max-width: 480px) {
|
||
.item-grid {
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 6px;
|
||
padding: 8px;
|
||
}
|
||
}
|
||
|
||
/* 平板:5列 */
|
||
@media (min-width: 481px) and (max-width: 768px) {
|
||
.item-grid {
|
||
grid-template-columns: repeat(5, 1fr);
|
||
}
|
||
}
|
||
|
||
.item-slot {
|
||
aspect-ratio: 1;
|
||
background: rgba(255, 255, 255, 0.8);
|
||
border: 3px solid #5a4a3a;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 28px;
|
||
cursor: pointer;
|
||
position: relative;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.item-slot:hover {
|
||
background: #ffeaa7;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.item-slot.filled {
|
||
background: rgba(255, 255, 255, 1);
|
||
}
|
||
|
||
.item-count {
|
||
position: absolute;
|
||
bottom: 2px;
|
||
right: 4px;
|
||
font-size: 10px;
|
||
font-weight: bold;
|
||
background: rgba(0, 0, 0, 0.6);
|
||
color: white;
|
||
padding: 2px 4px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.action-bar {
|
||
display: flex;
|
||
gap: 8px;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.action-btn {
|
||
padding: 12px 24px;
|
||
background: linear-gradient(135deg, #55efc4 0%, #00b894 100%);
|
||
border: 3px solid #5a4a3a;
|
||
border-radius: 8px;
|
||
color: white;
|
||
font-weight: bold;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.action-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
.action-btn:active {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.action-btn.sort {
|
||
background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%);
|
||
}
|
||
</style>
|