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