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