Compare commits

..

No commits in common. '3ea356b115fca716cf268575428aa109a054c25c' and '328d0d474480401fd8d8acf5bfac901538dc1842' have entirely different histories.

@ -1,18 +1,12 @@
<template>
<!-- 使用Element Plus的配置提供者组件来包裹应用的主要内容 -->
<el-config-provider namespace="el">
<!-- 路由视图组件它是Vue Router的一部分用于渲染与当前路由匹配的组件 -->
<el-config-provider
namespace="el"
>
<router-view />
</el-config-provider>
</template>
<!--
下面的样式部分使用SCSS预处理器并且引入了一个全局样式文件
注意这里的注释指出了一个ESLint规则被禁用这可能是为了允许在scoped样式中使用特定的style类型`lang="scss"`
-->
<!-- eslint-disable-next-line vue-scoped-css/enforce-style-type -->
<style lang="scss">
// 使@useSCSSapp.scss
// mixin
@use '@/assets/app.scss';
</style>

File diff suppressed because one or more lines are too long

@ -1,87 +1,46 @@
<template>
<div style="position: relative;">
<!-- 图片验证码部分 -->
<div
v-if="type === '2'"
class="verify-img-out"
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
v-show="showRefresh"
class="verify-refresh"
@click="refresh"
>
<i class="iconfont icon-refresh"/>
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 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>
<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}"
>
<!-- 滑动条上的消息文本 -->
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, transition: transitionWidth}"
>
<!-- 完成后的消息文本 -->
: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"
>
<!-- 滑动块内的图标 -->
@mousedown="start">
<i
:class="['verify-icon iconfont', iconClass]"
:style="{color: iconColor}"
/>
<!-- type '2' 时显示的小图块 -->
: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',
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;-webkit-user-drag:none;"
/>
}">
<img :src="'data:image/png;base64,'+blockBackImgBase" alt="" style="width:100%;height:100%;display:block;-webkit-user-drag:none;"/>
</div>
</div>
</div>
@ -91,36 +50,37 @@
<script type="text/babel">
/**
* VerifySlide
* @description 滑块验证组件用于用户交互验证防止机器人操作
*/
import { aesEncrypt } from './../utils/ase' // AES
import { resetSize } from './../utils/util' //
import { reqCheck, reqGet } from './../api/index' // API
* @description 滑块
* */
import { aesEncrypt } from './../utils/ase'
import { resetSize } from './../utils/util'
import { reqCheck, reqGet } from './../api/index'
import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs, watch } from 'vue'
// "captchaType":"blockPuzzle",
export default {
name: 'VerifySlide',
props: {
captchaType: { //
captchaType: {
type: String
},
type: { // '1'
type: {
type: String,
default: '1'
},
mode: { //
// popfixed
mode: {
type: String,
default: 'fixed'
},
vSpace: { //
vSpace: {
type: Number,
default: 5
},
explain: { //
explain: {
type: String,
default: '向右滑动完成验证'
},
imgSize: { //
imgSize: {
type: Object,
default () {
return {
@ -129,7 +89,7 @@ export default {
}
}
},
blockSize: { //
blockSize: {
type: Object,
default () {
return {
@ -138,7 +98,7 @@ export default {
}
}
},
barSize: { //
barSize: {
type: Object,
default () {
return {
@ -151,110 +111,133 @@ export default {
setup (props) {
const { mode, captchaType, type, blockSize, explain } = toRefs(props)
const { proxy } = getCurrentInstance()
//
const secretKey = ref('') //
const passFlag = ref(false) //
const backImgBase = ref('') // base64
const blockBackImgBase = ref('') // base64
const secretKey = ref('') // ase
const passFlag = ref('') //
const backImgBase = ref('') //
const blockBackImgBase = ref('') //
const backToken = ref('') // token
const startMoveTime = ref(0) //
const endMovetime = ref(0) //
const startMoveTime = ref('') //
const endMovetime = ref('') //
const tipsBackColor = ref('') //
const tipWords = ref('') //
const text = ref(explain.value) //
const finishText = ref('') //
const tipWords = ref('')
const text = ref('')
const finishText = ref('')
const setSize = reactive({
imgHeight: 0,
imgWidth: 0,
barHeight: 0,
barWidth: 0
}) //
const top = ref(0) //
const left = ref(0) //
const moveBlockLeft = ref(undefined) //
const leftBarWidth = ref(undefined) //
//
const moveBlockBackgroundColor = ref(undefined) //
const leftBarBorderColor = ref('#ddd') //
const iconColor = ref(undefined) //
const iconClass = ref('icon-right') //
const status = ref(false) //
const isEnd = ref(false) //
const showRefresh = ref(true) //
const transitionLeft = ref('') //
const transitionWidth = ref('') //
const startLeft = ref(0) // left
})
const top = ref(0)
const left = ref(0)
const moveBlockLeft = ref(undefined)
const leftBarWidth = ref(undefined)
//
const moveBlockBackgroundColor = ref(undefined)
const leftBarBorderColor = ref('#ddd')
const iconColor = ref(undefined)
const iconClass = ref('icon-right')
const status = ref(false) //
const isEnd = ref(false) //
const showRefresh = ref(true)
const transitionLeft = ref('')
const transitionWidth = ref('')
const startLeft = ref(0)
const barArea = computed(() => {
return proxy.$el.querySelector('.verify-bar-area') // DOM
return proxy.$el.querySelector('.verify-bar-area')
})
function init () {
//
text.value = explain.value
getPictrue()
// ready
nextTick(() => {
const sizeInfo = resetSize(proxy)
Object.assign(setSize, sizeInfo)
const { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
setSize.imgHeight = imgHeight
setSize.imgWidth = imgWidth
setSize.barHeight = barHeight
setSize.barWidth = barWidth
proxy.$parent.$emit('ready', proxy)
})
// PC
window.addEventListener('touchmove', move)
window.addEventListener('mousemove', move)
window.addEventListener('touchend', end)
window.addEventListener('mouseup', end)
}
// type
window.removeEventListener('touchmove', (e) => {
move(e)
})
window.removeEventListener('mousemove', (e) => {
move(e)
})
//
window.removeEventListener('touchend', () => {
end()
})
window.removeEventListener('mouseup', () => {
end()
})
window.addEventListener('touchmove', (e) => {
move(e)
})
window.addEventListener('mousemove', (e) => {
move(e)
})
//
window.addEventListener('touchend', () => {
end()
})
window.addEventListener('mouseup', () => {
end()
})
}
watch(type, () => {
init()
})
//
onMounted(() => {
//
init()
//
proxy.$el.onselectstart = function () {
return false
}
})
//
//
function start (e) {
e = e || window.event
let x = !e.touches ? e.clientX : e.touches[0].pageX //
startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left) // left
startMoveTime.value = Date.now() //
if (!isEnd.value) {
text.value = '' //
//
let x
if (!e.touches) { // PC
x = e.clientX
} else { //
x = e.touches[0].pageX
}
startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left)
startMoveTime.value = Date.now() //
if (isEnd.value === false) {
text.value = ''
moveBlockBackgroundColor.value = '#337ab7'
leftBarBorderColor.value = '#337AB7'
iconColor.value = '#fff'
status.value = true //
e.stopPropagation()
status.value = true
}
}
//
//
function move (e) {
e = e || window.event
let x = !e.touches ? e.clientX : e.touches[0].pageX //
if (status.value && !isEnd.value) {
let x
if (status.value && isEnd.value === false) {
if (!e.touches) { // PC
x = e.clientX
} else { //
x = e.touches[0].pageX
}
const bar_area_left = barArea.value.getBoundingClientRect().left
let move_block_left = x - bar_area_left // left
//
if (move_block_left >= barArea.value.offsetWidth - parseInt(blockSize.value.width) / 2 - 2) {
move_block_left = barArea.value.offsetWidth - parseInt(blockSize.value.width) / 2 - 2
let move_block_left = x - bar_area_left // left
if (move_block_left >= barArea.value.offsetWidth - Number.parseInt(Number.parseInt(blockSize.value.width) / 2) - 2) {
move_block_left = barArea.value.offsetWidth - Number.parseInt(Number.parseInt(blockSize.value.width) / 2) - 2
}
if (move_block_left <= 0) {
move_block_left = parseInt(blockSize.value.width) / 2
move_block_left = Number.parseInt(Number.parseInt(blockSize.value.width) / 2)
}
//
// left
moveBlockLeft.value = `${move_block_left - startLeft.value}px`
leftBarWidth.value = `${move_block_left - startLeft.value}px`
}
@ -262,168 +245,99 @@ export default {
//
function end () {
//
endMovetime.value = Date.now()
// statustrueisEndfalse
//
if (status.value && isEnd.value === false) {
//
let moveLeftDistance = Number.parseInt((moveBlockLeft.value || '').replace('px', ''))
moveLeftDistance = moveLeftDistance * 310 / Number.parseInt(setSize.imgWidth)
// xy5.0
const data = {
captchaType: captchaType.value, //
pointJson: secretKey.value
? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), secretKey.value) //
: JSON.stringify({ x: moveLeftDistance, y: 5.0 }), //
token: backToken.value //
captchaType: captchaType.value,
pointJson: secretKey.value ? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), secretKey.value) : JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
token: backToken.value
}
//
reqCheck(data).then(res => {
// '0000'
if (res.data.repCode === '0000') {
// UI
moveBlockBackgroundColor.value = '#5cb85c'
leftBarBorderColor.value = '#5cb85c'
iconColor.value = '#fff'
iconClass.value = 'icon-check'
showRefresh.value = false
isEnd.value = true
//
if (mode.value === 'pop') {
setTimeout(() => {
proxy.$parent.clickShow = false
refresh() //
refresh()
}, 1500)
}
passFlag.value = true //
tipWords.value = `${((endMovetime.value - startMoveTime.value) / 1000).toFixed(2)}s验证成功` //
//
const captchaVerification = secretKey.value
? aesEncrypt(`${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}`, secretKey.value) //
: `${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}` //
//
passFlag.value = true
tipWords.value = `${((endMovetime.value - startMoveTime.value) / 1000).toFixed(2)}s验证成功`
const captchaVerification = secretKey.value ? aesEncrypt(`${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}`, secretKey.value) : `${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}`
setTimeout(() => {
tipWords.value = ''
proxy.$parent.closeBox()
proxy.$parent.$emit('success', { captchaVerification })
}, 1000)
} else {
// UI
moveBlockBackgroundColor.value = '#d9534f'
leftBarBorderColor.value = '#d9534f'
iconColor.value = '#fff'
iconClass.value = 'icon-close'
passFlag.value = false
//
setTimeout(() => {
refresh()
}, 1000)
//
proxy.$parent.$emit('error', proxy)
tipWords.value = '验证失败'
//
setTimeout(() => {
tipWords.value = ''
}, 1000)
}
})
status.value = false //
status.value = false
}
}
const refresh = () => {
// showRefresh true
//
showRefresh.value = true;
// finishText finishText
//
finishText.value = '';
// transitionLeft 0.3
// 使
transitionLeft.value = 'left .3s';
// moveBlockLeft 0
moveBlockLeft.value = 0;
// leftBarWidth undefined
//
leftBarWidth.value = undefined;
// transitionWidth 0.3
// 使
transitionWidth.value = 'width .3s';
// #ddd
leftBarBorderColor.value = '#ddd';
// #fff
moveBlockBackgroundColor.value = '#fff';
showRefresh.value = true
finishText.value = ''
// #000
iconColor.value = '#000';
transitionLeft.value = 'left .3s'
moveBlockLeft.value = 0
// 'icon-right'
//
iconClass.value = 'icon-right';
leftBarWidth.value = undefined
transitionWidth.value = 'width .3s'
// isEnd false
isEnd.value = false;
leftBarBorderColor.value = '#ddd'
moveBlockBackgroundColor.value = '#fff'
iconColor.value = '#000'
iconClass.value = 'icon-right'
isEnd.value = false
// getPictrue
// 使
getPictrue();
// 使 setTimeout 300
// - transitionWidth transitionLeft CSS
// - text explain
//
getPictrue()
setTimeout(() => {
transitionWidth.value = '';
transitionLeft.value = '';
text.value = explain.value;
}, 300);
};
transitionWidth.value = ''
transitionLeft.value = ''
text.value = explain.value
}, 300)
}
//
function getPictrue () {
//
const data = {
captchaType: captchaType.value, //
clientUid: localStorage.getItem('b2cSlider'), //
ts: Date.now() //
};
//
captchaType: captchaType.value,
clientUid: localStorage.getItem('b2cSlider'),
ts: Date.now() //
}
reqGet(data).then(res => {
// '0000'
if (res.data.repCode === '0000') {
// Base64
backImgBase.value = res.data.repData.originalImageBase64; // Base64
blockBackImgBase.value = res.data.repData.jigsawImageBase64; // Base64
//
backToken.value = res.data.repData.token;
secretKey.value = res.data.repData.secretKey;
backImgBase.value = res.data.repData.originalImageBase64
blockBackImgBase.value = res.data.repData.jigsawImageBase64
backToken.value = res.data.repData.token
secretKey.value = res.data.repData.secretKey
} else {
//
tipWords.value = res.data.repMsg; //
tipWords.value = res.data.repMsg
}
});
})
}
return {
secretKey, // ase

@ -1,66 +1,27 @@
export const tableOption = {
// 搜索菜单所占的栅格数默认为6适用于响应式布局。
searchMenuSpan: 6,
// 是否显示列按钮如排序、筛选等false表示不显示。
columnBtn: false,
// 是否启用表格边框true表示启用。
border: true,
// 是否显示多选框true表示启用允许用户选择一行或多行记录。
selection: true,
// 是否显示行号默认为false即不显示行号。
index: false,
// 行号标题当index为true时生效默认值为'序号'。
indexLabel: '序号',
// 是否启用斑马纹样式true表示启用使表格更易读。
stripe: true,
// 菜单对齐方式,设置为居中对齐。
menuAlign: 'center',
// 菜单宽度默认为350px。
menuWidth: 350,
// 表格内容的对齐方式,设置为居中对齐。
align: 'center',
// 是否显示刷新按钮true表示显示允许用户刷新表格数据。
refreshBtn: true,
// 搜索框大小设置为mini提供紧凑型搜索输入框。
searchSize: 'mini',
// 是否显示添加按钮默认为false即不显示。
addBtn: false,
// 是否显示编辑按钮默认为false即不显示。
editBtn: false,
// 是否显示删除按钮默认为false即不显示。
delBtn: false,
// 是否显示查看按钮默认为false即不显示。
viewBtn: false,
// 配置选项指定下拉框等组件的label和value字段名。
props: {
label: 'label', // 下拉框选项显示的文本字段名
value: 'value' // 下拉框选项对应的值字段名
label: 'label',
value: 'value'
},
// 定义表格中的列,每一列由一个对象表示。
column: [
{
// 列标题
column: [{
label: '表单名称',
// 对应的数据字段名
prop: 'formName',
// 是否允许在该列上进行搜索过滤
search: true
}, {
label: '按钮文本',
@ -69,27 +30,41 @@ export const tableOption = {
}, {
label: '提交次数',
prop: 'submitNum',
type: 'select', // 列类型,这里是下拉选择框
dicData: [ // 下拉选择框的数据源
{ label: '不做限制', value: 0 },
{ label: '每个IP限填一次', value: 1 }
type: 'select',
dicData: [
{
label: '不做限制',
value: 0
}, {
label: '每个IP限填一次',
value: 1
}
]
}, {
label: '开启验证',
prop: 'needValidation',
type: 'select',
dicData: [
{ label: '不需要', value: 0 },
{ label: '需要', value: 1 }
{
label: '不需要',
value: 0
}, {
label: '需要',
value: 1
}
]
}, {
label: '提交权限',
prop: 'submitPerm',
type: 'select',
dicData: [
{ label: '所有人', value: 0 },
{ label: '仅会员可提交', value: 1 }
]
{
label: '所有人',
value: 0
}, {
label: '仅会员可提交',
value: 1
}
]
}]
}

@ -1,92 +1,47 @@
export const tableOption = {
// 搜索菜单所占的栅格数默认为6适用于响应式布局。
searchMenuSpan: 6,
// 是否显示列按钮如排序、筛选等false表示不显示。
columnBtn: false,
// 是否启用表格边框true表示启用。
border: true,
// 是否显示多选框true表示启用允许用户选择一行或多行记录。
selection: true,
// 是否显示行号默认为false即不显示行号。
index: false,
// 行号标题当index为true时生效默认值为'序号'。
indexLabel: '序号',
// 是否启用斑马纹样式true表示启用使表格更易读。
stripe: true,
// 菜单对齐方式,设置为居中对齐。
menuAlign: 'center',
// 菜单宽度默认为350px。
menuWidth: 350,
// 表格内容的对齐方式,设置为居中对齐。
align: 'center',
// 是否显示刷新按钮true表示显示允许用户刷新表格数据。
refreshBtn: true,
// 搜索框大小设置为mini提供紧凑型搜索输入框。
searchSize: 'mini',
// 是否显示添加按钮默认为false即不显示。
addBtn: false,
// 是否显示编辑按钮默认为false即不显示。
editBtn: false,
// 是否显示删除按钮默认为false即不显示。
delBtn: false,
// 是否显示查看按钮默认为false即不显示。
viewBtn: false,
// 配置选项指定下拉框等组件的label和value字段名。
props: {
label: 'label', // 下拉框选项显示的文本字段名
value: 'value' // 下拉框选项对应的值字段名
label: 'label',
value: 'value'
},
// 定义表格中的列,每一列由一个对象表示。
column: [
{
// 列标题
column: [{
label: '轮播图片',
// 对应的数据字段名
prop: 'imgUrl',
// 列类型,这里是上传组件
type: 'upload',
// 使用插槽自定义列的内容true表示启用
slot: true,
// 上传列表的类型,这里是指定为图片格式
listType: 'picture-img'
}, {
// 列标题
label: '顺序',
// 对应的数据字段名
prop: 'seq'
}, {
// 设置该列的宽度为150px
width: 150,
// 列标题
label: '状态',
// 对应的数据字段名
prop: 'status',
// 是否允许在该列上进行搜索过滤
search: true,
// 列类型,这里是下拉选择框
type: 'select',
// 下拉选择框的数据源
dicData: [
{ label: '禁用', value: 0 },
{ label: '正常', value: 1 }
]
{
label: '禁用',
value: 0
}, {
label: '正常',
value: 1
}
]
}]
}

@ -1,102 +1,52 @@
export const tableOption = {
// 搜索菜单所占的栅格数默认为6适用于响应式布局。
searchMenuSpan: 6,
// 是否显示列按钮如排序、筛选等false表示不显示。
columnBtn: false,
// 是否启用表格边框true表示启用。
border: true,
// 是否显示行号默认为false即不显示行号。
index: false,
// 行号标题当index为true时生效默认值为'序号'。
indexLabel: '序号',
// 是否显示多选框true表示启用允许用户选择一行或多行记录。
selection: true,
// 是否启用斑马纹样式true表示启用使表格更易读。
stripe: true,
// 菜单对齐方式,设置为居中对齐。
menuAlign: 'center',
// 菜单宽度默认为350px。
menuWidth: 350,
// 表格内容的对齐方式,设置为居中对齐。
align: 'center',
// 是否显示刷新按钮true表示显示允许用户刷新表格数据。
refreshBtn: true,
// 搜索框大小设置为mini提供紧凑型搜索输入框。
searchSize: 'mini',
// 是否显示添加按钮默认为false即不显示。
addBtn: false,
// 是否显示编辑按钮默认为false即不显示。
editBtn: false,
// 是否显示删除按钮默认为false即不显示。
delBtn: false,
// 是否显示查看按钮默认为false即不显示。
viewBtn: false,
// 配置选项指定下拉框等组件的label和value字段名。
props: {
label: 'label', // 下拉框选项显示的文本字段名
value: 'value' // 下拉框选项对应的值字段名
label: 'label',
value: 'value'
},
// 定义表格中的列,每一列由一个对象表示。
column: [
{
// 列标题
column: [{
label: '创建时间',
// 对应的数据字段名
prop: 'createTime'
},
{
// 列标题
label: '姓名',
// 对应的数据字段名
prop: 'userName',
// 是否允许在该列上进行搜索过滤
search: true
},
{
// 列标题
}, {
label: '邮箱',
// 对应的数据字段名
prop: 'email'
},
{
// 列标题
}, {
label: '联系方式',
// 对应的数据字段名
prop: 'contact'
},
{
// 列标题
}, {
label: '审核',
// 对应的数据字段名
prop: 'status',
// 是否允许在该列上进行搜索过滤
search: true,
// 使用插槽自定义列的内容true表示启用
slot: true,
// 列类型,这里是下拉选择框
type: 'select',
// 下拉选择框的数据源
dicData: [
{ label: '未审核', value: 0 },
{ label: '审核通过', value: 1 }
]
{
label: '未审核',
value: 0
}, {
label: '审核通过',
value: 1
}
]
}]
}

Loading…
Cancel
Save