新增:行为验证码

Signed-off-by: skyselang <215817969@qq.com>
This commit is contained in:
skyselang 2022-08-05 16:12:52 +08:00
parent 8cf37080f4
commit 2789086138
10 changed files with 1288 additions and 21 deletions

View File

@ -13,6 +13,7 @@
"axios": "0.27.2",
"clipboard": "2.0.11",
"core-js": "3.23.5",
"crypto-js": "^4.1.1",
"e-icon-picker": "^1.1.7",
"echarts": "5.3.3",
"element-ui": "2.15.8",

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,270 @@
<template>
<div
style="position: relative"
>
<div class="verify-img-out">
<div
class="verify-img-panel"
:style="{'width': setSize.imgWidth,
'height': setSize.imgHeight,
'background-size' : setSize.imgWidth + ' '+ setSize.imgHeight,
'margin-bottom': vSpace + 'px'}"
>
<div v-show="showRefresh" class="verify-refresh" style="z-index:3" @click="refresh">
<i class="iconfont icon-refresh" />
</div>
<img
ref="canvas"
:src="pointBackImgBase?('data:image/png;base64,'+pointBackImgBase):defaultImg"
alt=""
style="width:100%;height:100%;display:block"
@click="bindingClick?canvasClick($event):undefined"
>
<div
v-for="(tempPoint, index) in tempPoints"
:key="index"
class="point-area"
:style="{
'background-color':'#1abd6c',
color:'#fff',
'z-index':9999,
width:'20px',
height:'20px',
'text-align':'center',
'line-height':'20px',
'border-radius': '50%',
position:'absolute',
top:parseInt(tempPoint.y-10) + 'px',
left:parseInt(tempPoint.x-10) + 'px'
}"
>
{{ index + 1 }}
</div>
</div>
</div>
<div
class="verify-bar-area"
:style="{'width': setSize.imgWidth,
'color': barAreaColor,
'border-color': barAreaBorderColor,
'line-height':barSize.height}"
>
<span class="verify-msg">{{ text }}</span>
</div>
</div>
</template>
<script type="text/babel">
//
import { resetSize } from './../utils/util'
import { aesEncrypt } from './../utils/ase'
import { reqGet, reqCheck } from './../api/index'
export default {
name: 'VerifyPoints',
props: {
captchaType: {
type: String,
default: 'clickWord'
},
// popfixed
mode: {
type: String,
default: 'pop'
},
type: {
type: String,
default: '2'
},
//
vSpace: {
type: Number,
default: 5
},
imgSize: {
type: Object,
default() {
return {
width: '310px',
height: '155px'
}
}
},
barSize: {
type: Object,
default() {
return {
width: '310px',
height: '40px'
}
}
},
defaultImg: {
type: String,
default: ''
}
},
data() {
return {
secretKey: '', // ase
checkNum: 3, //
fontPos: [], //
checkPosArr: [], //
num: 1, //
pointBackImgBase: '', //
poinTextList: [], //
backToken: '', // token
setSize: {
imgHeight: 0,
imgWidth: 0,
barHeight: 0,
barWidth: 0
},
tempPoints: [],
text: '',
barAreaColor: undefined,
barAreaBorderColor: undefined,
showRefresh: true,
bindingClick: true
}
},
computed: {
resetSize() {
return resetSize
}
},
watch: {
// type
type: {
immediate: true,
handler() {
this.init()
}
}
},
mounted() {
//
this.$el.onselectstart = function() {
return false
}
},
methods: {
init() {
//
this.fontPos.splice(0, this.fontPos.length)
this.checkPosArr.splice(0, this.checkPosArr.length)
this.num = 1
this.getPictrue()
this.$nextTick(() => {
this.setSize = this.resetSize(this) //
this.$parent.$emit('ready', this)
})
},
canvasClick(e) {
this.checkPosArr.push(this.getMousePos(this.$refs.canvas, e))
if (this.num === this.checkNum) {
this.num = this.createPoint(this.getMousePos(this.$refs.canvas, e))
//
this.checkPosArr = this.pointTransfrom(this.checkPosArr, this.setSize)
//
setTimeout(() => {
//
var captchaVerification = this.secretKey ? aesEncrypt(this.backToken + '---' + JSON.stringify(this.checkPosArr), this.secretKey) : this.backToken + '---' + JSON.stringify(this.checkPosArr)
const data = {
captchaType: this.captchaType,
'pointJson': this.secretKey ? aesEncrypt(JSON.stringify(this.checkPosArr), this.secretKey) : JSON.stringify(this.checkPosArr),
'token': this.backToken
}
reqCheck(data).then(ret => {
const res = ret.data
if (res.repCode === '0000') {
this.barAreaColor = '#4cae4c'
this.barAreaBorderColor = '#5cb85c'
this.text = '验证成功'
this.bindingClick = false
if (this.mode === 'pop') {
setTimeout(() => {
this.$parent.clickShow = false
}, 700)
}
this.$parent.closeBox()
this.$parent.$emit('success', { captchaVerification })
} else {
this.$parent.$emit('error', this)
this.barAreaColor = '#d9534f'
this.barAreaBorderColor = '#d9534f'
this.text = '验证失败'
setTimeout(() => {
this.refresh()
}, 700)
}
})
}, 400)
}
if (this.num < this.checkNum) {
this.num = this.createPoint(this.getMousePos(this.$refs.canvas, e))
}
},
//
getMousePos: function(obj, e) {
var x = e.offsetX
var y = e.offsetY
return { x, y }
},
//
createPoint: function(pos) {
this.tempPoints.push(Object.assign({}, pos))
return ++this.num
},
refresh: function() {
this.tempPoints.splice(0, this.tempPoints.length)
this.barAreaColor = '#000'
this.barAreaBorderColor = '#ddd'
this.bindingClick = true
this.fontPos.splice(0, this.fontPos.length)
this.checkPosArr.splice(0, this.checkPosArr.length)
this.num = 1
this.getPictrue()
this.text = '验证失败'
this.showRefresh = true
},
//
getPictrue() {
const data = {
captchaType: this.captchaType,
clientUid: localStorage.getItem('point'),
ts: Date.now() //
}
reqGet(data).then(ret => {
const res = ret.data
if (res.repCode === '0000') {
this.pointBackImgBase = res.repData.originalImageBase64
this.backToken = res.repData.token
this.secretKey = res.repData.secretKey
this.poinTextList = res.repData.wordList
this.text = '请依次点击【' + this.poinTextList.join(',') + '】'
} else {
this.text = res.repMsg
}
//
if (res.repCode === '6201') {
this.pointBackImgBase = null
}
}).catch(() => {
})
},
//
pointTransfrom(pointArr, imgSize) {
var newPointArr = pointArr.map(p => {
const x = Math.round(310 * p.x / parseInt(imgSize.imgWidth))
const y = Math.round(155 * p.y / parseInt(imgSize.imgHeight))
return { x, y }
})
return newPointArr
}
}
}
</script>

