6 changed files with 1576 additions and 99 deletions
-
1103package-lock.json
-
6package.json
-
BINsrc/assets/展开.png
-
4src/main.js
-
559src/views/choujiang/lottery/PrizePanel.vue
-
3vite.config.js
1103
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