feat(game): 新增酒桌小游戏功能并优化图标显示

- 添加了完整的酒桌小游戏页面,包含数字炸弹、猜拳大战、骰子大话和命运转盘四个游戏
- 实现了成就徽章组件的图标路径判断逻辑,支持图片和文字图标的动态切换
- 更新了饮酒分类常量中的白酒图标为酒杯样式,并修改成就图标为SVG路径
- 重构了动态卡片组件中的分类表情符号显示方式,支持SVG图标渲染
- 添加了游戏页面的路由配置和完整的交互功能实现
This commit is contained in:
cg
2026-07-20 23:25:55 +08:00
parent fabbb167cf
commit 49ef6eedb8
7 changed files with 1361 additions and 21 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
// 酒类分类
export const DRINK_CATEGORIES = [
{ id: 'baijiu', name: '白酒', emoji: '⚱️', icon: '/static/icons/baijiu.svg', defaultDegree: 52 },
{ id: 'baijiu', name: '白酒', emoji: '🥃', icon: '/static/icons/baijiu.svg', defaultDegree: 52 },
{ id: 'beer', name: '啤酒', emoji: '🍺', defaultDegree: 5 },
{ id: 'wine', name: '红酒', emoji: '🍷', defaultDegree: 13 },
{ id: 'whisky', name: '洋酒', emoji: '🥂', defaultDegree: 40 },
@@ -176,7 +176,7 @@ export const ACHIEVEMENTS = [
id: 'baijiu_master',
name: '白酒达人',
desc: '白酒累计打卡20次',
icon: '⚱️',
icon: '/static/icons/baijiu.svg',
condition: { type: 'category_records', category: 'baijiu', value: 20 }
},
{
+11 -2
View File
@@ -5,7 +5,8 @@
@click="$emit('click')"
>
<view class="badge-icon-wrap">
<text class="badge-icon">{{ achievement.icon }}</text>
<image v-if="isIconPath(achievement.icon)" :src="achievement.icon" class="badge-icon-img" mode="aspectFit"></image>
<text v-else class="badge-icon">{{ achievement.icon }}</text>
<view v-if="!achievement.unlocked" class="badge-lock">
<text class="badge-lock-icon">🔒</text>
</view>
@@ -15,11 +16,14 @@
</template>
<script>
import { isIconPath } from '../common/utils'
export default {
props: {
achievement: { type: Object, required: true }
},
emits: ['click']
emits: ['click'],
methods: { isIconPath }
}
</script>
@@ -74,6 +78,11 @@ export default {
font-size: 48rpx;
}
.badge-icon-img {
width: 52rpx;
height: 52rpx;
}
.badge-lock {
position: absolute;
bottom: -4rpx;
+12 -5
View File
@@ -20,7 +20,8 @@
<!-- 饮酒标签 -->
<view class="feed-drinks" v-if="feed.drinks && feed.drinks.length">
<view class="feed-drink-tag" v-for="(d, i) in feed.drinks" :key="i">
<text class="feed-drink-emoji">{{ categoryEmoji(d.category) }}</text>
<image v-if="isIconPath(getCatIcon(d.category))" :src="getCatIcon(d.category)" class="feed-drink-icon-img" mode="aspectFit"></image>
<text v-else class="feed-drink-emoji">{{ getCatIcon(d.category) }}</text>
<text class="feed-drink-name">{{ d.name }} {{ d.amount }}</text>
</view>
</view>
@@ -60,16 +61,16 @@
</template>
<script>
import { getCatIcon, isIconPath } from '../common/utils'
export default {
name: 'FeedCard',
props: {
feed: { type: Object, default: () => ({}) }
},
methods: {
categoryEmoji(cat) {
const map = { baijiu: '🏺', beer: '🍺', wine: '🍷', whisky: '🥃', huangjiu: '🫖', sake: '🍶', fruit: '🍹', cocktail: '🍸' }
return map[cat] || '🍻'
},
getCatIcon,
isIconPath,
feelingLabel(f) {
const map = { sober: '😌 清醒', buzzed: '😊 微醺', tipsy: '😄 小醉', drunk: '🥴 喝多了' }
return map[f] || '🍻 喝了点'
@@ -163,6 +164,12 @@ export default {
font-size: $fs-sm;
}
.feed-drink-icon-img {
width: 32rpx;
height: 32rpx;
flex-shrink: 0;
}
.feed-drink-name {
font-size: $fs-xs;
color: $text-secondary;
+7
View File
@@ -90,6 +90,13 @@
"navigationStyle": "custom",
"navigationBarTitleText": ""
}
},
{
"path": "pages/games/games",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": ""
}
}
],
"globalStyle": {
File diff suppressed because it is too large Load Diff
+4
View File
@@ -142,6 +142,10 @@ export default {
uni.showToast({ title: '请先同意用户协议和隐私政策', icon: 'none', duration: 2000 })
return
}
if (!this.nickname.trim()) {
uni.showToast({ title: '请先填写昵称', icon: 'none', duration: 2000 })
return
}
this.loginLoading = true
try {
// 1. 获取微信登录 code
+43 -12
View File
@@ -78,7 +78,8 @@
<view class="persona-glow"></view>
<view class="persona-content">
<view class="persona-header">
<text class="persona-emoji">{{ personaEmoji }}</text>
<image v-if="isIconPath(personaEmoji)" :src="personaEmoji" class="persona-icon-img" mode="aspectFit"></image>
<text v-else class="persona-emoji">{{ personaEmoji }}</text>
<view class="persona-text">
<text class="persona-title">{{ personaTitle }}</text>
<text class="persona-desc">{{ personaDesc }}</text>
@@ -118,17 +119,17 @@
<!-- 操作入口 -->
<view class="actions-section">
<!-- 友圈 -->
<view class="action-card" @click="goCircle">
<view class="action-icon-wrap action-icon-circle">
<text class="action-icon">🍻</text>
<!-- 桌小游戏 -->
<view class="action-card" @click="goGames">
<view class="action-icon-wrap action-icon-games">
<text class="action-icon">🎲</text>
</view>
<view class="action-text">
<view class="action-title-row">
<text class="action-title">友圈</text>
<view class="action-new-tag">NEW</view>
<text class="action-title">桌小游戏</text>
<view class="action-hot-tag">HOT</view>
</view>
<text class="action-desc">看看酒友们今晚都在喝什么</text>
<text class="action-desc">数字炸弹·猜拳·骰子·转盘今晚谁喝</text>
</view>
<text class="action-arrow"></text>
</view>
@@ -148,7 +149,8 @@
<view v-if="showDetail" class="detail-overlay" @click="showDetail = false">
<view class="detail-card" @click.stop>
<view class="detail-icon-ring" :class="detailAchievement.unlocked ? 'ring-unlocked' : 'ring-locked'">
<text class="detail-emoji">{{ detailAchievement.icon }}</text>
<image v-if="isIconPath(detailAchievement.icon)" :src="detailAchievement.icon" class="detail-icon-img" mode="aspectFit"></image>
<text v-else class="detail-emoji">{{ detailAchievement.icon }}</text>
</view>
<text class="detail-name">{{ detailAchievement.name }}</text>
<text class="detail-desc">{{ detailAchievement.desc }}</text>
@@ -164,7 +166,7 @@
<script>
import AchievementBadge from '../../components/AchievementBadge.vue'
import client, { clearAuth } from '../../common/api'
import { checkAchievements } from '../../common/utils'
import { checkAchievements, getCatIcon, isIconPath } from '../../common/utils'
import { ACHIEVEMENTS, DRINK_CATEGORIES } from '../../common/constants'
import themeMixin from '../../common/theme-mixin'
@@ -217,8 +219,8 @@ export default {
},
personaEmoji() {
const fav = this.stats.favCategory
const map = { baijiu: '⚱️', beer: '🍺', wine: '🍷', whisky: '🥂', huangjiu: '🫖', sake: '🍶', fruit: '🍹', cocktail: '🍸' }
return map[fav] || '🍻'
if (fav) return getCatIcon(fav)
return '🍻'
},
personaTitle() {
const fav = this.stats.favCategory
@@ -261,6 +263,7 @@ export default {
}
},
methods: {
isIconPath,
// 构建分享名片的 query 参数(携带公开档案数据)
buildShareQuery() {
const parts = [`nick=${encodeURIComponent(this.user.nickname || '酒友')}`]
@@ -351,6 +354,9 @@ export default {
goCircle() {
uni.switchTab({ url: '/pages/circle/circle' })
},
goGames() {
uni.navigateTo({ url: '/pages/games/games' })
},
goHome() {
uni.switchTab({ url: '/pages/index/index' })
},
@@ -541,6 +547,7 @@ export default {
background: rgba(232,168,56,0.10);
border: 1rpx solid rgba(232,168,56,0.20);
border-radius: $radius-full;
margin-left: 20rpx;
}
.hero-level-text {
@@ -720,6 +727,12 @@ export default {
font-size: 56rpx;
}
.persona-icon-img {
width: 64rpx;
height: 64rpx;
flex-shrink: 0;
}
.persona-text {
display: flex;
flex-direction: column;
@@ -838,6 +851,10 @@ export default {
background: rgba(139,133,184,0.12);
}
.action-icon-games {
background: rgba(232,168,56,0.12);
}
.action-icon-timeline {
background: rgba(94,198,160,0.12);
}
@@ -872,6 +889,15 @@ export default {
border-radius: $radius-full;
}
.action-hot-tag {
font-size: 18rpx;
font-weight: $fw-bold;
color: #fff;
background: linear-gradient(135deg, $coral, #e04848);
padding: 2rpx 10rpx;
border-radius: $radius-full;
}
.action-desc {
display: block;
font-size: $fs-xs;
@@ -955,6 +981,11 @@ export default {
font-size: 64rpx;
}
.detail-icon-img {
width: 72rpx;
height: 72rpx;
}
.detail-name {
display: block;
font-size: $fs-xl;