View File

@ -0,0 +1,377 @@
<template>
<div style="position: relative;">
<div
v-if="type === '2'"
class="verify-img-out"
:style="{height: (parseInt(setSize.imgHeight) + vSpace) + 'px'}"
>
<div
class="verify-img-panel"
:style="{width: setSize.imgWidth,
height: setSize.imgHeight,}"
>
<img :src="backImgBase?('data:image/png;base64,'+backImgBase):defaultImg" alt="" style="width:100%;height:100%;display:block">
<div v-show="showRefresh" class="verify-refresh" @click="refresh"><i class="iconfont icon-refresh" />
</div>
<transition name="tips">
<span v-if="tipWords" class="verify-tips" :class="passFlag ?'suc-bg':'err-bg'">{{ tipWords }}</span>
</transition>
</div>
</div>
<!-- 公共部分 -->
<div
class="verify-bar-area"
:style="{width: setSize.imgWidth,
height: barSize.height,
'line-height':barSize.height}"
>
<span class="verify-msg" v-text="text" />
<div
class="verify-left-bar"
:style="{width: (leftBarWidth!==undefined)?leftBarWidth: barSize.height, height: barSize.height, 'border-color': leftBarBorderColor, transaction: transitionWidth}"
>
<span class="verify-msg" v-text="finishText" />
<div
class="verify-move-block"
:style="{width: barSize.height, height: barSize.height, 'background-color': moveBlockBackgroundColor, left: moveBlockLeft, transition: transitionLeft}"
@touchstart="start"
@mousedown="start"
>
<i
:class="['verify-icon iconfont', iconClass]"
:style="{color: iconColor}"
/>
<div
v-if="type === '2'"
class="verify-sub-block"
:style="{'width':Math.floor(parseInt(setSize.imgWidth)*47/310)+ 'px',
'height': setSize.imgHeight,
'top':'-' + (parseInt(setSize.imgHeight) + vSpace) + 'px',
'background-size': setSize.imgWidth + ' ' + setSize.imgHeight,
}"
>
<img :src="'data:image/png;base64,'+blockBackImgBase" alt="" style="width:100%;height:100%;display:block">
</div>
</div>
</div>
</div>
</div>
</template>
<script type="text/babel">
//
import { aesEncrypt } from './../utils/ase'
import { resetSize } from './../utils/util'
import { reqGet, reqCheck } from './../api/index'
export default {
name: 'VerifySlide',
props: {
captchaType: {
type: String,
default: 'blockPuzzle'
},
// popfixed
mode: {
type: String,
default: 'pop'
},
type: {
type: String,
default: '2'
},
vSpace: {
type: Number,
default: 5
},
explain: {
type: String,
default: '向右滑动完成验证'
},
imgSize: {
type: Object,
default() {
return {
width: '310px',
height: '155px'
}
}
},
blockSize: {
type: Object,
default() {
return {
width: '50px',
height: '50px'
}
}
},
barSize: {
type: Object,
default() {
return {
width: '310px',
height: '40px'
}
}
},
defaultImg: {
type: String,
default: ''
}
},
data() {
return {
secretKey: '', //
passFlag: '', //
backImgBase: '', //
blockBackImgBase: '', //
backToken: '', // token
startMoveTime: '', //
endMovetime: '', //
tipsBackColor: '', //
tipWords: '',
text: '',
finishText: '',
setSize: {
imgHeight: 0,
imgWidth: 0,
barHeight: 0,
barWidth: 0
},
top: 0,
left: 0,
moveBlockLeft: undefined,
leftBarWidth: undefined,
//
moveBlockBackgroundColor: undefined,
leftBarBorderColor: '#ddd',
iconColor: undefined,
iconClass: 'icon-right',
status: false, //
isEnd: false, //
showRefresh: true,
transitionLeft: '',
transitionWidth: ''
}
},
computed: {
barArea() {
return this.$el.querySelector('.verify-bar-area')
},
resetSize() {
return resetSize
}
},
watch: {
// type
type: {
immediate: true,
handler() {
this.init()
}
}
},
mounted() {
//
this.$el.onselectstart = function() {
return false
}
},
methods: {
init() {
this.text = this.explain
this.getPictrue()
this.$nextTick(() => {
const setSize = this.resetSize(this) //
for (const key in setSize) {
this.$set(this.setSize, key, setSize[key])
}
this.$parent.$emit('ready', this)
})
var _this = this
window.removeEventListener('touchmove', function(e) {
_this.move(e)
})
window.removeEventListener('mousemove', function(e) {
_this.move(e)
})
//
window.removeEventListener('touchend', function() {
_this.end()
})
window.removeEventListener('mouseup', function() {
_this.end()
})
window.addEventListener('touchmove', function(e) {
_this.move(e)
})
window.addEventListener('mousemove', function(e) {
_this.move(e)
})
//
window.addEventListener('touchend', function() {
_this.end()
})
window.addEventListener('mouseup', function() {
_this.end()
})
},
//
start: function(e) {
e = e || window.event
var x
if (!e.touches) { // PC
x = e.clientX
} else { //
x = e.touches[0].pageX
}
this.startLeft = Math.floor(x - this.barArea.getBoundingClientRect().left)
this.startMoveTime = +new Date() //
if (this.isEnd === false) {
this.text = ''
this.moveBlockBackgroundColor = '#337ab7'
this.leftBarBorderColor = '#337AB7'
this.iconColor = '#fff'
e.stopPropagation()
this.status = true
}
},
//
move: function(e) {
e = e || window.event
if (this.status && this.isEnd === false) {
var x
if (!e.touches) { // PC
x = e.clientX
} else { //
x = e.touches[0].pageX
}
var bar_area_left = this.barArea.getBoundingClientRect().left
var move_block_left = x - bar_area_left // left
if (move_block_left >= this.barArea.offsetWidth - parseInt(parseInt(this.blockSize.width) / 2) - 2) {
move_block_left = this.barArea.offsetWidth - parseInt(parseInt(this.blockSize.width) / 2) - 2
}
if (move_block_left <= 0) {
move_block_left = parseInt(parseInt(this.blockSize.width) / 2)
}
// left
this.moveBlockLeft = (move_block_left - this.startLeft) + 'px'
this.leftBarWidth = (move_block_left - this.startLeft) + 'px'
}
},
//
end: function() {
this.endMovetime = +new Date()
var _this = this
//
if (this.status && this.isEnd === false) {
var moveLeftDistance = parseInt((this.moveBlockLeft || '').replace('px', ''))
moveLeftDistance = moveLeftDistance * 310 / parseInt(this.setSize.imgWidth)
const data = {
captchaType: this.captchaType,
'pointJson': this.secretKey ? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), this.secretKey) : JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
'token': this.backToken
}
reqCheck(data).then(ret => {
const res = ret.data
if (res.repCode === '0000') {
this.moveBlockBackgroundColor = '#5cb85c'
this.leftBarBorderColor = '#5cb85c'
this.iconColor = '#fff'
this.iconClass = 'icon-check'
this.showRefresh = false
this.isEnd = true
if (this.mode === 'pop') {
setTimeout(() => {
this.$parent.clickShow = false
}, 1500)
}
this.passFlag = true
this.tipWords = `${((this.endMovetime - this.startMoveTime) / 1000).toFixed(2)}s验证成功`
var captchaVerification = this.secretKey ? aesEncrypt(this.backToken + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 }), this.secretKey) : this.backToken + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 })
setTimeout(() => {
this.tipWords = ''
this.$parent.closeBox()
this.$parent.$emit('success', { captchaVerification })
}, 700)
} else {
this.moveBlockBackgroundColor = '#d9534f'
this.leftBarBorderColor = '#d9534f'
this.iconColor = '#fff'
this.iconClass = 'icon-close'
this.passFlag = false
setTimeout(function() {
_this.refresh()
}, 1000)
this.$parent.$emit('error', this)
this.tipWords = '验证失败'
setTimeout(() => {
this.tipWords = ''
}, 1000)
}
})
this.status = false
}
},
refresh: function() {
this.showRefresh = true
this.finishText = ''
this.transitionLeft = 'left .3s'
this.moveBlockLeft = 0
this.leftBarWidth = undefined
this.transitionWidth = 'width .3s'
this.leftBarBorderColor = '#ddd'
this.moveBlockBackgroundColor = '#fff'
this.iconColor = '#000'
this.iconClass = 'icon-right'
this.isEnd = false
this.getPictrue()
setTimeout(() => {
this.transitionWidth = ''
this.transitionLeft = ''
this.text = this.explain
}, 300)
},
//
getPictrue() {
const data = {
captchaType: this.captchaType,
clientUid: localStorage.getItem('slider'),
ts: Date.now() //
}
reqGet(data).then(ret => {
const res = ret.data
if (res.repCode === '0000') {
this.backImgBase = res.repData.originalImageBase64
this.blockBackImgBase = res.repData.jigsawImageBase64
this.backToken = res.repData.token
this.secretKey = res.repData.secretKey
} else {
this.tipWords = res.repMsg
}
//
if (res.repCode === '6201') {
this.backImgBase = null
this.blockBackImgBase = null
}
}).catch(() => {
})
}
}
}
</script>

