1、对接谷歌验证器
This commit is contained in:
		| @ -64,6 +64,7 @@ | ||||
|     "normalize.css": "8.0.1", | ||||
|     "nprogress": "0.2.0", | ||||
|     "path-to-regexp": "6.1.0", | ||||
|     "qrcode": "^1.5.4", | ||||
|     "remixicon": "^2.5.0", | ||||
|     "sass-resources-loader": "^2.0.3", | ||||
|     "screenfull": "5.0.2", | ||||
|  | ||||
| @ -53,3 +53,12 @@ export function clearAllAlarmLog() { | ||||
|     }) | ||||
| } | ||||
|  | ||||
| //导出告警记录 | ||||
| export function exportMmAlarmLog(query) { | ||||
|     return request({ | ||||
|         url: '/api/v1/mm-alarm-log/export', | ||||
|         method: 'get', | ||||
|         params: query, | ||||
|         responseType: 'blob', | ||||
|     }) | ||||
| } | ||||
|  | ||||
| @ -1,142 +1,178 @@ | ||||
| import request from '@/utils/request' | ||||
| import request from "@/utils/request"; | ||||
|  | ||||
| // 查询用户列表 | ||||
| export function listUser(query) { | ||||
|   return request({ | ||||
|     url: '/api/v1/sys-user', | ||||
|     method: 'get', | ||||
|     params: query | ||||
|   }) | ||||
|     url: "/api/v1/sys-user", | ||||
|     method: "get", | ||||
|     params: query, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 查询用户详细 | ||||
| export function getUser(userId) { | ||||
|   return request({ | ||||
|     url: '/api/v1/sys-user/' + userId, | ||||
|     method: 'get' | ||||
|   }) | ||||
|     url: "/api/v1/sys-user/" + userId, | ||||
|     method: "get", | ||||
|   }); | ||||
| } | ||||
|  | ||||
| export function getUserInit() { | ||||
|   return request({ | ||||
|     url: '/api/v1/sys-user/', | ||||
|     method: 'get' | ||||
|   }) | ||||
|     url: "/api/v1/sys-user/", | ||||
|     method: "get", | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 新增用户 | ||||
| export function addUser(data) { | ||||
|   return request({ | ||||
|     url: '/api/v1/sys-user', | ||||
|     method: 'post', | ||||
|     data: data | ||||
|   }) | ||||
|     url: "/api/v1/sys-user", | ||||
|     method: "post", | ||||
|     data: data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 修改用户 | ||||
| export function updateUser(data) { | ||||
|   return request({ | ||||
|     url: '/api/v1/sys-user', | ||||
|     method: 'put', | ||||
|     data: data | ||||
|   }) | ||||
|     url: "/api/v1/sys-user", | ||||
|     method: "put", | ||||
|     data: data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 删除用户 | ||||
| export function delUser(data) { | ||||
|   return request({ | ||||
|     url: '/api/v1/sys-user', | ||||
|     method: 'delete', | ||||
|     data: data | ||||
|   }) | ||||
|     url: "/api/v1/sys-user", | ||||
|     method: "delete", | ||||
|     data: data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 导出用户 | ||||
| export function exportUser(query) { | ||||
|   return request({ | ||||
|     url: '/api/v1/sys-user/export', | ||||
|     method: 'get', | ||||
|     params: query | ||||
|   }) | ||||
|     url: "/api/v1/sys-user/export", | ||||
|     method: "get", | ||||
|     params: query, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 用户密码重置 | ||||
| export function resetUserPwd(userId, password) { | ||||
|   const data = { | ||||
|     userId, | ||||
|     password | ||||
|   } | ||||
|     password, | ||||
|   }; | ||||
|   return request({ | ||||
|     url: '/api/v1/user/pwd/reset', | ||||
|     method: 'put', | ||||
|     data: data | ||||
|   }) | ||||
|     url: "/api/v1/user/pwd/reset", | ||||
|     method: "put", | ||||
|     data: data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 用户状态修改 | ||||
| export function changeUserStatus(e) { | ||||
|   const data = { | ||||
|     userId: e.userId, | ||||
|     status: e.status | ||||
|   } | ||||
|     status: e.status, | ||||
|   }; | ||||
|   return request({ | ||||
|     url: '/api/v1/user/status', | ||||
|     method: 'put', | ||||
|     data: data | ||||
|   }) | ||||
|     url: "/api/v1/user/status", | ||||
|     method: "put", | ||||
|     data: data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 修改用户个人信息 | ||||
| export function updateUserProfile(data) { | ||||
|   return request({ | ||||
|     url: '/api/v1/sys-user/profile', | ||||
|     method: 'put', | ||||
|     data: data | ||||
|   }) | ||||
|     url: "/api/v1/sys-user/profile", | ||||
|     method: "put", | ||||
|     data: data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 下载用户导入模板 | ||||
| export function importTemplate() { | ||||
|   return request({ | ||||
|     url: '/api/v1/sys-user/importTemplate', | ||||
|     method: 'get' | ||||
|   }) | ||||
|     url: "/api/v1/sys-user/importTemplate", | ||||
|     method: "get", | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 用户密码重置 | ||||
| export function updateUserPwd(oldPassword, newPassword) { | ||||
|   const data = { | ||||
|     oldPassword, | ||||
|     newPassword | ||||
|   } | ||||
|     newPassword, | ||||
|   }; | ||||
|   return request({ | ||||
|     url: '/api/v1/user/pwd/set', | ||||
|     method: 'put', | ||||
|     data: data | ||||
|   }) | ||||
|     url: "/api/v1/user/pwd/set", | ||||
|     method: "put", | ||||
|     data: data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 用户头像上传 | ||||
| export function uploadAvatar(data) { | ||||
|   return request({ | ||||
|     url: '/api/v1/user/avatar', | ||||
|     method: 'post', | ||||
|     data: data | ||||
|   }) | ||||
|     url: "/api/v1/user/avatar", | ||||
|     method: "post", | ||||
|     data: data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 查询用户个人信息 | ||||
| export function getUserProfile() { | ||||
|   return request({ | ||||
|     url: '/api/v1/user/profile', | ||||
|     method: 'get' | ||||
|   }) | ||||
|     url: "/api/v1/user/profile", | ||||
|     method: "get", | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 查询用户列表 | ||||
| export function getUserOptions() { | ||||
|   return request({ | ||||
|     url: '/api/v1/sys-user/options', | ||||
|     method: 'get' | ||||
|   }) | ||||
| } | ||||
|     url: "/api/v1/sys-user/options", | ||||
|     method: "get", | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // 检查是否需要谷歌验证 | ||||
| export function needCheckGoogleAuth(headers) { | ||||
|   return request({ | ||||
|     url: "/api/v1/need-check-google-auth", | ||||
|     method: "get", | ||||
|     headers: headers, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| //验证google验证码 | ||||
| export function checkGoogleCode(params,headers) { | ||||
|   return request({ | ||||
|     url: "/api/v1/google-code", | ||||
|     method: "get", | ||||
|     params: params, | ||||
|     headers: headers, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| //判断是否需要谷歌验证 | ||||
| export function needGoolAuth() { | ||||
|   return request({ | ||||
|     url: "/api/v1/need-auth", | ||||
|     method: "get", | ||||
|   }); | ||||
| } | ||||
|  | ||||
| //绑定谷歌认证器 | ||||
| export function setGoogleAuth(data) { | ||||
|   return request({ | ||||
|     url: "/api/v1/set-google-auth", | ||||
|     method: "put", | ||||
|     data: data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
							
								
								
									
										149
									
								
								src/layout/SetGoogleSecret.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								src/layout/SetGoogleSecret.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,149 @@ | ||||
| <template> | ||||
|     <el-dialog :visible.sync="dialogVisible" title="绑定谷歌验证器" width="680px" :show-close="false" :close-on-press-escape="false" | ||||
|         :close-on-click-modal="false"  class="google-bind-dialog"> | ||||
|         <div class="dialog-body"> | ||||
|             <p class="tip-text">1. 使用 <strong>Google Authenticator</strong> 扫描二维码,或手动输入密钥添加账号</p> | ||||
|  | ||||
|             <div class="qr-section"> | ||||
|                 <canvas ref="qrCanvas" class="qr-canvas"></canvas> | ||||
|                 <div class="manual-section"> | ||||
|                     <!-- <p><strong>账户:</strong> {{ account }}</p> --> | ||||
|                     <p> | ||||
|                         <strong>密钥:</strong> | ||||
|                         <el-tag size="small" type="success">{{ secret }}</el-tag> | ||||
|                         <el-button type="text" size="mini" @click="copySecret">复制</el-button> | ||||
|                     </p> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             <el-input v-model="code" placeholder="请输入 6 位验证码" maxlength="6" class="code-input" | ||||
|                 @keyup.enter.native="submit" ref="codeInput" /> | ||||
|  | ||||
|             <div class="error-text" v-if="errorMsg">{{ errorMsg }}</div> | ||||
|         </div> | ||||
|  | ||||
|         <template #footer> | ||||
|             <el-button type="primary" :loading="loading" :disabled="code.length !== 6" @click="submit"> | ||||
|                 确认绑定 | ||||
|             </el-button> | ||||
|         </template> | ||||
|     </el-dialog> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import QRCode from 'qrcode' | ||||
| import { setGoogleAuth } from '@/api/admin/sys-user' | ||||
|  | ||||
| export default { | ||||
|     name: 'BindGoogleDialog', | ||||
|     props: { | ||||
|         visible: { type: Boolean, default: true }, | ||||
|         otpAuthUrl: { type: String, default: '' }, | ||||
|         secret: { type: String, default: '' }, | ||||
|         account: { type: String, default: '' }, | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             code: '', | ||||
|             loading: false, | ||||
|             errorMsg: '' | ||||
|         } | ||||
|     }, | ||||
|     computed: { | ||||
|         dialogVisible: { | ||||
|             get() { | ||||
|                 return this.visible | ||||
|             }, | ||||
|             set(val) { | ||||
|                 this.$emit('update:visible', val) | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
|         visible(val) { | ||||
|             if (val) { | ||||
|                 this.code = '' | ||||
|                 this.errorMsg = '' | ||||
|                 this.$nextTick(() => { | ||||
|                     QRCode.toCanvas(this.$refs.qrCanvas, this.otpAuthUrl, { width: 180 }) | ||||
|                     this.$refs.codeInput.focus() | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         submit() { | ||||
|             this.loading = true | ||||
|             setGoogleAuth({ | ||||
|                 secret: this.secret, | ||||
|                 code: this.code | ||||
|             }) | ||||
|                 .then(res => { | ||||
|                     if (res && res.code === 200) { | ||||
|                         this.dialogVisible = false | ||||
|                         this.$message.success('绑定成功') | ||||
|                     } else { | ||||
|                         this.errorMsg = res.message || '绑定失败' | ||||
|                     } | ||||
|                 }) | ||||
|                 .finally(() => { | ||||
|                     this.loading = false | ||||
|                 }) | ||||
|         }, | ||||
|         copySecret() { | ||||
|             navigator.clipboard.writeText(this.secret).then(() => { | ||||
|                 this.$message.success('密钥已复制') | ||||
|             }).catch(() => { | ||||
|                 this.$message.error('复制失败,请手动复制') | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .google-bind-dialog ::v-deep .el-dialog { | ||||
|     border-radius: 10px; | ||||
| } | ||||
|  | ||||
| .dialog-body { | ||||
|     padding: 10px 20px; | ||||
| } | ||||
|  | ||||
| .tip-text { | ||||
|     font-size: 14px; | ||||
|     color: #666; | ||||
|     margin-bottom: 15px; | ||||
| } | ||||
|  | ||||
| .qr-section { | ||||
|     display: flex; | ||||
|     align-items: flex-start; | ||||
|     gap: 16px; | ||||
|     margin-bottom: 15px; | ||||
| } | ||||
|  | ||||
| .qr-canvas { | ||||
|     width: 180px; | ||||
|     height: 180px; | ||||
|     border: 1px solid #eee; | ||||
| } | ||||
|  | ||||
| .manual-section { | ||||
|     flex: 1; | ||||
|     font-size: 13px; | ||||
|     color: #333; | ||||
| } | ||||
|  | ||||
| .code-input { | ||||
|     width: 240px; | ||||
|     display: block; | ||||
|     margin: 0 auto; | ||||
| } | ||||
|  | ||||
| .error-text { | ||||
|     text-align: center; | ||||
|     margin-top: 8px; | ||||
|     color: red; | ||||
| } | ||||
| </style> | ||||
| @ -13,16 +13,22 @@ | ||||
|         <settings /> | ||||
|       </right-panel> | ||||
|     </div> | ||||
|  | ||||
|     <!-- 绑定谷歌验证码弹窗 --> | ||||
|     <set-google-secret :visible.sync="showSetGooleSecret" :otpAuthUrl="needGoolAuthData.otpAuthUrl" | ||||
|       :secret="needGoolAuthData.secret" @success="handleBindSuccess"></set-google-secret> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import RightPanel from '@/components/RightPanel' | ||||
| import { needGoolAuth, setGoogleAuth } from '@/api/admin/sys-user' | ||||
| import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components' | ||||
| import SetGoogleSecret from './SetGoogleSecret' | ||||
| import ResizeMixin from './mixin/ResizeHandler' | ||||
| import { mapState } from 'vuex' | ||||
| import variables from '@/styles/variables.scss' | ||||
| import dingSound from '@/assets/tiktok/sisfus.mp3' | ||||
| // import dingSound from '@/assets/tiktok/sisfus.mp3' | ||||
| import checkPermisAction from '@/utils/permisaction' | ||||
|  | ||||
| export default { | ||||
| @ -33,11 +39,14 @@ export default { | ||||
|     RightPanel, | ||||
|     Settings, | ||||
|     Sidebar, | ||||
|     TagsView | ||||
|     TagsView, | ||||
|     SetGoogleSecret | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       voice: null, | ||||
|       showSetGooleSecret: false, | ||||
|       needGoolAuthData: {} | ||||
|     } | ||||
|   }, | ||||
|   mixins: [ResizeMixin], | ||||
| @ -63,35 +72,52 @@ export default { | ||||
|   }, | ||||
|  | ||||
|   created() { | ||||
|     // if (!this.roles.includes('admin')) { | ||||
|     //   this.currentRole = 'editorDashboard' | ||||
|     // } | ||||
|     this.getNeedGoolAuth() | ||||
|  | ||||
|     if (checkPermisAction(['admin:mmAlarmLog:notice'])) { | ||||
|       this.$confirm('是否接收警告?', '提示', { | ||||
|         distinguishCancelAndClose: true, | ||||
|         confirmButtonText: '确定', | ||||
|         cancelButtonText: '取消', | ||||
|         type: 'warning' | ||||
|       }).then(() => { | ||||
|         this.voice = new Audio(dingSound) | ||||
|       // this.$confirm('是否接收警告?', '提示', { | ||||
|       //   distinguishCancelAndClose: true, | ||||
|       //   confirmButtonText: '确定', | ||||
|       //   cancelButtonText: '取消', | ||||
|       //   type: 'warning' | ||||
|       // }).then(() => { | ||||
|       //   this.voice = new Audio(dingSound) | ||||
|  | ||||
|         this.initWebSocket() | ||||
|       }).catch(() => { | ||||
|         console.log('取消') | ||||
|       }); | ||||
|       this.initWebSocket() | ||||
|       // }).catch(() => { | ||||
|       //   console.log('取消') | ||||
|       // }); | ||||
|     } | ||||
|   }, | ||||
|   destroyed() { | ||||
|     console.log('断开websocket连接') | ||||
|     this.websock.close() // 离开路由之后断开websocket连接 | ||||
|     // unWsLogout(this.id, this.group).then(response => { | ||||
|     //   console.log(response.data) | ||||
|     // } | ||||
|     // ) | ||||
|   }, | ||||
|   methods: { | ||||
|     checkPermisAction, | ||||
|     getNeedGoolAuth() { | ||||
|       const loading = this.$loading({ | ||||
|         lock: true, | ||||
|         text: 'Loading', | ||||
|         spinner: 'el-icon-loading', | ||||
|         background: 'rgba(0, 0, 0, 0.7)' | ||||
|       }); | ||||
|  | ||||
|       needGoolAuth() | ||||
|         .then(response => { | ||||
|           console.log("needGoogleAuth", response) | ||||
|           if (response.code === 200) { | ||||
|             if (response.data.needGooglAuth) { | ||||
|               this.needGoolAuthData.otpAuthUrl = response.data.otpAuthUrl | ||||
|               this.needGoolAuthData.secret = response.data.secret | ||||
|               this.showSetGooleSecret = true | ||||
|             } | ||||
|           } | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           loading.close() | ||||
|         }) | ||||
|     }, | ||||
|     initWebSocket() { // 初始化weosocket | ||||
|       const wsuri = `ws://${process.env.VUE_APP_WEBSOCKET_URL}/ws?token=${this.$store.state.user.token}` | ||||
|  | ||||
| @ -110,7 +136,6 @@ export default { | ||||
|       this.initWebSocket() | ||||
|     }, | ||||
|     websocketonmessage(e) { // 数据接收 | ||||
|       console.log("ws:", e.data) | ||||
|       try { | ||||
|         let data = JSON.parse(e.data) | ||||
|  | ||||
| @ -122,7 +147,7 @@ export default { | ||||
|           duration: 0 | ||||
|         }); | ||||
|  | ||||
|         this.playVoice("钱包告警") | ||||
|         // this.playVoice("钱包告警") | ||||
|       } catch (err) { | ||||
|         console.log("接收websocket数据失败:", err) | ||||
|       } | ||||
| @ -146,6 +171,9 @@ export default { | ||||
|     }, | ||||
|     handleClickOutside() { | ||||
|       this.$store.dispatch('app/closeSideBar', { withoutAnimation: false }) | ||||
|     }, | ||||
|     handleBindSuccess() { | ||||
|       this.showSetGooleSecret = false | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,153 +1,174 @@ | ||||
| import { login, logout, getInfo, refreshtoken } from '@/api/user' | ||||
| import { getToken, setToken, removeToken } from '@/utils/auth' | ||||
| import router, { resetRouter } from '@/router' | ||||
| import storage from '@/utils/storage' | ||||
| import { login, logout, getInfo, refreshtoken } from "@/api/user"; | ||||
| import { getToken, setToken, removeToken } from "@/utils/auth"; | ||||
| import router, { resetRouter } from "@/router"; | ||||
| import storage from "@/utils/storage"; | ||||
|  | ||||
| const state = { | ||||
|   token: getToken(), | ||||
|   name: '', | ||||
|   avatar: '', | ||||
|   introduction: '', | ||||
|   name: "", | ||||
|   avatar: "", | ||||
|   introduction: "", | ||||
|   roles: [], | ||||
|   permissions: [], | ||||
|   permisaction: [] | ||||
| } | ||||
|   permisaction: [], | ||||
| }; | ||||
|  | ||||
| const mutations = { | ||||
|   SET_TOKEN: (state, token) => { | ||||
|     state.token = token | ||||
|     state.token = token; | ||||
|   }, | ||||
|   SET_INTRODUCTION: (state, introduction) => { | ||||
|     state.introduction = introduction | ||||
|     state.introduction = introduction; | ||||
|   }, | ||||
|   SET_NAME: (state, name) => { | ||||
|     state.name = name | ||||
|     state.name = name; | ||||
|   }, | ||||
|   SET_AVATAR: (state, avatar) => { | ||||
|     if (avatar.indexOf('http') !== -1) { | ||||
|       state.avatar = avatar | ||||
|     if (avatar.indexOf("http") !== -1) { | ||||
|       state.avatar = avatar; | ||||
|     } else { | ||||
|       state.avatar = process.env.VUE_APP_BASE_API + avatar | ||||
|       state.avatar = process.env.VUE_APP_BASE_API + avatar; | ||||
|     } | ||||
|   }, | ||||
|   SET_ROLES: (state, roles) => { | ||||
|     state.roles = roles | ||||
|     state.roles = roles; | ||||
|   }, | ||||
|   SET_PERMISSIONS: (state, permisaction) => { | ||||
|     state.permisaction = permisaction | ||||
|   } | ||||
| } | ||||
|     state.permisaction = permisaction; | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| const actions = { | ||||
|   // user login | ||||
|   login({ commit }, userInfo) { | ||||
|   login({ commit }, { userInfo, handleResponse }) { | ||||
|     return new Promise((resolve, reject) => { | ||||
|       login(userInfo).then(response => { | ||||
|         const { token } = response | ||||
|         commit('SET_TOKEN', token) | ||||
|         setToken(token) | ||||
|         resolve() | ||||
|       }).catch(error => { | ||||
|         reject(error) | ||||
|       }) | ||||
|     }) | ||||
|       login(userInfo) | ||||
|         .then(async(res) => { | ||||
|           // 自定义回调处理(判断是否需要谷歌验证) | ||||
|           if (handleResponse) { | ||||
|             const shouldSetToken =await handleResponse(res); | ||||
|  | ||||
|             console.log("shouldSetToken", shouldSetToken); | ||||
|             if (!shouldSetToken) return resolve(); // 不设置 token | ||||
|           } | ||||
|  | ||||
|           const { token } = res; | ||||
|           if (token) { | ||||
|             commit("SET_TOKEN", token); | ||||
|             setToken(token); | ||||
|             resolve(res); | ||||
|           } else { | ||||
|             reject(new Error("登录失败:无 token")); | ||||
|           } | ||||
|         }) | ||||
|         .catch(reject); | ||||
|     }); | ||||
|   }, | ||||
|  | ||||
|   // get user info | ||||
|   getInfo({ commit, state }) { | ||||
|     return new Promise((resolve, reject) => { | ||||
|       getInfo().then(response => { | ||||
|         if (!response || !response.data) { | ||||
|           commit('SET_TOKEN', '') | ||||
|           removeToken() | ||||
|           resolve() | ||||
|         } | ||||
|       getInfo() | ||||
|         .then((response) => { | ||||
|           if (!response || !response.data) { | ||||
|             commit("SET_TOKEN", ""); | ||||
|             removeToken(); | ||||
|             resolve(); | ||||
|           } | ||||
|  | ||||
|         const { roles, name, avatar, introduction, permissions } = response.data | ||||
|           const { roles, name, avatar, introduction, permissions } = | ||||
|             response.data; | ||||
|  | ||||
|         // roles must be a non-empty array | ||||
|         if (!roles || roles.length <= 0) { | ||||
|           reject('getInfo: roles must be a non-null array!') | ||||
|         } | ||||
|         commit('SET_PERMISSIONS', permissions) | ||||
|         commit('SET_ROLES', roles) | ||||
|         commit('SET_NAME', name) | ||||
|         commit('SET_AVATAR', avatar) | ||||
|         commit('SET_INTRODUCTION', introduction) | ||||
|         resolve(response) | ||||
|       }).catch(error => { | ||||
|         reject(error) | ||||
|       }) | ||||
|     }) | ||||
|           // roles must be a non-empty array | ||||
|           if (!roles || roles.length <= 0) { | ||||
|             reject("getInfo: roles must be a non-null array!"); | ||||
|           } | ||||
|           commit("SET_PERMISSIONS", permissions); | ||||
|           commit("SET_ROLES", roles); | ||||
|           commit("SET_NAME", name); | ||||
|           commit("SET_AVATAR", avatar); | ||||
|           commit("SET_INTRODUCTION", introduction); | ||||
|           resolve(response); | ||||
|         }) | ||||
|         .catch((error) => { | ||||
|           reject(error); | ||||
|         }); | ||||
|     }); | ||||
|   }, | ||||
|   // 退出系统 | ||||
|   LogOut({ commit, state }) { | ||||
|     return new Promise((resolve, reject) => { | ||||
|       logout(state.token).then(() => { | ||||
|         commit('SET_TOKEN', '') | ||||
|         commit('SET_ROLES', []) | ||||
|         commit('SET_PERMISSIONS', []) | ||||
|         removeToken() | ||||
|         storage.clear() | ||||
|         resolve() | ||||
|       }).catch(error => { | ||||
|         reject(error) | ||||
|       }) | ||||
|     }) | ||||
|       logout(state.token) | ||||
|         .then(() => { | ||||
|           commit("SET_TOKEN", ""); | ||||
|           commit("SET_ROLES", []); | ||||
|           commit("SET_PERMISSIONS", []); | ||||
|           removeToken(); | ||||
|           storage.clear(); | ||||
|           resolve(); | ||||
|         }) | ||||
|         .catch((error) => { | ||||
|           reject(error); | ||||
|         }); | ||||
|     }); | ||||
|   }, | ||||
|   // 刷新token | ||||
|   refreshToken({ commit, state }) { | ||||
|     return new Promise((resolve, reject) => { | ||||
|       refreshtoken({ token: state.token }).then(response => { | ||||
|         const { token } = response | ||||
|         commit('SET_TOKEN', token) | ||||
|         setToken(token) | ||||
|         resolve() | ||||
|       }).catch(error => { | ||||
|         reject(error) | ||||
|       }) | ||||
|     }) | ||||
|       refreshtoken({ token: state.token }) | ||||
|         .then((response) => { | ||||
|           const { token } = response; | ||||
|           commit("SET_TOKEN", token); | ||||
|           setToken(token); | ||||
|           resolve(); | ||||
|         }) | ||||
|         .catch((error) => { | ||||
|           reject(error); | ||||
|         }); | ||||
|     }); | ||||
|   }, | ||||
|  | ||||
|   // remove token | ||||
|   resetToken({ commit }) { | ||||
|     return new Promise(resolve => { | ||||
|       commit('SET_TOKEN', '') | ||||
|       removeToken() | ||||
|       resolve() | ||||
|     }) | ||||
|     return new Promise((resolve) => { | ||||
|       commit("SET_TOKEN", ""); | ||||
|       removeToken(); | ||||
|       resolve(); | ||||
|     }); | ||||
|   }, | ||||
|  | ||||
|   // dynamically modify permissions | ||||
|   changeRoles({ commit, dispatch }, role) { | ||||
|     // eslint-disable-next-line no-async-promise-executor | ||||
|     return new Promise(async resolve => { | ||||
|       const token = role + '-token' | ||||
|     return new Promise(async (resolve) => { | ||||
|       const token = role + "-token"; | ||||
|  | ||||
|       commit('SET_TOKEN', token) | ||||
|       setToken(token) | ||||
|       commit("SET_TOKEN", token); | ||||
|       setToken(token); | ||||
|  | ||||
|       const { roles } = await dispatch('getInfo') | ||||
|       const { roles } = await dispatch("getInfo"); | ||||
|  | ||||
|       resetRouter() | ||||
|       resetRouter(); | ||||
|  | ||||
|       // generate accessible routes map based on roles | ||||
|       const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true }) | ||||
|       const accessRoutes = await dispatch("permission/generateRoutes", roles, { | ||||
|         root: true, | ||||
|       }); | ||||
|  | ||||
|       // dynamically add accessible routes | ||||
|       router.addRoutes(accessRoutes) | ||||
|       router.addRoutes(accessRoutes); | ||||
|  | ||||
|       // reset visited views and cached views | ||||
|       dispatch('tagsView/delAllViews', null, { root: true }) | ||||
|       dispatch("tagsView/delAllViews", null, { root: true }); | ||||
|  | ||||
|       resolve() | ||||
|     }) | ||||
|   } | ||||
| } | ||||
|       resolve(); | ||||
|     }); | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| export default { | ||||
|   namespaced: true, | ||||
|   state, | ||||
|   mutations, | ||||
|   actions | ||||
| } | ||||
|   actions, | ||||
| }; | ||||
|  | ||||
| @ -48,6 +48,8 @@ service.interceptors.response.use( | ||||
|       return response.data | ||||
|     } | ||||
|     const code = response.data.code | ||||
|     console.log("xx",code) | ||||
|  | ||||
|     if (code === 401) { | ||||
|       store.dispatch('user/resetToken') | ||||
|       if (location.href.indexOf('login') !== -1) { | ||||
|  | ||||
| @ -15,6 +15,11 @@ | ||||
|                             clearable size="small" @keyup.enter.native="handleQuery" /> | ||||
|                     </el-form-item> --> | ||||
|  | ||||
|                     <el-form-item label="时间范围"> | ||||
|                         <el-date-picker v-model="times" type="daterange" align="right" unlink-panels range-separator="至" | ||||
|                             start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="pickerOptions"> | ||||
|                         </el-date-picker> | ||||
|                     </el-form-item> | ||||
|                     <el-form-item> | ||||
|                         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> | ||||
|                         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> | ||||
| @ -37,6 +42,14 @@ | ||||
|                             size="mini" :disabled="multiple" @click="handleDelete">删除 | ||||
|                         </el-button> | ||||
|                     </el-col> | ||||
|                     <el-col :span="1.5"> | ||||
|                         <el-popconfirm class="delete-popconfirm" title="确定要导出记录吗?" confirm-button-text="确定" | ||||
|                             @confirm="handleExport"> | ||||
|                             <el-button slot="reference" v-permisaction="['admin:mmAlarmLog:export']" | ||||
|                                 icon="el-icon-files" size="mini">导出 | ||||
|                             </el-button> | ||||
|                         </el-popconfirm> | ||||
|                     </el-col> | ||||
|                     <el-col :span="1.5"> | ||||
|                         <el-popconfirm class="delete-popconfirm" title="确认要清除所有吗?" confirm-button-text="清除" | ||||
|                             @confirm="handleClearAll()"> | ||||
| @ -107,8 +120,9 @@ | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { addMmAlarmLog, delMmAlarmLog, getMmAlarmLog, listMmAlarmLog, updateMmAlarmLog, clearAllAlarmLog } from '@/api/admin/mm-alarm-log' | ||||
| import { addMmAlarmLog, delMmAlarmLog, getMmAlarmLog, listMmAlarmLog, updateMmAlarmLog, clearAllAlarmLog, exportMmAlarmLog } from '@/api/admin/mm-alarm-log' | ||||
| import { getMmMachineList } from '@/api/admin/mm-machine' | ||||
| import { resolveBlob } from '@/utils/zipdownload' | ||||
|  | ||||
| export default { | ||||
|     name: 'MmAlarmLog', | ||||
| @ -137,11 +151,13 @@ export default { | ||||
|             mmAlarmLogList: [], | ||||
|  | ||||
|             // 关系表类型 | ||||
|  | ||||
|             times: [], | ||||
|             // 查询参数 | ||||
|             queryParams: { | ||||
|                 pageIndex: 1, | ||||
|                 pageSize: 10, | ||||
|                 startTime: undefined, | ||||
|                 endTime: undefined, | ||||
|                 machineId: undefined, | ||||
|                 biosId: undefined, | ||||
|                 idOrder: 'desc', | ||||
| @ -153,7 +169,34 @@ export default { | ||||
|             rules: { | ||||
|                 machineId: [{ required: true, message: '设备id不能为空', trigger: 'blur' }], | ||||
|                 biosId: [{ required: true, message: '设备码不能为空', trigger: 'blur' }], | ||||
|             } | ||||
|             }, | ||||
|             pickerOptions: { | ||||
|                 shortcuts: [{ | ||||
|                     text: '最近一周', | ||||
|                     onClick(picker) { | ||||
|                         const end = new Date(); | ||||
|                         const start = new Date(); | ||||
|                         start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); | ||||
|                         picker.$emit('pick', [start, end]); | ||||
|                     } | ||||
|                 }, { | ||||
|                     text: '最近一个月', | ||||
|                     onClick(picker) { | ||||
|                         const end = new Date(); | ||||
|                         const start = new Date(); | ||||
|                         start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); | ||||
|                         picker.$emit('pick', [start, end]); | ||||
|                     } | ||||
|                 }, { | ||||
|                     text: '最近三个月', | ||||
|                     onClick(picker) { | ||||
|                         const end = new Date(); | ||||
|                         const start = new Date(); | ||||
|                         start.setTime(start.getTime() - 3600 * 1000 * 24 * 90); | ||||
|                         picker.$emit('pick', [start, end]); | ||||
|                     } | ||||
|                 }] | ||||
|             }, | ||||
|         } | ||||
|     }, | ||||
|     created() { | ||||
| @ -164,7 +207,20 @@ export default { | ||||
|         /** 查询参数列表 */ | ||||
|         getList() { | ||||
|             this.loading = true | ||||
|             listMmAlarmLog(this.addDateRange(this.queryParams, this.dateRange)).then(response => { | ||||
|             this.queryParams.startTime = undefined | ||||
|             this.queryParams.endTime = undefined | ||||
|  | ||||
|             if (this.times) { | ||||
|                 console.log(this.times.length) | ||||
|                 if (this.times.length >= 0) { | ||||
|                     this.queryParams.startTime = this.times[0] | ||||
|                 } | ||||
|  | ||||
|                 if (this.times.length >= 1) { | ||||
|                     this.queryParams.endTime = this.times[1] | ||||
|                 } | ||||
|             } | ||||
|             listMmAlarmLog(this.queryParams).then(response => { | ||||
|                 this.mmAlarmLogList = response.data.list | ||||
|                 this.total = response.data.count | ||||
|                 this.loading = false | ||||
| @ -300,6 +356,36 @@ export default { | ||||
|             }).finally(() => { | ||||
|                 this.loading = false | ||||
|             }) | ||||
|         }, | ||||
|         // 导出按钮 | ||||
|         handleExport() { | ||||
|             this.loading = true | ||||
|             this.queryParams.startTime = undefined | ||||
|             this.queryParams.endTime = undefined | ||||
|  | ||||
|             if (this.times) { | ||||
|                 console.log(this.times.length) | ||||
|                 if (this.times.length >= 0) { | ||||
|                     this.queryParams.startTime = this.times[0] | ||||
|                 } | ||||
|  | ||||
|                 if (this.times.length >= 1) { | ||||
|                     this.queryParams.endTime = this.times[1] | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             exportMmAlarmLog(this.queryParams) | ||||
|                 .then(response => { | ||||
|                     console.log(response) | ||||
|                     resolveBlob(response, '钱包告警记录.xlsx') | ||||
|                     this.msgSuccess('导出成功') | ||||
|                 }) | ||||
|                 .catch(err => { | ||||
|                     console.log(err) | ||||
|                 }) | ||||
|                 .finally(() => { | ||||
|                     this.loading = false | ||||
|                 }) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										90
									
								
								src/views/login/google_auth.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/views/login/google_auth.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | ||||
| <template> | ||||
|   <el-dialog | ||||
|     title="谷歌验证码验证" | ||||
|     :visible.sync="visible" | ||||
|     width="400px" | ||||
|     :close-on-click-modal="false" | ||||
|     :before-close="handleCancel" | ||||
|     :show-close="false"  | ||||
|     :close-on-press-escape="false" | ||||
|     center | ||||
|     class="google-dialog" | ||||
|   > | ||||
|     <div class="dialog-body"> | ||||
|       <p class="tip-text">为了您的账户安全,请输入谷歌验证码</p> | ||||
|       <el-input | ||||
|         v-model="code" | ||||
|         placeholder="请输入6位谷歌验证码" | ||||
|         maxlength="6" | ||||
|         class="code-input" | ||||
|         ref="codeInput" | ||||
|         @keyup.enter.native="handleConfirm" | ||||
|       ></el-input> | ||||
|     </div> | ||||
|  | ||||
|     <div slot="footer" class="dialog-footer"> | ||||
|       <el-button @click="handleCancel">取消</el-button> | ||||
|       <el-button | ||||
|         type="primary" | ||||
|         :loading="loading" | ||||
|         :disabled="code.length !== 6" | ||||
|         @click="handleConfirm" | ||||
|       >验证</el-button> | ||||
|     </div> | ||||
|   </el-dialog> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "GoogleAuth", | ||||
|   props: { | ||||
|     visible: { | ||||
|       type: Boolean, | ||||
|       required: true | ||||
|     }, | ||||
|     loading: { | ||||
|       type: Boolean, | ||||
|       default: false | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       code: "" | ||||
|     }; | ||||
|   }, | ||||
|   watch: { | ||||
|     visible(val) { | ||||
|       if (val) { | ||||
|         this.code = ""; | ||||
|         this.$nextTick(() => this.$refs.codeInput.focus()); | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     handleConfirm() { | ||||
|       this.$emit("confirm", this.code); | ||||
|     }, | ||||
|     handleCancel() { | ||||
|       this.$emit("cancel"); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .google-dialog ::v-deep .el-dialog { | ||||
|   border-radius: 10px; | ||||
| } | ||||
| .dialog-body { | ||||
|   text-align: center; | ||||
| } | ||||
| .tip-text { | ||||
|   font-size: 14px; | ||||
|   color: #666; | ||||
|   margin-bottom: 15px; | ||||
| } | ||||
| .code-input { | ||||
|   width: 80%; | ||||
|   text-align: center; | ||||
| } | ||||
| </style> | ||||
| @ -30,58 +30,27 @@ | ||||
|       <div class="login-border"> | ||||
|         <div class="login-main"> | ||||
|           <div class="login-title">用户登录</div> | ||||
|           <el-form | ||||
|             ref="loginForm" | ||||
|             :model="loginForm" | ||||
|             :rules="loginRules" | ||||
|             class="login-form" | ||||
|             autocomplete="on" | ||||
|             label-position="left" | ||||
|           > | ||||
|           <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" | ||||
|             label-position="left"> | ||||
|             <el-form-item prop="username"> | ||||
|               <span class="svg-container"> | ||||
|                 <i class="el-icon-user" /> | ||||
|               </span> | ||||
|               <el-input | ||||
|                 ref="username" | ||||
|                 v-model="loginForm.username" | ||||
|                 placeholder="用户名" | ||||
|                 name="username" | ||||
|                 type="text" | ||||
|                 tabindex="1" | ||||
|                 autocomplete="on" | ||||
|               /> | ||||
|               <el-input ref="username" v-model="loginForm.username" placeholder="用户名" name="username" type="text" | ||||
|                 tabindex="1" autocomplete="on" /> | ||||
|             </el-form-item> | ||||
|  | ||||
|             <el-tooltip | ||||
|               v-model="capsTooltip" | ||||
|               content="Caps lock is On" | ||||
|               placement="right" | ||||
|               manual | ||||
|             > | ||||
|             <el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual> | ||||
|               <el-form-item prop="password"> | ||||
|                 <span class="svg-container"> | ||||
|                   <svg-icon icon-class="password" /> | ||||
|                 </span> | ||||
|                 <el-input | ||||
|                   :key="passwordType" | ||||
|                   ref="password" | ||||
|                   v-model="loginForm.password" | ||||
|                   :type="passwordType" | ||||
|                   placeholder="密码" | ||||
|                   name="password" | ||||
|                   tabindex="2" | ||||
|                   autocomplete="on" | ||||
|                   @keyup.native="checkCapslock" | ||||
|                   @blur="capsTooltip = false" | ||||
|                   @keyup.enter.native="handleLogin" | ||||
|                 /> | ||||
|                 <el-input :key="passwordType" ref="password" v-model="loginForm.password" :type="passwordType" | ||||
|                   placeholder="密码" name="password" tabindex="2" autocomplete="on" @keyup.native="checkCapslock" | ||||
|                   @blur="capsTooltip = false" @keyup.enter.native="handleLogin" /> | ||||
|                 <span class="show-pwd" @click="showPwd"> | ||||
|                   <svg-icon | ||||
|                     :icon-class=" | ||||
|                       passwordType === 'password' ? 'eye' : 'eye-open' | ||||
|                     " | ||||
|                   /> | ||||
|                   <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open' | ||||
|                     " /> | ||||
|                 </span> | ||||
|               </el-form-item> | ||||
|             </el-tooltip> | ||||
| @ -89,47 +58,26 @@ | ||||
|               <span class="svg-container"> | ||||
|                 <svg-icon icon-class="validCode" /> | ||||
|               </span> | ||||
|               <el-input | ||||
|                 ref="username" | ||||
|                 v-model="loginForm.code" | ||||
|                 placeholder="验证码" | ||||
|                 name="username" | ||||
|                 type="text" | ||||
|                 tabindex="3" | ||||
|                 maxlength="5" | ||||
|                 autocomplete="off" | ||||
|                 style="width: 75%" | ||||
|                 @keyup.enter.native="handleLogin" | ||||
|               /> | ||||
|               <el-input ref="username" v-model="loginForm.code" placeholder="验证码" name="username" type="text" | ||||
|                 tabindex="3" maxlength="5" autocomplete="off" style="width: 75%" @keyup.enter.native="handleLogin" /> | ||||
|             </el-form-item> | ||||
|             <div | ||||
|               class="login-code" | ||||
|               style=" | ||||
|             <div class="login-code" style=" | ||||
|                 cursor: pointer; | ||||
|                 width: 30%; | ||||
|                 height: 48px; | ||||
|                 float: right; | ||||
|                 background-color: #f0f1f5; | ||||
|               " | ||||
|             > | ||||
|               <img | ||||
|                 style=" | ||||
|               "> | ||||
|               <img style=" | ||||
|                   height: 48px; | ||||
|                   width: 100%; | ||||
|                   border: 1px solid rgba(0, 0, 0, 0.1); | ||||
|                   border-radius: 5px; | ||||
|                 " | ||||
|                 :src="codeUrl" | ||||
|                 @click="getCode" | ||||
|               > | ||||
|                 " :src="codeUrl" @click="getCode"> | ||||
|             </div> | ||||
|  | ||||
|             <el-button | ||||
|               :loading="loading" | ||||
|               type="primary" | ||||
|               style="width: 100%; padding: 12px 20px; margin-bottom: 30px" | ||||
|               @click.native.prevent="handleLogin" | ||||
|             > | ||||
|             <el-button :loading="loading" type="primary" style="width: 100%; padding: 12px 20px; margin-bottom: 30px" | ||||
|               @click.native.prevent="handleLogin"> | ||||
|               <span v-if="!loading">登 录</span> | ||||
|               <span v-else>登 录 中...</span> | ||||
|             </el-button> | ||||
| @ -146,11 +94,7 @@ | ||||
|       <br> | ||||
|       <social-sign /> | ||||
|     </el-dialog> | ||||
|     <div | ||||
|       id="bottom_layer" | ||||
|       class="s-bottom-layer s-isindex-wrap" | ||||
|       style="visibility: visible; width: 100%" | ||||
|     > | ||||
|     <div id="bottom_layer" class="s-bottom-layer s-isindex-wrap" style="visibility: visible; width: 100%"> | ||||
|       <div class="s-bottom-layer-content"> | ||||
|  | ||||
|         <div class="lh"> | ||||
| @ -163,11 +107,7 @@ | ||||
|             <div class="rest_info_tip"> | ||||
|               <div class="tip-wrapper"> | ||||
|                 <div class="lh tip-item" style="display: none"> | ||||
|                   <a | ||||
|                     class="text-color" | ||||
|                     href="https://beian.miit.gov.cn" | ||||
|                     target="_blank" | ||||
|                   > | ||||
|                   <a class="text-color" href="https://beian.miit.gov.cn" target="_blank"> | ||||
|                     沪ICP备XXXXXXXXX号-1 | ||||
|                   </a> | ||||
|                 </div> | ||||
| @ -177,17 +117,26 @@ | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <GoogleAuth :visible.sync="showGoogleVerifyDialog" :loading="googleVerifying" @confirm="submitGoogleCode" | ||||
|       @cancel="showGoogleVerifyDialog = false" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { getCodeImg } from '@/api/login' | ||||
| import { needCheckGoogleAuth, checkGoogleCode } from '@/api/admin/sys-user' | ||||
| import moment from 'moment' | ||||
| import SocialSign from './components/SocialSignin' | ||||
| import GoogleAuth from './google_auth' | ||||
| import { setToken } from "@/utils/auth"; | ||||
|  | ||||
| export default { | ||||
|   name: 'Login', | ||||
|   components: { SocialSign }, | ||||
|   components: { | ||||
|     SocialSign, | ||||
|     GoogleAuth | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       codeUrl: '', | ||||
| @ -218,12 +167,15 @@ export default { | ||||
|       redirect: undefined, | ||||
|       otherQuery: {}, | ||||
|       currentTime: null, | ||||
|       sysInfo: '' | ||||
|       sysInfo: '', | ||||
|       showGoogleVerifyDialog: false, | ||||
|       googleVerifying: false, | ||||
|       googleTokenCache: '' | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     $route: { | ||||
|       handler: function(route) { | ||||
|       handler: function (route) { | ||||
|         const query = route.query | ||||
|         if (query) { | ||||
|           this.redirect = query.redirect | ||||
| @ -252,10 +204,11 @@ export default { | ||||
|   }, | ||||
|   destroyed() { | ||||
|     clearInterval(this.timer) | ||||
|     window.removeEventListener('resize', () => {}) | ||||
|     window.removeEventListener('resize', () => { }) | ||||
|     // window.removeEventListener('storage', this.afterQRScan) | ||||
|   }, | ||||
|   methods: { | ||||
|     setToken, | ||||
|     getSystemSetting() { | ||||
|       this.$store.dispatch('system/settingDetail').then((ret) => { | ||||
|         this.sysInfo = ret | ||||
| @ -300,26 +253,55 @@ export default { | ||||
|         this.$refs.password.focus() | ||||
|       }) | ||||
|     }, | ||||
|     handleLogin() { | ||||
|       this.$refs.loginForm.validate((valid) => { | ||||
|         if (valid) { | ||||
|           this.loading = true | ||||
|           this.$store | ||||
|             .dispatch('user/login', this.loginForm) | ||||
|             .then(() => { | ||||
|               this.$router | ||||
|                 .push({ path: this.redirect || '/', query: this.otherQuery }) | ||||
|                 .catch(() => {}) | ||||
|             }) | ||||
|             .catch(() => { | ||||
|               this.loading = false | ||||
|               this.getCode() | ||||
|             }) | ||||
|         } else { | ||||
|           console.log('error submit!!') | ||||
|           return false | ||||
|     async validateForm() { | ||||
|       return new Promise((resolve) => { | ||||
|         this.$refs.loginForm.validate((valid) => { | ||||
|           resolve(valid); | ||||
|         }); | ||||
|       }); | ||||
|     }, | ||||
|     async handleLogin() { | ||||
|       const valid = await this.validateForm(); | ||||
|       if (!valid) return; | ||||
|  | ||||
|       this.loading = true; | ||||
|  | ||||
|       try { | ||||
|         const res = await this.$store.dispatch('user/login', { | ||||
|           userInfo: this.loginForm, | ||||
|           handleResponse: async (res) => { | ||||
|  | ||||
|             if (res.code === 200) { | ||||
|               let headers = { | ||||
|                 "Authorization": "Bearer " + res.token | ||||
|               } | ||||
|               const res2 = await needCheckGoogleAuth(headers); | ||||
|               let cacheToken = false; | ||||
|  | ||||
|               if (res2 && res2.code === 200 && res2.data) { | ||||
|                 this.googleTokenCache = res.token; | ||||
|                 this.showGoogleVerifyDialog = true; | ||||
|                 cacheToken = false; | ||||
|               } else { | ||||
|                 cacheToken = true; | ||||
|               } | ||||
|  | ||||
|               return cacheToken; | ||||
|             } else { | ||||
|               this.$message.error(res.message || '登录失败'); | ||||
|               return false; | ||||
|             } | ||||
|           } | ||||
|         }); | ||||
|  | ||||
|         if (res) { | ||||
|           this.$router.push({ path: this.redirect || '/', query: this.otherQuery }).catch(() => { }); | ||||
|         } | ||||
|       }) | ||||
|       } catch (error) { | ||||
|         this.getCode(); | ||||
|       } finally { | ||||
|         this.loading = false; | ||||
|       } | ||||
|     }, | ||||
|     getOtherQuery(query) { | ||||
|       return Object.keys(query).reduce((acc, cur) => { | ||||
| @ -328,6 +310,30 @@ export default { | ||||
|         } | ||||
|         return acc | ||||
|       }, {}) | ||||
|     }, | ||||
|     submitGoogleCode(code) { | ||||
|       this.googleVerifying = true | ||||
|       let headers = { | ||||
|         "Authorization": "Bearer " + this.googleTokenCache | ||||
|       } | ||||
|  | ||||
|       checkGoogleCode({ code }, headers) | ||||
|         .then((response) => { | ||||
|           if (response && response.code === 200) { | ||||
|             const token = this.googleTokenCache | ||||
|             this.$store.commit("user/SET_TOKEN", token) | ||||
|             setToken(token) | ||||
|  | ||||
|             this.$message.success("登录成功") | ||||
|             this.$router.push({ path: this.redirect || '/', query: this.otherQuery }) | ||||
|           } | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           console.log("err", err) | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           this.googleVerifying = false | ||||
|         }) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -355,76 +361,95 @@ $cursor: #fff; | ||||
|   line-height: 39px; | ||||
|   // background: #0e6cff; | ||||
| } | ||||
|  | ||||
| #bottom_layer .lh { | ||||
|   display: inline-block; | ||||
|   margin-right: 14px; | ||||
| } | ||||
|  | ||||
| #bottom_layer .lh .emphasize { | ||||
|   text-decoration: underline; | ||||
|   font-weight: 700; | ||||
| } | ||||
|  | ||||
| #bottom_layer .lh:last-child { | ||||
|   margin-left: -2px; | ||||
|   margin-right: 0; | ||||
| } | ||||
|  | ||||
| #bottom_layer .lh.activity { | ||||
|   font-weight: 700; | ||||
|   text-decoration: underline; | ||||
| } | ||||
|  | ||||
| #bottom_layer a { | ||||
|   font-size: 12px; | ||||
|   text-decoration: none; | ||||
| } | ||||
|  | ||||
| #bottom_layer .text-color { | ||||
|   color: #bbb; | ||||
| } | ||||
|  | ||||
| #bottom_layer .aria-img { | ||||
|   width: 49px; | ||||
|   height: 20px; | ||||
|   margin-bottom: -5px; | ||||
| } | ||||
|  | ||||
| #bottom_layer a:hover { | ||||
|   color: #fff; | ||||
| } | ||||
|  | ||||
| #bottom_layer .s-bottom-layer-content { | ||||
|   margin: 0 17px; | ||||
|   text-align: center; | ||||
| } | ||||
|  | ||||
| #bottom_layer .s-bottom-layer-content .auto-transform-line { | ||||
|   display: inline; | ||||
| } | ||||
|  | ||||
| #bottom_layer .s-bottom-layer-content .auto-transform-line:first-child { | ||||
|   margin-right: 14px; | ||||
| } | ||||
|  | ||||
| .s-bottom-space { | ||||
|   position: static; | ||||
|   width: 100%; | ||||
|   height: 40px; | ||||
|   margin: 23px auto 12px; | ||||
| } | ||||
|  | ||||
| #bottom_layer .open-content-info a:hover { | ||||
|   color: #fff; | ||||
| } | ||||
|  | ||||
| #bottom_layer .open-content-info .text-color { | ||||
|   color: #626675; | ||||
| } | ||||
|  | ||||
| .open-content-info { | ||||
|   position: relative; | ||||
|   display: inline-block; | ||||
|   width: 20px; | ||||
| } | ||||
| .open-content-info > span { | ||||
|  | ||||
| .open-content-info>span { | ||||
|   cursor: pointer; | ||||
|   font-size: 14px; | ||||
| } | ||||
| .open-content-info > span:hover { | ||||
|  | ||||
| .open-content-info>span:hover { | ||||
|   color: #fff; | ||||
| } | ||||
|  | ||||
| .open-content-info .tip-hover-panel { | ||||
|   position: absolute; | ||||
|   display: none; | ||||
|   padding-bottom: 18px; | ||||
| } | ||||
|  | ||||
| .open-content-info .tip-hover-panel .rest_info_tip { | ||||
|   max-width: 560px; | ||||
|   padding: 8px 12px 8px 12px; | ||||
| @ -434,29 +459,31 @@ $cursor: #fff; | ||||
|   box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1); | ||||
|   text-align: left; | ||||
| } | ||||
|  | ||||
| .open-content-info .tip-hover-panel .rest_info_tip .tip-wrapper { | ||||
|   white-space: nowrap; | ||||
|   line-height: 20px; | ||||
| } | ||||
|  | ||||
| .open-content-info .tip-hover-panel .rest_info_tip .tip-wrapper .tip-item { | ||||
|   height: 20px; | ||||
|   line-height: 20px; | ||||
| } | ||||
| .open-content-info | ||||
|   .tip-hover-panel | ||||
|   .rest_info_tip | ||||
|   .tip-wrapper | ||||
|   .tip-item:last-child { | ||||
|  | ||||
| .open-content-info .tip-hover-panel .rest_info_tip .tip-wrapper .tip-item:last-child { | ||||
|   margin-right: 0; | ||||
| } | ||||
|  | ||||
| @media screen and (max-width: 515px) { | ||||
|   .open-content-info { | ||||
|     width: 16px; | ||||
|   } | ||||
|  | ||||
|   .open-content-info .tip-hover-panel { | ||||
|     right: -16px !important; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .footer { | ||||
|   background-color: #0e6cff; | ||||
|   margin-bottom: -20px; | ||||
| @ -517,6 +544,7 @@ $cursor: #fff; | ||||
|   display: -webkit-box; | ||||
|   display: -ms-flexbox; | ||||
|   display: flex; | ||||
|  | ||||
|   .login-time { | ||||
|     position: absolute; | ||||
|     left: 25px; | ||||
| @ -613,6 +641,7 @@ $cursor: #fff; | ||||
|     color: #454545; | ||||
|   } | ||||
| } | ||||
|  | ||||
| $bg: #2d3a4b; | ||||
| $dark_gray: #889aa4; | ||||
| $light_gray: #eee; | ||||
| @ -670,18 +699,22 @@ $light_gray: #eee; | ||||
|     .thirdparty-button { | ||||
|       display: none; | ||||
|     } | ||||
|  | ||||
|     .login-weaper { | ||||
|       width: 100%; | ||||
|       padding: 0 30px; | ||||
|       box-sizing: border-box; | ||||
|       box-shadow: none; | ||||
|     } | ||||
|  | ||||
|     .login-main { | ||||
|       width: 80%; | ||||
|     } | ||||
|  | ||||
|     .login-left { | ||||
|       display: none !important; | ||||
|     } | ||||
|  | ||||
|     .login-border { | ||||
|       width: 100%; | ||||
|     } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user