feat: done

This commit is contained in:
ex_zhangwenlei@exiot.cmcc
2024-01-13 00:27:44 +08:00
parent eaba58f91f
commit 6e8340c300
28 changed files with 397 additions and 147 deletions

View File

@@ -39,12 +39,16 @@ const exportData = () => {
delete data[i].id
delete data[i].createTime
delete data[i].updateTime
delete data[i].prizeId
// 修改字段名称
if (data[i].isWin) {
data[i].isWin = '是'
} else {
data[i].isWin = '否'
}
// 格式化数组为
data[i].prizeTime=data[i].prizeTime.join(',')
data[i].prizeName=data[i].prizeName.join(',')
}
let dataString = JSON.stringify(data)
dataString = dataString

View File

@@ -56,14 +56,6 @@ const tableColumnsList = [
handleMoveNotPerson(row)
}
},
// {
// label: '删除',
// type: 'btn-error',
// onClick: (row: any) => {
// console.log('删除:', row)
// }
// },
]
},
]

View File

@@ -86,7 +86,7 @@ watch(() => prizeList.value, (val:IPrizeConfig[]) => {
</div>
<ul>
<li v-for="item in prizeList" :key="item.id" class="flex gap-10"
:class="currentPrize.id == item.id ? 'border-1 border-solid rounded-xl' : null">
:class="currentPrize.id == item.id ? 'border-1 border-dotted rounded-xl' : null">
<label class="max-w-xs mb-10 form-control">
<!-- 向上向下 -->
<div class="flex flex-col items-center gap-2 pt-5">

View File

@@ -64,7 +64,10 @@ const skip = (path: string) => {
</div>
<footer class="p-10 rounded footer footer-center bg-base-200 text-base-content">
<nav class="grid grid-flow-col gap-4">
<a class="link link-hover cursor-pointer text-inherit" target="_blank" href="https://24years.top">行有不得反求诸己</a>
<a class="cursor-pointer link link-hover text-inherit" target="_blank" href="https://24years.top">行有不得反求诸己</a>
</nav>
<nav>
<a class="cursor-pointer link link-hover text-inherit" target="_blank" href="https://24years.top">破山中贼易破心中贼难</a>
</nav>
<nav>
<div class="grid grid-flow-col gap-4">

View File

@@ -8,24 +8,43 @@ import defaultPrizeImage from '@/assets/images/龙.png'
const prizeConfig = useStore().prizeConfig
const globalConfig = useStore().globalConfig
const system= useStore().system
const { getPrizeConfig: localPrizeList, getCurrentPrize: currentPrize } = storeToRefs(prizeConfig)
const {getIsShowPrizeList:isShowPrizeList}= storeToRefs(globalConfig)
const {getIsMobile:isMobile} = storeToRefs(system)
const system = useStore().system
const { getPrizeConfig: localPrizeList, getCurrentPrize: currentPrize, getTemporaryPrize: temporaryPrize } = storeToRefs(prizeConfig)
const { getIsShowPrizeList: isShowPrizeList, getImageList: localImageList } = storeToRefs(globalConfig)
const { getIsMobile: isMobile } = storeToRefs(system)
const prizeListRef = ref()
const prizeListContainerRef = ref()
const temporaryPrizeRef = ref()
// 获取prizeListRef高度
const getPrizeListHeight = () => {
let height=200;
if(prizeListRef.value){
height = (prizeListRef.value as HTMLElement).offsetHeight}
let height = 200;
if (prizeListRef.value) {
height = (prizeListRef.value as HTMLElement).offsetHeight
}
return height
}
const prizeShow = ref(structuredClone(isShowPrizeList.value))
const addTemporaryPrize = () => {
console.log('addTemporaryPrize')
temporaryPrizeRef.value.showModal()
}
const deleteTemporaryPrize = () => {
temporaryPrize.value.isShow = false
prizeConfig.setTemporaryPrize(temporaryPrize.value)
}
const submitTemporaryPrize = () => {
if (!temporaryPrize.value.name || !temporaryPrize.value.count) {
alert('请填写完整信息')
return
}
temporaryPrize.value.isShow = true
temporaryPrize.value.id=new Date().getTime().toString()
prizeConfig.setCurrentPrize(temporaryPrize.value)
prizeConfig.setCurrentPrize(temporaryPrize.value)
}
onMounted(() => {
prizeListContainerRef.value.style.height = getPrizeListHeight() + 'px'
@@ -34,23 +53,129 @@ onMounted(() => {
<template>
<div class="flex items-center">
<dialog id="my_modal_1" ref="temporaryPrizeRef" class="border-none modal">
<div class="modal-box">
<h3 class="text-lg font-bold">增加临时抽奖</h3>
<div class="flex flex-col gap-3">
<label class="flex w-full max-w-xs">
<div class="label">
<span class="label-text">名称:</span>
</div>
<input type="text" v-model="temporaryPrize.name" placeholder="名称"
class="max-w-xs input-sm input input-bordered" />
</label>
<label class="flex w-full max-w-xs">
<div class="label">
<span class="label-text">是否全员参加</span>
</div>
<input type="checkbox" :checked="temporaryPrize.isAll"
@change="temporaryPrize.isAll = !temporaryPrize.isAll"
class="mt-2 border-solid checkbox checkbox-secondary border-1" />
</label>
<label class="flex w-full max-w-xs">
<div class="label">
<span class="label-text">获奖人数</span>
</div>
<input type="number" v-model="temporaryPrize.count" placeholder="获奖人数"
class="max-w-xs input-sm input input-bordered" />
</label>
<label class="flex w-full max-w-xs">
<div class="label">
<span class="label-text">已获奖人数</span>
</div>
<input disabled type="number" v-model="temporaryPrize.isUsedCount" placeholder="获奖人数"
class="max-w-xs input-sm input input-bordered" />
</label>
<label class="flex w-full max-w-xs">
<div class="label">
<span class="label-text">已抽取</span>
</div>
<input type="checkbox" :checked="temporaryPrize.isUsed"
@change="temporaryPrize.isUsed ? (() => { temporaryPrize.isUsed = false; temporaryPrize.isUsedCount = 0 })() : (() => { temporaryPrize.isUsed = true; temporaryPrize.isUsedCount = temporaryPrize.count })()"
class="mt-2 border-solid checkbox checkbox-secondary border-1" />
</label>
<label class="flex w-full max-w-xs">
<div class="label">
<span class="label-text">图片</span>
</div>
<select class="flex-1 w-12 select select-warning select-sm" v-model="temporaryPrize.picture">
<option v-if="temporaryPrize.picture.id" :value="{ id: '', name: '', url: '' }"><span></span>
</option>
<option disabled selected>选择一张图片</option>
<option class="w-auto" v-for="picItem in localImageList" :key="picItem.id" :value="picItem">{{
picItem.name }}
</option>
</select>
</label>
</div>
<div class="modal-action">
<form method="dialog" class="flex gap-3">
<button class="btn btn-sm" @click="submitTemporaryPrize">确定</button>
<button class="btn btn-sm">取消</button>
</form>
</div>
</div>
</dialog>
<div ref="prizeListContainerRef">
<div class="h-20 w-72" :class="temporaryPrize.isShow ? 'current-prize' : ''" v-if="temporaryPrize.isShow">
<div class="relative flex flex-row items-center justify-between w-full h-full shadow-xl card bg-base-100">
<div v-if="temporaryPrize.isUsed"
class="absolute z-50 w-full h-full bg-gray-800/70 item-mask rounded-xl"></div>
<figure class="w-10 h-10 rounded-xl">
<ImageSync v-if="temporaryPrize.picture.url" :imgItem="temporaryPrize.picture"></ImageSync>
<img v-else :src="defaultPrizeImage" alt="Prize" class="object-cover h-full rounded-xl" />
</figure>
<div class="items-center p-0 text-center card-body">
<div class="tooltip tooltip-left" :data-tip="temporaryPrize.name">
<h2 class="p-0 m-0 overflow-hidden w-28 card-title whitespace-nowrap text-ellipsis">{{
temporaryPrize.name }}</h2>
</div>
<p class="absolute z-40 p-0 m-0 text-gray-300/80 mt-9">{{ temporaryPrize.isUsedCount }}/{{
temporaryPrize.count }}</p>
<progress class="w-3/4 h-6 progress progress-primary" :value="temporaryPrize.isUsedCount"
:max="temporaryPrize.count"></progress>
<!-- <p class="p-0 m-0">{{ item.isUsedCount }}/{{ item.count }}</p> -->
</div>
<div class="flex flex-col gap-1 mr-2">
<div class="tooltip tooltip-left" data-tip="编辑">
<div class="cursor-pointer hover:text-blue-400" @click="addTemporaryPrize">
<svg-icon name="edit"></svg-icon>
</div>
</div>
<div class="tooltip tooltip-left" data-tip="删除">
<div class="cursor-pointer hover:text-blue-400" @click="deleteTemporaryPrize">
<svg-icon name="delete"></svg-icon>
</div>
</div>
</div>
</div>
</div>
<transition name="prize-list" :appear="true">
<div v-if="prizeShow&&!isMobile" class="flex items-center">
<div v-if="prizeShow && !isMobile && !temporaryPrize.isShow" class="flex items-center">
<ul class="flex flex-col gap-1 p-2 rounded-xl bg-slate-500/50" ref="prizeListRef">
<li v-for="item in localPrizeList" :key="item.id"
:class="currentPrize.id == item.id ? 'current-prize' : ''">
<div
class="relative flex flex-row items-center justify-between w-64 h-20 shadow-xl card bg-base-100" v-if="item.isShow">
<div v-if="item.isUsed" class="absolute z-50 w-full h-full bg-gray-800/70 item-mask rounded-xl"></div>
<div class="relative flex flex-row items-center justify-between w-64 h-20 shadow-xl card bg-base-100"
v-if="item.isShow">
<div v-if="item.isUsed"
class="absolute z-50 w-full h-full bg-gray-800/70 item-mask rounded-xl"></div>
<figure class="w-10 h-10 rounded-xl">
<ImageSync v-if="item.picture.url" :imgItem="item.picture"></ImageSync>
<img v-else :src="defaultPrizeImage" alt="Prize" class="object-cover h-full rounded-xl" />
<img v-else :src="defaultPrizeImage" alt="Prize"
class="object-cover h-full rounded-xl" />
</figure>
<div class="items-center p-0 text-center card-body">
<h2 class="p-0 m-0 card-title">{{ item.name }}</h2>
<p class="absolute z-40 p-0 m-0 text-gray-300/80 pt-9">{{ item.isUsedCount }}/{{ item.count }}</p>
<progress class="w-3/4 h-6 progress progress-primary" :value="item.isUsedCount" :max="item.count"></progress>
<div class="tooltip tooltip-left" :data-tip="item.name">
<h2
class="w-24 p-0 m-0 overflow-hidden text-center card-title whitespace-nowrap text-ellipsis">
{{ item.name }}</h2>
</div>
<p class="absolute z-40 p-0 m-0 text-gray-300/80 mt-9">{{ item.isUsedCount }}/{{
item.count }}</p>
<progress class="w-3/4 h-6 progress progress-primary" :value="item.isUsedCount"
:max="item.count"></progress>
<!-- <p class="p-0 m-0">{{ item.isUsedCount }}/{{ item.count }}</p> -->
</div>
</div>
@@ -71,7 +196,10 @@ onMounted(() => {
</div>
</div>
</div>
</transition>
</div>
<transition name="prize-operate" :appear="true">
@@ -86,6 +214,10 @@ onMounted(() => {
</template>
<style lang='scss' scoped>
.label {
width: 120px;
}
.prize-list-enter-active {
-webkit-animation: slide-right 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
animation: slide-right 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
@@ -102,12 +234,6 @@ onMounted(() => {
-webkit-animation: show-operate 0.6s;
}
// .prize-operate-leave-active {
// -webkit-animation-delay: 0.5s;
// -webkit-animation: slide-right 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
// animation-delay: 0.5s;
// animation: slide-right 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
// }
.current-prize {
position: relative;
display: block;
@@ -131,7 +257,6 @@ onMounted(() => {
// animation-play-state: paused;
translate: -5% 0%;
transition: translate 0.25s ease-out;
animation-play-state: running;
transition-duration: 0.75s;
translate: 0% 0%;

View File

@@ -29,7 +29,7 @@ const personConfig = useStore().personConfig
const globalConfig = useStore().globalConfig
const prizeConfig = useStore().prizeConfig
const { getAllPersonList: allPersonList, getNotPersonList: notPersonList } = storeToRefs(personConfig)
const { getAllPersonList: allPersonList, getNotPersonList: notPersonList,getNotThisPrizePersonList: notThisPrizePersonList } = storeToRefs(personConfig)
const { getCurrentPrize: currentPrize } = storeToRefs(prizeConfig)
const { getTopTitle: topTitle, getCardColor: cardColor,getPatterColor: patternColor, getPatternList: patternList, getTextColor: textColor, getLuckyColor: luckyColor, getCardSize: cardSize, getTextSize: textSize, getRowCount: rowCount } = storeToRefs(globalConfig)
const tableData = ref<any[]>([])
@@ -57,6 +57,7 @@ const targets = {
const luckyTargets = ref<any[]>([])
const luckyCardList = ref<number[]>([])
let luckyCount = ref(10)
const personPool=ref<IPersonConfig[]>([])
const intervalTimer = ref<any>(null)
// const currentPrizeValue = ref(JSON.parse(JSON.stringify(currentPrize.value)))
@@ -381,8 +382,9 @@ const startLottery = () => {
return
}
personPool.value=currentPrize.value.isAll ? notThisPrizePersonList.value : notPersonList.value
// 验证抽奖人数是否还够
if (notPersonList.value.length < currentPrize.value.count) {
if (personPool.value.length < currentPrize.value.count-currentPrize.value.isUsedCount) {
toast.open({
message: '抽奖人数不够',
type: 'warning',
@@ -406,11 +408,11 @@ const stopLottery = async () => {
// TWEEN.removeAll();
rollBall(0, 1)
// 抽奖池是否为全体人员
const personPool = currentPrize.value.isAll ? allPersonList.value : notPersonList.value
// personPool=currentPrize.value.isAll ? notThisPrizePersonList.value : notPersonList.value
// 每次最多抽十个
const leftover = currentPrize.value.count - currentPrize.value.isUsedCount
leftover < luckyCount.value ? luckyCount.value = leftover : luckyCount
if (personPool.length < leftover) {
if (personPool.value.length < leftover) {
toast.open({
message: '抽奖人数不够',
type: 'warning',
@@ -421,23 +423,10 @@ const stopLottery = async () => {
return;
}
for (let i = 0; i < luckyCount.value; i++) {
if (personPool.length > 0) {
const randomIndex = Math.round(Math.random() * (personPool.length - 1))
luckyTargets.value.push(personPool[randomIndex])
// console.log(
// 'leftover:', leftover, '\n',
// 'luckyCount', luckyCount, '\n',
// 'currentPrize.value.isUsedCount', currentPrize.value.isUsedCount, '\n',
// 'randomIndex', randomIndex, '\n',
// 'personPool.length - 1', personPool.length - 1, '\n',
// 'personPool[randomIndex]', personPool[randomIndex], '\n',
// )
personPool.splice(randomIndex, 1)
// console.log(
// 'objects.value[personPool[randomIndex].id]', LuckyCard
// )
if (personPool.value.length > 0) {
const randomIndex = Math.round(Math.random() * (personPool.value.length - 1))
luckyTargets.value.push(personPool.value[randomIndex])
personPool.value.splice(randomIndex, 1)
}
}
const windowSize = { width: window.innerWidth, height: window.innerHeight }
@@ -475,7 +464,7 @@ const stopLottery = async () => {
}
// 继续
const continueLottery = async () => {
// currentPrize.value.isUsedCount += luckyCount.value
currentPrize.value.isUsedCount += luckyCount.value
if (currentPrize.value.isUsedCount >= currentPrize.value.count) {
currentPrize.value.isUsed = true
}
@@ -590,7 +579,7 @@ onUnmounted(() => {
<template>
<div class="absolute z-10 flex flex-col items-center justify-center -translate-x-1/2 left-1/2">
<h2 class="pt-12 m-0 mb-12 font-mono tracking-wide text-center leading-12"
<h2 class="pt-12 m-0 mb-12 font-mono tracking-wide text-center leading-12 header-title"
:style="{ fontSize: textSize * 1.5 + 'px', color: textColor }">{{ topTitle }}</h2>
<div class="flex gap-3">
<button v-if="tableData.length <= 0" class="cursor-pointer btn btn-outline btn-secondary btn-lg"
@@ -670,15 +659,10 @@ onUnmounted(() => {
margin: 0 auto;
font-size: 32px;
}
// .enter-enter-active{
// -webkit-animation: fade-in-fwd 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
// animation: fade-in-fwd 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
// animation-delay:0.5s ;
// }
// .enter-leave-active{
// -webkit-animation: swing-out-top-bck 0.45s cubic-bezier(0.600, -0.280, 0.735, 0.045) both;
// animation: swing-out-top-bck 0.45s cubic-bezier(0.600, -0.280, 0.735, 0.045) both;
// }
.header-title{
-webkit-animation: tracking-in-expand-fwd 0.8s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
animation: tracking-in-expand-fwd 0.8s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
}
.start {
// 居中
display: flex;
@@ -967,61 +951,38 @@ strong {
transform: scale(1);
}
}
@-webkit-keyframes fade-in-fwd {
@-webkit-keyframes tracking-in-expand-fwd {
0% {
-webkit-transform: translateZ(-80px);
transform: translateZ(-80px);
letter-spacing: -0.5em;
-webkit-transform: translateZ(-700px);
transform: translateZ(-700px);
opacity: 0;
}
40% {
opacity: 0.6;
}
100% {
-webkit-transform: translateZ(0);
transform: translateZ(0);
opacity: 1;
}
}
@keyframes fade-in-fwd {
@keyframes tracking-in-expand-fwd {
0% {
-webkit-transform: translateZ(-80px);
transform: translateZ(-80px);
letter-spacing: -0.5em;
-webkit-transform: translateZ(-700px);
transform: translateZ(-700px);
opacity: 0;
}
40% {
opacity: 0.6;
}
100% {
-webkit-transform: translateZ(0);
transform: translateZ(0);
opacity: 1;
}
}
@-webkit-keyframes swing-out-top-bck {
0% {
-webkit-transform: rotateX(0deg);
transform: rotateX(0deg);
-webkit-transform-origin: top;
transform-origin: top;
opacity: 1;
}
100% {
-webkit-transform: rotateX(-100deg);
transform: rotateX(-100deg);
-webkit-transform-origin: top;
transform-origin: top;
opacity: 0;
}
}
@keyframes swing-out-top-bck {
0% {
-webkit-transform: rotateX(0deg);
transform: rotateX(0deg);
-webkit-transform-origin: top;
transform-origin: top;
opacity: 1;
}
100% {
-webkit-transform: rotateX(-100deg);
transform: rotateX(-100deg);
-webkit-transform-origin: top;
transform-origin: top;
opacity: 0;
}
}
</style>