View File

@ -0,0 +1,21 @@
import request from '@/utils/request'
// 登录退出
const url = '/admin/admin.Login/'
// 获取验证图片以及token
export function reqGet(params) {
return request({
url: url + 'captcha',
method: 'get',
params: params
})
}
// 滑动或者点选验证
export function reqCheck(data) {
return request({
url: url + 'captcha',
method: 'post',
data
})
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
import CryptoJS from 'crypto-js'
/**
* @word 要加密的内容
* @keyWord String 服务器随机返回的关键字
* */
export function aesEncrypt(word, keyWord = 'XwKsGlMcdPMEhR1B') {
var key = CryptoJS.enc.Utf8.parse(keyWord)
var srcs = CryptoJS.enc.Utf8.parse(word)
var encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 })
return encrypted.toString()
}

View File

@ -0,0 +1,36 @@
export function resetSize(vm) {
var img_width, img_height, bar_width, bar_height // 图片的宽度、高度,移动条的宽度、高度
var parentWidth = vm.$el.parentNode.offsetWidth || window.offsetWidth
var parentHeight = vm.$el.parentNode.offsetHeight || window.offsetHeight
if (vm.imgSize.width.indexOf('%') !== -1) {
img_width = parseInt(this.imgSize.width) / 100 * parentWidth + 'px'
} else {
img_width = this.imgSize.width
}
if (vm.imgSize.height.indexOf('%') !== -1) {
img_height = parseInt(this.imgSize.height) / 100 * parentHeight + 'px'
} else {
img_height = this.imgSize.height
}
if (vm.barSize.width.indexOf('%') !== -1) {
bar_width = parseInt(this.barSize.width) / 100 * parentWidth + 'px'
} else {
bar_width = this.barSize.width
}
if (vm.barSize.height.indexOf('%') !== -1) {
bar_height = parseInt(this.barSize.height) / 100 * parentHeight + 'px'
} else {
bar_height = this.barSize.height
}
return { imgWidth: img_width, imgHeight: img_height, barWidth: bar_width, barHeight: bar_height }
}
export const _code_chars = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
export const _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0']
export const _code_color2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC']

