6 changed files with 1576 additions and 99 deletions
-
1101package-lock.json
-
6package.json
-
BINsrc/assets/展开.png
-
4src/main.js
-
559src/views/choujiang/lottery/PrizePanel.vue
-
3vite.config.js
1101
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
After Width: 40 | Height: 34 | Size: 2.9 KiB |
@ -0,0 +1,559 @@ |
|||||
|
<template> |
||||
|
<div v-if="showOne"> |
||||
|
<div class="prize-panel-root"> |
||||
|
<div class="prize-panel-list" v-if="prizes && prizes.length"> |
||||
|
<div |
||||
|
class="prize-panel-item" |
||||
|
v-for="(prize, idx) in prizes" |
||||
|
:key="prize.type || idx" |
||||
|
:class="{ 'revealed-highlight': idx === lastRevealedIdx }" |
||||
|
@click="handleReveal(idx)" |
||||
|
style="cursor: pointer" |
||||
|
> |
||||
|
<div v-if="isRevealed(idx)" class="prize-card"> |
||||
|
<div class="prize-img-wrap"> |
||||
|
<img class="prize-img" :src="prize.img" :alt="prize.title" /> |
||||
|
</div> |
||||
|
<div class="prize-info"> |
||||
|
<div class="prize-row prize-row-top"> |
||||
|
<span class="prize-level">{{ prize.title }}</span> |
||||
|
<span class="prize-name">{{ prize.text }}</span> |
||||
|
</div> |
||||
|
<div class="prize-row prize-row-bottom"> |
||||
|
<div class="progress-bar-bg"> |
||||
|
<div |
||||
|
class="progress-bar-fill" |
||||
|
:style="{ width: getProgressPercent(prize) + '%' }" |
||||
|
></div> |
||||
|
<span class="progress-bar-text"> |
||||
|
{{ prize.count - getLeftCount(prize) }}/{{ prize.count }} |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div v-else class="prize-card prize-card-mask"> |
||||
|
<img |
||||
|
src="../../../assets/daijiemi.png" |
||||
|
alt="待揭秘" |
||||
|
class="prize-mask-img" |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div></div> |
||||
|
<div></div> |
||||
|
<div></div> |
||||
|
<div></div> |
||||
|
<div class="prize-panel-footer"> |
||||
|
<div class="arrow-up" @click="openWinnerList"></div> |
||||
|
<button |
||||
|
ref="winnerBtnRef" |
||||
|
class="winner-btn" |
||||
|
@click="toggleWinnerList" |
||||
|
> |
||||
|
获奖名单 |
||||
|
</button> |
||||
|
<!-- <div |
||||
|
v-if="showWinnerList" |
||||
|
class="winner-modal-mask" |
||||
|
@click="closeWinnerList" |
||||
|
> |
||||
|
<div |
||||
|
class="winner-modal" |
||||
|
:style="{ |
||||
|
position: 'absolute', |
||||
|
left: modalLeft + 'px', |
||||
|
top: modalTop + 'px', |
||||
|
}" |
||||
|
@click.stop |
||||
|
> |
||||
|
<div class="winner-modal-title">Homily ID</div> |
||||
|
<ul class="winner-list"> |
||||
|
<li v-for="(user, idx) in fakeWinners" :key="idx"> |
||||
|
<span>{{ user.id }}</span> |
||||
|
<span>{{ user.prize }}</span> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> --> |
||||
|
<!-- </div> --> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div v-else> |
||||
|
<div class="prize-panel-root"> |
||||
|
<div class="prize-panel-list" v-if="prizes && prizes.length && lastRevealedIdx >= 0"> |
||||
|
<div |
||||
|
class="prize-panel-item" |
||||
|
:key="prizes[lastRevealedIdx].type || lastRevealedIdx" |
||||
|
:class="{ 'revealed-highlight': true }" |
||||
|
style="cursor: pointer" |
||||
|
> |
||||
|
<div class="prize-card"> |
||||
|
<div class="prize-img-wrap"> |
||||
|
<img class="prize-img" :src="prizes[lastRevealedIdx].img" :alt="prizes[lastRevealedIdx].title" /> |
||||
|
</div> |
||||
|
<div class="prize-info"> |
||||
|
<div class="prize-row prize-row-top"> |
||||
|
<span class="prize-level">{{ prizes[lastRevealedIdx].title }}</span> |
||||
|
<span class="prize-name">{{ prizes[lastRevealedIdx].text }}</span> |
||||
|
</div> |
||||
|
<div class="prize-row prize-row-bottom"> |
||||
|
<div class="progress-bar-bg"> |
||||
|
<div |
||||
|
class="progress-bar-fill" |
||||
|
:style="{ width: getProgressPercent(prizes[lastRevealedIdx]) + '%' }" |
||||
|
></div> |
||||
|
<span class="progress-bar-text"> |
||||
|
{{ prizes[lastRevealedIdx].count - getLeftCount(prizes[lastRevealedIdx]) }}/{{ prizes[lastRevealedIdx].count }} |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="prize-panel-footer"> |
||||
|
<div class="arrow-down" @click="toggleWinnerList"></div> |
||||
|
<!-- <div class="arrow-up " @click="openWinnerList"> </div> |
||||
|
<button |
||||
|
|
||||
|
ref="winnerBtnRef" |
||||
|
class="winner-btn" |
||||
|
@click="toggleWinnerList" |
||||
|
> |
||||
|
获奖名单 |
||||
|
</button> --> |
||||
|
<div |
||||
|
v-if="showWinnerList" |
||||
|
class="winner-modal-mask" |
||||
|
@click="closeWinnerList" |
||||
|
> |
||||
|
<div |
||||
|
class="winner-modal" |
||||
|
:style="{ |
||||
|
position: 'absolute', |
||||
|
left: modalLeft + 'px', |
||||
|
top: modalTop + 'px', |
||||
|
}" |
||||
|
@click.stop |
||||
|
> |
||||
|
<div class="winner-modal-title"><span>Homily ID</span><span>奖项</span></div> |
||||
|
<div style="background:linear-gradient(to left, rgb(232 76 10), rgb(195 6 6), rgb(240 90 9));height:1px;"></div> |
||||
|
<ul class="winner-list"> |
||||
|
<li v-for="(user, idx) in fakeWinners" :key="idx" style="display: flex; justify-content: space-between; align-items: center;" > |
||||
|
<!-- <span>{{ user.id }}</span> - <span>{{ user.name }}</span> - --> |
||||
|
<span >{{ user.id }}</span> |
||||
|
<span>{{ user.prize }}</span> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, computed, nextTick } from "vue"; |
||||
|
const props = defineProps({ |
||||
|
prizes: Array, |
||||
|
}); |
||||
|
// 新增:控制已揭秘奖品数量 |
||||
|
const revealedCount = ref(0); |
||||
|
// 新增:记录最新揭秘的奖品索引 |
||||
|
const lastRevealedIdx = ref(-1); |
||||
|
|
||||
|
const showOne = ref(true); |
||||
|
|
||||
|
|
||||
|
// 计算哪些奖品已揭秘 |
||||
|
const isRevealed = (idx) => |
||||
|
idx >= (props.prizes?.length || 0) - revealedCount.value; |
||||
|
// 允许点击的卡片index |
||||
|
const nextRevealIdx = computed( |
||||
|
() => (props.prizes?.length || 0) - revealedCount.value - 1 |
||||
|
); |
||||
|
// 卡片点击事件 |
||||
|
function handleReveal(idx) { |
||||
|
if (idx === nextRevealIdx.value) { |
||||
|
revealedCount.value++; |
||||
|
lastRevealedIdx.value = idx; // 记录最新揭秘的索引 |
||||
|
} |
||||
|
} |
||||
|
// 计算未抽取数量 |
||||
|
function getLeftCount(prize) { |
||||
|
// 这里假设奖品有 type 字段,且 dataManager.state.basicData.luckyUsers 可用 |
||||
|
// 由于本组件无 luckyUsers 数据,建议父组件传入或全局可访问 |
||||
|
// 这里用 window.dataManager 兼容演示 |
||||
|
let luckyUsers = |
||||
|
(window.dataManager && window.dataManager.state.basicData.luckyUsers) || {}; |
||||
|
let got = luckyUsers[prize.type]?.length || 0; |
||||
|
return prize.count - got; |
||||
|
} |
||||
|
|
||||
|
// 新增部分 |
||||
|
const showWinnerList = ref(false); |
||||
|
const fakeWinners = ref([ |
||||
|
{ id: "90044065", name: "张三", prize: "六等奖" }, |
||||
|
{ id: "90044066", name: "李四", prize: "六等奖" }, |
||||
|
{ id: "90044067", name: "王五", prize: "六等奖" }, |
||||
|
{ id: "90044068", name: "赵六", prize: "六等奖" }, |
||||
|
{ id: "90044069", name: "小明", prize: "六等奖" }, |
||||
|
]); |
||||
|
function openWinnerList() { |
||||
|
// showWinnerList.value = true; |
||||
|
if (!showWinnerList.value) { |
||||
|
if (revealedCount.value === 0) { |
||||
|
alert('请先揭晓奖品,并抽奖!');} |
||||
|
} |
||||
|
if(revealedCount.value > 0) |
||||
|
{ showWinnerList.value = true;} |
||||
|
// 当存在最新揭秘的奖品时,点击获奖名单将showOne设置为false |
||||
|
if (lastRevealedIdx.value >= 0) { |
||||
|
showOne.value = false; |
||||
|
} |
||||
|
// 设置弹窗位置 |
||||
|
// nextTick(() => { |
||||
|
// const btn = winnerBtnRef.value; |
||||
|
// if (btn) { |
||||
|
// const rect = btn.getBoundingClientRect(); |
||||
|
// modalLeft.value = rect.left - 23; |
||||
|
// modalTop.value = rect.bottom + 18; // 4px间距 |
||||
|
// } |
||||
|
// }); |
||||
|
} |
||||
|
function closeWinnerList() { |
||||
|
showWinnerList.value = false; |
||||
|
// 当关闭获奖名单且showOne为false时,将其切换回true |
||||
|
if (!showOne.value) { |
||||
|
showOne.value = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const winnerBtnRef = ref(null); |
||||
|
const modalLeft = ref(0); |
||||
|
const modalTop = ref(0); |
||||
|
|
||||
|
function toggleWinnerList() { |
||||
|
showWinnerList.value = !showWinnerList.value; |
||||
|
console.log('toggleWinnerList - showWinnerList:', showWinnerList.value, 'showOne:', showOne.value, 'lastRevealedIdx:', lastRevealedIdx.value); |
||||
|
if (!showWinnerList.value) { |
||||
|
if (revealedCount.value === 0) { |
||||
|
alert('请先揭晓奖品,并抽奖!');} |
||||
|
} |
||||
|
|
||||
|
if (showWinnerList.value) { |
||||
|
// 当存在最新揭秘的奖品时,点击获奖名单将showOne设置为false |
||||
|
|
||||
|
if (lastRevealedIdx.value > 0) { |
||||
|
showOne.value = false; |
||||
|
console.log('设置 showOne 为 false'); |
||||
|
|
||||
|
} |
||||
|
// 设置弹窗位置 |
||||
|
// nextTick(() => { |
||||
|
// const btn = winnerBtnRef.value; |
||||
|
// if (btn) { |
||||
|
// const rect = btn.getBoundingClientRect(); |
||||
|
// modalLeft.value = rect.left - 23; |
||||
|
// modalTop.value = rect.bottom + 18; // 4px间距 |
||||
|
// } |
||||
|
// }); |
||||
|
} else { |
||||
|
// 当关闭获奖名单且showOne为false时,将其切换回true |
||||
|
if (!showOne.value) { |
||||
|
showOne.value = true; |
||||
|
|
||||
|
console.log('设置 showOne 为 true'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function getProgressPercent(prize) { |
||||
|
const total = prize.count || 1; |
||||
|
const left = getLeftCount(prize); |
||||
|
const got = total - left; |
||||
|
return Math.round((got / total) * 100); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.prize-panel-list { |
||||
|
position: absolute; |
||||
|
top: 20px; |
||||
|
left: 20px; |
||||
|
background: none; |
||||
|
z-index: 10; |
||||
|
min-width: 320px; |
||||
|
max-width: 342px; |
||||
|
text-align: left; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 18px; |
||||
|
} |
||||
|
.prize-panel-item { |
||||
|
background: #ffd283; |
||||
|
border-radius: 6px 6px 6px 6px; |
||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
min-width: 320px; |
||||
|
} |
||||
|
.prize-card { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
width: 100%; |
||||
|
padding: 10px 18px; |
||||
|
} |
||||
|
.prize-img-wrap { |
||||
|
width: 64px; |
||||
|
height: 64px; |
||||
|
border-radius: 50%; |
||||
|
background: #fff; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
overflow: hidden; |
||||
|
margin-right: 18px; |
||||
|
border: 2px solid #fff3e0; |
||||
|
} |
||||
|
.prize-img { |
||||
|
width: 60px; |
||||
|
height: 60px; |
||||
|
object-fit: contain; |
||||
|
} |
||||
|
.prize-info { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.prize-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
background: #ffffff; |
||||
|
border-radius: 93px 93px 93px 93px; |
||||
|
} |
||||
|
.prize-row-top { |
||||
|
margin-bottom: 8px; |
||||
|
border: 1px solid #ea2b0a; |
||||
|
} |
||||
|
.prize-level { |
||||
|
background: linear-gradient(90deg, #ff9800 0%, #ff5722 100%); |
||||
|
color: #fff; |
||||
|
border-radius: 15.71px 15.71px 15.71px 15.71px; |
||||
|
padding: 2px 18px; |
||||
|
font-size: 18px; |
||||
|
font-weight: bold; |
||||
|
margin-right: 12px; |
||||
|
} |
||||
|
.prize-name { |
||||
|
font-size: 18px; |
||||
|
color: #d84315; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
/* .prize-row-bottom { |
||||
|
background: linear-gradient(90deg, #ff9800 0%, #ff5722 100%); |
||||
|
background: #8a3500; |
||||
|
border-radius: 16px; |
||||
|
color: #fff; |
||||
|
font-size: 20px; |
||||
|
font-weight: bold; |
||||
|
padding: 2px 0 2px 0; |
||||
|
justify-content: center; |
||||
|
min-width: 80px; |
||||
|
} */ |
||||
|
.custom-arrow-icon { |
||||
|
font-size: 24px; /* 图标大小 */ |
||||
|
color: #d84315; /* 图标颜色,使用项目主题橙色 */ |
||||
|
margin: 5px; /* 外边距 */ |
||||
|
cursor: pointer; /* 鼠标悬停样式 */ |
||||
|
transition: transform 0.3s ease; /* 过渡动画 */ |
||||
|
} |
||||
|
|
||||
|
.custom-arrow-icon:hover { |
||||
|
transform: scale(1.1); /* 悬停放大效果 */ |
||||
|
} |
||||
|
.prize-count { |
||||
|
font-size: 20px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
.prize-divider { |
||||
|
margin: 0 4px; |
||||
|
font-size: 20px; |
||||
|
} |
||||
|
.prize-total { |
||||
|
font-size: 20px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
.prize-panel-footer { |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
bottom: -26px; |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
z-index: 20; |
||||
|
/* 移除 move-up 相关 */ |
||||
|
} |
||||
|
.arrow-up { |
||||
|
position: relative; |
||||
|
width: 36px; |
||||
|
height: 24px; |
||||
|
margin-bottom: 4px; |
||||
|
cursor: pointer; |
||||
|
background-image: url('../../../assets/展开.png'); |
||||
|
background-size: cover; |
||||
|
background-position: center; |
||||
|
} |
||||
|
.arrow-down { |
||||
|
position: fixed; |
||||
|
top: 120px; |
||||
|
left: 165px; |
||||
|
width: 36px; |
||||
|
height: 38px; |
||||
|
margin-bottom: 4px; |
||||
|
cursor: pointer; |
||||
|
background-image: url('../../../assets/展开.png'); |
||||
|
background-size: cover; |
||||
|
background-position: center; |
||||
|
transform: rotate(180deg); |
||||
|
} |
||||
|
.winner-btn { |
||||
|
background: rgba(255, 210, 131, 0.8); |
||||
|
color: #fff; |
||||
|
border: #fff; |
||||
|
border-radius: 8px; |
||||
|
padding: 15px 79px; |
||||
|
font-size: 20px; |
||||
|
font-weight: bold; |
||||
|
cursor: pointer; |
||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
||||
|
} |
||||
|
.winner-modal-mask { |
||||
|
position: fixed; |
||||
|
top: 155px; |
||||
|
left:10px; |
||||
|
width: 100vw; |
||||
|
height: 100vh; |
||||
|
background: rgba(0, 0, 0, 0.01); |
||||
|
z-index: 1000; |
||||
|
display: flex; |
||||
|
align-items: flex-start; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.winner-modal { |
||||
|
background: rgba(255, 210, 131, 0.8); |
||||
|
border-radius: 12px; |
||||
|
padding-top: 12px; |
||||
|
padding-left: 25px; |
||||
|
padding-right: 25px; |
||||
|
min-width: 280px; |
||||
|
max-width: 90vw; |
||||
|
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12); |
||||
|
position: relative; |
||||
|
} |
||||
|
.winner-modal-title { |
||||
|
font-size: 22px; |
||||
|
font-weight: bold; |
||||
|
color: #e64f39; |
||||
|
margin-bottom: 5px; |
||||
|
text-align: center; |
||||
|
display: flex; |
||||
|
justify-content: space-between; /* 左右对齐 */ |
||||
|
align-items: center; /* 垂直居中对齐 */ |
||||
|
/* 可添加padding或margin调整整体间距 */ |
||||
|
padding: 5px 0; |
||||
|
} |
||||
|
.winner-modal-close { |
||||
|
position: absolute; |
||||
|
right: 18px; |
||||
|
top: 12px; |
||||
|
font-size: 26px; |
||||
|
color: #888; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.winner-list { |
||||
|
max-height: 260px; |
||||
|
/* background: rgba(255, 210, 131, 0.8);/ */ |
||||
|
overflow-y: auto; |
||||
|
padding: 0; |
||||
|
margin: 0; |
||||
|
list-style: none; |
||||
|
} |
||||
|
.winner-list li { |
||||
|
padding: 8px 0; |
||||
|
/* border-bottom: 1px solid #f2f2f2; */ |
||||
|
font-size: 17px; |
||||
|
color: #d84315; |
||||
|
display: flex; |
||||
|
gap: 12px; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
text-align: center; |
||||
|
} |
||||
|
.progress-bar-bg { |
||||
|
position: relative; |
||||
|
width: 220px; |
||||
|
height: 28px; |
||||
|
background: #e9620e; |
||||
|
border-radius: 16px; |
||||
|
overflow: hidden; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0 auto; |
||||
|
border: #e13726; |
||||
|
} |
||||
|
.progress-bar-fill { |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
height: 100%; |
||||
|
/* background: linear-gradient(90deg, #ff9800 0%, #8a3500 100%); */ |
||||
|
background: #8a3500; |
||||
|
border-radius: 16px; |
||||
|
transition: width 0.4s; |
||||
|
z-index: 1; |
||||
|
} |
||||
|
.progress-bar-text { |
||||
|
position: relative; |
||||
|
width: 100%; |
||||
|
text-align: center; |
||||
|
color: #ffffff; |
||||
|
font-size: 18px; |
||||
|
font-weight: bold; |
||||
|
z-index: 2; |
||||
|
letter-spacing: 1px; |
||||
|
} |
||||
|
.prize-card-mask { |
||||
|
position: relative; |
||||
|
width: 342px; |
||||
|
height: 88px; |
||||
|
display: flex; |
||||
|
/* align-items: center; |
||||
|
justify-content: center; */ |
||||
|
padding: 0; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
.prize-mask-img { |
||||
|
object-fit: cover; |
||||
|
position: absolute; |
||||
|
width: 100%; |
||||
|
height: 98%; |
||||
|
object-fit: cover; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
border-radius: 8px 8px 8px 8px; |
||||
|
} |
||||
|
.prize-panel-item.revealed-highlight { |
||||
|
border: 3px solid #ff9800; |
||||
|
box-shadow: 0 0 16px 4px #ff9800aa; |
||||
|
transform: scale(1.05); |
||||
|
z-index: 2; |
||||
|
transition: all 0.3s; |
||||
|
} |
||||
|
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue