Files
nl-game/src/components/GameCard.vue
李琦 cd1ab90013 初始化
需要注意的是饥荒不够完善:动作、手持工具、细节交互、系统逻辑
2026-08-15 07:24:34 +08:00

143 lines
3.9 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.
<script setup>
// 游戏卡片:大厅与商城通用(图标、名称、分类、价格/拥有标识、热度)
import GameIcon from './GameIcon.vue'
defineProps({
game: { type: Object, required: true }, // 游戏对象(含 owned 字段)
showBuy: { type: Boolean, default: false }, // 商城模式显示购买按钮
})
defineEmits(['play', 'buy', 'addCart'])
</script>
<template>
<div class="game-card card">
<div class="icon-area">
<GameIcon class="game-icon" :code="game.code" :icon="game.icon" :size="48" />
<span v-if="game.price > 0 && !game.owned && game.weekly_free" class="weekly-tag"> 本周周免</span>
<span v-else-if="game.price > 0 && !game.owned" class="price-tag">💰{{ game.price }}</span>
<span v-else-if="game.price > 0 && game.owned" class="owned-tag">已拥有</span>
<span v-else class="free-tag">免费</span>
</div>
<div class="info">
<div class="name-row">
<span class="name">{{ game.name }}</span>
<span class="tag">{{ game.category }}</span>
</div>
<p class="desc text-dim">{{ game.description }}</p>
<!-- 商城模式无论购买还是开始游戏按钮都独占一行避免与热度挤在一起换行错乱 -->
<div class="foot" :class="{ stacked: showBuy }">
<span class="plays text-dim">🔥 {{ game.play_count }} 次游玩</span>
<div class="actions">
<template v-if="showBuy && game.price > 0 && !game.owned">
<button class="btn btn-ghost btn-sm" @click.stop="$emit('addCart', game)">加购物车</button>
<button class="btn btn-sm" @click.stop="$emit('buy', game)">立即购买</button>
</template>
<button v-else class="btn btn-sm" :class="{ 'btn-accent': game.engine === 'online' && game.playable }" @click.stop="$emit('play', game)">
{{ !game.playable && game.price > 0 ? '去购买' : game.engine === 'online' ? '去对战' : '开始游戏' }}
</button>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.game-card {
overflow: hidden;
cursor: default;
}
.icon-area {
position: relative;
height: 96px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, color-mix(in srgb, var(--primary) 16%, transparent), color-mix(in srgb, var(--accent) 12%, transparent));
border-bottom: 1px solid var(--border);
}
.game-icon {
font-size: 48px;
filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.35));
}
.price-tag,
.owned-tag,
.free-tag,
.weekly-tag {
position: absolute;
top: 8px;
right: 8px;
font-size: 11px;
padding: 2px 8px;
border-radius: 999px;
font-weight: 700;
}
.price-tag {
background: linear-gradient(135deg, var(--primary), var(--primary-2));
color: #fff;
}
.weekly-tag {
background: linear-gradient(135deg, #f7b733, #fc4a1a);
color: #fff;
}
.owned-tag {
background: color-mix(in srgb, var(--accent) 22%, transparent);
color: var(--accent);
border: 1px solid var(--accent);
}
.free-tag {
background: color-mix(in srgb, #2f9e6e 25%, transparent);
color: #5fd6a2;
border: 1px solid #2f9e6e;
}
.info {
padding: 12px 14px 14px;
}
.name-row {
display: flex;
align-items: center;
gap: 8px;
}
.name {
font-weight: 700;
font-size: 15px;
}
.desc {
font-size: 12px;
margin: 6px 0 10px;
height: 34px;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
line-height: 17px;
}
.foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
/* 购买模式:热度一行、两个按钮另起一行平分宽度 */
.foot.stacked {
flex-direction: column;
align-items: stretch;
}
.foot.stacked .actions {
width: 100%;
}
.foot.stacked .actions .btn {
flex: 1;
}
.plays {
font-size: 11px;
white-space: nowrap;
}
.actions {
display: flex;
gap: 6px;
}
.actions .btn {
white-space: nowrap;
}
</style>