mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-12-26 10:05:57 +08:00
349 lines
11 KiB
Vue
349 lines
11 KiB
Vue
<template>
|
||
<div class="wg-cap-wrap" :style="{ width: width }">
|
||
<div class="wg-cap-wrap__header">
|
||
<span>请在下图<em>依次</em>点击:</span>
|
||
<img class="wg-cap-wrap__thumb" v-if="thumbBase64Code" :src="thumbBase64Code" alt=" " />
|
||
</div>
|
||
<div class="wg-cap-wrap__body">
|
||
<img class="wg-cap-wrap__picture" v-if="imageBase64Code" :src="imageBase64Code" alt=" " @click="handleClickPos($event)" />
|
||
<img
|
||
class="wg-cap-wrap__loading"
|
||
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBzdHlsZT0ibWFyZ2luOiBhdXRvOyBiYWNrZ3JvdW5kOiByZ2JhKDI0MSwgMjQyLCAyNDMsIDApOyBkaXNwbGF5OiBibG9jazsgc2hhcGUtcmVuZGVyaW5nOiBhdXRvOyIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIj4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjM2LjgxMDEiIHI9IjEzIiBmaWxsPSIjM2U3Y2ZmIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN5IiBkdXI9IjFzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgY2FsY01vZGU9InNwbGluZSIga2V5U3BsaW5lcz0iMC40NSAwIDAuOSAwLjU1OzAgMC40NSAwLjU1IDAuOSIga2V5VGltZXM9IjA7MC41OzEiIHZhbHVlcz0iMjM7Nzc7MjMiPjwvYW5pbWF0ZT4KICA8L2NpcmNsZT4KPC9zdmc+"
|
||
alt="正在加载中..."
|
||
/>
|
||
<div v-for="(dot, key) in dots" :key="key" class="wg-cap-wrap__dot" :style="`top: ${dot.y}px; left:${dot.x}px;`">
|
||
<span>{{ dot.index }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="wg-cap-wrap__footer">
|
||
<div class="wg-cap-wrap__ico flex">
|
||
<img
|
||
@click="handleCloseEvent"
|
||
src="data:image/svg+xml;base64,PHN2ZyB0PSIxNjI2NjE0NDM5NDIzIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijg2NzUiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNNTEyIDIzLjI3MjcyN2MyNjkuOTE3MDkxIDAgNDg4LjcyNzI3MyAyMTguODEwMTgyIDQ4OC43MjcyNzMgNDg4LjcyNzI3M2E0ODYuNjMyNzI3IDQ4Ni42MzI3MjcgMCAwIDEtODQuOTQ1NDU1IDI3NS40MDk0NTUgNDYuNTQ1NDU1IDQ2LjU0NTQ1NSAwIDAgMS03Ni44NDY1NDUtNTIuNTI2NTQ2QTM5My41NDE4MTggMzkzLjU0MTgxOCAwIDAgMCA5MDcuNjM2MzY0IDUxMmMwLTIxOC41MDc2MzYtMTc3LjEyODcyNy0zOTUuNjM2MzY0LTM5NS42MzYzNjQtMzk1LjYzNjM2NFMxMTYuMzYzNjM2IDI5My40OTIzNjQgMTE2LjM2MzYzNiA1MTJzMTc3LjEyODcyNyAzOTUuNjM2MzY0IDM5NS42MzYzNjQgMzk1LjYzNjM2NGEzOTUuMTcwOTA5IDM5NS4xNzA5MDkgMCAwIDAgMTI1LjQ0LTIwLjI5MzgxOSA0Ni41NDU0NTUgNDYuNTQ1NDU1IDAgMCAxIDI5LjQ4NjU0NSA4OC4yOTY3MjhBNDg4LjI2MTgxOCA0ODguMjYxODE4IDAgMCAxIDUxMiAxMDAwLjcyNzI3M0MyNDIuMDgyOTA5IDEwMDAuNzI3MjczIDIzLjI3MjcyNyA3ODEuOTE3MDkxIDIzLjI3MjcyNyA1MTJTMjQyLjA4MjkwOSAyMy4yNzI3MjcgNTEyIDIzLjI3MjcyN3ogbS0xMTUuMiAzMDcuNzEyTDUxMiA0NDYuMTM4MTgybDExNS4yLTExNS4yYTQ2LjU0NTQ1NSA0Ni41NDU0NTUgMCAxIDEgNjUuODE1MjczIDY1Ljg2MTgxOEw1NzcuODYxODE4IDUxMmwxMTUuMiAxMTUuMmE0Ni41NDU0NTUgNDYuNTQ1NDU1IDAgMSAxLTY1Ljg2MTgxOCA2NS44MTUyNzNMNTEyIDU3Ny44NjE4MThsLTExNS4yIDExNS4yYTQ2LjU0NTQ1NSA0Ni41NDU0NTUgMCAxIDEtNjUuODE1MjczLTY1Ljg2MTgxOEw0NDYuMTM4MTgyIDUxMmwtMTE1LjItMTE1LjJhNDYuNTQ1NDU1IDQ2LjU0NTQ1NSAwIDEgMSA2NS44NjE4MTgtNjUuODE1MjczeiIgcC1pZD0iODY3NiIgZmlsbD0iIzcwNzA3MCI+PC9wYXRoPjwvc3ZnPg=="
|
||
alt="关闭"
|
||
/>
|
||
<img
|
||
@click="handleRefreshEvent"
|
||
src="data:image/svg+xml;base64,PHN2ZyB0PSIxNjI2NjE0NDk5NjM4IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjEzNjAiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNMTg3LjQ1NiA0MjUuMDI0YTMzNiAzMzYgMCAwIDAgMzY4LjM4NCA0MjAuMjI0IDQ4IDQ4IDAgMCAxIDEyLjU0NCA5NS4xNjggNDMyIDQzMiAwIDAgMS00NzMuNjY0LTU0MC4xNmwtNTcuMjgtMTUuMzZhMTIuOCAxMi44IDAgMCAxLTYuMjcyLTIwLjkyOGwxNTkuMTY4LTE3OS40NTZhMTIuOCAxMi44IDAgMCAxIDIyLjE0NCA1Ljg4OGw0OC4wNjQgMjM1LjA3MmExMi44IDEyLjggMCAwIDEtMTUuODA4IDE0LjkxMmwtNTcuMjgtMTUuMzZ6TTgzNi40OCA1OTkuMDRhMzM2IDMzNiAwIDAgMC0zNjguMzg0LTQyMC4yMjQgNDggNDggMCAxIDEtMTIuNTQ0LTk1LjE2OCA0MzIgNDMyIDAgMCAxIDQ3My42NjQgNTQwLjE2bDU3LjI4IDE1LjM2YTEyLjggMTIuOCAwIDAgMSA2LjI3MiAyMC45MjhsLTE1OS4xNjggMTc5LjQ1NmExMi44IDEyLjggMCAwIDEtMjIuMTQ0LTUuODg4bC00OC4wNjQtMjM1LjA3MmExMi44IDEyLjggMCAwIDEgMTUuODA4LTE0LjkxMmw1Ny4yOCAxNS4zNnoiIGZpbGw9IiM3MDcwNzAiIHAtaWQ9IjEzNjEiPjwvcGF0aD48L3N2Zz4="
|
||
alt="刷新"
|
||
/>
|
||
</div>
|
||
<div class="wg-cap-wrap__btn">
|
||
<el-button type="primary" @click="handleConfirmEvent">确认</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: "CaptchaPlus",
|
||
mounted() {
|
||
this.$emit("refresh");
|
||
},
|
||
props: {
|
||
value: Boolean,
|
||
width: {
|
||
type: String,
|
||
default: "300px",
|
||
},
|
||
calcPosType: {
|
||
type: String,
|
||
default: "dom",
|
||
validator: (value) => ["dom", "screen"].includes(value),
|
||
},
|
||
maxDot: {
|
||
type: Number,
|
||
default: 5,
|
||
// validator: value => value > 10
|
||
},
|
||
imageBase64: String,
|
||
thumbBase64: String,
|
||
},
|
||
data() {
|
||
return {
|
||
dots: [],
|
||
imageBase64Code: "",
|
||
thumbBase64Code: "",
|
||
};
|
||
},
|
||
watch: {
|
||
value() {
|
||
this.dots = [];
|
||
this.imageBase64Code = "";
|
||
this.thumbBase64Code = "";
|
||
},
|
||
imageBase64(val) {
|
||
this.dots = [];
|
||
this.imageBase64Code = val;
|
||
},
|
||
thumbBase64(val) {
|
||
this.dots = [];
|
||
this.thumbBase64Code = val;
|
||
},
|
||
},
|
||
methods: {
|
||
/**
|
||
* @Description: 处理关闭事件
|
||
*/
|
||
handleCloseEvent() {
|
||
this.$emit("close");
|
||
// this.dots = []
|
||
// this.imageBase64Code = ''
|
||
// this.thumbBase64Code = ''
|
||
},
|
||
/**
|
||
* @Description: 处理刷新事件
|
||
*/
|
||
handleRefreshEvent() {
|
||
this.dots = [];
|
||
this.$emit("refresh");
|
||
},
|
||
/**
|
||
* @Description: 处理确认事件
|
||
*/
|
||
handleConfirmEvent() {
|
||
this.$emit("confirm", this.dots);
|
||
},
|
||
/**
|
||
* @Description: 处理dot
|
||
* @param ev
|
||
*/
|
||
handleClickPos(ev) {
|
||
if (this.dots.length >= this.maxDot) {
|
||
return;
|
||
}
|
||
const e = ev || window.event;
|
||
e.preventDefault();
|
||
const dom = e.currentTarget;
|
||
|
||
const { domX, domY } = this.getDomXY(dom);
|
||
// ===============================================
|
||
// @notice 如 getDomXY 不准确可尝试使用 calcLocationLeft 或 calcLocationTop
|
||
// const domX = this.calcLocationLeft(dom)
|
||
// const domY = this.calcLocationTop(dom)
|
||
// ===============================================
|
||
let mouseX = navigator.vendor === "Netscape" ? e.pageX : e.x + document.body.offsetTop;
|
||
let mouseY = navigator.vendor === "Netscape" ? e.pageY : e.y + document.body.offsetTop;
|
||
// 兼容移动触摸事件
|
||
if (e.touches && e.touches.length > 0) {
|
||
mouseX = e.touches[0].clientX;
|
||
mouseY = e.touches[0].clientY;
|
||
} else {
|
||
mouseX = e.clientX;
|
||
mouseY = e.clientY;
|
||
}
|
||
|
||
// 计算点击的相对位置
|
||
const xPos = mouseX - domX;
|
||
const yPos = mouseY - domY;
|
||
|
||
// 转整形
|
||
const xp = parseInt(xPos.toString());
|
||
const yp = parseInt(yPos.toString());
|
||
|
||
// 减去点的一半
|
||
this.dots.push({
|
||
x: xp - 11,
|
||
y: yp - 11,
|
||
index: this.dots.length + 1,
|
||
});
|
||
return false;
|
||
},
|
||
/**
|
||
* @Description: 找到元素的屏幕位置
|
||
* @param el
|
||
*/
|
||
calcLocationLeft(el) {
|
||
let tmp = el.offsetLeft;
|
||
let val = el.offsetParent;
|
||
while (val != null) {
|
||
tmp += val.offsetLeft;
|
||
val = val.offsetParent;
|
||
}
|
||
return tmp;
|
||
},
|
||
/**
|
||
* @Description: 找到元素的屏幕位置
|
||
* @param el
|
||
*/
|
||
calcLocationTop(el) {
|
||
let tmp = el.offsetTop;
|
||
let val = el.offsetParent;
|
||
while (val != null) {
|
||
tmp += val.offsetTop;
|
||
val = val.offsetParent;
|
||
}
|
||
return tmp;
|
||
},
|
||
/**
|
||
* @Description: 找到元素的屏幕位置
|
||
* @param dom
|
||
*/
|
||
getDomXY(dom) {
|
||
let x = 0;
|
||
let y = 0;
|
||
if (dom.getBoundingClientRect) {
|
||
let box = dom.getBoundingClientRect();
|
||
let D = document.documentElement;
|
||
x = box.left + Math.max(D.scrollLeft, document.body.scrollLeft) - D.clientLeft;
|
||
y = box.top + Math.max(D.scrollTop, document.body.scrollTop) - D.clientTop;
|
||
} else {
|
||
while (dom !== document.body) {
|
||
x += dom.offsetLeft;
|
||
y += dom.offsetTop;
|
||
dom = dom.offsetParent;
|
||
}
|
||
}
|
||
return {
|
||
domX: x,
|
||
domY: y,
|
||
};
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="stylus">
|
||
.wg-cap-wrap {
|
||
background: var(--el-bg-color);
|
||
|
||
-webkit-border-radius: 10px;
|
||
-moz-border-radius: 10px;
|
||
border-radius: 10px;
|
||
|
||
-webkit-touch-callout: none;
|
||
-webkit-user-select: none;
|
||
-moz-user-select: none;
|
||
-ms-user-select: none;
|
||
user-select: none;
|
||
|
||
.wg-cap-wrap__header {
|
||
height: 50px;
|
||
width: 100%;
|
||
font-size: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
span {
|
||
padding-right: 5px;
|
||
|
||
em {
|
||
padding: 0 3px;
|
||
font-weight: bold;
|
||
color: #3e7cff;
|
||
font-style: normal;
|
||
}
|
||
}
|
||
|
||
.wg-cap-wrap__image {
|
||
-webkit-border-radius: 5px;
|
||
-moz-border-radius: 5px;
|
||
border-radius: 5px;
|
||
overflow: hidden;
|
||
text-align: center;
|
||
line-height: 1;
|
||
}
|
||
|
||
.wg-cap-wrap__thumb {
|
||
min-width: 150px;
|
||
text-align: center;
|
||
line-height: 1;
|
||
max-height: 100%;
|
||
}
|
||
|
||
.wg-cap-wrap__thumb.wg-cap-wrap__hidden {
|
||
display: none;
|
||
}
|
||
|
||
}
|
||
|
||
.wg-cap-wrap__body {
|
||
position: relative;
|
||
display: -webkit-box;
|
||
display: -moz-box;
|
||
display: -ms-flexbox;
|
||
display: -webkit-flex;
|
||
display: flex;
|
||
background: #34383e;
|
||
margin: auto;
|
||
-webkit-border-radius: 5px;
|
||
-moz-border-radius: 5px;
|
||
border-radius: 5px;
|
||
overflow: hidden;
|
||
|
||
.wg-cap-wrap__picture {
|
||
position: relative;
|
||
z-index: 10;
|
||
width: 100%;
|
||
}
|
||
|
||
.wg-cap-wrap__picture.wg-cap-wrap__hidden {
|
||
display: none;
|
||
}
|
||
|
||
.wg-cap-wrap__loading {
|
||
position: absolute;
|
||
z-index: 9;
|
||
top: 50%;
|
||
left: 50%;
|
||
width: 68px;
|
||
height: 68px;
|
||
margin-left: -34px;
|
||
margin-top: -34px;
|
||
line-height: 68px;
|
||
text-align: center;
|
||
}
|
||
|
||
.wg-cap-wrap__dot {
|
||
position: absolute;
|
||
z-index: 10;
|
||
width: 22px;
|
||
height: 22px;
|
||
color: #cedffe;
|
||
background: #3e7cff;
|
||
border: 2px solid #f7f9fb;
|
||
line-height: 20px;
|
||
text-align: center;
|
||
-webkit-border-radius: 22px;
|
||
-moz-border-radius: 22px;
|
||
border-radius: 22px;
|
||
cursor: default;
|
||
}
|
||
}
|
||
|
||
.wg-cap-wrap__footer {
|
||
width: 100%;
|
||
height: 40px;
|
||
color: #34383e;
|
||
display: -webkit-box;
|
||
display: -webkit-flex;
|
||
display: -ms-flexbox;
|
||
display: flex;
|
||
-webkit-box-align: center;
|
||
-webkit-align-items: center;
|
||
-ms-flex-align: center;
|
||
align-items: center;
|
||
padding-top: 15px;
|
||
|
||
.wg-cap-wrap__ico {
|
||
flex: 1;
|
||
|
||
img {
|
||
width: 24px;
|
||
height: 24px;
|
||
color: #34383e;
|
||
margin: 0 5px;
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
|
||
.wg-cap-wrap__btn {
|
||
display flex
|
||
width: 120px;
|
||
justify-content right
|
||
}
|
||
}
|
||
}
|
||
</style>
|