feat: done
75
README.md
@@ -15,12 +15,62 @@ log-lottery是一个可配置可定制化的抽奖应用,炫酷3D球体,可
|
|||||||
## 功能描述
|
## 功能描述
|
||||||
|
|
||||||
- 🕍 炫酷3D球体,年会抽奖必备,开箱即用
|
- 🕍 炫酷3D球体,年会抽奖必备,开箱即用
|
||||||
|
- 🧿 持久化存储,数据不丢失
|
||||||
- 🎁 奖品奖项配置
|
- 🎁 奖品奖项配置
|
||||||
- 👱 抽奖名单设置管理
|
- 👱 抽奖名单设置管理
|
||||||
- 🛞 界面信息自定义
|
- 🛞 界面信息自定义
|
||||||
- 🎼 图片、背景音乐管理,使用本地存储
|
- 🎼 播放背景音乐
|
||||||
|
- 💾 图片、背景音乐管理,使用本地存储,有默认资源可直接使用
|
||||||
- 🖼️ excel表格导入人员名单、抽奖结果使用excel导出
|
- 🖼️ excel表格导入人员名单、抽奖结果使用excel导出
|
||||||
- 🎈 临时增加抽奖
|
- 🎈 可增加临时抽奖
|
||||||
|
|
||||||
|
## 详细介绍
|
||||||
|
|
||||||
|
### 配置参与人员
|
||||||
|
|
||||||
|
于人员配置管理界面下载excel模板,按要求填好数据后导入即可。
|
||||||
|
|
||||||
|
### 配置奖项
|
||||||
|
|
||||||
|
于奖项配置管理界面添加奖项后,自定义修改名称、抽取人数、是否全员参加、图片显示。
|
||||||
|
|
||||||
|
### 界面配置
|
||||||
|
|
||||||
|
可自定义配置标题、列数、卡片颜色、首页图案等。
|
||||||
|
|
||||||
|
### 图片和音乐管理
|
||||||
|
|
||||||
|
上传图片或音乐即可,数据使用indexdb在浏览器本地进行存储。
|
||||||
|
|
||||||
|
## 预览
|
||||||
|
|
||||||
|
首页
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
抽奖
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
配置
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
图片音乐配置
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 技术
|
## 技术
|
||||||
|
|
||||||
@@ -30,7 +80,28 @@ log-lottery是一个可配置可定制化的抽奖应用,炫酷3D球体,可
|
|||||||
- pinia
|
- pinia
|
||||||
- daisyui
|
- daisyui
|
||||||
|
|
||||||
|
## 开发
|
||||||
|
|
||||||
|
安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm i
|
||||||
|
```
|
||||||
|
|
||||||
|
开发运行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
打包
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
> 项目思路来源于 https://github.com/moshang-xc/lottery
|
> 项目思路来源于 https://github.com/moshang-xc/lottery
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](http://opensource.org/licenses/MIT)
|
[MIT](http://opensource.org/licenses/MIT)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"@vueuse/core": "^10.6.1",
|
"@vueuse/core": "^10.6.1",
|
||||||
"axios": "^1.6.1",
|
"axios": "^1.6.1",
|
||||||
"canvas-confetti": "^1.9.2",
|
"canvas-confetti": "^1.9.2",
|
||||||
|
"dayjs": "^1.11.10",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"pinia-plugin-persist": "^1.0.0",
|
"pinia-plugin-persist": "^1.0.0",
|
||||||
|
|||||||
18
pnpm-lock.yaml
generated
@@ -17,6 +17,9 @@ dependencies:
|
|||||||
canvas-confetti:
|
canvas-confetti:
|
||||||
specifier: ^1.9.2
|
specifier: ^1.9.2
|
||||||
version: 1.9.2
|
version: 1.9.2
|
||||||
|
dayjs:
|
||||||
|
specifier: ^1.11.10
|
||||||
|
version: 1.11.10
|
||||||
localforage:
|
localforage:
|
||||||
specifier: ^1.10.0
|
specifier: ^1.10.0
|
||||||
version: 1.10.0
|
version: 1.10.0
|
||||||
@@ -38,9 +41,6 @@ dependencies:
|
|||||||
three-css3d:
|
three-css3d:
|
||||||
specifier: ^1.0.6
|
specifier: ^1.0.6
|
||||||
version: 1.0.6(three@0.160.0)
|
version: 1.0.6(three@0.160.0)
|
||||||
three-trackballcontrols:
|
|
||||||
specifier: ^0.9.0
|
|
||||||
version: 0.9.0(three@0.160.0)
|
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.3.8
|
specifier: ^3.3.8
|
||||||
version: 3.3.8(typescript@5.2.2)
|
version: 3.3.8(typescript@5.2.2)
|
||||||
@@ -1942,6 +1942,10 @@ packages:
|
|||||||
whatwg-url: 12.0.1
|
whatwg-url: 12.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/dayjs@1.11.10:
|
||||||
|
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/de-indent@1.0.2:
|
/de-indent@1.0.2:
|
||||||
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -4985,14 +4989,6 @@ packages:
|
|||||||
three: 0.160.0
|
three: 0.160.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/three-trackballcontrols@0.9.0(three@0.160.0):
|
|
||||||
resolution: {integrity: sha512-Z6HmIJnP70r5uONvcPCdLEF0SsG1kbGzNb7qQYj3c7b6v2E3XTlbNpZsgTjt36oKm0Z2tU11D6EbW4i8KIHuqA==}
|
|
||||||
peerDependencies:
|
|
||||||
three: '>= 0.86 <= 1.0'
|
|
||||||
dependencies:
|
|
||||||
three: 0.160.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/three@0.160.0:
|
/three@0.160.0:
|
||||||
resolution: {integrity: sha512-DLU8lc0zNIPkM7rH5/e1Ks1Z8tWCGRq6g8mPowdDJpw1CFBJMU7UoJjC6PefXW7z//SSl0b2+GCw14LB+uDhng==}
|
resolution: {integrity: sha512-DLU8lc0zNIPkM7rH5/e1Ks1Z8tWCGRq6g8mPowdDJpw1CFBJMU7UoJjC6PefXW7z//SSl0b2+GCw14LB+uDhng==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|||||||
1
src/icons/delete.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1705070782236" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5231" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M840 288H688v-56c0-40-32-72-72-72h-208C368 160 336 192 336 232V288h-152c-12.8 0-24 11.2-24 24s11.2 24 24 24h656c12.8 0 24-11.2 24-24s-11.2-24-24-24zM384 288v-56c0-12.8 11.2-24 24-24h208c12.8 0 24 11.2 24 24V288H384zM758.4 384c-12.8 0-24 11.2-24 24v363.2c0 24-19.2 44.8-44.8 44.8H332.8c-24 0-44.8-19.2-44.8-44.8V408c0-12.8-11.2-24-24-24s-24 11.2-24 24v363.2c0 51.2 41.6 92.8 92.8 92.8h358.4c51.2 0 92.8-41.6 92.8-92.8V408c-1.6-12.8-12.8-24-25.6-24z" p-id="5232"></path><path d="M444.8 744v-336c0-12.8-11.2-24-24-24s-24 11.2-24 24v336c0 12.8 11.2 24 24 24s24-11.2 24-24zM627.2 744v-336c0-12.8-11.2-24-24-24s-24 11.2-24 24v336c0 12.8 11.2 24 24 24s24-11.2 24-24z" p-id="5233"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1019 B |
1
src/icons/edit.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1705070974672" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6209" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M846.8 255L744.5 152.8c-33.1-33.1-87.9-33.1-122.5 0L149.6 625.3c-10.1 10.1-15.8 24.5-15.8 38.9L128 814v4.3c1.4 15.8 7.2 30.2 18.8 40.3 10.1 10.1 24.5 14.4 38.9 14.4h2.9l146.9-5.8c14.4 0 27.3-7.2 37.4-17.3l472.5-472.4c36-34.6 36-89.3 1.4-122.5z m-675 574.3l4.4-124.3 118.3 118.3-122.7 6z m181.1-34.5L204.7 646.7 597 254.4l148.2 148.2-392.3 392.2z m465.8-465.5l-31.5 31.4-148.4-148.2 31.6-31.4c6-6 13.5-9 21-9s15 3 21 9l106.3 106.3c11.9 11.9 11.9 29.9 0 41.9zM907.9 819.8H457.4c-16.6 0-30 13.4-30 30s13.4 30 30 30h450.5c16.6 0 30-13.4 30-30s-13.4-30-30-30zM497.9 769.6c0 16.6 13.4 30 30 30h380c16.6 0 30-13.4 30-30s-13.4-30-30-30h-380c-16.5 0-30 13.4-30 30z" p-id="6210"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1015 B |
@@ -2,6 +2,8 @@ import { defineStore } from 'pinia';
|
|||||||
import { IPersonConfig } from '@/types/storeType';
|
import { IPersonConfig } from '@/types/storeType';
|
||||||
import { IPrizeConfig } from '@/types/storeType';
|
import { IPrizeConfig } from '@/types/storeType';
|
||||||
import { defaultPersonList } from './data'
|
import { defaultPersonList } from './data'
|
||||||
|
import { usePrizeConfig } from './prizeConfig';
|
||||||
|
import dayjs from 'dayjs'
|
||||||
export const usePersonConfig = defineStore('person', {
|
export const usePersonConfig = defineStore('person', {
|
||||||
state() {
|
state() {
|
||||||
return {
|
return {
|
||||||
@@ -18,7 +20,18 @@ export const usePersonConfig = defineStore('person', {
|
|||||||
},
|
},
|
||||||
// 获取全部人员名单
|
// 获取全部人员名单
|
||||||
getAllPersonList(state) {
|
getAllPersonList(state) {
|
||||||
return state.personConfig.allPersonList;
|
return state.personConfig.allPersonList.filter((item: IPersonConfig) => {
|
||||||
|
return item
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 获取未获此奖的人员名单
|
||||||
|
getNotThisPrizePersonList(state: any) {
|
||||||
|
const currentPrize = usePrizeConfig().prizeConfig.currentPrize;
|
||||||
|
const data = state.personConfig.allPersonList.filter((item: IPersonConfig) => {
|
||||||
|
return !item.prizeId.includes(currentPrize.id as string);
|
||||||
|
});
|
||||||
|
|
||||||
|
return data
|
||||||
},
|
},
|
||||||
// 获取已中奖人员名单
|
// 获取已中奖人员名单
|
||||||
getAlreadyPersonList(state) {
|
getAlreadyPersonList(state) {
|
||||||
@@ -57,11 +70,14 @@ export const usePersonConfig = defineStore('person', {
|
|||||||
if (item.id === person.id && prize != null) {
|
if (item.id === person.id && prize != null) {
|
||||||
item.isWin = true
|
item.isWin = true
|
||||||
// person.isWin = true
|
// person.isWin = true
|
||||||
item.prizeName += prize.name
|
item.prizeName.push(prize.name)
|
||||||
// person.prizeName += prize.name
|
// person.prizeName += prize.name
|
||||||
item.prizeTime = new Date().toString()
|
item.prizeTime.push(dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'))
|
||||||
// person.prizeTime = new Date().toString()
|
// person.prizeTime = new Date().toString()
|
||||||
|
item.prizeId.push(prize.id as string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
});
|
});
|
||||||
this.personConfig.alreadyPersonList.push(person);
|
this.personConfig.alreadyPersonList.push(person);
|
||||||
});
|
});
|
||||||
@@ -75,16 +91,17 @@ export const usePersonConfig = defineStore('person', {
|
|||||||
for (let i = 0; i < this.personConfig.allPersonList.length; i++) {
|
for (let i = 0; i < this.personConfig.allPersonList.length; i++) {
|
||||||
if (person.id === this.personConfig.allPersonList[i].id) {
|
if (person.id === this.personConfig.allPersonList[i].id) {
|
||||||
this.personConfig.allPersonList[i].isWin = false
|
this.personConfig.allPersonList[i].isWin = false
|
||||||
this.personConfig.allPersonList[i].prizeName = ''
|
this.personConfig.allPersonList[i].prizeName = []
|
||||||
this.personConfig.allPersonList[i].prizeTime = ''
|
this.personConfig.allPersonList[i].prizeTime = []
|
||||||
|
this.personConfig.allPersonList[i].prizeId = []
|
||||||
|
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let i = 0; i < alreadyPersonListLength; i++) {
|
for (let i = 0; i < alreadyPersonListLength; i++) {
|
||||||
this.personConfig.alreadyPersonList=this.personConfig.alreadyPersonList.filter((item:IPersonConfig)=>{
|
this.personConfig.alreadyPersonList = this.personConfig.alreadyPersonList.filter((item: IPersonConfig) =>
|
||||||
return item.id!==person.id
|
item.id !== person.id
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 删除指定人员
|
// 删除指定人员
|
||||||
@@ -110,8 +127,9 @@ export const usePersonConfig = defineStore('person', {
|
|||||||
// 把已中奖人员合并到未中奖人员,要验证是否已存在
|
// 把已中奖人员合并到未中奖人员,要验证是否已存在
|
||||||
this.personConfig.allPersonList.forEach((item: IPersonConfig) => {
|
this.personConfig.allPersonList.forEach((item: IPersonConfig) => {
|
||||||
item.isWin = false;
|
item.isWin = false;
|
||||||
item.prizeName = '';
|
item.prizeName = [];
|
||||||
item.prizeTime = '';
|
item.prizeTime = [];
|
||||||
|
item.prizeId = []
|
||||||
});
|
});
|
||||||
this.personConfig.alreadyPersonList = [];
|
this.personConfig.alreadyPersonList = [];
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,6 +22,23 @@ export const usePrizeConfig = defineStore('prize', {
|
|||||||
isShow: true,
|
isShow: true,
|
||||||
isUsed: false,
|
isUsed: false,
|
||||||
frequency: 1,
|
frequency: 1,
|
||||||
|
} as IPrizeConfig,
|
||||||
|
temporaryPrize:{
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
sort: 0,
|
||||||
|
isAll: false,
|
||||||
|
count: 1,
|
||||||
|
isUsedCount:0,
|
||||||
|
picture: {
|
||||||
|
id: '-1',
|
||||||
|
name: '',
|
||||||
|
url: ''
|
||||||
|
},
|
||||||
|
desc: '',
|
||||||
|
isShow: false,
|
||||||
|
isUsed: false,
|
||||||
|
frequency: 1,
|
||||||
} as IPrizeConfig
|
} as IPrizeConfig
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -45,6 +62,10 @@ return state.prizeConfig.prizeList;
|
|||||||
getCurrentPrize(state) {
|
getCurrentPrize(state) {
|
||||||
return state.prizeConfig.currentPrize;
|
return state.prizeConfig.currentPrize;
|
||||||
},
|
},
|
||||||
|
// 获取临时的奖项
|
||||||
|
getTemporaryPrize(state){
|
||||||
|
return state.prizeConfig.temporaryPrize;
|
||||||
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
@@ -62,12 +83,25 @@ return state.prizeConfig.prizeList;
|
|||||||
},
|
},
|
||||||
// 更新奖项数据
|
// 更新奖项数据
|
||||||
updatePrizeConfig(prizeConfigItem: IPrizeConfig) {
|
updatePrizeConfig(prizeConfigItem: IPrizeConfig) {
|
||||||
const index = this.prizeConfig.prizeList.findIndex(item => item.id === prizeConfigItem.id);
|
// const index = this.prizeConfig.prizeList.findIndex(item => item.id === prizeConfigItem.id);
|
||||||
this.prizeConfig.prizeList[index] = prizeConfigItem;
|
// this.prizeConfig.prizeList[index] = prizeConfigItem;
|
||||||
if(prizeConfigItem.isUsed&&index+1<this.prizeConfig.prizeList.length){
|
// if(prizeConfigItem.isUsed&&index+1<this.prizeConfig.prizeList.length){
|
||||||
|
// // 设置下一个为currentPrize
|
||||||
|
// this.setCurrentPrize(this.prizeConfig.prizeList[index+1]);
|
||||||
|
// }
|
||||||
|
if(prizeConfigItem.isUsed&&this.prizeConfig.prizeList.length){
|
||||||
// 设置下一个为currentPrize
|
// 设置下一个为currentPrize
|
||||||
this.setCurrentPrize(this.prizeConfig.prizeList[index+1]);
|
for(let i=0;i<this.prizeConfig.prizeList.length;i++){
|
||||||
|
if(!this.prizeConfig.prizeList[i].isUsed){
|
||||||
|
this.setCurrentPrize(this.prizeConfig.prizeList[i]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.resetTemporaryPrize()
|
||||||
},
|
},
|
||||||
// 删除全部奖项
|
// 删除全部奖项
|
||||||
deleteAllPrizeConfig() {
|
deleteAllPrizeConfig() {
|
||||||
@@ -77,6 +111,43 @@ return state.prizeConfig.prizeList;
|
|||||||
setCurrentPrize(prizeConfigItem: IPrizeConfig) {
|
setCurrentPrize(prizeConfigItem: IPrizeConfig) {
|
||||||
this.prizeConfig.currentPrize = prizeConfigItem
|
this.prizeConfig.currentPrize = prizeConfigItem
|
||||||
},
|
},
|
||||||
|
// 设置临时奖项
|
||||||
|
setTemporaryPrize(prizeItem: IPrizeConfig) {
|
||||||
|
if(prizeItem.isShow==false){
|
||||||
|
for(let i=0;i<this.prizeConfig.prizeList.length;i++){
|
||||||
|
if(this.prizeConfig.prizeList[i].isUsed==false){
|
||||||
|
this.setCurrentPrize(this.prizeConfig.prizeList[i]);
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.resetTemporaryPrize()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prizeConfig.temporaryPrize = prizeItem
|
||||||
|
},
|
||||||
|
// 重置临时奖项
|
||||||
|
resetTemporaryPrize() {
|
||||||
|
this.prizeConfig.temporaryPrize = {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
sort: 0,
|
||||||
|
isAll: false,
|
||||||
|
count: 1,
|
||||||
|
isUsedCount:0,
|
||||||
|
picture: {
|
||||||
|
id: '-1',
|
||||||
|
name: '',
|
||||||
|
url: ''
|
||||||
|
},
|
||||||
|
desc: '',
|
||||||
|
isShow: false,
|
||||||
|
isUsed: false,
|
||||||
|
frequency: 1,
|
||||||
|
} as IPrizeConfig;
|
||||||
|
},
|
||||||
// 重置所有配置
|
// 重置所有配置
|
||||||
resetDefault() {
|
resetDefault() {
|
||||||
this.prizeConfig = {
|
this.prizeConfig = {
|
||||||
@@ -97,7 +168,8 @@ return state.prizeConfig.prizeList;
|
|||||||
isShow: true,
|
isShow: true,
|
||||||
isUsed: false,
|
isUsed: false,
|
||||||
frequency: 1,
|
frequency: 1,
|
||||||
} as IPrizeConfig
|
} as IPrizeConfig,
|
||||||
|
temporaryPrize:{} as IPrizeConfig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
.element-card {
|
.element-card {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
user-select: none;
|
||||||
.card-id {
|
.card-id {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 20px;
|
top: 20px;
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ export interface IPersonConfig {
|
|||||||
y:number
|
y:number
|
||||||
createTime: string;
|
createTime: string;
|
||||||
updateTime: string;
|
updateTime: string;
|
||||||
prizeName: string;
|
prizeName:string[];
|
||||||
prizeTime: string;
|
prizeId:string[];
|
||||||
|
prizeTime: string[];
|
||||||
}
|
}
|
||||||
export interface IPrizeConfig {
|
export interface IPrizeConfig {
|
||||||
id: number|string;
|
id: number|string;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import dayjs from 'dayjs';
|
||||||
// 筛选人员数据
|
// 筛选人员数据
|
||||||
export const filterData = (tableData: any[],localRowCount: number,startIndex=0) => {
|
export const filterData = (tableData: any[],localRowCount: number,startIndex=0) => {
|
||||||
const dataLength = tableData.length
|
const dataLength = tableData.length
|
||||||
@@ -19,8 +20,8 @@ return tableData
|
|||||||
export const addOtherInfo=(personList:any[])=>{
|
export const addOtherInfo=(personList:any[])=>{
|
||||||
const len=personList.length;
|
const len=personList.length;
|
||||||
for(let i=0;i<len;i++){
|
for(let i=0;i<len;i++){
|
||||||
personList[i].createTime=new Date().toString();
|
personList[i].createTime=dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
|
||||||
personList[i].updateTime=new Date().toString();
|
personList[i].updateTime=dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
|
||||||
personList[i].prizeName='';
|
personList[i].prizeName='';
|
||||||
personList[i].prizeTime='';
|
personList[i].prizeTime='';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,16 @@ const exportData = () => {
|
|||||||
delete data[i].id
|
delete data[i].id
|
||||||
delete data[i].createTime
|
delete data[i].createTime
|
||||||
delete data[i].updateTime
|
delete data[i].updateTime
|
||||||
|
delete data[i].prizeId
|
||||||
// 修改字段名称
|
// 修改字段名称
|
||||||
if (data[i].isWin) {
|
if (data[i].isWin) {
|
||||||
data[i].isWin = '是'
|
data[i].isWin = '是'
|
||||||
} else {
|
} else {
|
||||||
data[i].isWin = '否'
|
data[i].isWin = '否'
|
||||||
}
|
}
|
||||||
|
// 格式化数组为
|
||||||
|
data[i].prizeTime=data[i].prizeTime.join(',')
|
||||||
|
data[i].prizeName=data[i].prizeName.join(',')
|
||||||
}
|
}
|
||||||
let dataString = JSON.stringify(data)
|
let dataString = JSON.stringify(data)
|
||||||
dataString = dataString
|
dataString = dataString
|
||||||
|
|||||||
@@ -56,14 +56,6 @@ const tableColumnsList = [
|
|||||||
handleMoveNotPerson(row)
|
handleMoveNotPerson(row)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// label: '删除',
|
|
||||||
// type: 'btn-error',
|
|
||||||
// onClick: (row: any) => {
|
|
||||||
// console.log('删除:', row)
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ watch(() => prizeList.value, (val:IPrizeConfig[]) => {
|
|||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="item in prizeList" :key="item.id" class="flex gap-10"
|
<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">
|
<label class="max-w-xs mb-10 form-control">
|
||||||
<!-- 向上向下 -->
|
<!-- 向上向下 -->
|
||||||
<div class="flex flex-col items-center gap-2 pt-5">
|
<div class="flex flex-col items-center gap-2 pt-5">
|
||||||
|
|||||||
@@ -64,7 +64,10 @@ const skip = (path: string) => {
|
|||||||
</div>
|
</div>
|
||||||
<footer class="p-10 rounded footer footer-center bg-base-200 text-base-content">
|
<footer class="p-10 rounded footer footer-center bg-base-200 text-base-content">
|
||||||
<nav class="grid grid-flow-col gap-4">
|
<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>
|
||||||
<nav>
|
<nav>
|
||||||
<div class="grid grid-flow-col gap-4">
|
<div class="grid grid-flow-col gap-4">
|
||||||
|
|||||||
@@ -9,23 +9,42 @@ import defaultPrizeImage from '@/assets/images/龙.png'
|
|||||||
const prizeConfig = useStore().prizeConfig
|
const prizeConfig = useStore().prizeConfig
|
||||||
const globalConfig = useStore().globalConfig
|
const globalConfig = useStore().globalConfig
|
||||||
const system = useStore().system
|
const system = useStore().system
|
||||||
const { getPrizeConfig: localPrizeList, getCurrentPrize: currentPrize } = storeToRefs(prizeConfig)
|
const { getPrizeConfig: localPrizeList, getCurrentPrize: currentPrize, getTemporaryPrize: temporaryPrize } = storeToRefs(prizeConfig)
|
||||||
const {getIsShowPrizeList:isShowPrizeList}= storeToRefs(globalConfig)
|
const { getIsShowPrizeList: isShowPrizeList, getImageList: localImageList } = storeToRefs(globalConfig)
|
||||||
const { getIsMobile: isMobile } = storeToRefs(system)
|
const { getIsMobile: isMobile } = storeToRefs(system)
|
||||||
const prizeListRef = ref()
|
const prizeListRef = ref()
|
||||||
const prizeListContainerRef = ref()
|
const prizeListContainerRef = ref()
|
||||||
|
|
||||||
|
const temporaryPrizeRef = ref()
|
||||||
// 获取prizeListRef高度
|
// 获取prizeListRef高度
|
||||||
const getPrizeListHeight = () => {
|
const getPrizeListHeight = () => {
|
||||||
let height = 200;
|
let height = 200;
|
||||||
if (prizeListRef.value) {
|
if (prizeListRef.value) {
|
||||||
height = (prizeListRef.value as HTMLElement).offsetHeight}
|
height = (prizeListRef.value as HTMLElement).offsetHeight
|
||||||
|
}
|
||||||
|
|
||||||
return height
|
return height
|
||||||
}
|
}
|
||||||
const prizeShow = ref(structuredClone(isShowPrizeList.value))
|
const prizeShow = ref(structuredClone(isShowPrizeList.value))
|
||||||
|
|
||||||
const addTemporaryPrize = () => {
|
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(() => {
|
onMounted(() => {
|
||||||
prizeListContainerRef.value.style.height = getPrizeListHeight() + 'px'
|
prizeListContainerRef.value.style.height = getPrizeListHeight() + 'px'
|
||||||
@@ -34,23 +53,129 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex items-center">
|
<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 ref="prizeListContainerRef">
|
||||||
<transition name="prize-list" :appear="true">
|
<div class="h-20 w-72" :class="temporaryPrize.isShow ? 'current-prize' : ''" v-if="temporaryPrize.isShow">
|
||||||
<div v-if="prizeShow&&!isMobile" class="flex items-center">
|
<div class="relative flex flex-row items-center justify-between w-full h-full shadow-xl card bg-base-100">
|
||||||
<ul class="flex flex-col gap-1 p-2 rounded-xl bg-slate-500/50" ref="prizeListRef">
|
<div v-if="temporaryPrize.isUsed"
|
||||||
<li v-for="item in localPrizeList" :key="item.id"
|
class="absolute z-50 w-full h-full bg-gray-800/70 item-mask rounded-xl"></div>
|
||||||
: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>
|
|
||||||
<figure class="w-10 h-10 rounded-xl">
|
<figure class="w-10 h-10 rounded-xl">
|
||||||
<ImageSync v-if="item.picture.url" :imgItem="item.picture"></ImageSync>
|
<ImageSync v-if="temporaryPrize.picture.url" :imgItem="temporaryPrize.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>
|
</figure>
|
||||||
<div class="items-center p-0 text-center card-body">
|
<div class="items-center p-0 text-center card-body">
|
||||||
<h2 class="p-0 m-0 card-title">{{ item.name }}</h2>
|
<div class="tooltip tooltip-left" :data-tip="temporaryPrize.name">
|
||||||
<p class="absolute z-40 p-0 m-0 text-gray-300/80 pt-9">{{ item.isUsedCount }}/{{ item.count }}</p>
|
<h2 class="p-0 m-0 overflow-hidden w-28 card-title whitespace-nowrap text-ellipsis">{{
|
||||||
<progress class="w-3/4 h-6 progress progress-primary" :value="item.isUsedCount" :max="item.count"></progress>
|
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 && !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>
|
||||||
|
<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" />
|
||||||
|
</figure>
|
||||||
|
<div class="items-center p-0 text-center card-body">
|
||||||
|
<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> -->
|
<!-- <p class="p-0 m-0">{{ item.isUsedCount }}/{{ item.count }}</p> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,7 +196,10 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</transition>
|
</transition>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<transition name="prize-operate" :appear="true">
|
<transition name="prize-operate" :appear="true">
|
||||||
@@ -86,6 +214,10 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
|
.label {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
.prize-list-enter-active {
|
.prize-list-enter-active {
|
||||||
-webkit-animation: slide-right 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
|
-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;
|
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;
|
-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 {
|
.current-prize {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -131,7 +257,6 @@ onMounted(() => {
|
|||||||
// animation-play-state: paused;
|
// animation-play-state: paused;
|
||||||
translate: -5% 0%;
|
translate: -5% 0%;
|
||||||
transition: translate 0.25s ease-out;
|
transition: translate 0.25s ease-out;
|
||||||
|
|
||||||
animation-play-state: running;
|
animation-play-state: running;
|
||||||
transition-duration: 0.75s;
|
transition-duration: 0.75s;
|
||||||
translate: 0% 0%;
|
translate: 0% 0%;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const personConfig = useStore().personConfig
|
|||||||
const globalConfig = useStore().globalConfig
|
const globalConfig = useStore().globalConfig
|
||||||
const prizeConfig = useStore().prizeConfig
|
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 { 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 { getTopTitle: topTitle, getCardColor: cardColor,getPatterColor: patternColor, getPatternList: patternList, getTextColor: textColor, getLuckyColor: luckyColor, getCardSize: cardSize, getTextSize: textSize, getRowCount: rowCount } = storeToRefs(globalConfig)
|
||||||
const tableData = ref<any[]>([])
|
const tableData = ref<any[]>([])
|
||||||
@@ -57,6 +57,7 @@ const targets = {
|
|||||||
const luckyTargets = ref<any[]>([])
|
const luckyTargets = ref<any[]>([])
|
||||||
const luckyCardList = ref<number[]>([])
|
const luckyCardList = ref<number[]>([])
|
||||||
let luckyCount = ref(10)
|
let luckyCount = ref(10)
|
||||||
|
const personPool=ref<IPersonConfig[]>([])
|
||||||
|
|
||||||
const intervalTimer = ref<any>(null)
|
const intervalTimer = ref<any>(null)
|
||||||
// const currentPrizeValue = ref(JSON.parse(JSON.stringify(currentPrize.value)))
|
// const currentPrizeValue = ref(JSON.parse(JSON.stringify(currentPrize.value)))
|
||||||
@@ -381,8 +382,9 @@ const startLottery = () => {
|
|||||||
|
|
||||||
return
|
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({
|
toast.open({
|
||||||
message: '抽奖人数不够',
|
message: '抽奖人数不够',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
@@ -406,11 +408,11 @@ const stopLottery = async () => {
|
|||||||
// TWEEN.removeAll();
|
// TWEEN.removeAll();
|
||||||
rollBall(0, 1)
|
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
|
const leftover = currentPrize.value.count - currentPrize.value.isUsedCount
|
||||||
leftover < luckyCount.value ? luckyCount.value = leftover : luckyCount
|
leftover < luckyCount.value ? luckyCount.value = leftover : luckyCount
|
||||||
if (personPool.length < leftover) {
|
if (personPool.value.length < leftover) {
|
||||||
toast.open({
|
toast.open({
|
||||||
message: '抽奖人数不够',
|
message: '抽奖人数不够',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
@@ -421,23 +423,10 @@ const stopLottery = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < luckyCount.value; i++) {
|
for (let i = 0; i < luckyCount.value; i++) {
|
||||||
if (personPool.length > 0) {
|
if (personPool.value.length > 0) {
|
||||||
const randomIndex = Math.round(Math.random() * (personPool.length - 1))
|
const randomIndex = Math.round(Math.random() * (personPool.value.length - 1))
|
||||||
luckyTargets.value.push(personPool[randomIndex])
|
luckyTargets.value.push(personPool.value[randomIndex])
|
||||||
// console.log(
|
personPool.value.splice(randomIndex, 1)
|
||||||
// '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
|
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const windowSize = { width: window.innerWidth, height: window.innerHeight }
|
const windowSize = { width: window.innerWidth, height: window.innerHeight }
|
||||||
@@ -475,7 +464,7 @@ const stopLottery = async () => {
|
|||||||
}
|
}
|
||||||
// 继续
|
// 继续
|
||||||
const continueLottery = async () => {
|
const continueLottery = async () => {
|
||||||
// currentPrize.value.isUsedCount += luckyCount.value
|
currentPrize.value.isUsedCount += luckyCount.value
|
||||||
if (currentPrize.value.isUsedCount >= currentPrize.value.count) {
|
if (currentPrize.value.isUsedCount >= currentPrize.value.count) {
|
||||||
currentPrize.value.isUsed = true
|
currentPrize.value.isUsed = true
|
||||||
}
|
}
|
||||||
@@ -590,7 +579,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="absolute z-10 flex flex-col items-center justify-center -translate-x-1/2 left-1/2">
|
<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>
|
:style="{ fontSize: textSize * 1.5 + 'px', color: textColor }">{{ topTitle }}</h2>
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<button v-if="tableData.length <= 0" class="cursor-pointer btn btn-outline btn-secondary btn-lg"
|
<button v-if="tableData.length <= 0" class="cursor-pointer btn btn-outline btn-secondary btn-lg"
|
||||||
@@ -670,15 +659,10 @@ onUnmounted(() => {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
}
|
}
|
||||||
// .enter-enter-active{
|
.header-title{
|
||||||
// -webkit-animation: fade-in-fwd 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
|
-webkit-animation: tracking-in-expand-fwd 0.8s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
|
||||||
// animation: fade-in-fwd 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
|
animation: tracking-in-expand-fwd 0.8s cubic-bezier(0.215, 0.610, 0.355, 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;
|
|
||||||
// }
|
|
||||||
.start {
|
.start {
|
||||||
// 居中
|
// 居中
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -967,61 +951,38 @@ strong {
|
|||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@-webkit-keyframes fade-in-fwd {
|
@-webkit-keyframes tracking-in-expand-fwd {
|
||||||
0% {
|
0% {
|
||||||
-webkit-transform: translateZ(-80px);
|
letter-spacing: -0.5em;
|
||||||
transform: translateZ(-80px);
|
-webkit-transform: translateZ(-700px);
|
||||||
|
transform: translateZ(-700px);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
40% {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
100% {
|
100% {
|
||||||
-webkit-transform: translateZ(0);
|
-webkit-transform: translateZ(0);
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@keyframes fade-in-fwd {
|
@keyframes tracking-in-expand-fwd {
|
||||||
0% {
|
0% {
|
||||||
-webkit-transform: translateZ(-80px);
|
letter-spacing: -0.5em;
|
||||||
transform: translateZ(-80px);
|
-webkit-transform: translateZ(-700px);
|
||||||
|
transform: translateZ(-700px);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
40% {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
100% {
|
100% {
|
||||||
-webkit-transform: translateZ(0);
|
-webkit-transform: translateZ(0);
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
opacity: 1;
|
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>
|
</style>
|
||||||
|
|||||||
BIN
static/images/config-view.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
static/images/config_pattern.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
static/images/config_personall.png
Normal file
|
After Width: | Height: | Size: 233 KiB |
BIN
static/images/config_prize.png
Normal file
|
After Width: | Height: | Size: 181 KiB |
BIN
static/images/home.png
Normal file
|
After Width: | Height: | Size: 669 KiB |
BIN
static/images/home_prizelist.png
Normal file
|
After Width: | Height: | Size: 727 KiB |
BIN
static/images/image_config.png
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
static/images/lottery-done.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
static/images/lottery-enter.png
Normal file
|
After Width: | Height: | Size: 641 KiB |
BIN
static/images/music_music.png
Normal file
|
After Width: | Height: | Size: 199 KiB |