pet_data/components/pixel/InventoryModal.vue

469 lines
9.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>