long
2022-01-24 c40e9437492f6973e631f42d8859d50182f777df
提交 | 用户 | age
2a61f6 1 <template>
L 2   <div class="login-container">
3     <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left">
4
5       <div class="title-container">
36b711 6         <h3 class="title">”项目名称“</h3>
2a61f6 7       </div>
L 8
9       <el-form-item prop="username">
10         <span class="svg-container">
11           <svg-icon icon-class="user" />
12         </span>
13         <el-input
14           ref="username"
15           v-model="loginForm.username"
16           placeholder="请输入账号"
17           name="username"
18           type="text"
19           tabindex="1"
20           autocomplete="on"
36b711 21           maxlength="20"
2a61f6 22           @keyup.enter.native="handleLogin"
L 23         />
24       </el-form-item>
25
26       <el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual>
27         <el-form-item prop="password">
28           <span class="svg-container">
29             <svg-icon icon-class="password" />
30           </span>
31           <el-input
32             :key="passwordType"
33             ref="password"
34             v-model="loginForm.password"
35             :type="passwordType"
36             placeholder="请输入密码"
37             name="password"
38             tabindex="2"
39             autocomplete="on"
36b711 40             maxlength="20"
2a61f6 41             @keyup.native="checkCapslock"
L 42             @blur="capsTooltip = false"
43             @keyup.enter.native="handleLogin"
44           />
45           <span class="show-pwd" @click="showPwd">
46             <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
47           </span>
48         </el-form-item>
49       </el-tooltip>
50
51       <el-form-item prop="safecode">
52         <span class="svg-container">
53           <svg-icon icon-class="safecode" />
54         </span>
55         <el-input
56           ref="safecode"
57           v-model="loginForm.safecode"
58           style="width: 60%"
59           placeholder="请输入验证码"
60           name="safecode"
61           type="text"
62           tabindex="3"
36b711 63           maxlength="10"
2a61f6 64           autocomplete="off"
L 65           @keyup.enter.native="handleLogin"
66         />
67         <el-image
68           class="safecode-img"
69           :src="safecodeImg"
70           fit="fill"
71           @click="getSafeCodeImg"
72         >
73           <div slot="error" class="image-slot">
74             <i class="el-icon-picture-outline" />
75           </div>
76         </el-image>
77       </el-form-item>
78
79       <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button>
80     </el-form>
81   </div>
82 </template>
83
84 <script>
85 import { setToken, setUserData } from '@/utils/auth' // get token from session
86
87 export default {
88   name: 'Login',
89   components: {},
90   data() {
91     const validateUsername = (rule, value, callback) => {
92       if (!value) {
93         callback(new Error('请输入账号'))
94       } else {
95         callback()
96       }
97     }
98     const validatePassword = (rule, value, callback) => {
99       if (value.length < 5) {
d9cc69 100         callback(new Error('密码不能少于5位'))
2a61f6 101       } else {
L 102         callback()
103       }
104     }
105     const validateSafecode = (rule, value, callback) => {
106       if (!value) {
107         callback(new Error('请输入验证码'))
108       } else {
109         callback()
110       }
111     }
112     return {
113       loginForm: {
114         username: '',
115         password: '',
116         safecode: '',
117         checkedId: ''
118       },
119       loginRules: {
36b711 120         username: [{ required: true, trigger: 'blur', validator: validateUsername }],
L 121         password: [{ required: true, trigger: 'blur', validator: validatePassword }],
122         safecode: [{ required: true, trigger: 'blur', validator: validateSafecode }]
2a61f6 123       },
L 124       passwordType: 'password',
125       capsTooltip: false,
126       loading: false,
127       showDialog: false,
128       redirect: undefined,
129       otherQuery: {},
130       safecodeImg: ''
131     }
132   },
133   watch: {
134     $route: {
135       handler: function(route) {
136         const query = route.query
137         if (query) {
138           this.redirect = query.redirect
139           this.otherQuery = this.getOtherQuery(query)
140         }
141       },
142       immediate: true
143     }
144   },
145   mounted() {
146     this.init()
147   },
148   methods: {
149     init() {
36b711 150       var loginData = localStorage.getItem('loginData_XXXX')
2a61f6 151       // 获取保存的账号密码
L 152       if (loginData) this.loginForm = JSON.parse(loginData)
153
154       if (this.loginForm.username === '') {
155         this.$refs.username.focus()
156       } else if (this.loginForm.password === '') {
157         this.$refs.password.focus()
158       } else if (this.loginForm.safecode === '') {
159         this.$refs.safecode.focus()
160       }
161
162       this.getSafeCodeImg()
163     },
164     checkCapslock(e) {
165       const { key } = e
166       this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z')
167     },
168     showPwd() {
169       if (this.passwordType === 'password') {
170         this.passwordType = ''
171       } else {
172         this.passwordType = 'password'
173       }
174       this.$nextTick(() => {
175         this.$refs.password.focus()
176       })
177     },
178     // 获取验证码图
179     getSafeCodeImg() {
180       var checkedId = this.getCheckedId()
181       this.postFN({
182         url: `admin/create/cheked`,
183         params: { checkedId: checkedId },
184         udData: { noToken: true },
185         mockData: {
186           code: 100,
187           msg: '',
188           data: {
189             imgStr: 'xxxx'
190           }
191         }
192       }, (inf) => {
193         this.safecodeImg = this.perfectSafeCode(inf.imgStr)
194         this.loginForm.checkedId = checkedId
195       })
196     },
197     // 补全验证码
198     perfectSafeCode(img) {
199       if (!img) return
200       return `data:image/jpeg;base64,${img}`
201     },
202     // 登录
203     handleLogin() {
204       this.$refs.loginForm.validate(valid => {
205         if (valid) {
206           const { username, password, safecode, checkedId } = this.loginForm
207           const params = {
208             account: username.trim(),
209             password: password.trim(),
210             checkedId: checkedId,
211             checkedCode: safecode.trim()
212           }
213           this.loading = true
214           this.postFN({
215             url: 'admin/login',
216             params: params,
217             udData: { noToken: true },
218             mockData: {
219               code: 100,
220               msg: '',
221               data: {
222                 adminToken: 'mockdAmintoken', // 登录凭证
223                 name: '潘多拉的臭嘴', // 用户名称
224                 // arr: ['sys_admin_see'], // 权限列表
225                 arr: ['sys_admin_see', 'sys_admin_add', 'sys_admin_edit', 'sys_admin_del', 'sys_admin_role_see', 'sys_admin_role_add', 'sys_admin_role_edit', 'sys_admin_role_del', 'corp_user_schedule_see', 'corp_user_schedule_add', 'corp_user_schedule_edit', 'corp_user_schedule_del', 'user_mp_banner_see', 'user_mp_banner_add', 'user_mp_banner_edit', 'user_mp_banner_del', 'shop_see', 'shop_add', 'shop_edit', 'shop_del', 'shop_device', 'corp_user_see', 'corp_user_add', 'corp_user_edit', 'corp_user_del', 'change_assistant_see', 'ser_pro_material_see', 'service_project_see', 'service_project_add', 'service_project_edit', 'service_project_del', 'service_topic_see', 'service_topic_add', 'service_topic_edit', 'service_topic_del', 'service_project_order_see', 'service_project_order_edit', 'goods_type_see', 'goods_type_add', 'goods_type_edit', 'goods_type_del', 'goods_see', 'goods_add', 'goods_edit', 'goods_del', 'goods_inventory', 'goods_order_see', 'goods_order_edit', 'goods_index_banner_see', 'goods_index_banner_add', 'goods_index_banner_edit', 'goods_index_banner_del', 'user_see', 'user_edit', 'user_cash_see', 'user_cash_edit', 'distribute_relate', 'distribute_order', 'document_edit', 'operation_see',
133cfa 226                   'coupon_see', 'coupon_add', 'coupon_edit', 'coupon_del', 'activity_see', 'activity_add', 'activity_edit', 'activity_del', 'banner_see', 'banner_add', 'banner_edit', 'banner_del', 'sys_text_explain_edit'
2a61f6 227                 ]
L 228               }
229             }
230           }, (inf) => {
231             // 保存登录凭证
232             setToken(inf.adminToken)
233
234             // 保存账号密码
235             var loginData = JSON.stringify({
236               username: this.loginForm.username.trim(),
237               password: this.loginForm.password.trim()
238             })
36b711 239             localStorage.setItem('loginData_XXXX', loginData)
2a61f6 240
L 241             // 保存用户数据
242             setUserData(inf)
243
244             this.loading = false
245
246             // 登录成功跳转页面
247             this.$messageSuc('登录成功')
248             setTimeout(() => {
249               this.$router.push({ path: this.redirect || '/activity/index', query: this.otherQuery })
250             }, 1000)
251           }, (res) => {
252             this.loading = false
253             this.$messageError(res.msg)
254           })
255         } else {
256           console.log('表单验证失败')
257           return false
258         }
259       })
260     },
261     getOtherQuery(query) {
262       return Object.keys(query).reduce((acc, cur) => {
263         if (cur !== 'redirect') {
264           acc[cur] = query[cur]
265         }
266         return acc
267       }, {})
268     }
269   }
270 }
271 </script>
272
273 <style lang="scss">
274 /* 修复input 背景不协调 和光标变色 */
275 /* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
276
277 $bg:#283443;
278 $light_gray:#fff;
279 $cursor: #fff;
280
281 @supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
282   .login-container .el-input input {
283     color: $cursor;
284   }
285 }
286
287 /* reset element-ui css */
288 .login-container {
36b711 289   // 背景 ↓↓↓↓↓↓↓↓↓↓
L 290   // background: url(../../assets/imgs/loginBg.jpg) no-repeat;
291   // background-size: cover;
292   // 背景 ↑↑↑↑↑↑↑↑↑↑
293   background-color: $bg;
2a61f6 294   .el-input {
L 295     display: inline-block;
296     height: 47px;
297     width: 85%;
298
299     input {
300       background: transparent;
301       border: 0px;
302       -webkit-appearance: none;
303       border-radius: 0px;
304       padding: 12px 5px 12px 15px;
305       color: $light_gray;
306       height: 47px;
307       caret-color: $cursor;
308
309       &:-webkit-autofill {
310         box-shadow: 0 0 0px 1000px $bg inset !important;
311         -webkit-text-fill-color: $cursor !important;
312       }
313     }
314
315   }
316
317   .el-form-item {
318     border: 1px solid rgba(255, 255, 255, 0.1);
319     background: rgba(0, 0, 0, 0.1);
320     border-radius: 5px;
321     color: #454545;
322   }
323 }
324 </style>
325
326 <style lang="scss" scoped>
327 $bg:#2d3a4b;
328 $dark_gray:#889aa4;
329 $light_gray:#eee;
330
331 .login-container {
332   min-height: 100%;
333   width: 100%;
36b711 334   // background-color: $bg;
2a61f6 335   overflow: hidden;
L 336
337   .login-form {
338     position: relative;
339     width: 520px;
340     max-width: 100%;
36b711 341     padding: 35px 35px 0;
2a61f6 342     margin: 0 auto;
36b711 343     margin-top: 160px;
2a61f6 344     overflow: hidden;
36b711 345     // 背景 ↓↓↓↓↓↓↓↓↓↓
L 346     // background-color: $bg;
347     // opacity: .96;
348     // border-radius: 10px;
349     // box-shadow: 0px 20px 60px rgba(0,0,0,0.4), 0px 0px 150px rgba(0,0,0,0.4);
350     // 背景 ↑↑↑↑↑↑↑↑↑↑
2a61f6 351   }
L 352
353   .tips {
354     font-size: 14px;
355     color: #fff;
356     margin-bottom: 10px;
357
358     span {
359       &:first-of-type {
360         margin-right: 16px;
361       }
362     }
363   }
364
365   .svg-container {
366     padding: 6px 5px 6px 15px;
367     color: $dark_gray;
368     vertical-align: middle;
369     width: 30px;
370     display: inline-block;
371   }
372
373   .title-container {
374     position: relative;
375
376     .title {
377       font-size: 26px;
378       color: $light_gray;
379       margin: 0px auto 40px auto;
380       text-align: center;
381       font-weight: bold;
382     }
383   }
384
385   .show-pwd {
386     position: absolute;
387     right: 10px;
388     top: 7px;
389     font-size: 16px;
390     color: $dark_gray;
391     cursor: pointer;
392     user-select: none;
393   }
394
395   .safecode-img{
396     width: 100px;
397     height: 80%;
398     position: absolute;
399     right: 10px;
400     top: 50%;
401     transform: translateY(-50%);
402     font-size: 30px;
403     text-align: right;
404     color: #999;
405     cursor: pointer;
406   }
407
408   .thirdparty-button {
409     position: absolute;
410     right: 0;
411     bottom: 6px;
412   }
413
414   @media only screen and (max-width: 470px) {
415     .thirdparty-button {
416       display: none;
417     }
418   }
419 }
420 </style>