84 lines
3.8 KiB
Vue
84 lines
3.8 KiB
Vue
<template>
|
|
<view class="flip-root">
|
|
<view v-if="controls" class="flip-actions">
|
|
<button class="flip-btn" @tap="onDraw(1)">单次抽选</button>
|
|
<button class="flip-btn" @tap="onDraw(10)">十次抽选</button>
|
|
</view>
|
|
<view class="flip-grid">
|
|
<view v-for="(cell, i) in cells" :key="i" class="flip-card" :class="{ flipped: cell.flipped }">
|
|
<view class="flip-inner">
|
|
<view class="flip-front">
|
|
<view class="front-placeholder"></view>
|
|
</view>
|
|
<view class="flip-back" @tap="onPreview(cell)">
|
|
<image v-if="cell.image" class="flip-image" :src="cell.image" mode="widthFix" />
|
|
<text class="flip-title">{{ cell.title || '' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view v-if="controls" class="flip-toolbar">
|
|
<button class="flip-reset" @tap="reset">重置</button>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, defineExpose } from 'vue'
|
|
|
|
const props = defineProps({ rewards: { type: Array, default: () => [] }, controls: { type: Boolean, default: true } })
|
|
const emit = defineEmits(['draw'])
|
|
|
|
const total = 16
|
|
const cells = ref(Array(total).fill(0).map(() => ({ flipped: false, title: '', image: '' })))
|
|
|
|
function onDraw(count) { emit('draw', count) }
|
|
|
|
function revealResults(list) {
|
|
const arr = Array.isArray(list) ? list : list ? [list] : []
|
|
const toFill = Math.min(arr.length, total)
|
|
const indices = Array(total).fill(0).map((_, i) => i)
|
|
for (let i = indices.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); const tmp = indices[i]; indices[i] = indices[j]; indices[j] = tmp }
|
|
const chosen = indices.slice(0, toFill)
|
|
const res = arr.slice(0, toFill)
|
|
for (let i = res.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); const t = res[i]; res[i] = res[j]; res[j] = t }
|
|
chosen.forEach((pos, i) => {
|
|
const it = res[i] || {}
|
|
const title = String(it.title || it.name || '')
|
|
const image = String(it.image || it.img || it.pic || '')
|
|
cells.value[pos] = { flipped: false, title, image }
|
|
const delay = 100 * i + Math.floor(Math.random() * 120)
|
|
setTimeout(() => { cells.value[pos].flipped = true }, delay)
|
|
})
|
|
}
|
|
|
|
function reset() {
|
|
cells.value = Array(total).fill(0).map(() => ({ flipped: false, title: '', image: '' }))
|
|
}
|
|
|
|
function onPreview(cell) {
|
|
const img = String(cell && cell.image || '')
|
|
if (img) uni.previewImage({ urls: [img], current: img })
|
|
}
|
|
|
|
defineExpose({ revealResults, reset })
|
|
</script>
|
|
|
|
<style scoped>
|
|
.flip-root { display: flex; flex-direction: column; gap: 16rpx; padding: 16rpx }
|
|
.flip-actions { display: flex; gap: 12rpx }
|
|
.flip-btn { flex: 1; background: #007AFF; color: #fff; border-radius: 8rpx }
|
|
.flip-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12rpx }
|
|
.flip-card { perspective: 1000px }
|
|
.flip-inner { position: relative; width: 100%; height: 200rpx; transform-style: preserve-3d; transition: transform 0.5s }
|
|
.flip-card.flipped .flip-inner { transform: rotateY(180deg) }
|
|
.flip-front, .flip-back { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; border-radius: 12rpx; overflow: hidden }
|
|
.flip-front { background: #e2e8f0; display: flex; align-items: center; justify-content: center }
|
|
.front-placeholder { width: 80%; height: 80%; border-radius: 12rpx; background: linear-gradient(135deg, #f8fafc, #e2e8f0) }
|
|
.flip-back { background: #fff; transform: rotateY(180deg); display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 12rpx }
|
|
.flip-image { width: 80%; border-radius: 8rpx; margin-bottom: 8rpx; background: #f5f5f5 }
|
|
.flip-title { font-size: 26rpx; color: #222; text-align: center; max-width: 90%; word-break: break-all }
|
|
.flip-toolbar { display: flex; justify-content: flex-end }
|
|
.flip-reset { background: #ffd166; color: #6b4b1f; border-radius: 999rpx }
|
|
</style>
|