init
0 parents
Showing
32 changed files
with
1184 additions
and
0 deletions
.browserslistrc
0 → 100644
.eslintignore
0 → 100644
| 1 | ./src/assets/lib/tagcanvas.js | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
.eslintrc.js
0 → 100644
| 1 | module.exports = { | ||
| 2 | root: true, | ||
| 3 | env: { | ||
| 4 | node: true | ||
| 5 | }, | ||
| 6 | extends: ["plugin:vue/essential", "@vue/prettier"], | ||
| 7 | rules: { | ||
| 8 | "no-console": process.env.NODE_ENV === "production" ? "error" : "off", | ||
| 9 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off" | ||
| 10 | }, | ||
| 11 | parserOptions: { | ||
| 12 | parser: "babel-eslint" | ||
| 13 | } | ||
| 14 | }; |
.gitignore
0 → 100644
| 1 | .DS_Store | ||
| 2 | node_modules | ||
| 3 | /dist | ||
| 4 | |||
| 5 | # local env files | ||
| 6 | .env.local | ||
| 7 | .env.*.local | ||
| 8 | |||
| 9 | # Log files | ||
| 10 | npm-debug.log* | ||
| 11 | yarn-debug.log* | ||
| 12 | yarn-error.log* | ||
| 13 | |||
| 14 | # Editor directories and files | ||
| 15 | .idea | ||
| 16 | .vscode | ||
| 17 | *.suo | ||
| 18 | *.ntvs* | ||
| 19 | *.njsproj | ||
| 20 | *.sln | ||
| 21 | *.sw? | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
README.md
0 → 100644
| 1 | # luckydraw | ||
| 2 | |||
| 3 | ## Project setup | ||
| 4 | ``` | ||
| 5 | npm install | ||
| 6 | ``` | ||
| 7 | |||
| 8 | ### Compiles and hot-reloads for development | ||
| 9 | ``` | ||
| 10 | npm run serve | ||
| 11 | ``` | ||
| 12 | |||
| 13 | ### Compiles and minifies for production | ||
| 14 | ``` | ||
| 15 | npm run build | ||
| 16 | ``` | ||
| 17 | |||
| 18 | ### Lints and fixes files | ||
| 19 | ``` | ||
| 20 | npm run lint | ||
| 21 | ``` | ||
| 22 | |||
| 23 | ### Customize configuration | ||
| 24 | See [Configuration Reference](https://cli.vuejs.org/config/). |
babel.config.js
0 → 100644
package-lock.json
0 → 100644
This diff could not be displayed because it is too large.
package.json
0 → 100644
| 1 | { | ||
| 2 | "name": "luckydraw", | ||
| 3 | "version": "0.1.0", | ||
| 4 | "private": true, | ||
| 5 | "scripts": { | ||
| 6 | "dev": "vue-cli-service serve", | ||
| 7 | "serve": "vue-cli-service serve", | ||
| 8 | "build": "vue-cli-service build", | ||
| 9 | "lint": "vue-cli-service lint" | ||
| 10 | }, | ||
| 11 | "dependencies": { | ||
| 12 | "core-js": "^3.4.3", | ||
| 13 | "element-ui": "^2.13.0", | ||
| 14 | "vue": "^2.6.10", | ||
| 15 | "vue-router": "^3.1.3", | ||
| 16 | "vuex": "^3.1.2" | ||
| 17 | }, | ||
| 18 | "devDependencies": { | ||
| 19 | "@vue/cli-plugin-babel": "^4.1.0", | ||
| 20 | "@vue/cli-plugin-eslint": "^4.1.0", | ||
| 21 | "@vue/cli-plugin-router": "^4.1.0", | ||
| 22 | "@vue/cli-plugin-vuex": "^4.1.0", | ||
| 23 | "@vue/cli-service": "^4.1.0", | ||
| 24 | "@vue/eslint-config-prettier": "^5.0.0", | ||
| 25 | "babel-eslint": "^10.0.3", | ||
| 26 | "eslint": "^5.16.0", | ||
| 27 | "eslint-plugin-prettier": "^3.1.1", | ||
| 28 | "eslint-plugin-vue": "^5.0.0", | ||
| 29 | "node-sass": "^4.13.0", | ||
| 30 | "prettier": "^1.19.1", | ||
| 31 | "sass-loader": "^8.0.0", | ||
| 32 | "vue-template-compiler": "^2.6.10" | ||
| 33 | } | ||
| 34 | } |
public/favicon.ico
0 → 100644
No preview for this file type
public/index.html
0 → 100644
| 1 | <!DOCTYPE html> | ||
| 2 | <html lang="en"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8"> | ||
| 5 | <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
| 6 | <meta name="viewport" content="width=device-width,initial-scale=1.0"> | ||
| 7 | <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | ||
| 8 | <title>luckydraw</title> | ||
| 9 | </head> | ||
| 10 | <body> | ||
| 11 | <noscript> | ||
| 12 | <strong>We're sorry but luckydraw doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> | ||
| 13 | </noscript> | ||
| 14 | <div id="app"></div> | ||
| 15 | <!-- built files will be auto injected --> | ||
| 16 | </body> | ||
| 17 | </html> |
src/App.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div id="root"> | ||
| 3 | <header> | ||
| 4 | <Publicity v-show="!running" /> | ||
| 5 | <el-button class="res" type="text" @click="showResult = true"> | ||
| 6 | 抽奖结果 | ||
| 7 | </el-button> | ||
| 8 | <el-button class="con" type="text" @click="showConfig = true"> | ||
| 9 | 抽奖配置 | ||
| 10 | </el-button> | ||
| 11 | </header> | ||
| 12 | <div id="main" :class="{ mask: showRes }"></div> | ||
| 13 | <div id="tags"> | ||
| 14 | <ul v-for="item in datas" :key="item.key"> | ||
| 15 | <li> | ||
| 16 | <a href="javascript:void(0);" :style="{ color: '#fff' }"> | ||
| 17 | {{ item.key }} | ||
| 18 | </a> | ||
| 19 | </li> | ||
| 20 | </ul> | ||
| 21 | </div> | ||
| 22 | <div id="resbox" v-show="showRes"> | ||
| 23 | <p @click="showRes = false">{{ categoryName }}抽奖结果:</p> | ||
| 24 | <span | ||
| 25 | v-for="item in resArr" | ||
| 26 | :key="item" | ||
| 27 | class="itemres" | ||
| 28 | @click="showRes = false" | ||
| 29 | > | ||
| 30 | {{ item }} | ||
| 31 | </span> | ||
| 32 | </div> | ||
| 33 | |||
| 34 | <LotteryConfig :visible.sync="showConfig" @resetconfig="reloadTagCanvas" /> | ||
| 35 | <Tool @toggle="toggle" :running="running" /> | ||
| 36 | <Result :visible.sync="showResult"></Result> | ||
| 37 | </div> | ||
| 38 | </template> | ||
| 39 | <script> | ||
| 40 | import LotteryConfig from '@/components/LotteryConfig'; | ||
| 41 | import Publicity from '@/components/Publicity'; | ||
| 42 | import Tool from '@/components/Tool'; | ||
| 43 | import { | ||
| 44 | getData, | ||
| 45 | configField, | ||
| 46 | resultField, | ||
| 47 | conversionCategoryName | ||
| 48 | } from '@/helper/index'; | ||
| 49 | import { luckydrawHandler } from '@/helper/algorithm'; | ||
| 50 | import Result from '@/components/Result'; | ||
| 51 | export default { | ||
| 52 | name: 'App', | ||
| 53 | |||
| 54 | components: { LotteryConfig, Publicity, Tool, Result }, | ||
| 55 | |||
| 56 | computed: { | ||
| 57 | config: { | ||
| 58 | get() { | ||
| 59 | return this.$store.state.config; | ||
| 60 | } | ||
| 61 | }, | ||
| 62 | result: { | ||
| 63 | get() { | ||
| 64 | return this.$store.state.result; | ||
| 65 | }, | ||
| 66 | set(val) { | ||
| 67 | this.$store.commit('setResult', val); | ||
| 68 | } | ||
| 69 | }, | ||
| 70 | allresult() { | ||
| 71 | let allresult = []; | ||
| 72 | for (const key in this.result) { | ||
| 73 | if (this.result.hasOwnProperty(key)) { | ||
| 74 | const element = this.result[key]; | ||
| 75 | allresult = allresult.concat(element); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | return allresult; | ||
| 79 | }, | ||
| 80 | datas() { | ||
| 81 | const datas = []; | ||
| 82 | for (let index = 0; index < this.config.number; index++) { | ||
| 83 | datas.push({ | ||
| 84 | key: index + 1 | ||
| 85 | }); | ||
| 86 | } | ||
| 87 | return datas; | ||
| 88 | }, | ||
| 89 | categoryName() { | ||
| 90 | return conversionCategoryName(this.category); | ||
| 91 | } | ||
| 92 | }, | ||
| 93 | created() { | ||
| 94 | const data = getData(configField); | ||
| 95 | if (data) { | ||
| 96 | this.$store.commit('setConfig', Object.assign({}, data)); | ||
| 97 | } | ||
| 98 | const result = getData(resultField); | ||
| 99 | if (result) { | ||
| 100 | this.$store.commit('setResult', result); | ||
| 101 | } | ||
| 102 | }, | ||
| 103 | |||
| 104 | data() { | ||
| 105 | return { | ||
| 106 | running: false, | ||
| 107 | showRes: false, | ||
| 108 | showConfig: false, | ||
| 109 | showResult: false, | ||
| 110 | resArr: [], | ||
| 111 | category: '' | ||
| 112 | }; | ||
| 113 | }, | ||
| 114 | mounted() { | ||
| 115 | this.startTagCanvas(); | ||
| 116 | }, | ||
| 117 | methods: { | ||
| 118 | speed() { | ||
| 119 | return [0.1 * Math.random() + 0.01, -(0.1 * Math.random() + 0.01)]; | ||
| 120 | }, | ||
| 121 | createCanvas() { | ||
| 122 | const canvas = document.createElement('canvas'); | ||
| 123 | canvas.width = document.body.offsetWidth; | ||
| 124 | canvas.height = document.body.offsetHeight; | ||
| 125 | canvas.id = 'rootcanvas'; | ||
| 126 | this.$el.querySelector('#main').appendChild(canvas); | ||
| 127 | }, | ||
| 128 | startTagCanvas() { | ||
| 129 | this.createCanvas(); | ||
| 130 | const { speed } = this; | ||
| 131 | window.TagCanvas.Start('rootcanvas', 'tags', { | ||
| 132 | textColour: null, | ||
| 133 | initial: speed(), | ||
| 134 | dragControl: 1, | ||
| 135 | textHeight: 20, | ||
| 136 | noSelect: true, | ||
| 137 | lock: 'xy' | ||
| 138 | }); | ||
| 139 | }, | ||
| 140 | reloadTagCanvas() { | ||
| 141 | window.TagCanvas.Reload('rootcanvas'); | ||
| 142 | }, | ||
| 143 | toggle(form) { | ||
| 144 | const { speed, config } = this; | ||
| 145 | if (this.running) { | ||
| 146 | window.TagCanvas.SetSpeed('rootcanvas', speed()); | ||
| 147 | this.reloadTagCanvas(); | ||
| 148 | setTimeout(() => { | ||
| 149 | this.showRes = true; | ||
| 150 | }, 300); | ||
| 151 | } else { | ||
| 152 | this.showRes = false; | ||
| 153 | const { number } = config; | ||
| 154 | const { category, mode, qty, remain, allin } = form; | ||
| 155 | let num = 1; | ||
| 156 | if (mode === 1 || mode === 5) { | ||
| 157 | num = mode; | ||
| 158 | } else if (mode === 0) { | ||
| 159 | num = remain; | ||
| 160 | } else if (mode === 99) { | ||
| 161 | num = qty; | ||
| 162 | } | ||
| 163 | const resArr = luckydrawHandler( | ||
| 164 | number, | ||
| 165 | allin ? [] : this.allresult, | ||
| 166 | num | ||
| 167 | ); | ||
| 168 | this.resArr = resArr; | ||
| 169 | this.category = category; | ||
| 170 | const oldRes = this.result[category] || []; | ||
| 171 | this.result = { | ||
| 172 | [category]: oldRes.concat(resArr) | ||
| 173 | }; | ||
| 174 | window.TagCanvas.SetSpeed('rootcanvas', [5, 1]); | ||
| 175 | } | ||
| 176 | this.running = !this.running; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | }; | ||
| 180 | </script> | ||
| 181 | <style lang="scss"> | ||
| 182 | #root { | ||
| 183 | height: 100%; | ||
| 184 | position: relative; | ||
| 185 | background-image: url('./assets/bg.jpg'); | ||
| 186 | background-size: 100% 100%; | ||
| 187 | background-position: center center; | ||
| 188 | background-repeat: no-repeat; | ||
| 189 | background-color: #121936; | ||
| 190 | .mask { | ||
| 191 | -webkit-filter: blur(5px); | ||
| 192 | filter: blur(5px); | ||
| 193 | } | ||
| 194 | header { | ||
| 195 | height: 50px; | ||
| 196 | line-height: 50px; | ||
| 197 | position: relative; | ||
| 198 | .el-button { | ||
| 199 | position: absolute; | ||
| 200 | top: 17px; | ||
| 201 | padding: 0; | ||
| 202 | &.con { | ||
| 203 | right: 20px; | ||
| 204 | } | ||
| 205 | &.res { | ||
| 206 | right: 100px; | ||
| 207 | } | ||
| 208 | } | ||
| 209 | } | ||
| 210 | } | ||
| 211 | #main { | ||
| 212 | height: 100%; | ||
| 213 | } | ||
| 214 | |||
| 215 | #resbox { | ||
| 216 | position: absolute; | ||
| 217 | top: 45%; | ||
| 218 | left: 50%; | ||
| 219 | width: 1000px; | ||
| 220 | transform: translateX(-50%) translateY(-50%); | ||
| 221 | text-align: center; | ||
| 222 | p { | ||
| 223 | color: red; | ||
| 224 | font-size: 50px; | ||
| 225 | line-height: 120px; | ||
| 226 | } | ||
| 227 | .itemres { | ||
| 228 | background: #fff; | ||
| 229 | display: inline-block; | ||
| 230 | width: 160px; | ||
| 231 | height: 160px; | ||
| 232 | border-radius: 4px; | ||
| 233 | border: 1px solid #ccc; | ||
| 234 | line-height: 160px; | ||
| 235 | font-size: 100px; | ||
| 236 | font-weight: bold; | ||
| 237 | margin-right: 20px; | ||
| 238 | margin-top: 20px; | ||
| 239 | cursor: pointer; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | </style> |
src/assets/bg.jpg
0 → 100644
534 KB
src/assets/lib/tagcanvas.js
0 → 100644
This diff is collapsed.
Click to expand it.
src/assets/lib/zepto.js
0 → 100644
This diff is collapsed.
Click to expand it.
src/assets/logo.png
0 → 100644
6.69 KB
src/assets/style/animation.scss
0 → 100644
src/assets/style/base.scss
0 → 100644
| 1 | body, | ||
| 2 | html { | ||
| 3 | height: 100%; | ||
| 4 | font-size: 14px; | ||
| 5 | font-family: 'PingFang SC', Microsoft YaHei, '微软雅黑', Tahoma, Arial, HeiTi, | ||
| 6 | '黑体', sans-serif; | ||
| 7 | } | ||
| 8 | li { | ||
| 9 | list-style: none; | ||
| 10 | } | ||
| 11 | a { | ||
| 12 | text-decoration: none; | ||
| 13 | color: #333; | ||
| 14 | } | ||
| 15 | i { | ||
| 16 | font-style: normal; | ||
| 17 | } | ||
| 18 | p, | ||
| 19 | body, | ||
| 20 | ul, | ||
| 21 | figure { | ||
| 22 | padding: 0px; | ||
| 23 | margin: 0px; | ||
| 24 | } | ||
| 25 | input { | ||
| 26 | outline: none; | ||
| 27 | } | ||
| 28 | ::-webkit-scrollbar { | ||
| 29 | width: 5px; | ||
| 30 | height: 5px; | ||
| 31 | background-color: transparent; | ||
| 32 | } | ||
| 33 | body { | ||
| 34 | overflow: hidden; | ||
| 35 | } | ||
| 36 | .el-form-item { | ||
| 37 | margin-bottom: 5px !important; | ||
| 38 | } | ||
| 39 | .el-dialog__body { | ||
| 40 | padding-top: 20px !important; | ||
| 41 | padding-bottom: 20px !important; | ||
| 42 | } |
src/assets/style/index.scss
0 → 100644
src/components/LotteryConfig.vue
0 → 100644
| 1 | <template> | ||
| 2 | <el-dialog | ||
| 3 | :visible="visible" | ||
| 4 | :append-to-body="true" | ||
| 5 | title="抽奖配置" | ||
| 6 | width="400px" | ||
| 7 | :lock-scroll="true" | ||
| 8 | @close="$emit('update:visible', false)" | ||
| 9 | class="c-LotteryConfig" | ||
| 10 | > | ||
| 11 | <el-form ref="form" :model="form" label-width="100px" size="mini"> | ||
| 12 | <el-form-item label="抽奖标题"> | ||
| 13 | <el-input v-model="form.name"></el-input> | ||
| 14 | </el-form-item> | ||
| 15 | <el-form-item label="抽奖总人数"> | ||
| 16 | <el-input v-model="form.number"></el-input> | ||
| 17 | </el-form-item> | ||
| 18 | <el-form-item label="特等奖人数"> | ||
| 19 | <el-input v-model="form.specialAward"></el-input> | ||
| 20 | </el-form-item> | ||
| 21 | <el-form-item label="一等奖人数"> | ||
| 22 | <el-input v-model="form.firstPrize"></el-input> | ||
| 23 | </el-form-item> | ||
| 24 | <el-form-item label="二等奖人数"> | ||
| 25 | <el-input v-model="form.secondPrize"></el-input> | ||
| 26 | </el-form-item> | ||
| 27 | <el-form-item label="三等奖人数"> | ||
| 28 | <el-input v-model="form.thirdPrize"></el-input> | ||
| 29 | </el-form-item> | ||
| 30 | <el-form-item label="四等奖人数"> | ||
| 31 | <el-input v-model="form.fourthPrize"></el-input> | ||
| 32 | </el-form-item> | ||
| 33 | <el-form-item label="五等奖人数"> | ||
| 34 | <el-input v-model="form.fifthPrize"></el-input> | ||
| 35 | </el-form-item> | ||
| 36 | <el-form-item label="追加奖(1)人数"> | ||
| 37 | <el-input v-model="form.additionalPrize1"></el-input> | ||
| 38 | </el-form-item> | ||
| 39 | <el-form-item label="追加奖(2)人数"> | ||
| 40 | <el-input v-model="form.additionalPrize2"></el-input> | ||
| 41 | </el-form-item> | ||
| 42 | <el-form-item label="追加奖(3)人数"> | ||
| 43 | <el-input v-model="form.additionalPrize3"></el-input> | ||
| 44 | </el-form-item> | ||
| 45 | <el-form-item label="追加奖(4)人数"> | ||
| 46 | <el-input v-model="form.additionalPrize4"></el-input> | ||
| 47 | </el-form-item> | ||
| 48 | <el-form-item label="追加奖(5)人数"> | ||
| 49 | <el-input v-model="form.additionalPrize5"></el-input> | ||
| 50 | </el-form-item> | ||
| 51 | |||
| 52 | <el-form-item> | ||
| 53 | <el-button type="primary" @click="onSubmit">保存配置</el-button> | ||
| 54 | <el-button @click="$emit('update:visible', false)">取消</el-button> | ||
| 55 | </el-form-item> | ||
| 56 | </el-form> | ||
| 57 | </el-dialog> | ||
| 58 | </template> | ||
| 59 | <script> | ||
| 60 | import { setData, configField } from '@/helper/index'; | ||
| 61 | export default { | ||
| 62 | name: 'LotteryConfig', | ||
| 63 | props: { | ||
| 64 | visible: Boolean | ||
| 65 | }, | ||
| 66 | computed: { | ||
| 67 | form: { | ||
| 68 | get() { | ||
| 69 | return this.$store.state.config; | ||
| 70 | }, | ||
| 71 | set(val) { | ||
| 72 | this.$store.commit('setConfig', val); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | }, | ||
| 76 | methods: { | ||
| 77 | onSubmit() { | ||
| 78 | setData(configField, this.form); | ||
| 79 | this.$emit('update:visible', false); | ||
| 80 | this.$emit('resetconfig'); | ||
| 81 | this.$message({ | ||
| 82 | message: '保存成功', | ||
| 83 | type: 'success' | ||
| 84 | }); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | }; | ||
| 88 | </script> | ||
| 89 | <style lang="scss"> | ||
| 90 | .c-LotteryConfig { | ||
| 91 | } | ||
| 92 | </style> |
src/components/Publicity.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div class="c-Publicity"> | ||
| 3 | <div class="message"> | ||
| 4 | <span class="title"> | ||
| 5 | {{ config.name }} | ||
| 6 | </span> | ||
| 7 | <span v-html="message"> </span> | ||
| 8 | </div> | ||
| 9 | </div> | ||
| 10 | </template> | ||
| 11 | <script> | ||
| 12 | import { conversionCategoryName } from '@/helper/index'; | ||
| 13 | |||
| 14 | export default { | ||
| 15 | name: 'Publicity', | ||
| 16 | computed: { | ||
| 17 | config() { | ||
| 18 | return this.$store.state.config; | ||
| 19 | }, | ||
| 20 | result() { | ||
| 21 | return this.$store.state.result; | ||
| 22 | }, | ||
| 23 | message() { | ||
| 24 | const { | ||
| 25 | specialAward, | ||
| 26 | additionalPrize1, | ||
| 27 | additionalPrize2, | ||
| 28 | additionalPrize3 | ||
| 29 | } = this.config; | ||
| 30 | const fields = [ | ||
| 31 | 'firstPrize', | ||
| 32 | 'secondPrize', | ||
| 33 | 'thirdPrize', | ||
| 34 | 'fourthPrize', | ||
| 35 | 'fifthPrize' | ||
| 36 | ]; | ||
| 37 | if (specialAward > 0) { | ||
| 38 | fields.unshift('specialAward'); | ||
| 39 | } | ||
| 40 | if (additionalPrize1 > 0) { | ||
| 41 | fields.push('additionalPrize1'); | ||
| 42 | } | ||
| 43 | if (additionalPrize2 > 0) { | ||
| 44 | fields.push('additionalPrize2'); | ||
| 45 | } | ||
| 46 | if (additionalPrize3 > 0) { | ||
| 47 | fields.push('additionalPrize3'); | ||
| 48 | } | ||
| 49 | const { result } = this; | ||
| 50 | |||
| 51 | let message = ''; | ||
| 52 | fields.forEach(item => { | ||
| 53 | let label = conversionCategoryName(item); | ||
| 54 | message += ` ${label}抽奖结果: ${ | ||
| 55 | result[item].length > 0 ? result[item].join('、') : '暂未抽取' | ||
| 56 | }`; | ||
| 57 | }); | ||
| 58 | |||
| 59 | return message; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | }; | ||
| 63 | </script> | ||
| 64 | <style lang="scss"> | ||
| 65 | .c-Publicity { | ||
| 66 | height: 100%; | ||
| 67 | width: 1000px; | ||
| 68 | background-color: rgba(0, 0, 0, 0.2); | ||
| 69 | margin: 0 auto; | ||
| 70 | position: relative; | ||
| 71 | overflow: hidden; | ||
| 72 | .message { | ||
| 73 | font-size: 16px; | ||
| 74 | color: #fff; | ||
| 75 | position: absolute; | ||
| 76 | left: 500px; | ||
| 77 | animation-name: slowMovingToLeft; | ||
| 78 | animation-iteration-count: infinite; | ||
| 79 | animation-timing-function: linear; | ||
| 80 | animation-direction: normal; | ||
| 81 | animation-duration: 40s; | ||
| 82 | .title { | ||
| 83 | color: #ff2200; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | </style> |
src/components/Result.vue
0 → 100644
| 1 | <template> | ||
| 2 | <el-dialog | ||
| 3 | :visible="visible" | ||
| 4 | @close="$emit('update:visible', false)" | ||
| 5 | title="抽奖结果" | ||
| 6 | width="600px" | ||
| 7 | class="c-Result" | ||
| 8 | > | ||
| 9 | <div | ||
| 10 | v-for="(item, index) in resultList" | ||
| 11 | :key="index" | ||
| 12 | class="listrow" | ||
| 13 | @click=" | ||
| 14 | event => { | ||
| 15 | deleteRes(event, item); | ||
| 16 | } | ||
| 17 | " | ||
| 18 | > | ||
| 19 | <span class="name"> | ||
| 20 | {{ item.name }} | ||
| 21 | </span> | ||
| 22 | <span class="value"> | ||
| 23 | <span v-if="item.value && item.value.length === 0"> | ||
| 24 | 暂未抽奖 | ||
| 25 | </span> | ||
| 26 | <span | ||
| 27 | class="card" | ||
| 28 | v-for="(data, j) in item.value" | ||
| 29 | :key="j" | ||
| 30 | :data-res="data" | ||
| 31 | > | ||
| 32 | {{ data }} | ||
| 33 | </span> | ||
| 34 | </span> | ||
| 35 | </div> | ||
| 36 | </el-dialog> | ||
| 37 | </template> | ||
| 38 | <script> | ||
| 39 | import { conversionCategoryName, getDomData } from '@/helper/index'; | ||
| 40 | export default { | ||
| 41 | name: 'c-Result', | ||
| 42 | props: { | ||
| 43 | visible: Boolean | ||
| 44 | }, | ||
| 45 | computed: { | ||
| 46 | result: { | ||
| 47 | get() { | ||
| 48 | return this.$store.state.result; | ||
| 49 | }, | ||
| 50 | set(val) { | ||
| 51 | this.$store.commit('setResult', val); | ||
| 52 | } | ||
| 53 | }, | ||
| 54 | resultList() { | ||
| 55 | const list = []; | ||
| 56 | for (const key in this.result) { | ||
| 57 | if (this.result.hasOwnProperty(key)) { | ||
| 58 | const element = this.result[key]; | ||
| 59 | let name = conversionCategoryName(key); | ||
| 60 | list.push({ | ||
| 61 | label: key, | ||
| 62 | name, | ||
| 63 | value: element | ||
| 64 | }); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | return list; | ||
| 68 | } | ||
| 69 | }, | ||
| 70 | methods: { | ||
| 71 | deleteRes(event, row) { | ||
| 72 | this.$confirm('此操作将移除该中奖号码,确认删除?', '警告', { | ||
| 73 | confirmButtonText: '确定', | ||
| 74 | cancelButtonText: '取消', | ||
| 75 | type: 'warning' | ||
| 76 | }) | ||
| 77 | .then(() => { | ||
| 78 | const Index = getDomData(event.target, 'res'); | ||
| 79 | if (Index) { | ||
| 80 | const result = this.result; | ||
| 81 | result[row.label] = this.result[row.label].filter( | ||
| 82 | item => item !== Number(Index) | ||
| 83 | ); | ||
| 84 | this.result = result; | ||
| 85 | this.$message({ | ||
| 86 | type: 'success', | ||
| 87 | message: '删除成功!' | ||
| 88 | }); | ||
| 89 | } | ||
| 90 | }) | ||
| 91 | .catch(() => { | ||
| 92 | this.$message({ | ||
| 93 | type: 'info', | ||
| 94 | message: '已取消' | ||
| 95 | }); | ||
| 96 | }); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | }; | ||
| 100 | </script> | ||
| 101 | <style lang="scss"> | ||
| 102 | .c-Result { | ||
| 103 | .listrow { | ||
| 104 | display: flex; | ||
| 105 | line-height: 30px; | ||
| 106 | .name { | ||
| 107 | width: 80px; | ||
| 108 | font-weight: bold; | ||
| 109 | } | ||
| 110 | .value { | ||
| 111 | flex: 1; | ||
| 112 | } | ||
| 113 | .card { | ||
| 114 | display: inline-block; | ||
| 115 | width: 40px; | ||
| 116 | height: 40px; | ||
| 117 | line-height: 40px; | ||
| 118 | text-align: center; | ||
| 119 | font-size: 18px; | ||
| 120 | font-weight: bold; | ||
| 121 | border-radius: 4px; | ||
| 122 | border: 1px solid #ccc; | ||
| 123 | background-color: #f2f2f2; | ||
| 124 | margin-left: 5px; | ||
| 125 | margin-top: 5px; | ||
| 126 | position: relative; | ||
| 127 | cursor: pointer; | ||
| 128 | &:hover { | ||
| 129 | &::before { | ||
| 130 | content: '删除'; | ||
| 131 | width: 100%; | ||
| 132 | height: 100%; | ||
| 133 | background-color: #ccc; | ||
| 134 | position: absolute; | ||
| 135 | left: 0; | ||
| 136 | top: 0; | ||
| 137 | color: red; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | } | ||
| 143 | </style> |
src/components/Tool.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div id="tool"> | ||
| 3 | <el-button @click="startHandler" size="mini">{{ | ||
| 4 | running ? '停止' : '开始' | ||
| 5 | }}</el-button> | ||
| 6 | <el-button size="mini" @click="resetConfig"> | ||
| 7 | 重置 | ||
| 8 | </el-button> | ||
| 9 | <el-dialog | ||
| 10 | :append-to-body="true" | ||
| 11 | :visible.sync="showSetwat" | ||
| 12 | class="setwat-dialog" | ||
| 13 | width="400px" | ||
| 14 | > | ||
| 15 | <el-form ref="form" :model="form" label-width="80px" size="mini"> | ||
| 16 | <el-form-item label="抽取奖项"> | ||
| 17 | <el-select v-model="form.category" placeholder="请选取本次抽取的奖项"> | ||
| 18 | <el-option | ||
| 19 | :label="item.label" | ||
| 20 | :value="item.value" | ||
| 21 | v-for="(item, index) in categorys" | ||
| 22 | :key="index" | ||
| 23 | ></el-option> | ||
| 24 | </el-select> | ||
| 25 | </el-form-item> | ||
| 26 | |||
| 27 | <el-form-item label=" " v-if="form.category"> | ||
| 28 | <span> | ||
| 29 | 共<span class="colorred">{{ config[form.category] }}</span | ||
| 30 | >名 | ||
| 31 | </span> | ||
| 32 | <span :style="{ marginLeft: '20px' }"> | ||
| 33 | 剩余<span class="colorred">{{ remain }}</span | ||
| 34 | >名 | ||
| 35 | </span> | ||
| 36 | </el-form-item> | ||
| 37 | |||
| 38 | <el-form-item label="抽取方式"> | ||
| 39 | <el-select v-model="form.mode" placeholder="请选取本次抽取方式"> | ||
| 40 | <el-option label="抽1人" :value="1"></el-option> | ||
| 41 | <el-option label="抽5人" :value="5"></el-option> | ||
| 42 | <el-option label="一次抽取完" :value="0"></el-option> | ||
| 43 | <el-option label="自定义" :value="99"></el-option> | ||
| 44 | </el-select> | ||
| 45 | </el-form-item> | ||
| 46 | |||
| 47 | <el-form-item label="抽取人数" v-if="form.mode === 99"> | ||
| 48 | <el-input | ||
| 49 | v-model="form.qty" | ||
| 50 | type="number" | ||
| 51 | :clearable="true" | ||
| 52 | :min="1" | ||
| 53 | :max="remain ? remain : 100" | ||
| 54 | :step="1" | ||
| 55 | ></el-input> | ||
| 56 | </el-form-item> | ||
| 57 | |||
| 58 | <el-form-item label="全员参与"> | ||
| 59 | <el-switch v-model="form.allin"></el-switch> | ||
| 60 | <span :style="{ fontSize: '12px' }"> | ||
| 61 | (开启后将在全体成员[无论有无中奖]中抽奖) | ||
| 62 | </span> | ||
| 63 | </el-form-item> | ||
| 64 | |||
| 65 | <el-form-item> | ||
| 66 | <el-button type="primary" @click="onSubmit">立即抽奖</el-button> | ||
| 67 | <el-button @click="showSetwat = false">取消</el-button> | ||
| 68 | </el-form-item> | ||
| 69 | </el-form> | ||
| 70 | </el-dialog> | ||
| 71 | </div> | ||
| 72 | </template> | ||
| 73 | |||
| 74 | <script> | ||
| 75 | import { clearData, conversionCategoryName } from '@/helper/index'; | ||
| 76 | |||
| 77 | export default { | ||
| 78 | props: { | ||
| 79 | running: Boolean | ||
| 80 | }, | ||
| 81 | computed: { | ||
| 82 | config: { | ||
| 83 | get() { | ||
| 84 | return this.$store.state.config; | ||
| 85 | } | ||
| 86 | }, | ||
| 87 | remain() { | ||
| 88 | return ( | ||
| 89 | this.config[this.form.category] - | ||
| 90 | (this.result[this.form.category] | ||
| 91 | ? this.result[this.form.category].length | ||
| 92 | : 0) | ||
| 93 | ); | ||
| 94 | }, | ||
| 95 | result() { | ||
| 96 | return this.$store.state.result; | ||
| 97 | }, | ||
| 98 | categorys() { | ||
| 99 | const options = []; | ||
| 100 | for (const key in this.config) { | ||
| 101 | if (this.config.hasOwnProperty(key)) { | ||
| 102 | const item = this.config[key]; | ||
| 103 | if (item > 0) { | ||
| 104 | let name = conversionCategoryName(key); | ||
| 105 | name && | ||
| 106 | options.push({ | ||
| 107 | label: name, | ||
| 108 | value: key | ||
| 109 | }); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | return options; | ||
| 114 | } | ||
| 115 | }, | ||
| 116 | data() { | ||
| 117 | return { | ||
| 118 | showSetwat: false, | ||
| 119 | form: { | ||
| 120 | category: '', | ||
| 121 | mode: 1, | ||
| 122 | qty: 1, | ||
| 123 | allin: false | ||
| 124 | } | ||
| 125 | }; | ||
| 126 | }, | ||
| 127 | methods: { | ||
| 128 | resetConfig() { | ||
| 129 | this.$confirm('此操作将重置所有数据,是否继续?', '提示', { | ||
| 130 | confirmButtonText: '确定', | ||
| 131 | cancelButtonText: '取消', | ||
| 132 | type: 'warning' | ||
| 133 | }) | ||
| 134 | .then(() => { | ||
| 135 | clearData(); | ||
| 136 | this.$message({ | ||
| 137 | type: 'success', | ||
| 138 | message: '重置成功!' | ||
| 139 | }); | ||
| 140 | setTimeout(() => { | ||
| 141 | window.location.reload(); | ||
| 142 | }, 3000); | ||
| 143 | }) | ||
| 144 | .catch(() => { | ||
| 145 | this.$message({ | ||
| 146 | type: 'info', | ||
| 147 | message: '已取消' | ||
| 148 | }); | ||
| 149 | }); | ||
| 150 | }, | ||
| 151 | onSubmit() { | ||
| 152 | if (!this.form.category) { | ||
| 153 | return this.$message.error('请选择本次抽取的奖项'); | ||
| 154 | } | ||
| 155 | if (this.remain <= 0) { | ||
| 156 | return this.$message.error('该奖项剩余人数不足'); | ||
| 157 | } | ||
| 158 | if (this.form.mode === 99) { | ||
| 159 | if (this.form.qty <= 0) { | ||
| 160 | return this.$message.error('必须输入本次抽取人数'); | ||
| 161 | } | ||
| 162 | if (this.form.qty > this.remain) { | ||
| 163 | return this.$message.error('本次抽奖人数已超过本奖项的剩余人数'); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | if (this.form.mode === 1 || this.form.mode === 5) { | ||
| 167 | if (this.form.mode > this.remain) { | ||
| 168 | return this.$message.error('本次抽奖人数已超过本奖项的剩余人数'); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | this.showSetwat = false; | ||
| 172 | this.$emit( | ||
| 173 | 'toggle', | ||
| 174 | Object.assign({}, this.form, { remain: this.remain }) | ||
| 175 | ); | ||
| 176 | }, | ||
| 177 | startHandler() { | ||
| 178 | if (this.running) { | ||
| 179 | this.$emit('toggle'); | ||
| 180 | } else { | ||
| 181 | this.showSetwat = true; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | } | ||
| 185 | }; | ||
| 186 | </script> | ||
| 187 | <style lang="scss"> | ||
| 188 | #tool { | ||
| 189 | position: fixed; | ||
| 190 | width: 60px; | ||
| 191 | height: 500px; | ||
| 192 | top: 50%; | ||
| 193 | right: 20px; | ||
| 194 | transform: translateY(-50%); | ||
| 195 | text-align: center; | ||
| 196 | display: flex; | ||
| 197 | flex-direction: column; | ||
| 198 | align-items: center; | ||
| 199 | justify-content: center; | ||
| 200 | .el-button + .el-button { | ||
| 201 | margin-top: 10px; | ||
| 202 | margin-left: 0px; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | .setwat-dialog { | ||
| 206 | .colorred { | ||
| 207 | color: red; | ||
| 208 | font-weight: bold; | ||
| 209 | } | ||
| 210 | } | ||
| 211 | </style> |
src/helper/algorithm.js
0 → 100644
| 1 | /** | ||
| 2 | * 取范围内随机整数 | ||
| 3 | * @param {number} minNum | ||
| 4 | * @param {number} maxNum | ||
| 5 | */ | ||
| 6 | function randomNum(minNum = 1, maxNum) { | ||
| 7 | return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10); | ||
| 8 | } | ||
| 9 | /** | ||
| 10 | * 单次抽奖 | ||
| 11 | * @param {number} total 总人数 | ||
| 12 | * @param {array} won 已中奖 | ||
| 13 | * @param {number} num 本次抽取人数 | ||
| 14 | */ | ||
| 15 | export function luckydrawHandler(total, won = [], num) { | ||
| 16 | const peolist = []; | ||
| 17 | for (let i = 1; i <= total; i++) { | ||
| 18 | peolist.push(i); | ||
| 19 | } | ||
| 20 | |||
| 21 | const wons = won; | ||
| 22 | |||
| 23 | const res = []; | ||
| 24 | for (let j = 0; j < num; j++) { | ||
| 25 | const nodraws = peolist.filter(item => !wons.includes(item)); | ||
| 26 | const current = nodraws[randomNum(1, nodraws.length) - 1]; | ||
| 27 | res.push(current); | ||
| 28 | wons.push(current); | ||
| 29 | } | ||
| 30 | return res; | ||
| 31 | } |
src/helper/index.js
0 → 100644
| 1 | export function setData(key, value) { | ||
| 2 | if (typeof value === 'string') { | ||
| 3 | return localStorage.setItem(key, value); | ||
| 4 | } | ||
| 5 | try { | ||
| 6 | localStorage.setItem(key, JSON.stringify(value)); | ||
| 7 | } catch (err) { | ||
| 8 | return err; | ||
| 9 | } | ||
| 10 | } | ||
| 11 | |||
| 12 | export function getData(key) { | ||
| 13 | const value = localStorage.getItem(key); | ||
| 14 | try { | ||
| 15 | return JSON.parse(value); | ||
| 16 | } catch (err) { | ||
| 17 | return value; | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | export function removeData(key) { | ||
| 22 | return localStorage.removeItem(key); | ||
| 23 | } | ||
| 24 | |||
| 25 | export function clearData() { | ||
| 26 | return localStorage.clear(); | ||
| 27 | } | ||
| 28 | |||
| 29 | export function getDomData(element, dataName) { | ||
| 30 | if (!element || !dataName || !element.getAttribute) { | ||
| 31 | return; | ||
| 32 | } | ||
| 33 | return element.getAttribute('data-' + dataName); | ||
| 34 | } | ||
| 35 | |||
| 36 | export const configField = 'config'; // 配置数据 | ||
| 37 | export const resultField = 'result'; // 抽奖结果 | ||
| 38 | |||
| 39 | export function conversionCategoryName(key) { | ||
| 40 | let name = ''; | ||
| 41 | switch (key) { | ||
| 42 | case 'specialAward': | ||
| 43 | name = '特等奖'; | ||
| 44 | break; | ||
| 45 | case 'firstPrize': | ||
| 46 | name = '一等奖'; | ||
| 47 | break; | ||
| 48 | case 'secondPrize': | ||
| 49 | name = '二等奖'; | ||
| 50 | break; | ||
| 51 | case 'thirdPrize': | ||
| 52 | name = '三等奖'; | ||
| 53 | break; | ||
| 54 | case 'fourthPrize': | ||
| 55 | name = '四等奖'; | ||
| 56 | break; | ||
| 57 | case 'fifthPrize': | ||
| 58 | name = '五等奖'; | ||
| 59 | break; | ||
| 60 | case 'additionalPrize1': | ||
| 61 | name = '追加奖(1)'; | ||
| 62 | break; | ||
| 63 | case 'additionalPrize2': | ||
| 64 | name = '追加奖(2)'; | ||
| 65 | break; | ||
| 66 | case 'additionalPrize3': | ||
| 67 | name = '追加奖(3)'; | ||
| 68 | break; | ||
| 69 | case 'additionalPrize4': | ||
| 70 | name = '追加奖(4)'; | ||
| 71 | break; | ||
| 72 | case 'additionalPrize5': | ||
| 73 | name = '追加奖(5)'; | ||
| 74 | break; | ||
| 75 | |||
| 76 | default: | ||
| 77 | break; | ||
| 78 | } | ||
| 79 | return name; | ||
| 80 | } |
src/main.js
0 → 100644
| 1 | import Vue from 'vue'; | ||
| 2 | import App from './App.vue'; | ||
| 3 | import router from './router'; | ||
| 4 | import store from './store'; | ||
| 5 | import Element from 'element-ui'; | ||
| 6 | import '@/assets/style/index.scss'; | ||
| 7 | import 'element-ui/lib/theme-chalk/index.css'; | ||
| 8 | import '@/assets/lib/tagcanvas.js'; | ||
| 9 | Vue.config.productionTip = false; | ||
| 10 | |||
| 11 | Vue.use(Element); | ||
| 12 | |||
| 13 | new Vue({ | ||
| 14 | router, | ||
| 15 | store, | ||
| 16 | render: h => h(App) | ||
| 17 | }).$mount('#app'); |
src/router/index.js
0 → 100644
| 1 | import Vue from 'vue'; | ||
| 2 | import VueRouter from 'vue-router'; | ||
| 3 | import Home from '../views/Home.vue'; | ||
| 4 | |||
| 5 | Vue.use(VueRouter); | ||
| 6 | |||
| 7 | const routes = [ | ||
| 8 | { | ||
| 9 | path: '/', | ||
| 10 | name: 'home', | ||
| 11 | component: Home | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | path: '/about', | ||
| 15 | name: 'about', | ||
| 16 | // route level code-splitting | ||
| 17 | // this generates a separate chunk (about.[hash].js) for this route | ||
| 18 | // which is lazy-loaded when the route is visited. | ||
| 19 | component: () => | ||
| 20 | import(/* webpackChunkName: "about" */ '../views/About.vue') | ||
| 21 | } | ||
| 22 | ]; | ||
| 23 | |||
| 24 | const router = new VueRouter({ | ||
| 25 | base: process.env.BASE_URL, | ||
| 26 | routes | ||
| 27 | }); | ||
| 28 | |||
| 29 | export default router; |
src/store/index.js
0 → 100644
| 1 | import Vue from 'vue'; | ||
| 2 | import Vuex from 'vuex'; | ||
| 3 | import { setData, resultField } from '@/helper/index'; | ||
| 4 | |||
| 5 | Vue.use(Vuex); | ||
| 6 | |||
| 7 | export default new Vuex.Store({ | ||
| 8 | state: { | ||
| 9 | config: { | ||
| 10 | name: '年会抽奖', | ||
| 11 | number: 70, | ||
| 12 | specialAward: 0, | ||
| 13 | firstPrize: 1, | ||
| 14 | secondPrize: 5, | ||
| 15 | thirdPrize: 8, | ||
| 16 | fourthPrize: 10, | ||
| 17 | fifthPrize: 20, | ||
| 18 | additionalPrize1: 0, | ||
| 19 | additionalPrize2: 0, | ||
| 20 | additionalPrize3: 0, | ||
| 21 | additionalPrize4: 0, | ||
| 22 | additionalPrize5: 0 | ||
| 23 | }, | ||
| 24 | result: { | ||
| 25 | specialAward: [], | ||
| 26 | firstPrize: [], | ||
| 27 | secondPrize: [], | ||
| 28 | thirdPrize: [], | ||
| 29 | fourthPrize: [], | ||
| 30 | fifthPrize: [], | ||
| 31 | additionalPrize1: [], | ||
| 32 | additionalPrize2: [], | ||
| 33 | additionalPrize3: [], | ||
| 34 | additionalPrize4: [], | ||
| 35 | additionalPrize5: [] | ||
| 36 | } | ||
| 37 | }, | ||
| 38 | mutations: { | ||
| 39 | setConfig(state, config) { | ||
| 40 | state.config = config; | ||
| 41 | }, | ||
| 42 | setResult(state, result = {}) { | ||
| 43 | Object.assign(state.result, result); | ||
| 44 | |||
| 45 | setData(resultField, state.result); | ||
| 46 | } | ||
| 47 | }, | ||
| 48 | actions: {}, | ||
| 49 | modules: {} | ||
| 50 | }); |
src/views/About.vue
0 → 100644
src/views/Home.vue
0 → 100644
vue.config.js
0 → 100644
yarn.lock
0 → 100644
This diff could not be displayed because it is too large.
-
Please register or sign in to post a comment