View File

@ -5,9 +5,18 @@
<el-switch v-model="model.captcha_switch" :active-value="1" :inactive-value="0" />
<span style="margin-left:180px">开启后后台登录需要输入验证码</span>
</el-form-item>
<el-form-item label="验证码方式" prop="captcha_mode">
<el-select v-model="model.captcha_mode" placeholder="" @change="moldChange">
<el-option v-for="item in mold" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<span>字符输入字符行为滑动或点击</span>
</el-form-item>
<el-form-item label="验证码类型" prop="captcha_type">
<el-select v-model="model.captcha_type" placeholder="">
<el-option v-for="item in type" :key="item.value" :label="item.label" :value="item.value" />
<el-select v-if="model.captcha_mode==1" v-model="model.captcha_type" placeholder="">
<el-option v-for="item in typestr" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-select v-else v-model="model.captcha_type" placeholder="">
<el-option v-for="item in typeaj" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item>
@ -32,15 +41,24 @@ export default {
loading: false,
model: {
captcha_switch: 0,
captcha_mode: 1,
captcha_type: 1
},
rules: {},
type: [
mold: [
{ value: 1, label: '字符' },
{ value: 2, label: '行为' }
],
typestr: [
{ value: 1, label: '数字' },
{ value: 2, label: '字母' },
{ value: 3, label: '数字字母' },
{ value: 4, label: '算术' },
{ value: 5, label: '中文' }
],
typeaj: [
{ value: 1, label: '滑动' },
{ value: 2, label: '文字' }
]
}
},
@ -79,6 +97,9 @@ export default {
})
}
})
},
moldChange(value) {
this.model.captcha_type = 1
}
}
}

