|
Before Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
@ -1,191 +0,0 @@ |
|||
#app { |
|||
.main-container { |
|||
min-height: 100%; |
|||
transition: margin-left 0.28s; |
|||
margin-left: $sideBarWidth; |
|||
position: relative; |
|||
} |
|||
.sidebar-container { |
|||
transition: width 0.28s; |
|||
width: $sideBarWidth !important; |
|||
background-color: $menuBg; |
|||
height: 100%; |
|||
position: fixed; |
|||
font-size: 0; |
|||
top: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
z-index: 1001; |
|||
overflow: hidden; |
|||
|
|||
// reset element-ui css |
|||
.horizontal-collapse-transition { |
|||
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; |
|||
} |
|||
.scrollbar-wrapper { |
|||
overflow-x: hidden !important; |
|||
} |
|||
.el-scrollbar__bar.is-vertical { |
|||
right: 0; |
|||
} |
|||
.el-scrollbar { |
|||
height: 100%; |
|||
} |
|||
&.has-logo { |
|||
.el-scrollbar { |
|||
height: calc(100% - 50px); |
|||
} |
|||
} |
|||
.is-horizontal { |
|||
display: none; |
|||
} |
|||
a { |
|||
display: inline-block; |
|||
width: 100%; |
|||
overflow: hidden; |
|||
} |
|||
.svg-icon { |
|||
margin-right: 16px; |
|||
} |
|||
.sub-el-icon { |
|||
margin-right: 12px; |
|||
margin-left: -2px; |
|||
} |
|||
.el-menu { |
|||
border: none; |
|||
height: 100%; |
|||
width: 100% !important; |
|||
} |
|||
|
|||
// menu hover |
|||
.submenu-title-noDropdown, |
|||
.el-submenu__title { |
|||
&:hover { |
|||
background-color: $menuHover !important; |
|||
} |
|||
} |
|||
.is-active > .el-submenu__title { |
|||
color: $subMenuActiveText !important; |
|||
} |
|||
& .nest-menu .el-submenu > .el-submenu__title, |
|||
& .el-submenu .el-menu-item { |
|||
min-width: $sideBarWidth !important; |
|||
background-color: $subMenuBg !important; |
|||
&:hover { |
|||
background-color: $subMenuHover !important; |
|||
} |
|||
} |
|||
} |
|||
.hideSidebar { |
|||
.sidebar-container { |
|||
width: 54px !important; |
|||
} |
|||
.main-container { |
|||
margin-left: 54px; |
|||
} |
|||
.submenu-title-noDropdown { |
|||
padding: 0 !important; |
|||
position: relative; |
|||
.el-tooltip { |
|||
padding: 0 !important; |
|||
.svg-icon { |
|||
margin-left: 20px; |
|||
} |
|||
.sub-el-icon { |
|||
margin-left: 19px; |
|||
} |
|||
} |
|||
} |
|||
.el-submenu { |
|||
overflow: hidden; |
|||
& > .el-submenu__title { |
|||
padding: 0 !important; |
|||
.svg-icon { |
|||
margin-left: 20px; |
|||
} |
|||
.sub-el-icon { |
|||
margin-left: 19px; |
|||
} |
|||
.el-submenu__icon-arrow { |
|||
display: none; |
|||
} |
|||
} |
|||
} |
|||
.el-menu--collapse { |
|||
.el-submenu { |
|||
& > .el-submenu__title { |
|||
& > span { |
|||
height: 0; |
|||
width: 0; |
|||
overflow: hidden; |
|||
visibility: hidden; |
|||
display: inline-block; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.el-menu--collapse .el-menu .el-submenu { |
|||
min-width: $sideBarWidth !important; |
|||
} |
|||
|
|||
// mobile responsive |
|||
.mobile { |
|||
.main-container { |
|||
margin-left: 0; |
|||
} |
|||
.sidebar-container { |
|||
transition: transform 0.28s; |
|||
width: $sideBarWidth !important; |
|||
} |
|||
&.hideSidebar { |
|||
.sidebar-container { |
|||
pointer-events: none; |
|||
transition-duration: 0.3s; |
|||
transform: translate3d(-$sideBarWidth, 0, 0); |
|||
} |
|||
} |
|||
} |
|||
.withoutAnimation { |
|||
.main-container, |
|||
.sidebar-container { |
|||
transition: none; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// when menu collapsed |
|||
.el-menu--vertical { |
|||
& > .el-menu { |
|||
.svg-icon { |
|||
margin-right: 16px; |
|||
} |
|||
.sub-el-icon { |
|||
margin-right: 12px; |
|||
margin-left: -2px; |
|||
} |
|||
} |
|||
.nest-menu .el-submenu > .el-submenu__title, |
|||
.el-menu-item { |
|||
&:hover { |
|||
// you can use $subMenuHover |
|||
background-color: $menuHover !important; |
|||
} |
|||
} |
|||
|
|||
// the scroll bar appears when the subMenu is too long |
|||
>.el-menu--popup { |
|||
max-height: 100vh; |
|||
overflow-y: auto; |
|||
&::-webkit-scrollbar-track-piece { |
|||
background: #d3dce6; |
|||
} |
|||
&::-webkit-scrollbar { |
|||
width: 6px; |
|||
} |
|||
&::-webkit-scrollbar-thumb { |
|||
background: #99a9bf; |
|||
border-radius: 20px; |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
import Vue from 'vue' |
|||
|
|||
const component = require('./main.vue').default |
|||
const constructor = Vue.extend(component) |
|||
|
|||
const exampleNotice = options => { |
|||
options = options || {} |
|||
let instance = new constructor({ |
|||
data: options |
|||
}) |
|||
instance.vm = instance.$mount() |
|||
instance.dom = instance.vm.$el |
|||
document.body.appendChild(instance.dom) |
|||
return instance.vm |
|||
} |
|||
|
|||
export default { |
|||
install: Vue => { |
|||
Vue.prototype[`$${component.name}`] = exampleNotice |
|||
} |
|||
} |
|||
@ -1,42 +0,0 @@ |
|||
<template> |
|||
<transition name="notice"> |
|||
<div v-if="show" class="notice"> |
|||
{{ content }} |
|||
</div> |
|||
</transition> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'ExampleNotice', |
|||
data() { |
|||
return { |
|||
show: false, |
|||
content: '' |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.show = true |
|||
setTimeout(() => { |
|||
this.show = false |
|||
}, 2000) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.notice { |
|||
padding: 10px; |
|||
background-color: #eee; |
|||
border-radius: 10px; |
|||
@include position-center(xy); |
|||
} |
|||
.notice-leave-active, |
|||
.notice-enter-active { |
|||
transition: all 0.3s; |
|||
} |
|||
.notice-enter, |
|||
.notice-leave-to { |
|||
opacity: 0; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,245 @@ |
|||
<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 class="verify-refresh" style="z-index:3" @click="refresh" v-show="showRefresh"> |
|||
<i class="iconfont icon-refresh"></i> |
|||
</div> |
|||
<img :src="'data:image/png;base64,'+pointBackImgBase" |
|||
ref="canvas" |
|||
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> |
|||
<!-- 'height': this.barSize.height, --> |
|||
<div class="verify-bar-area" |
|||
:style="{'width': setSize.imgWidth, |
|||
'color': this.barAreaColor, |
|||
'border-color': this.barAreaBorderColor, |
|||
'line-height':this.barSize.height}"> |
|||
<span class="verify-msg">{{text}}</span> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script type="text/babel"> |
|||
/** |
|||
* VerifyPoints |
|||
* @description 点选 |
|||
* */ |
|||
import {resetSize, _code_chars, _code_color1, _code_color2} from './../utils/util' |
|||
import {aesEncrypt} from "./../utils/ase" |
|||
import {reqGet,reqCheck} from "../api/verifition-api" |
|||
|
|||
export default { |
|||
name: 'VerifyPoints', |
|||
props: { |
|||
//弹出式pop,固定fixed |
|||
mode: { |
|||
type: String, |
|||
default: 'fixed' |
|||
}, |
|||
captchaType:{ |
|||
type:String, |
|||
}, |
|||
//间隔 |
|||
vSpace: { |
|||
type: Number, |
|||
default: 5 |
|||
}, |
|||
imgSize: { |
|||
type: Object, |
|||
default() { |
|||
return { |
|||
width: '310px', |
|||
height: '155px' |
|||
} |
|||
} |
|||
}, |
|||
barSize: { |
|||
type: Object, |
|||
default() { |
|||
return { |
|||
width: '310px', |
|||
height: '40px' |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
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 |
|||
} |
|||
}, |
|||
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 flag = this.comparePos(this.fontPos, this.checkPosArr); |
|||
//发送后端请求 |
|||
var captchaVerification = this.secretKey? aesEncrypt(this.backToken+'---'+JSON.stringify(this.checkPosArr),this.secretKey):this.backToken+'---'+JSON.stringify(this.checkPosArr) |
|||
let data = { |
|||
captchaType:this.captchaType, |
|||
"pointJson":this.secretKey? aesEncrypt(JSON.stringify(this.checkPosArr),this.secretKey):JSON.stringify(this.checkPosArr), |
|||
"token":this.backToken |
|||
} |
|||
reqCheck(data).then(res=>{ |
|||
if (res.repCode == "0000") { |
|||
this.barAreaColor = '#4cae4c' |
|||
this.barAreaBorderColor = '#5cb85c' |
|||
this.text = '验证成功' |
|||
this.bindingClick = false |
|||
if (this.mode=='pop') { |
|||
setTimeout(()=>{ |
|||
this.$parent.clickShow = false; |
|||
this.refresh(); |
|||
},1500) |
|||
} |
|||
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(){ |
|||
let data = { |
|||
captchaType:this.captchaType |
|||
} |
|||
reqGet(data).then(res=>{ |
|||
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; |
|||
} |
|||
}) |
|||
}, |
|||
//坐标转换函数 |
|||
pointTransfrom(pointArr,imgSize){ |
|||
var newPointArr = pointArr.map(p=>{ |
|||
let x = Math.round(310 * p.x/parseInt(imgSize.imgWidth)) |
|||
let y =Math.round(155 * p.y/parseInt(imgSize.imgHeight)) |
|||
return {x,y} |
|||
}) |
|||
// console.log(newPointArr,"newPointArr"); |
|||
return newPointArr |
|||
} |
|||
}, |
|||
watch: { |
|||
// type变化则全面刷新 |
|||
type: { |
|||
immediate: true, |
|||
handler() { |
|||
this.init() |
|||
} |
|||
} |
|||
}, |
|||
mounted() { |
|||
// 禁止拖拽 |
|||
this.$el.onselectstart = function () { |
|||
return false |
|||
} |
|||
}, |
|||
} |
|||
</script> |
|||
@ -0,0 +1,347 @@ |
|||
<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="'data:image/png;base64,'+backImgBase" alt="" style="width:100%;height:100%;display:block"> |
|||
<div class="verify-refresh" @click="refresh" v-show="showRefresh"><i class="iconfont icon-refresh"></i> |
|||
</div> |
|||
<transition name="tips"> |
|||
<span class="verify-tips" v-if="tipWords" :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"></span> |
|||
<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"></span> |
|||
<div class="verify-move-block" |
|||
@touchstart="start" |
|||
@mousedown="start" |
|||
:style="{width: barSize.height, height: barSize.height, 'background-color': moveBlockBackgroundColor, left: moveBlockLeft, transition: transitionLeft}"> |
|||
<i :class="['verify-icon iconfont', iconClass]" |
|||
:style="{color: iconColor}"></i> |
|||
<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"> |
|||
/** |
|||
* VerifySlide |
|||
* @description 滑块 |
|||
* */ |
|||
import {aesEncrypt} from "./../utils/ase" |
|||
import {resetSize} from './../utils/util' |
|||
import {reqGet,reqCheck} from "../api/verifition-api" |
|||
|
|||
// "captchaType":"blockPuzzle", |
|||
export default { |
|||
name: 'VerifySlide', |
|||
props: { |
|||
captchaType:{ |
|||
type:String, |
|||
}, |
|||
type: { |
|||
type: String, |
|||
default: '1' |
|||
}, |
|||
//弹出式pop,固定fixed |
|||
mode: { |
|||
type: String, |
|||
default: 'fixed' |
|||
}, |
|||
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' |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
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 |
|||
} |
|||
}, |
|||
methods: { |
|||
init() { |
|||
this.text = this.explain |
|||
this.getPictrue(); |
|||
this.$nextTick(() => { |
|||
let setSize = this.resetSize(this) //重新设置宽度高度 |
|||
for (let 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 |
|||
if (!e.touches) { //兼容PC端 |
|||
var x = e.clientX; |
|||
} else { //兼容移动端 |
|||
var 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) { |
|||
if (!e.touches) { //兼容PC端 |
|||
var x = e.clientX; |
|||
} else { //兼容移动端 |
|||
var 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) |
|||
let 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(res=>{ |
|||
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; |
|||
this.refresh(); |
|||
},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}) |
|||
},1000) |
|||
}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(){ |
|||
let data = { |
|||
captchaType:this.captchaType |
|||
} |
|||
reqGet(data).then(res=>{ |
|||
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; |
|||
} |
|||
}) |
|||
}, |
|||
}, |
|||
watch: { |
|||
// type变化则全面刷新 |
|||
type: { |
|||
immediate: true, |
|||
handler() { |
|||
this.init() |
|||
} |
|||
} |
|||
}, |
|||
mounted() { |
|||
// 禁止拖拽 |
|||
this.$el.onselectstart = function () { |
|||
return false |
|||
} |
|||
}, |
|||
} |
|||
</script> |
|||
|
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* 此处可直接引用自己项目封装好的 axios 配合后端联调 |
|||
*/ |
|||
|
|||
import request from "@/api/index.js" //调用项目封装的axios
|
|||
|
|||
//获取验证图片 以及token
|
|||
export function reqGet(data) { |
|||
return request({ |
|||
url: '/captcha/get', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
//滑动或者点选验证
|
|||
export function reqCheck(data) { |
|||
return request({ |
|||
url: '/captcha/check', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
|
|||
@ -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(); |
|||
} |
|||
@ -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'] |
|||
@ -0,0 +1,101 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<div class="pwd-container"> |
|||
<label>找回密码</label> |
|||
<el-tabs |
|||
v-model="retrieveType" class="login-form" style="width: 300px;" |
|||
> |
|||
<el-tab-pane label="手机号找回" name="phone"> |
|||
<el-form ref="form" :model="retrieveAccountForm" label-width="0px"> |
|||
<el-form-item label=""> |
|||
<el-input v-model="retrieveAccountForm.username" placeholder="请输入手机号" autocomplete="off" /> |
|||
</el-form-item> |
|||
<el-form-item label=""> |
|||
<el-input v-model="retrieveAccountForm.password" show-password placeholder="请输入密码" |
|||
autocomplete="off" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label=""> |
|||
<el-input v-model="retrieveAccountForm.code" style="width: 150px;" placeholder="请输入验证码" |
|||
autocomplete="off" |
|||
/> |
|||
<el-button style="margin-left: 20px;" type="primary">发送验证码</el-button> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" style="width: 100%;">登录</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-tab-pane> |
|||
<el-tab-pane label="邮箱注册" name="email"> |
|||
<el-form ref="emailRegForm" status-icon :rules="emailRetrieveRules" :model="retrieveAccountForm" |
|||
label-width="0px" |
|||
> |
|||
<el-form-item label="" prop="email"> |
|||
<el-input v-model="retrieveAccountForm.email" placeholder="请输入邮箱" autocomplete="off" /> |
|||
</el-form-item> |
|||
<el-form-item label="" prop="password"> |
|||
<el-input v-model="retrieveAccountForm.password" show-password placeholder="请输入密码" |
|||
autocomplete="off" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="" prop="code"> |
|||
<el-input v-model="retrieveAccountForm.code" oninput="value=value.replace(/[^\d]/g,'')" |
|||
style="width: 150px;" maxlength="4" placeholder="请输入验证码" |
|||
autocomplete="off" |
|||
/> |
|||
<el-button style="margin-left: 20px;" :disabled="validateCodeBtn" type="primary" |
|||
@click="sendEmailCodeHandle" |
|||
> |
|||
{{ validateCodeBtnText }} |
|||
</el-button> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" style="width: 100%;" @click="emailRegHandle">确定</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'RetrievePwd', |
|||
data() { |
|||
return { |
|||
retrieveType: 'phone', |
|||
retrieveAccountForm: { |
|||
email: '', |
|||
password: '' |
|||
}, |
|||
emailRetrieveRules: { |
|||
email: [ |
|||
{required: true, trigger: 'blur', message: '请输入邮箱'}, |
|||
{ |
|||
pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/, |
|||
message: '请输入正确的邮箱,多个使用;分隔' |
|||
} |
|||
], |
|||
// password: [{required: true, trigger: 'blur', validator: validatePassword}], |
|||
code: {required: true, trigger: 'blur', message: '请输入验证码'} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.app-container { |
|||
height: 100%; |
|||
width: 100%; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-content: center; |
|||
} |
|||
.pwd-container { |
|||
width: 20%; |
|||
height: 50%; |
|||
border: 1px red solid; |
|||
} |
|||
</style> |
|||
@ -1,139 +0,0 @@ |
|||
<template> |
|||
<div class="preview-form"> |
|||
<div class="" v-if="!formState"> |
|||
<el-row type="flex" justify="center" align="middle"> |
|||
<el-col :sm="{span:20}" :xs="{span:24,offset:0}" style="text-align: center"> |
|||
<h4 class="form-name-text"> |
|||
{{formConf.title}}</h4> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row type="flex" justify="center" align="middle"> |
|||
<el-col :sm="{span:20}" :xs="{span:24,offset:0}" style="text-align: center"> |
|||
<p class="form-name-text"> |
|||
{{formConf.description}} |
|||
</p> |
|||
</el-col> |
|||
</el-row> |
|||
<el-divider></el-divider> |
|||
<parser v-if="formConf.fields.length" :form-conf="formConf" @submit="submitForm"/> |
|||
</div> |
|||
<div v-if="formState"> |
|||
<p style="text-align: center"> |
|||
<i class="el-icon-check"/>您已完成本次问卷,感谢您的帮助与支持</p> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import Parser from '@/components/parser/Parser' |
|||
import {dbDataConvertForItemJson} from '@/utils/convert' |
|||
// 若parser是通过安装npm方式集成到项目中的,使用此行引入 |
|||
// import Parser from 'form-gen-parser' |
|||
window.onload = function() { |
|||
document.addEventListener('touchstart', function(event) { |
|||
if (event.touches.length > 1) { |
|||
event.preventDefault() |
|||
} |
|||
}) |
|||
document.addEventListener('gesturestart', function(event) { |
|||
event.preventDefault() |
|||
}) |
|||
} |
|||
export default { |
|||
components: { |
|||
Parser |
|||
}, |
|||
props: { |
|||
projectKey: '' |
|||
}, |
|||
data() { |
|||
return { |
|||
key2: +new Date(), |
|||
projectKey: '', |
|||
formState: false,//是否填写 |
|||
formConf: { |
|||
fields: [], |
|||
__methods__: { |
|||
clickTestButton1() { |
|||
console.log( |
|||
`%c【测试按钮1】点击事件里可以访问当前表单: |
|||
1) formModel='formData', 所以this.formData可以拿到当前表单的model |
|||
2) formRef='elForm', 所以this.$refs.elForm可以拿到当前表单的ref(vue组件) |
|||
`, |
|||
'color:#409EFF;font-size: 15px' |
|||
) |
|||
console.log('表单的Model:', this.formData) |
|||
console.log('表单的ref:', this.$refs.elForm) |
|||
} |
|||
}, |
|||
formRef: 'elForm', |
|||
formModel: 'formData', |
|||
size: 'small', |
|||
labelPosition: 'top', |
|||
labelWidth: 100, |
|||
formRules: 'rules', |
|||
gutter: 15, |
|||
disabled: false, |
|||
span: 24, |
|||
formBtns: true, |
|||
resetBtn: true, |
|||
unFocusedComponentBorder: true |
|||
} |
|||
} |
|||
}, |
|||
computed: {}, |
|||
watch: {}, |
|||
created() { |
|||
this.formConf.size = window.innerWidth < 480 ? 'medium' : 'small' |
|||
}, |
|||
mounted() { |
|||
this.$api.get(`/user/project/details/${this.$route.query.key}`).then(res => { |
|||
if (res.data) { |
|||
let fields = res.data.projectItems.map(item => { |
|||
return dbDataConvertForItemJson(item) |
|||
}) |
|||
this.formConf.fields = fields |
|||
this.formConf.title = res.data.project.name |
|||
this.formConf.description = res.data.project.describe |
|||
} |
|||
}) |
|||
this.projectKey = this.$route.query.key |
|||
}, |
|||
methods: { |
|||
fillFormData(form, data) { |
|||
form.fields.forEach(item => { |
|||
const val = data[item.__vModel__] |
|||
if (val) { |
|||
item.__config__.defaultValue = val |
|||
} |
|||
}) |
|||
}, |
|||
submitForm(data) { |
|||
this.$api.post('/user/project/result/create', { |
|||
'projectKey': this.projectKey, |
|||
'collectData': data |
|||
}).then(res => { |
|||
this.formState = true |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.preview-form { |
|||
margin: 15px auto; |
|||
width: 800px; |
|||
padding: 15px; |
|||
background-repeat: repeat; |
|||
background-color: rgba(229, 239, 247, 0.87); |
|||
} |
|||
|
|||
@media screen and (max-width: 750px) { |
|||
.preview-form { |
|||
margin: 0px; |
|||
width: 93% !important; |
|||
background-color: white; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,33 @@ |
|||
<template> |
|||
<div> |
|||
<Verify |
|||
ref="verify" |
|||
:mode="'pop'" |
|||
:captcha-type="'blockPuzzle'" |
|||
:img-size="{ width: '330px', height: '155px' }" |
|||
@success="'success'" |
|||
/> |
|||
<button @click="useVerify">调用验证组件</button> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
// 引入组件 |
|||
import Verify from '@/components/verifition/Verify' |
|||
|
|||
export default { |
|||
name: 'App', |
|||
components: { |
|||
Verify |
|||
}, |
|||
methods: { |
|||
success(params) { |
|||
// params 返回的二次验证参数, 和登录参数一起回传给登录接口,方便后台进行二次验证 |
|||
console.log(params) |
|||
}, |
|||
useVerify() { |
|||
this.$refs.verify.show() |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||