pet_data/app/components/pixel/ShopOverlay.vue

146 lines
6.4 KiB
Vue
Raw Normal View History

2025-11-26 06:53:44 +00:00
<template>
<div class="flex flex-col h-full gap-2 relative">
<!-- Top Bar: Gold & Title -->
<div class="flex items-center justify-between bg-[#1b1026] p-2 border border-[#f6b26b]">
<div class="flex items-center gap-2 text-[#9fd75b] font-bold tracking-widest">
<ShoppingBag :size="20" />
<span>商店 (SHOP)</span>
</div>
<div class="flex items-center gap-2 bg-[#2b193f] px-3 py-1 rounded border border-[#4a3b5e]">
<span class="text-[#99e550] text-sm uppercase">您的金幣:</span>
<span class="text-[#f6b26b] font-mono text-lg font-bold">{{ playerGold }}</span>
<Coins :size="16" class="text-[#f6b26b]" />
<div class="border-l border-[#4a3b5e] pl-2 ml-1">
<RefreshCw :size="14" class="text-[#8f80a0] cursor-pointer hover:text-white hover:rotate-180 transition-transform" />
</div>
</div>
</div>
<!-- Main Action Tabs -->
<div class="flex gap-4 justify-center my-2">
<PixelButton
:variant="mode === 'BUY' ? 'primary' : 'secondary'"
@click="mode = 'BUY'"
class="w-32"
:class="{ 'bg-[#3d9e8f] border-[#2c7a6f]': mode === 'BUY' }"
:style="mode === 'BUY' ? { backgroundColor: '#3d9e8f', borderColor: '#2c7a6f' } : {}"
>
購買 (BUY)
</PixelButton>
<PixelButton
:variant="mode === 'SELL' ? 'primary' : 'secondary'"
@click="mode = 'SELL'"
class="w-32"
:style="mode === 'SELL' ? { backgroundColor: '#d95763', borderColor: '#ac3232', color: 'white' } : {}"
>
賣出 (SELL)
</PixelButton>
</div>
<!-- Category Filters -->
<div class="flex gap-1 overflow-x-auto pb-2 custom-scrollbar">
<button
v-for="cat in CATEGORY_FILTERS"
:key="cat.id"
@click="filter = cat.id"
class="flex items-center gap-1 px-3 py-1 text-xs border whitespace-nowrap transition-colors"
:class="filter === cat.id ? 'bg-[#9fd75b] text-[#1b1026] border-[#f6b26b]' : 'bg-[#150c1f] text-[#8f80a0] border-[#4a3b5e] hover:bg-[#2b193f]'"
>
<component :is="cat.icon" :size="12" />
{{ cat.label }}
</button>
</div>
<!-- Items List -->
<div class="flex-grow bg-[#0f0816] border border-[#2b193f] p-2 overflow-y-auto custom-scrollbar">
<div v-if="displayedItems.length === 0" class="h-full flex items-center justify-center text-[#4a3b5e] flex-col gap-2">
<Search :size="32" />
<span>NO ITEMS FOUND</span>
</div>
<div v-else class="flex flex-col gap-2">
<div
v-for="item in displayedItems"
:key="item.id"
class="flex items-center justify-between p-2 bg-[#1b1026] border border-[#2b193f] hover:border-[#4a3b5e] transition-colors group"
>
<!-- Item Icon & Info -->
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-[#231533] border border-[#4a3b5e] flex items-center justify-center relative">
<!-- Simple Icon Logic -->
<Cookie v-if="item.category === ItemCategory.Food" color="#f6b26b" />
<Pill v-else-if="item.category === ItemCategory.Medicine" color="#d95763" />
<Sword v-else-if="item.category === ItemCategory.Equipment" color="#2ce8f4" />
<Gamepad2 v-else-if="item.category === ItemCategory.Toy" color="#99e550" />
<Gem v-else-if="item.category === ItemCategory.Accessory" color="#d584fb" />
<span v-if="item.quantity && item.quantity > 1" class="absolute bottom-0 right-0 bg-black text-white text-[9px] px-1">{{ item.quantity }}</span>
</div>
<div class="flex flex-col">
<span class="font-bold text-sm tracking-wide" :class="item.rarity === Rarity.Legendary ? 'text-[#ffa500]' : 'text-[#9fd75b]'">
{{ item.name }}
</span>
<div class="flex items-center gap-2">
<span class="text-[10px] text-[#f6b26b] font-mono">
$ {{ mode === 'SELL' ? Math.floor(item.price / 2) : item.price }}
</span>
<span v-if="item.quantity" class="text-[10px] text-[#8f80a0]">x {{ item.quantity }}</span>
</div>
</div>
</div>
<!-- Action Button -->
<button
@click="mode === 'BUY' ? $emit('buy', item) : $emit('sell', item)"
class="px-4 py-1 border-2 text-xs font-bold tracking-widest active:translate-y-0.5"
:class="mode === 'BUY'
? 'bg-[#1b1026] border-[#9fd75b] text-[#9fd75b] hover:bg-[#9fd75b] hover:text-[#1b1026]'
: 'bg-[#1b1026] border-[#d95763] text-[#d95763] hover:bg-[#d95763] hover:text-[#1b1026]'"
>
{{ mode === 'BUY' ? '購買' : '賣出' }}
</button>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ShoppingBag, Coins, Filter, Cookie, Pill, Sword, Gamepad2, Gem, Search, RefreshCw } from 'lucide-vue-next';
import PixelButton from './PixelButton.vue';
import { ItemCategory, Rarity } from '~/types/pixel';
import type { Item } from '~/types/pixel';
interface Props {
playerGold: number;
inventory: Item[];
shopItems: Item[];
}
const props = defineProps<Props>();
defineEmits(['buy', 'sell']);
const mode = ref<'BUY' | 'SELL'>('BUY');
const filter = ref<string>('ALL');
const CATEGORY_FILTERS = [
{ id: 'ALL', label: '全部 (ALL)', icon: Search },
{ id: ItemCategory.Food, label: '食物', icon: Cookie },
{ id: ItemCategory.Medicine, label: '藥品', icon: Pill },
{ id: ItemCategory.Equipment, label: '裝備', icon: Sword },
{ id: ItemCategory.Toy, label: '玩具', icon: Gamepad2 },
{ id: ItemCategory.Accessory, label: '飾品', icon: Gem },
];
const displayedItems = computed(() => {
const source = mode.value === 'BUY' ? props.shopItems : props.inventory;
return source.filter(item => {
if (mode.value === 'SELL' && item.isEquipped) return false; // Cannot sell equipped items
if (filter.value === 'ALL') return true;
return item.category === filter.value;
});
});
</script>