View File

@ -16,7 +16,7 @@
<el-form-item prop="password">
<el-input v-model="model.password" type="password" placeholder="请输入密码" prefix-icon="el-icon-lock" autocomplete="on" clearable show-password />
</el-form-item>
<el-form-item v-if="captcha_switch" prop="captcha_code">
<el-form-item v-if="captcha_switch && captcha_src" prop="captcha_code">
<el-col :span="13">
<el-input v-model="model.captcha_code" placeholder="请输入验证码" prefix-icon="el-icon-picture" autocomplete="off" clearable />
</el-col>
@ -24,18 +24,29 @@
<el-image class="login-captcha" :src="captcha_src" fit="fill" alt="验证码" title="点击刷新验证码" @click="captcha" />
</el-col>
</el-form-item>
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px" @click.native.prevent="handleLogin">登录</el-button>
<AjCaptcha
v-if="captcha_switch && captcha_mode==2"
ref="ajcaptcha"
mode="pop"
:captcha-type="captcha_type"
:img-size="{ width: '330px', height: '155px' }"
@success="ajcaptchaSuccess"
/>
<el-button v-if="captcha_switch && captcha_mode==2" :loading="loading" type="primary" style="width:100%;margin-bottom:30px" @click="ajcaptchaShow">登录</el-button>
<el-button v-else :loading="loading" type="primary" style="width:100%;margin-bottom:30px" @click.native.prevent="handleLogin">登录</el-button>
</el-form>
</div>
</template>
<script>
import AjCaptcha from '@/components/AjCaptcha'
import { captcha, setting } from '@/api/admin/login'
import { delNotice } from '@/utils/settings'
export default {
name: 'AdminLogin',
components: {},
components: { AjCaptcha },
data() {
return {
name: '登录',
@ -44,6 +55,8 @@ export default {
redirect: undefined,
otherQuery: {},
captcha_switch: 0,
captcha_mode: 1,
captcha_type: 'blockPuzzle',
captcha_src: '',
logo_url: '',
login_bg_url: '',
@ -51,7 +64,8 @@ export default {
username: '',
password: '',
captcha_id: '',
captcha_code: ''
captcha_code: '',
ajcaptcha: {}
},
rules: {
username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
@ -81,31 +95,53 @@ export default {
//
captcha() {
captcha().then(res => {
this.captchaSet(res)
this.captchaData(res.data)
})
},
captchaSet(res) {
captchaData(data) {
this.model.captcha_id = ''
this.model.captcha_code = ''
if (res.data.captcha_switch) {
this.captcha_src = res.data.captcha_src
this.model.captcha_id = res.data.captcha_id
if (data.captcha_switch) {
if (data.captcha_mode === 1) {
this.captcha_src = data.captcha_src
this.model.captcha_id = data.captcha_id
}
}
this.captcha_switch = res.data.captcha_switch
this.captcha_switch = data.captcha_switch
this.captcha_mode = data.captcha_mode
if (data.captcha_type === 1) {
this.captcha_type = 'blockPuzzle'
} else {
this.captcha_type = 'clickWord'
}
},
ajcaptchaSuccess(params) {
this.model.ajcaptcha = params
this.handleLogin()
},
ajcaptchaShow() {
this.$refs['ref'].validate(valid => {
if (valid) {
this.$refs.ajcaptcha.show()
} else {
return false
}
})
},
//
setting() {
this.model.captcha_id = ''
this.model.captcha_code = ''
setting().then(res => {
this.captchaSet(res)
this.login_bg_url = res.data.login_bg_url
this.system_name = res.data.system_name
this.logo_url = res.data.logo_url
this.$store.dispatch('settings/changeSetting', { key: 'systemName', value: res.data.system_name })
this.$store.dispatch('settings/changeSetting', { key: 'pageTitle', value: res.data.page_title })
this.$store.dispatch('settings/changeSetting', { key: 'logoUrl', value: res.data.logo_url })
this.$store.dispatch('settings/changeSetting', { key: 'faviconUrl', value: res.data.favicon_url })
const data = res.data
this.captchaData(data)
this.login_bg_url = data.login_bg_url
this.system_name = data.system_name
this.logo_url = data.logo_url
this.$store.dispatch('settings/changeSetting', { key: 'systemName', value: data.system_name })
this.$store.dispatch('settings/changeSetting', { key: 'pageTitle', value: data.page_title })
this.$store.dispatch('settings/changeSetting', { key: 'logoUrl', value: data.logo_url })
this.$store.dispatch('settings/changeSetting', { key: 'faviconUrl', value: data.favicon_url })
})
},
//