Commit ec3e1983 by zhangyongfeng

init

parents
> 1%
last 2 versions
./src/assets/lib/tagcanvas.js
\ No newline at end of file
module.exports = {
root: true,
env: {
node: true
},
extends: ["plugin:vue/essential", "@vue/prettier"],
rules: {
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
},
parserOptions: {
parser: "babel-eslint"
}
};
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
\ No newline at end of file
{
"tabWidth": 2,
"semi": true,
"singleQuote": true
}
# luckydraw
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"]
};
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "luckydraw",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vue-cli-service serve",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.4.3",
"element-ui": "^2.13.0",
"vue": "^2.6.10",
"vue-router": "^3.1.3",
"vuex": "^3.1.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.1.0",
"@vue/cli-plugin-eslint": "^4.1.0",
"@vue/cli-plugin-router": "^4.1.0",
"@vue/cli-plugin-vuex": "^4.1.0",
"@vue/cli-service": "^4.1.0",
"@vue/eslint-config-prettier": "^5.0.0",
"babel-eslint": "^10.0.3",
"eslint": "^5.16.0",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-vue": "^5.0.0",
"node-sass": "^4.13.0",
"prettier": "^1.19.1",
"sass-loader": "^8.0.0",
"vue-template-compiler": "^2.6.10"
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>luckydraw</title>
</head>
<body>
<noscript>
<strong>We're sorry but luckydraw doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
<template>
<div id="root">
<header>
<Publicity v-show="!running" />
<el-button class="res" type="text" @click="showResult = true">
抽奖结果
</el-button>
<el-button class="con" type="text" @click="showConfig = true">
抽奖配置
</el-button>
</header>
<div id="main" :class="{ mask: showRes }"></div>
<div id="tags">
<ul v-for="item in datas" :key="item.key">
<li>
<a href="javascript:void(0);" :style="{ color: '#fff' }">
{{ item.key }}
</a>
</li>
</ul>
</div>
<div id="resbox" v-show="showRes">
<p @click="showRes = false">{{ categoryName }}抽奖结果:</p>
<span
v-for="item in resArr"
:key="item"
class="itemres"
@click="showRes = false"
>
{{ item }}
</span>
</div>
<LotteryConfig :visible.sync="showConfig" @resetconfig="reloadTagCanvas" />
<Tool @toggle="toggle" :running="running" />
<Result :visible.sync="showResult"></Result>
</div>
</template>
<script>
import LotteryConfig from '@/components/LotteryConfig';
import Publicity from '@/components/Publicity';
import Tool from '@/components/Tool';
import {
getData,
configField,
resultField,
conversionCategoryName
} from '@/helper/index';
import { luckydrawHandler } from '@/helper/algorithm';
import Result from '@/components/Result';
export default {
name: 'App',
components: { LotteryConfig, Publicity, Tool, Result },
computed: {
config: {
get() {
return this.$store.state.config;
}
},
result: {
get() {
return this.$store.state.result;
},
set(val) {
this.$store.commit('setResult', val);
}
},
allresult() {
let allresult = [];
for (const key in this.result) {
if (this.result.hasOwnProperty(key)) {
const element = this.result[key];
allresult = allresult.concat(element);
}
}
return allresult;
},
datas() {
const datas = [];
for (let index = 0; index < this.config.number; index++) {
datas.push({
key: index + 1
});
}
return datas;
},
categoryName() {
return conversionCategoryName(this.category);
}
},
created() {
const data = getData(configField);
if (data) {
this.$store.commit('setConfig', Object.assign({}, data));
}
const result = getData(resultField);
if (result) {
this.$store.commit('setResult', result);
}
},
data() {
return {
running: false,
showRes: false,
showConfig: false,
showResult: false,
resArr: [],
category: ''
};
},
mounted() {
this.startTagCanvas();
},
methods: {
speed() {
return [0.1 * Math.random() + 0.01, -(0.1 * Math.random() + 0.01)];
},
createCanvas() {
const canvas = document.createElement('canvas');
canvas.width = document.body.offsetWidth;
canvas.height = document.body.offsetHeight;
canvas.id = 'rootcanvas';
this.$el.querySelector('#main').appendChild(canvas);
},
startTagCanvas() {
this.createCanvas();
const { speed } = this;
window.TagCanvas.Start('rootcanvas', 'tags', {
textColour: null,
initial: speed(),
dragControl: 1,
textHeight: 20,
noSelect: true,
lock: 'xy'
});
},
reloadTagCanvas() {
window.TagCanvas.Reload('rootcanvas');
},
toggle(form) {
const { speed, config } = this;
if (this.running) {
window.TagCanvas.SetSpeed('rootcanvas', speed());
this.reloadTagCanvas();
setTimeout(() => {
this.showRes = true;
}, 300);
} else {
this.showRes = false;
const { number } = config;
const { category, mode, qty, remain, allin } = form;
let num = 1;
if (mode === 1 || mode === 5) {
num = mode;
} else if (mode === 0) {
num = remain;
} else if (mode === 99) {
num = qty;
}
const resArr = luckydrawHandler(
number,
allin ? [] : this.allresult,
num
);
this.resArr = resArr;
this.category = category;
const oldRes = this.result[category] || [];
this.result = {
[category]: oldRes.concat(resArr)
};
window.TagCanvas.SetSpeed('rootcanvas', [5, 1]);
}
this.running = !this.running;
}
}
};
</script>
<style lang="scss">
#root {
height: 100%;
position: relative;
background-image: url('./assets/bg.jpg');
background-size: 100% 100%;
background-position: center center;
background-repeat: no-repeat;
background-color: #121936;
.mask {
-webkit-filter: blur(5px);
filter: blur(5px);
}
header {
height: 50px;
line-height: 50px;
position: relative;
.el-button {
position: absolute;
top: 17px;
padding: 0;
&.con {
right: 20px;
}
&.res {
right: 100px;
}
}
}
}
#main {
height: 100%;
}
#resbox {
position: absolute;
top: 45%;
left: 50%;
width: 1000px;
transform: translateX(-50%) translateY(-50%);
text-align: center;
p {
color: red;
font-size: 50px;
line-height: 120px;
}
.itemres {
background: #fff;
display: inline-block;
width: 160px;
height: 160px;
border-radius: 4px;
border: 1px solid #ccc;
line-height: 160px;
font-size: 100px;
font-weight: bold;
margin-right: 20px;
margin-top: 20px;
cursor: pointer;
}
}
</style>
@-webkit-keyframes slowMovingToLeft {
from {
left: 1000px;
}
to {
left: -1500px;
}
}
@keyframes slowMovingToLeft {
from {
left: 1000px;
}
to {
left: -1500px;
}
}
body,
html {
height: 100%;
font-size: 14px;
font-family: 'PingFang SC', Microsoft YaHei, '微软雅黑', Tahoma, Arial, HeiTi,
'黑体', sans-serif;
}
li {
list-style: none;
}
a {
text-decoration: none;
color: #333;
}
i {
font-style: normal;
}
p,
body,
ul,
figure {
padding: 0px;
margin: 0px;
}
input {
outline: none;
}
::-webkit-scrollbar {
width: 5px;
height: 5px;
background-color: transparent;
}
body {
overflow: hidden;
}
.el-form-item {
margin-bottom: 5px !important;
}
.el-dialog__body {
padding-top: 20px !important;
padding-bottom: 20px !important;
}
@import './base.scss';
@import './animation.scss';
<template>
<el-dialog
:visible="visible"
:append-to-body="true"
title="抽奖配置"
width="400px"
:lock-scroll="true"
@close="$emit('update:visible', false)"
class="c-LotteryConfig"
>
<el-form ref="form" :model="form" label-width="100px" size="mini">
<el-form-item label="抽奖标题">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="抽奖总人数">
<el-input v-model="form.number"></el-input>
</el-form-item>
<el-form-item label="特等奖人数">
<el-input v-model="form.specialAward"></el-input>
</el-form-item>
<el-form-item label="一等奖人数">
<el-input v-model="form.firstPrize"></el-input>
</el-form-item>
<el-form-item label="二等奖人数">
<el-input v-model="form.secondPrize"></el-input>
</el-form-item>
<el-form-item label="三等奖人数">
<el-input v-model="form.thirdPrize"></el-input>
</el-form-item>
<el-form-item label="四等奖人数">
<el-input v-model="form.fourthPrize"></el-input>
</el-form-item>
<el-form-item label="五等奖人数">
<el-input v-model="form.fifthPrize"></el-input>
</el-form-item>
<el-form-item label="追加奖(1)人数">
<el-input v-model="form.additionalPrize1"></el-input>
</el-form-item>
<el-form-item label="追加奖(2)人数">
<el-input v-model="form.additionalPrize2"></el-input>
</el-form-item>
<el-form-item label="追加奖(3)人数">
<el-input v-model="form.additionalPrize3"></el-input>
</el-form-item>
<el-form-item label="追加奖(4)人数">
<el-input v-model="form.additionalPrize4"></el-input>
</el-form-item>
<el-form-item label="追加奖(5)人数">
<el-input v-model="form.additionalPrize5"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">保存配置</el-button>
<el-button @click="$emit('update:visible', false)">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
</template>
<script>
import { setData, configField } from '@/helper/index';
export default {
name: 'LotteryConfig',
props: {
visible: Boolean
},
computed: {
form: {
get() {
return this.$store.state.config;
},
set(val) {
this.$store.commit('setConfig', val);
}
}
},
methods: {
onSubmit() {
setData(configField, this.form);
this.$emit('update:visible', false);
this.$emit('resetconfig');
this.$message({
message: '保存成功',
type: 'success'
});
}
}
};
</script>
<style lang="scss">
.c-LotteryConfig {
}
</style>
<template>
<div class="c-Publicity">
<div class="message">
<span class="title">
{{ config.name }}
</span>
<span v-html="message"> </span>
</div>
</div>
</template>
<script>
import { conversionCategoryName } from '@/helper/index';
export default {
name: 'Publicity',
computed: {
config() {
return this.$store.state.config;
},
result() {
return this.$store.state.result;
},
message() {
const {
specialAward,
additionalPrize1,
additionalPrize2,
additionalPrize3
} = this.config;
const fields = [
'firstPrize',
'secondPrize',
'thirdPrize',
'fourthPrize',
'fifthPrize'
];
if (specialAward > 0) {
fields.unshift('specialAward');
}
if (additionalPrize1 > 0) {
fields.push('additionalPrize1');
}
if (additionalPrize2 > 0) {
fields.push('additionalPrize2');
}
if (additionalPrize3 > 0) {
fields.push('additionalPrize3');
}
const { result } = this;
let message = '';
fields.forEach(item => {
let label = conversionCategoryName(item);
message += `&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${label}抽奖结果: ${
result[item].length > 0 ? result[item].join('、') : '暂未抽取'
}`;
});
return message;
}
}
};
</script>
<style lang="scss">
.c-Publicity {
height: 100%;
width: 1000px;
background-color: rgba(0, 0, 0, 0.2);
margin: 0 auto;
position: relative;
overflow: hidden;
.message {
font-size: 16px;
color: #fff;
position: absolute;
left: 500px;
animation-name: slowMovingToLeft;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-direction: normal;
animation-duration: 40s;
.title {
color: #ff2200;
}
}
}
</style>
<template>
<el-dialog
:visible="visible"
@close="$emit('update:visible', false)"
title="抽奖结果"
width="600px"
class="c-Result"
>
<div
v-for="(item, index) in resultList"
:key="index"
class="listrow"
@click="
event => {
deleteRes(event, item);
}
"
>
<span class="name">
{{ item.name }}
</span>
<span class="value">
<span v-if="item.value && item.value.length === 0">
暂未抽奖
</span>
<span
class="card"
v-for="(data, j) in item.value"
:key="j"
:data-res="data"
>
{{ data }}
</span>
</span>
</div>
</el-dialog>
</template>
<script>
import { conversionCategoryName, getDomData } from '@/helper/index';
export default {
name: 'c-Result',
props: {
visible: Boolean
},
computed: {
result: {
get() {
return this.$store.state.result;
},
set(val) {
this.$store.commit('setResult', val);
}
},
resultList() {
const list = [];
for (const key in this.result) {
if (this.result.hasOwnProperty(key)) {
const element = this.result[key];
let name = conversionCategoryName(key);
list.push({
label: key,
name,
value: element
});
}
}
return list;
}
},
methods: {
deleteRes(event, row) {
this.$confirm('此操作将移除该中奖号码,确认删除?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
const Index = getDomData(event.target, 'res');
if (Index) {
const result = this.result;
result[row.label] = this.result[row.label].filter(
item => item !== Number(Index)
);
this.result = result;
this.$message({
type: 'success',
message: '删除成功!'
});
}
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消'
});
});
}
}
};
</script>
<style lang="scss">
.c-Result {
.listrow {
display: flex;
line-height: 30px;
.name {
width: 80px;
font-weight: bold;
}
.value {
flex: 1;
}
.card {
display: inline-block;
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 18px;
font-weight: bold;
border-radius: 4px;
border: 1px solid #ccc;
background-color: #f2f2f2;
margin-left: 5px;
margin-top: 5px;
position: relative;
cursor: pointer;
&:hover {
&::before {
content: '删除';
width: 100%;
height: 100%;
background-color: #ccc;
position: absolute;
left: 0;
top: 0;
color: red;
}
}
}
}
}
</style>
<template>
<div id="tool">
<el-button @click="startHandler" size="mini">{{
running ? '停止' : '开始'
}}</el-button>
<el-button size="mini" @click="resetConfig">
重置
</el-button>
<el-dialog
:append-to-body="true"
:visible.sync="showSetwat"
class="setwat-dialog"
width="400px"
>
<el-form ref="form" :model="form" label-width="80px" size="mini">
<el-form-item label="抽取奖项">
<el-select v-model="form.category" placeholder="请选取本次抽取的奖项">
<el-option
:label="item.label"
:value="item.value"
v-for="(item, index) in categorys"
:key="index"
></el-option>
</el-select>
</el-form-item>
<el-form-item label=" " v-if="form.category">
<span>
<span class="colorred">{{ config[form.category] }}</span
>
</span>
<span :style="{ marginLeft: '20px' }">
剩余<span class="colorred">{{ remain }}</span
>
</span>
</el-form-item>
<el-form-item label="抽取方式">
<el-select v-model="form.mode" placeholder="请选取本次抽取方式">
<el-option label="抽1人" :value="1"></el-option>
<el-option label="抽5人" :value="5"></el-option>
<el-option label="一次抽取完" :value="0"></el-option>
<el-option label="自定义" :value="99"></el-option>
</el-select>
</el-form-item>
<el-form-item label="抽取人数" v-if="form.mode === 99">
<el-input
v-model="form.qty"
type="number"
:clearable="true"
:min="1"
:max="remain ? remain : 100"
:step="1"
></el-input>
</el-form-item>
<el-form-item label="全员参与">
<el-switch v-model="form.allin"></el-switch>
<span :style="{ fontSize: '12px' }">
(开启后将在全体成员[无论有无中奖]中抽奖)
</span>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">立即抽奖</el-button>
<el-button @click="showSetwat = false">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import { clearData, conversionCategoryName } from '@/helper/index';
export default {
props: {
running: Boolean
},
computed: {
config: {
get() {
return this.$store.state.config;
}
},
remain() {
return (
this.config[this.form.category] -
(this.result[this.form.category]
? this.result[this.form.category].length
: 0)
);
},
result() {
return this.$store.state.result;
},
categorys() {
const options = [];
for (const key in this.config) {
if (this.config.hasOwnProperty(key)) {
const item = this.config[key];
if (item > 0) {
let name = conversionCategoryName(key);
name &&
options.push({
label: name,
value: key
});
}
}
}
return options;
}
},
data() {
return {
showSetwat: false,
form: {
category: '',
mode: 1,
qty: 1,
allin: false
}
};
},
methods: {
resetConfig() {
this.$confirm('此操作将重置所有数据,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
clearData();
this.$message({
type: 'success',
message: '重置成功!'
});
setTimeout(() => {
window.location.reload();
}, 3000);
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消'
});
});
},
onSubmit() {
if (!this.form.category) {
return this.$message.error('请选择本次抽取的奖项');
}
if (this.remain <= 0) {
return this.$message.error('该奖项剩余人数不足');
}
if (this.form.mode === 99) {
if (this.form.qty <= 0) {
return this.$message.error('必须输入本次抽取人数');
}
if (this.form.qty > this.remain) {
return this.$message.error('本次抽奖人数已超过本奖项的剩余人数');
}
}
if (this.form.mode === 1 || this.form.mode === 5) {
if (this.form.mode > this.remain) {
return this.$message.error('本次抽奖人数已超过本奖项的剩余人数');
}
}
this.showSetwat = false;
this.$emit(
'toggle',
Object.assign({}, this.form, { remain: this.remain })
);
},
startHandler() {
if (this.running) {
this.$emit('toggle');
} else {
this.showSetwat = true;
}
}
}
};
</script>
<style lang="scss">
#tool {
position: fixed;
width: 60px;
height: 500px;
top: 50%;
right: 20px;
transform: translateY(-50%);
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.el-button + .el-button {
margin-top: 10px;
margin-left: 0px;
}
}
.setwat-dialog {
.colorred {
color: red;
font-weight: bold;
}
}
</style>
/**
* 取范围内随机整数
* @param {number} minNum
* @param {number} maxNum
*/
function randomNum(minNum = 1, maxNum) {
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
}
/**
* 单次抽奖
* @param {number} total 总人数
* @param {array} won 已中奖
* @param {number} num 本次抽取人数
*/
export function luckydrawHandler(total, won = [], num) {
const peolist = [];
for (let i = 1; i <= total; i++) {
peolist.push(i);
}
const wons = won;
const res = [];
for (let j = 0; j < num; j++) {
const nodraws = peolist.filter(item => !wons.includes(item));
const current = nodraws[randomNum(1, nodraws.length) - 1];
res.push(current);
wons.push(current);
}
return res;
}
export function setData(key, value) {
if (typeof value === 'string') {
return localStorage.setItem(key, value);
}
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (err) {
return err;
}
}
export function getData(key) {
const value = localStorage.getItem(key);
try {
return JSON.parse(value);
} catch (err) {
return value;
}
}
export function removeData(key) {
return localStorage.removeItem(key);
}
export function clearData() {
return localStorage.clear();
}
export function getDomData(element, dataName) {
if (!element || !dataName || !element.getAttribute) {
return;
}
return element.getAttribute('data-' + dataName);
}
export const configField = 'config'; // 配置数据
export const resultField = 'result'; // 抽奖结果
export function conversionCategoryName(key) {
let name = '';
switch (key) {
case 'specialAward':
name = '特等奖';
break;
case 'firstPrize':
name = '一等奖';
break;
case 'secondPrize':
name = '二等奖';
break;
case 'thirdPrize':
name = '三等奖';
break;
case 'fourthPrize':
name = '四等奖';
break;
case 'fifthPrize':
name = '五等奖';
break;
case 'additionalPrize1':
name = '追加奖(1)';
break;
case 'additionalPrize2':
name = '追加奖(2)';
break;
case 'additionalPrize3':
name = '追加奖(3)';
break;
case 'additionalPrize4':
name = '追加奖(4)';
break;
case 'additionalPrize5':
name = '追加奖(5)';
break;
default:
break;
}
return name;
}
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import Element from 'element-ui';
import '@/assets/style/index.scss';
import 'element-ui/lib/theme-chalk/index.css';
import '@/assets/lib/tagcanvas.js';
Vue.config.productionTip = false;
Vue.use(Element);
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app');
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from '../views/Home.vue';
Vue.use(VueRouter);
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ '../views/About.vue')
}
];
const router = new VueRouter({
base: process.env.BASE_URL,
routes
});
export default router;
import Vue from 'vue';
import Vuex from 'vuex';
import { setData, resultField } from '@/helper/index';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
config: {
name: '年会抽奖',
number: 70,
specialAward: 0,
firstPrize: 1,
secondPrize: 5,
thirdPrize: 8,
fourthPrize: 10,
fifthPrize: 20,
additionalPrize1: 0,
additionalPrize2: 0,
additionalPrize3: 0,
additionalPrize4: 0,
additionalPrize5: 0
},
result: {
specialAward: [],
firstPrize: [],
secondPrize: [],
thirdPrize: [],
fourthPrize: [],
fifthPrize: [],
additionalPrize1: [],
additionalPrize2: [],
additionalPrize3: [],
additionalPrize4: [],
additionalPrize5: []
}
},
mutations: {
setConfig(state, config) {
state.config = config;
},
setResult(state, result = {}) {
Object.assign(state.result, result);
setData(resultField, state.result);
}
},
actions: {},
modules: {}
});
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
</div>
</template>
<script>
export default {
name: 'home'
};
</script>
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? '/lucky-draw' : '/'
};
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment