Browse Source

庞-更改

songtongtong/feature-20250717104937-众筹
pangluotong 4 weeks ago
parent
commit
350c553243
  1. 1101
      package-lock.json
  2. 6
      package.json
  3. BIN
      src/assets/展开.png
  4. 4
      src/main.js
  5. 559
      src/views/choujiang/lottery/PrizePanel.vue
  6. 3
      vite.config.js

1101
package-lock.json
File diff suppressed because it is too large
View File

6
package.json

@ -11,11 +11,15 @@
"dependencies": { "dependencies": {
"@vitejs/plugin-vue": "^4.6.2", "@vitejs/plugin-vue": "^4.6.2",
"axios": "^1.10.0", "axios": "^1.10.0",
"element-plus": "^2.10.3",
"element-plus": "^2.10.4",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.4.1", "pinia-plugin-persistedstate": "^4.4.1",
"vite": "^4.5.3", "vite": "^4.5.3",
"vue": "^3.5.17", "vue": "^3.5.17",
"vue-router": "^4.5.1" "vue-router": "^4.5.1"
},
"devDependencies": {
"unplugin-auto-import": "^19.3.0",
"unplugin-vue-components": "^28.8.0"
} }
} }

BIN
src/assets/展开.png

After

Width: 40  |  Height: 34  |  Size: 2.9 KiB

4
src/main.js

@ -3,8 +3,12 @@ import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
// import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App); const app = createApp(App);
const pinia = createPinia().use(piniaPluginPersistedstate); const pinia = createPinia().use(piniaPluginPersistedstate);
app.use(pinia) app.use(pinia)
.use(router) .use(router)
.use(ElementPlus)
.mount('#app'); .mount('#app');

559
src/views/choujiang/lottery/PrizePanel.vue

@ -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;}
// showOnefalse
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;
// showOnefalsetrue
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) {
// showOnefalse
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 {
// showOnefalsetrue
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>

3
vite.config.js

@ -3,5 +3,6 @@ import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [vue()],
plugins: [vue(),
],
}) })
Loading…
Cancel
Save