feat: feat
This commit is contained in:
@@ -97,7 +97,9 @@ watch(() => formData.value.rowCount, () => {
|
||||
formErr.value.rowCount = err.issues[0].message
|
||||
})
|
||||
})
|
||||
|
||||
watch(topTitleValue,(val)=>{
|
||||
globalConfig.setTopTitle(val)
|
||||
}),
|
||||
|
||||
watch(themeValue, (val: any) => {
|
||||
const selectedThemeDetail = daisyuiThemeList.value[val]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang='ts'>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { readImage } from '@/utils/file'
|
||||
import { IImage } from '@/types/storeType'
|
||||
import { readFileData } from '@/utils/file'
|
||||
import localforage from 'localforage'
|
||||
import useStore from '@/store'
|
||||
import { storeToRefs } from 'pinia'
|
||||
@@ -14,14 +15,14 @@ const imgUploadToast = ref(0) //0是不显示,1是成功,2是失败,3是不
|
||||
const imageDbStore = localforage.createInstance({
|
||||
name: 'imgStore'
|
||||
})
|
||||
const handleFileChange = async (e: any) => {
|
||||
const isImage= /image*/.test(e.target.files[0].type)
|
||||
const handleFileChange = async (e: Event) => {
|
||||
const isImage= /image*/.test(((e.target as HTMLInputElement).files as FileList)[0].type)
|
||||
if (!isImage) {
|
||||
imgUploadToast.value = 3
|
||||
|
||||
return
|
||||
}
|
||||
let { dataUrl, fileName } = await readImage(e.target.files[0])
|
||||
let { dataUrl, fileName } = await readFileData(((e.target as HTMLInputElement).files as FileList)[0])
|
||||
imageDbStore.setItem(new Date().getTime().toString() + '+' + fileName, dataUrl)
|
||||
.then(() => {
|
||||
imgUploadToast.value = 1
|
||||
@@ -45,7 +46,7 @@ const getImageDbStore =async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const removeImage=(item:any)=>{
|
||||
const removeImage=(item:IImage)=>{
|
||||
if(item.url=='Storage'){
|
||||
imageDbStore.removeItem(item.id).then(() => {
|
||||
globalConfig.removeImage(item.id)
|
||||
@@ -91,8 +92,8 @@ watch(() => imgUploadToast.value, (val) => {
|
||||
<div class="flex items-center gap-8">
|
||||
<div class="avatar h-14">
|
||||
<div class="w-12 h-12 mask mask-squircle hover:w-14 hover:h-14">
|
||||
<img v-if="item.url!=='Storage'" :src="item.url" alt="Avatar Tailwind CSS Component" />
|
||||
<ImageSync v-else :imgItem="item"></ImageSync>
|
||||
<!-- <img v-if="item.url!=='Storage'" :src="item.url" alt="Avatar Tailwind CSS Component" /> -->
|
||||
<ImageSync :imgItem="item"></ImageSync>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-64">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script setup lang='ts'>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import {storeToRefs } from 'pinia'
|
||||
import { readMusic } from '@/utils/file'
|
||||
import { IMusic } from '@/types/storeType';
|
||||
import { readFileData } from '@/utils/file'
|
||||
import useStore from '@/store';
|
||||
|
||||
import localforage from 'localforage'
|
||||
@@ -15,11 +16,11 @@ const globalConfig = useStore().globalConfig
|
||||
const { getMusicList: localMusicList } = storeToRefs(globalConfig);
|
||||
const limitType = ref('audio/*')
|
||||
const localMusicListValue = ref(localMusicList)
|
||||
const play = async (item: any) => {
|
||||
const play = async (item: IMusic) => {
|
||||
globalConfig.setCurrentMusic(item,false)
|
||||
}
|
||||
|
||||
const deleteMusic = (item: any) => {
|
||||
const deleteMusic = (item: IMusic) => {
|
||||
globalConfig.removeMusic(item.id)
|
||||
audioDbStore.removeItem(item.name)
|
||||
// setTimeout(()=>{
|
||||
@@ -46,14 +47,14 @@ const getMusicDbStore = async () => {
|
||||
})
|
||||
}
|
||||
}
|
||||
const handleFileChange = async (e: any) => {
|
||||
const isAudio = /audio*/.test(e.target.files[0].type)
|
||||
const handleFileChange = async (e: Event) => {
|
||||
const isAudio = /audio*/.test(((e.target as HTMLInputElement).files as FileList)[0].type)
|
||||
if (!isAudio) {
|
||||
audioUploadToast.value = 3
|
||||
|
||||
return
|
||||
}
|
||||
let { dataUrl, fileName } = await readMusic(e.target.files[0])
|
||||
let { dataUrl, fileName } = await readFileData(((e.target as HTMLInputElement).files as FileList)[0])
|
||||
audioDbStore.setItem(new Date().getTime().toString() + '+' + fileName, dataUrl)
|
||||
.then(() => {
|
||||
audioUploadToast.value = 1
|
||||
|
||||
@@ -2,23 +2,26 @@
|
||||
<script setup lang='ts'>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import useStore from '@/store'
|
||||
import { IPersonConfig } from '@/types/storeType';
|
||||
import { storeToRefs } from 'pinia'
|
||||
import * as XLSX from 'xlsx'
|
||||
import { readFile } from '@/utils/file'
|
||||
import { readFileBinary } from '@/utils/file'
|
||||
import { filterData, addOtherInfo } from '@/utils'
|
||||
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
||||
|
||||
const personConfig = useStore().personConfig
|
||||
const globalConfig = useStore().globalConfig
|
||||
const { getAllPersonList: allPersonList,getAlreadyPersonList:alreadyPersonList } = storeToRefs(personConfig)
|
||||
const { getAllPersonList: allPersonList, getAlreadyPersonList: alreadyPersonList } = storeToRefs(personConfig)
|
||||
const { getRowCount: rowCount } = storeToRefs(globalConfig)
|
||||
const limitType = '.xlsx,.xls'
|
||||
const excelData = ref<any[]>([])
|
||||
// const personList = ref<any[]>([])
|
||||
|
||||
const resetDataDialog=ref()
|
||||
const delAllDataDialog=ref()
|
||||
|
||||
const handleFileChange = async (e: any) => {
|
||||
let dataBinary = await readFile(e.target.files[0])
|
||||
const handleFileChange = async (e: Event) => {
|
||||
let dataBinary = await readFileBinary(((e.target as HTMLInputElement).files as FileList)[0]!)
|
||||
let workBook = XLSX.read(dataBinary, { type: 'binary', cellDates: true })
|
||||
let workSheet = workBook.Sheets[workBook.SheetNames[0]]
|
||||
excelData.value = XLSX.utils.sheet_to_json(workSheet)
|
||||
@@ -27,12 +30,51 @@ const handleFileChange = async (e: any) => {
|
||||
personConfig.resetPerson()
|
||||
personConfig.addNotPersonList(allData)
|
||||
}
|
||||
const exportData = () => {
|
||||
let data = JSON.parse(JSON.stringify(allPersonList.value))
|
||||
// 排除一些字段
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
delete data[i].x
|
||||
delete data[i].y
|
||||
delete data[i].id
|
||||
delete data[i].createTime
|
||||
delete data[i].updateTime
|
||||
// 修改字段名称
|
||||
if (data[i].isWin) {
|
||||
data[i].isWin = '是'
|
||||
} else {
|
||||
data[i].isWin = '否'
|
||||
}
|
||||
}
|
||||
let dataString = JSON.stringify(data)
|
||||
dataString = dataString
|
||||
.replaceAll(/uid/g, '编号')
|
||||
.replaceAll(/isWin/g, '是否中奖')
|
||||
.replaceAll(/department/g, '部门')
|
||||
.replaceAll(/name/g, '姓名')
|
||||
.replaceAll(/identity/g, '身份')
|
||||
.replaceAll(/prizeName/g, '获奖')
|
||||
.replaceAll(/prizeTime/g, '获奖时间')
|
||||
|
||||
data = JSON.parse(dataString)
|
||||
|
||||
if (data.length > 0) {
|
||||
const dataBinary = XLSX.utils.json_to_sheet(data)
|
||||
const dataBinaryBinary = XLSX.utils.book_new()
|
||||
XLSX.utils.book_append_sheet(dataBinaryBinary, dataBinary, 'Sheet1')
|
||||
XLSX.writeFile(dataBinaryBinary, 'data.xlsx')
|
||||
}
|
||||
}
|
||||
|
||||
const resetData = () => {
|
||||
personConfig.resetAlreadyPerson()
|
||||
}
|
||||
|
||||
const deleteAll = () => {
|
||||
personConfig.deleteAllPerson()
|
||||
}
|
||||
|
||||
const delPersonItem = (row: any) => {
|
||||
const delPersonItem = (row: IPersonConfig) => {
|
||||
personConfig.deletePerson(row)
|
||||
}
|
||||
|
||||
@@ -56,7 +98,7 @@ const tableColumns = [
|
||||
{
|
||||
label: '是否已中奖',
|
||||
props: 'isWin',
|
||||
formatValue(row: any) {
|
||||
formatValue(row: IPersonConfig) {
|
||||
return row.isWin ? '是' : '否'
|
||||
}
|
||||
},
|
||||
@@ -73,7 +115,7 @@ const tableColumns = [
|
||||
{
|
||||
label: '删除',
|
||||
type: 'btn-error',
|
||||
onClick: (row: any) => {
|
||||
onClick: (row: IPersonConfig) => {
|
||||
delPersonItem(row)
|
||||
}
|
||||
},
|
||||
@@ -86,11 +128,38 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<dialog id="my_modal_1" ref="resetDataDialog" class="border-none modal">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold">提示!</h3>
|
||||
<p class="py-4">该操作会重置所有人员数据,是否继续?</p>
|
||||
<div class="modal-action">
|
||||
<form method="dialog" class="flex gap-3">
|
||||
<!-- if there is a button in form, it will close the modal -->
|
||||
<button class="btn" @click="resetDataDialog.close()">取消</button>
|
||||
<button class="btn" @click="resetData">确定</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
<dialog id="my_modal_1" ref="delAllDataDialog" class="border-none modal">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold">提示!</h3>
|
||||
<p class="py-4">该操作会删除所有人员数据,是否继续?</p>
|
||||
<div class="modal-action">
|
||||
<form method="dialog" class="flex gap-3">
|
||||
<!-- if there is a button in form, it will close the modal -->
|
||||
<button class="btn" @click="delAllDataDialog.close()">取消</button>
|
||||
<button class="btn" @click="deleteAll">确定</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
<div class="min-w-1000px">
|
||||
<div class="flex gap-3 justify-">
|
||||
<button class="btn btn-error btn-sm" @click="deleteAll">全部删除</button>
|
||||
<button class="btn btn-error btn-sm" @click="delAllDataDialog.showModal()">全部删除</button>
|
||||
<div class="tooltip tooltip-bottom" data-tip="下载文件后,请在excel中填写数据,并保存为xlsx格式">
|
||||
<a class="no-underline btn btn-secondary btn-sm" download="人口登记表.xlsx" target="_blank" href="/log-lottery/人口登记表.xlsx">下载模板</a>
|
||||
<a class="no-underline btn btn-secondary btn-sm" download="人口登记表.xlsx" target="_blank"
|
||||
href="/log-lottery/人口登记表.xlsx">下载模板</a>
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="explore">
|
||||
@@ -105,6 +174,8 @@ onMounted(() => {
|
||||
<!-- <button class="btn btn-primary btn-sm">上传excel</button> -->
|
||||
|
||||
</div>
|
||||
<button class="btn btn-error btn-sm" @click="resetDataDialog.showModal()">重置数据</button>
|
||||
<button class="btn btn-accent btn-sm" @click="exportData">导出结果</button>
|
||||
<div>
|
||||
<span>中奖人数:</span>
|
||||
<span>{{ alreadyPersonList.length }}</span>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<!-- eslint-disable vue/no-parsing-error -->
|
||||
<script setup lang='ts'>
|
||||
// import { ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import useStore from '@/store'
|
||||
import { IPersonConfig } from '@/types/storeType';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
||||
|
||||
const personConfig = useStore().personConfig
|
||||
|
||||
const { getAlreadyPersonList: alreadyPersonList } = storeToRefs(personConfig)
|
||||
const { getAlreadyPersonList: alreadyPersonList, getAlreadyPersonDetail: alreadyPersonDetail } = storeToRefs(personConfig)
|
||||
// const personList = ref<any[]>(
|
||||
// alreadyPersonList
|
||||
// )
|
||||
@@ -16,15 +17,17 @@ const { getAlreadyPersonList: alreadyPersonList } = storeToRefs(personConfig)
|
||||
// const deleteAll = () => {
|
||||
// personConfig.deleteAllPerson()
|
||||
// }
|
||||
const handleMoveNotPerson=(row:any)=>{
|
||||
|
||||
const isDetail = ref(false)
|
||||
const handleMoveNotPerson = (row: IPersonConfig) => {
|
||||
personConfig.moveAlreadyToNot(row)
|
||||
}
|
||||
|
||||
const tableColumns = [
|
||||
const tableColumnsList = [
|
||||
{
|
||||
label: '编号',
|
||||
props: 'uid',
|
||||
sort:true
|
||||
sort: true
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
@@ -39,9 +42,53 @@ const tableColumns = [
|
||||
props: 'identity',
|
||||
},
|
||||
{
|
||||
label:'奖品',
|
||||
props:'prizeName',
|
||||
sort:true
|
||||
label: '奖品',
|
||||
props: 'prizeName',
|
||||
sort: true
|
||||
},
|
||||
{
|
||||
label: '操作',
|
||||
actions: [
|
||||
{
|
||||
label: '移入未中奖名单',
|
||||
type: 'btn-info',
|
||||
onClick: (row: IPersonConfig) => {
|
||||
handleMoveNotPerson(row)
|
||||
}
|
||||
},
|
||||
// {
|
||||
// label: '删除',
|
||||
// type: 'btn-error',
|
||||
// onClick: (row: any) => {
|
||||
// console.log('删除:', row)
|
||||
// }
|
||||
// },
|
||||
|
||||
]
|
||||
},
|
||||
]
|
||||
const tableColumnsDetail = [
|
||||
{
|
||||
label: '编号',
|
||||
props: 'uid',
|
||||
sort: true
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
props: 'name',
|
||||
},
|
||||
{
|
||||
label: '部门',
|
||||
props: 'department',
|
||||
},
|
||||
{
|
||||
label: '身份',
|
||||
props: 'identity',
|
||||
},
|
||||
{
|
||||
label: '奖品',
|
||||
props: 'prizeName',
|
||||
sort: true
|
||||
},
|
||||
{
|
||||
label: '中奖时间',
|
||||
@@ -54,17 +101,10 @@ const tableColumns = [
|
||||
{
|
||||
label: '移入未中奖名单',
|
||||
type: 'btn-info',
|
||||
onClick: (row: any) => {
|
||||
onClick: (row: IPersonConfig) => {
|
||||
handleMoveNotPerson(row)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'btn-error',
|
||||
onClick: (row: any) => {
|
||||
console.log('删除:', row)
|
||||
}
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
@@ -73,14 +113,24 @@ const tableColumns = [
|
||||
|
||||
<template>
|
||||
<div class="overflow-y-auto">
|
||||
<div class="flex justify-start gap-3">
|
||||
<div class="flex items-center justify-start gap-10">
|
||||
<!-- <button class="btn btn-error btn-sm" @click="deleteAll">全部删除</button> -->
|
||||
<div>
|
||||
<span>中奖人数:</span>
|
||||
<span>{{ alreadyPersonList.length }}</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<span class="label-text">详细信息:</span>
|
||||
<input type="checkbox" class="border-solid toggle toggle-primary border-1" v-model="isDetail" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DaiysuiTable :tableColumns="tableColumns" :data="alreadyPersonList"></DaiysuiTable>
|
||||
<DaiysuiTable v-if="!isDetail" :tableColumns="tableColumnsList" :data="alreadyPersonList"></DaiysuiTable>
|
||||
|
||||
<DaiysuiTable v-if="isDetail" :tableColumns="tableColumnsDetail" :data="alreadyPersonDetail"></DaiysuiTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang='ts'>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import useStore from '@/store'
|
||||
import { IPrizeConfig } from '@/types/storeType'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import localforage from 'localforage'
|
||||
import { IPrizeConfig } from '@/types/prizeConfig';
|
||||
|
||||
const imageDbStore = localforage.createInstance({
|
||||
name: 'imgStore'
|
||||
@@ -51,7 +51,7 @@ const getImageDbStore = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const sort = (item: any, isUp: number) => {
|
||||
const sort = (item: IPrizeConfig, isUp: number) => {
|
||||
const itemIndex = prizeList.value.indexOf(item)
|
||||
if (isUp == 1) {
|
||||
prizeList.value.splice(itemIndex, 1)
|
||||
@@ -70,7 +70,7 @@ const delAll = async () => {
|
||||
onMounted(() => {
|
||||
getImageDbStore()
|
||||
})
|
||||
watch(() => prizeList, (val:any) => {
|
||||
watch(() => prizeList.value, (val:IPrizeConfig[]) => {
|
||||
prizeConfig.setPrizeConfig(val)
|
||||
}, { deep: true })
|
||||
</script>
|
||||
@@ -136,18 +136,21 @@ watch(() => prizeList, (val:any) => {
|
||||
<span class="label-text">图片</span>
|
||||
</div>
|
||||
<select class="w-full max-w-xs select select-warning select-sm" v-model="item.picture">
|
||||
|
||||
|
||||
<option v-if="item.picture.id" :value="{id:'',name:'',url:''}"><span>❌</span></option>
|
||||
<option disabled selected>选择一张图片</option>
|
||||
<option v-for="picItem in localImageList" :key="picItem.id" :value="picItem">{{ picItem.name }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="w-full max-w-xs mb-10 form-control">
|
||||
<!-- <label class="w-full max-w-xs mb-10 form-control">
|
||||
<div class="label">
|
||||
<span class="label-text">展示在主界面</span>
|
||||
</div>
|
||||
<input type="checkbox" :checked="item.isShow" @change="item.isShow = !item.isShow"
|
||||
class="mt-2 border-solid checkbox checkbox-secondary border-1" />
|
||||
</label>
|
||||
</label> -->
|
||||
<!-- <label class="w-full max-w-xs mb-10 form-control">
|
||||
<div class="label">
|
||||
<span class="label-text">抽取次数</span>
|
||||
|
||||
@@ -64,26 +64,17 @@ 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">About us</a>
|
||||
<a class="link link-hover">Contact</a>
|
||||
<a class="link link-hover">Jobs</a>
|
||||
<a class="link link-hover">Press kit</a>
|
||||
<a class="link link-hover">行有不得,反求诸己</a>
|
||||
</nav>
|
||||
<nav>
|
||||
<div class="grid grid-flow-col gap-4">
|
||||
<a href="https://github.com/LOG1997/log-lottery" target="_blank" class="cursor-pointer">
|
||||
<a href="https://github.com/LOG1997/log-lottery" target="_blank" class="cursor-pointer text-inherit">
|
||||
<svg-icon name="github"></svg-icon>
|
||||
</a>
|
||||
<a><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="fill-current">
|
||||
<path
|
||||
d="M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z">
|
||||
</path>
|
||||
</svg></a>
|
||||
<a><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="fill-current">
|
||||
<path
|
||||
d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z">
|
||||
</path>
|
||||
</svg></a>
|
||||
<a href="https://twitter.com/TaborSwift" target="_blank" class="cursor-pointer "><svg-icon name="twitter"></svg-icon></a>
|
||||
<a href="https://www.instagram.com/log.z1997/" target="_blank" class="cursor-pointer ">
|
||||
<svg-icon name="instagram"></svg-icon>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<aside>
|
||||
|
||||
@@ -3,6 +3,9 @@ import { ref, onMounted } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import useStore from '@/store'
|
||||
|
||||
import ImageSync from '@/components/ImageSync/index.vue'
|
||||
import defaultPrizeImage from '@/assets/images/龙.png'
|
||||
|
||||
const prizeConfig = useStore().prizeConfig
|
||||
const globalConfig = useStore().globalConfig
|
||||
const { getPrizeConfig: localPrizeList, getCurrentPrize: currentPrize } = storeToRefs(prizeConfig)
|
||||
@@ -39,7 +42,8 @@ onMounted(() => {
|
||||
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">
|
||||
<img :src="item.picture.url" alt="Shoes" class="object-cover h-full 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" />
|
||||
</figure>
|
||||
<div class="items-center p-0 text-center card-body">
|
||||
<h2 class="p-0 m-0 card-title">{{ item.name }}</h2>
|
||||
|
||||
@@ -5,12 +5,16 @@ import { useElementStyle, useElementPosition } from '@/hooks/useElement'
|
||||
import StarsBackground from '@/components/StarsBackground/index.vue'
|
||||
import confetti from 'canvas-confetti'
|
||||
import { filterData } from '@/utils'
|
||||
import * as THREE from 'three'
|
||||
import {
|
||||
CSS3DRenderer, CSS3DObject
|
||||
} from 'three/examples/jsm/renderers/CSS3DRenderer.js';
|
||||
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls.js';
|
||||
import TWEEN from 'three/examples/jsm/libs/tween.module.js';
|
||||
// import * as THREE from 'three'
|
||||
import {Scene,PerspectiveCamera,Object3D,Vector3} from 'three'
|
||||
// import {
|
||||
// CSS3DRenderer, CSS3DObject
|
||||
// } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
|
||||
import {CSS3DRenderer, CSS3DObject} from 'three-css3d'
|
||||
// import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls.js';
|
||||
import TrackballControls from 'three-trackballcontrols';
|
||||
// import TWEEN from 'three/examples/jsm/libs/tween.module.js';
|
||||
import * as TWEEN from '@tweenjs/tween.js'
|
||||
import useStore from '@/store'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useRouter } from 'vue-router'
|
||||
@@ -28,7 +32,6 @@ const { getCurrentPrize: currentPrize } = storeToRefs(prizeConfig)
|
||||
const { getTopTitle: topTitle, getCardColor: cardColor, getTextColor: textColor, getLuckyColor: luckyColor, getCardSize: cardSize, getTextSize: textSize, getRowCount: rowCount } = storeToRefs(globalConfig)
|
||||
const tableData = ref<any[]>([])
|
||||
// const tableData = ref<any[]>(JSON.parse(JSON.stringify(alreadyPersonList.value)).concat(JSON.parse(JSON.stringify(notPersonList.value))))
|
||||
|
||||
const currentStatus = ref(0) // 0为初始状态, 1为抽奖准备状态,2为抽奖中状态,3为抽奖结束状态
|
||||
const ballRotationY = ref(0)
|
||||
const containerRef = ref<HTMLElement>()
|
||||
@@ -80,8 +83,8 @@ const init = () => {
|
||||
const farPlane = 10000;
|
||||
const WebGLoutput = containerRef.value
|
||||
|
||||
scene.value = new THREE.Scene();
|
||||
camera.value = new THREE.PerspectiveCamera(felidView, aspect, nearPlane, farPlane);
|
||||
scene.value = new Scene();
|
||||
camera.value = new PerspectiveCamera(felidView, aspect, nearPlane, farPlane);
|
||||
camera.value.position.z = cameraZ.value
|
||||
renderer.value = new CSS3DRenderer()
|
||||
renderer.value.setSize(width, height * 0.9)
|
||||
@@ -138,7 +141,7 @@ const init = () => {
|
||||
const tableLen = tableData.value.length;
|
||||
|
||||
for (let i = 0; i < tableLen; i++) {
|
||||
const object = new THREE.Object3D();
|
||||
const object = new Object3D();
|
||||
|
||||
object.position.x = tableData.value[i].x * (cardSize.value.width + 40) - rowCount.value * 90;
|
||||
object.position.y = -tableData.value[i].y * (cardSize.value.height + 20) + 1000;
|
||||
@@ -151,12 +154,12 @@ const init = () => {
|
||||
function createSphereVertices() {
|
||||
let i = 0;
|
||||
const objLength = objects.value.length;
|
||||
const vector = new THREE.Vector3();
|
||||
const vector = new Vector3();
|
||||
|
||||
for (; i < objLength; ++i) {
|
||||
let phi = Math.acos(-1 + (2 * i) / objLength);
|
||||
let theta = Math.sqrt(objLength * Math.PI) * phi;
|
||||
const object = new THREE.Object3D();
|
||||
const object = new Object3D();
|
||||
|
||||
object.position.x = 800 * Math.cos(theta) * Math.sin(phi);
|
||||
object.position.y = 800 * Math.sin(theta) * Math.sin(phi);
|
||||
@@ -171,12 +174,12 @@ const init = () => {
|
||||
}
|
||||
function createHelixVertices() {
|
||||
let i = 0;
|
||||
const vector = new THREE.Vector3();
|
||||
const vector = new Vector3();
|
||||
const objLength = objects.value.length;
|
||||
for (; i < objLength; ++i) {
|
||||
let phi = i * 0.213 + Math.PI;
|
||||
|
||||
const object = new THREE.Object3D();
|
||||
const object = new Object3D();
|
||||
|
||||
object.position.x = 800 * Math.sin(phi);
|
||||
object.position.y = -(i * 8) + 450;
|
||||
@@ -378,14 +381,13 @@ const stopLottery = async () => {
|
||||
rollBall(0, 1)
|
||||
currentStatus.value = 0
|
||||
// 抽奖池是否为全体人员
|
||||
// const personPool=currentPrize.value.isAll?
|
||||
|
||||
// const notPersonListLength = notPersonList.value.length;
|
||||
const personPool = currentPrize.value.isAll ? allPersonList.value : notPersonList.value
|
||||
// const notPersonListLength = personPool.length;
|
||||
// 每次最多抽十个
|
||||
let luckyCount = 10
|
||||
const leftover = currentPrize.value.count - currentPrize.value.isUsedCount
|
||||
leftover < luckyCount ? luckyCount = leftover : luckyCount
|
||||
if (notPersonList.value.length < leftover) {
|
||||
if (personPool.length < leftover) {
|
||||
toast.open({
|
||||
message: '抽奖人数不够',
|
||||
type: 'warning',
|
||||
@@ -396,24 +398,25 @@ const stopLottery = async () => {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < luckyCount; i++) {
|
||||
if (notPersonList.value.length > 0) {
|
||||
const randomIndex = Math.round(Math.random() * notPersonList.value.length - 1)
|
||||
luckyTargets.value.push(notPersonList.value[randomIndex])
|
||||
// console.log(
|
||||
// 'leftover:', leftover, '\n',
|
||||
// 'luckyCount', luckyCount, '\n',
|
||||
// 'currentPrize.value.isUsedCount', currentPrize.value.isUsedCount, '\n',
|
||||
// 'randomIndex', randomIndex, '\n',
|
||||
// 'notPersonList.value.length - 1', notPersonList.value.length - 1, '\n',
|
||||
// 'notPersonList.value[randomIndex]', notPersonList.value[randomIndex], '\n',
|
||||
// 'cadd id:', notPersonList.value[randomIndex].id
|
||||
// )
|
||||
let LuckyCard = objects.value[notPersonList.value[randomIndex].id]
|
||||
luckyCardList.value.push(LuckyCard)
|
||||
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',
|
||||
)
|
||||
console.log(
|
||||
'cadd id:', personPool[randomIndex].id)
|
||||
let luckyCard = objects.value[personPool[randomIndex].id]
|
||||
luckyCardList.value.push(luckyCard)
|
||||
|
||||
notPersonList.value.splice(randomIndex, 1)
|
||||
personPool.splice(randomIndex, 1)
|
||||
// console.log(
|
||||
// 'objects.value[notPersonList.value[randomIndex].id]', LuckyCard
|
||||
// 'objects.value[personPool[randomIndex].id]', LuckyCard
|
||||
// )
|
||||
}
|
||||
}
|
||||
@@ -443,16 +446,16 @@ const stopLottery = async () => {
|
||||
resetCamera()
|
||||
})
|
||||
})
|
||||
currentPrize.value.isUsedCount += luckyCount
|
||||
if (currentPrize.value.isUsedCount >= currentPrize.value.count) {
|
||||
currentPrize.value.isUsed = true
|
||||
}
|
||||
// currentPrize.value.isUsedCount += luckyCount
|
||||
// if (currentPrize.value.isUsedCount >= currentPrize.value.count) {
|
||||
// currentPrize.value.isUsed = true
|
||||
// }
|
||||
// luckyCardList.value = []
|
||||
|
||||
personConfig.addAlreadyPersonList(luckyTargets.value, currentPrize.value)
|
||||
prizeConfig.updatePrizeConfig(currentPrize.value)
|
||||
// personConfig.addAlreadyPersonList(luckyTargets.value, currentPrize.value)
|
||||
// prizeConfig.updatePrizeConfig(currentPrize.value)
|
||||
|
||||
prizeConfig.setCurrentPrize(currentPrize.value)
|
||||
// prizeConfig.setCurrentPrize(currentPrize.value)
|
||||
luckyTargets.value = []
|
||||
}
|
||||
// 庆祝动画
|
||||
@@ -548,19 +551,19 @@ onUnmounted(() => {
|
||||
|
||||
<!-- 选中菜单结构 start-->
|
||||
<div id="menu">
|
||||
<button class="btn-end " @click="enterLottery" v-if="currentStatus == 0&&tableData.length > 0">进入抽奖</button>
|
||||
<button class="btn-end " @click="enterLottery" v-if="currentStatus == 0 && tableData.length > 0">进入抽奖</button>
|
||||
<div class="start">
|
||||
<button class="btn-start" @click="startLottery" v-if="currentStatus == 1"><strong>开始</strong>
|
||||
<div id="container-stars">
|
||||
<div id="stars"></div>
|
||||
</div>
|
||||
<button class="btn-start" @click="startLottery" v-if="currentStatus == 1"><strong>开始</strong>
|
||||
<div id="container-stars">
|
||||
<div id="stars"></div>
|
||||
</div>
|
||||
|
||||
<div id="glow">
|
||||
<div class="circle"></div>
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div id="glow">
|
||||
<div class="circle"></div>
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button class="btn-end btn glass btn-lg" @click="stopLottery" v-if="currentStatus == 2">抽取幸运儿</button>
|
||||
|
||||
@@ -588,182 +591,185 @@ onUnmounted(() => {
|
||||
margin: 0 auto;
|
||||
font-size: 32px;
|
||||
}
|
||||
.start{
|
||||
|
||||
.start {
|
||||
// 居中
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-start {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 13rem;
|
||||
overflow: hidden;
|
||||
height: 3rem;
|
||||
background-size: 300% 300%;
|
||||
backdrop-filter: blur(1rem);
|
||||
border-radius: 5rem;
|
||||
transition: 0.5s;
|
||||
animation: gradient_301 5s ease infinite;
|
||||
border: double 4px transparent;
|
||||
background-image: linear-gradient(#212121, #212121), linear-gradient(137.48deg, #ffdb3b 10%,#FE53BB 45%, #8F51EA 67%, #0044ff 87%);
|
||||
background-origin: border-box;
|
||||
background-clip: content-box, border-box;
|
||||
-webkit-animation: pulsate-fwd 1.2s ease-in-out infinite both;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 13rem;
|
||||
overflow: hidden;
|
||||
height: 3rem;
|
||||
background-size: 300% 300%;
|
||||
backdrop-filter: blur(1rem);
|
||||
border-radius: 5rem;
|
||||
transition: 0.5s;
|
||||
animation: gradient_301 5s ease infinite;
|
||||
border: double 4px transparent;
|
||||
background-image: linear-gradient(#212121, #212121), linear-gradient(137.48deg, #ffdb3b 10%, #FE53BB 45%, #8F51EA 67%, #0044ff 87%);
|
||||
background-origin: border-box;
|
||||
background-clip: content-box, border-box;
|
||||
-webkit-animation: pulsate-fwd 1.2s ease-in-out infinite both;
|
||||
animation: pulsate-fwd 1.2s ease-in-out infinite both;
|
||||
}
|
||||
|
||||
#container-stars {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
transition: 0.5s;
|
||||
backdrop-filter: blur(1rem);
|
||||
border-radius: 5rem;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
transition: 0.5s;
|
||||
backdrop-filter: blur(1rem);
|
||||
border-radius: 5rem;
|
||||
}
|
||||
|
||||
strong {
|
||||
z-index: 2;
|
||||
font-family: 'Avalors Personal Use';
|
||||
font-size: 12px;
|
||||
letter-spacing: 5px;
|
||||
color: #FFFFFF;
|
||||
text-shadow: 0 0 4px white;
|
||||
z-index: 2;
|
||||
font-family: 'Avalors Personal Use';
|
||||
font-size: 12px;
|
||||
letter-spacing: 5px;
|
||||
color: #FFFFFF;
|
||||
text-shadow: 0 0 4px white;
|
||||
}
|
||||
|
||||
#glow {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
width: 12rem;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
width: 12rem;
|
||||
}
|
||||
|
||||
.circle {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
filter: blur(2rem);
|
||||
animation: pulse_3011 4s infinite;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
filter: blur(2rem);
|
||||
animation: pulse_3011 4s infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.circle:nth-of-type(1) {
|
||||
background: rgba(254, 83, 186, 0.636);
|
||||
background: rgba(254, 83, 186, 0.636);
|
||||
}
|
||||
|
||||
.circle:nth-of-type(2) {
|
||||
background: rgba(142, 81, 234, 0.704);
|
||||
background: rgba(142, 81, 234, 0.704);
|
||||
}
|
||||
|
||||
.btn-start:hover #container-stars {
|
||||
z-index: 1;
|
||||
background-color: #212121;
|
||||
z-index: 1;
|
||||
background-color: #212121;
|
||||
}
|
||||
|
||||
.btn-start:hover {
|
||||
transform: scale(1.1)
|
||||
transform: scale(1.1)
|
||||
}
|
||||
|
||||
.btn-start:active {
|
||||
border: double 4px #FE53BB;
|
||||
background-origin: border-box;
|
||||
background-clip: content-box, border-box;
|
||||
animation: none;
|
||||
border: double 4px #FE53BB;
|
||||
background-origin: border-box;
|
||||
background-clip: content-box, border-box;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.btn-start:active .circle {
|
||||
background: #FE53BB;
|
||||
background: #FE53BB;
|
||||
}
|
||||
|
||||
#stars {
|
||||
position: relative;
|
||||
background: transparent;
|
||||
width: 200rem;
|
||||
height: 200rem;
|
||||
position: relative;
|
||||
background: transparent;
|
||||
width: 200rem;
|
||||
height: 200rem;
|
||||
}
|
||||
|
||||
#stars::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -10rem;
|
||||
left: -100rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
animation: animStarRotate 90s linear infinite;
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -10rem;
|
||||
left: -100rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
animation: animStarRotate 90s linear infinite;
|
||||
}
|
||||
|
||||
#stars::after {
|
||||
background-image: radial-gradient(#ffffff 1px, transparent 1%);
|
||||
background-size: 50px 50px;
|
||||
background-image: radial-gradient(#ffffff 1px, transparent 1%);
|
||||
background-size: 50px 50px;
|
||||
}
|
||||
|
||||
#stars::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -50%;
|
||||
width: 170%;
|
||||
height: 500%;
|
||||
animation: animStar 60s linear infinite;
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -50%;
|
||||
width: 170%;
|
||||
height: 500%;
|
||||
animation: animStar 60s linear infinite;
|
||||
}
|
||||
|
||||
#stars::before {
|
||||
background-image: radial-gradient(#ffffff 1px, transparent 1%);
|
||||
background-size: 50px 50px;
|
||||
opacity: 0.5;
|
||||
background-image: radial-gradient(#ffffff 1px, transparent 1%);
|
||||
background-size: 50px 50px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@keyframes animStar {
|
||||
from {
|
||||
transform: translateY(0);
|
||||
}
|
||||
from {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(-135rem);
|
||||
}
|
||||
to {
|
||||
transform: translateY(-135rem);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animStarRotate {
|
||||
from {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
from {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(0);
|
||||
}
|
||||
to {
|
||||
transform: rotate(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes gradient_301 {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse_3011 {
|
||||
0% {
|
||||
transform: scale(0.75);
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
0% {
|
||||
transform: scale(0.75);
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0.75);
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.75);
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-end {
|
||||
-webkit-animation: pulsate-fwd 0.9s ease-in-out infinite both;
|
||||
animation: pulsate-fwd 0.9s ease-in-out infinite both;
|
||||
|
||||
Reference in New Issue
Block a user