- 兼容触摸事件和鼠标事件,支持移动端和桌面端 - 添加鼠标移动、鼠标抬起和鼠标离开事件处理 - 修复坐标获取逻辑,统一处理触摸和鼠标坐标 - 移除调试日志,优化控制台输出 - 添加环境判断,生产环境不加载vConsole
427 lines
12 KiB
JavaScript
427 lines
12 KiB
JavaScript
import { Component } from "react";
|
|
import Taro from "@tarojs/taro";
|
|
import { Image, Text, View } from "@tarojs/components";
|
|
import auth from "@/services/auth";
|
|
import { CSSTransition } from "react-transition-group";
|
|
import "./index.css";
|
|
import { aesEncrypt, uuid } from "./utils";
|
|
|
|
class Captcha extends Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
blockSize: {
|
|
width: "50px",
|
|
height: "50px",
|
|
},
|
|
setSize: {
|
|
imgHeight: 155,
|
|
imgWidth: 310,
|
|
barHeight: 40,
|
|
barWidth: 310,
|
|
},
|
|
backImgBase: "",
|
|
blockBackImgBase: "",
|
|
backToken: "",
|
|
startMoveTime: "",
|
|
secretKey: "",
|
|
captchaType: "blockPuzzle",
|
|
moveBlockBackgroundColor: "rgb(255, 255, 255)",
|
|
leftBarBorderColor: "",
|
|
iconColor: "",
|
|
barAreaLeft: 0,
|
|
barAreaOffsetWidth: 0,
|
|
startLeft: null,
|
|
moveBlockLeft: null,
|
|
leftBarWidth: null,
|
|
status: false,
|
|
isEnd: false,
|
|
passFlag: "",
|
|
tipWords: "",
|
|
text: "向右滑动完成验证",
|
|
};
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.uuid();
|
|
this.init();
|
|
|
|
// 组件卸载时移除事件监听
|
|
Taro.eventCenter.off("touchmove", this.touchMoveHandler);
|
|
Taro.eventCenter.off("touchend", this.touchEndHandler);
|
|
|
|
// 调用 setBarArea 方法
|
|
setTimeout(() => {
|
|
this.setBarArea(this.bararea);
|
|
}, 0);
|
|
}
|
|
|
|
// 初始化 uuid
|
|
uuid() {
|
|
const s = uuid();
|
|
const slider = "slider" + "-" + s;
|
|
const point = "point" + "-" + s;
|
|
|
|
if (!Taro.getStorageSync("slider")) {
|
|
Taro.setStorageSync("slider", slider);
|
|
}
|
|
if (!Taro.getStorageSync("point")) {
|
|
Taro.setStorageSync("point", point);
|
|
}
|
|
}
|
|
|
|
init() {
|
|
this.getData();
|
|
}
|
|
|
|
getData() {
|
|
auth.captcha
|
|
.get({
|
|
captchaType: this.state.captchaType,
|
|
clientUid: Taro.getStorageSync("slider"),
|
|
ts: Date.now(),
|
|
})
|
|
.then((res) => {
|
|
if (res.data.repCode === "0000") {
|
|
console.log(res.data.repData, "res.data.repData");
|
|
// 在组件中使用转换后的URL加载图片
|
|
// const imgUrl = base64ToURL(res.data.repData.originalImageBase64);
|
|
// console.log(imgUrl,'imgUrl');
|
|
this.setState({
|
|
backImgBase: res.data.repData.originalImageBase64,
|
|
blockBackImgBase: res.data.repData.jigsawImageBase64,
|
|
backToken: res.data.repData.token,
|
|
secretKey: res.data.repData.secretKey,
|
|
});
|
|
}
|
|
// 请求次数超限
|
|
if (res.data.repCode === "6201") {
|
|
this.setState({
|
|
backImgBase: null,
|
|
blockBackImgBase: null,
|
|
leftBarBorderColor: "#d9534f",
|
|
iconColor: "#fff",
|
|
// eslint-disable-next-line react/no-unused-state
|
|
iconClass: "icon-close",
|
|
passFlag: false,
|
|
tipWords: res.data.repMsg,
|
|
});
|
|
setTimeout(() => {
|
|
this.setState({
|
|
tipWords: "",
|
|
});
|
|
}, 1000);
|
|
}
|
|
});
|
|
}
|
|
|
|
refresh = () => {
|
|
this.getData();
|
|
this.setState({
|
|
moveBlockLeft: "",
|
|
leftBarWidth: "",
|
|
text: "向右滑动完成验证",
|
|
moveBlockBackgroundColor: "#fff",
|
|
leftBarBorderColor: "#337AB7",
|
|
iconColor: "#fff",
|
|
status: false,
|
|
isEnd: false,
|
|
});
|
|
};
|
|
|
|
setBarArea = (barArea) => {
|
|
if (barArea) {
|
|
Taro.createSelectorQuery()
|
|
.select(".verify-bar-area")
|
|
.boundingClientRect((rect) => {
|
|
console.log("setBarArea", rect.left, rect.width);
|
|
if (rect) {
|
|
this.setState({
|
|
barAreaLeft: rect.left,
|
|
barAreaOffsetWidth: rect.width,
|
|
});
|
|
}
|
|
})
|
|
.exec();
|
|
}
|
|
};
|
|
|
|
// 鼠标按下时的事件处理函数
|
|
start = (e) => {
|
|
console.log(e, "按下");
|
|
this.setState({
|
|
startMoveTime: new Date().getTime(), //记录开始滑动的时间
|
|
});
|
|
if (!this.state.isEnd) {
|
|
this.setState({
|
|
text: "",
|
|
moveBlockBackgroundColor: "#337ab7", //移动块背景颜色
|
|
leftBarBorderColor: "#337AB7", //左侧滑块边框颜色
|
|
iconColor: "#fff", //图标颜色
|
|
status: true, //状态标识为true
|
|
});
|
|
e.stopPropagation(); //阻止事件冒泡
|
|
}
|
|
};
|
|
|
|
// 鼠标/触摸移动时的事件处理函数
|
|
move = (e) => {
|
|
console.log(e, "滑动滑动");
|
|
if (this.state.status && !this.state.isEnd) {
|
|
// 兼容触摸事件和鼠标事件
|
|
let x = e.touches ? e.touches[0].pageX : e.pageX; // 获取鼠标/触摸点的X坐标
|
|
console.log(x);
|
|
let bar_area_left = this.state.barAreaLeft; // 滑块区域左侧距离
|
|
let move_block_left = x - bar_area_left; // 计算移动块的左侧距离
|
|
console.log(
|
|
move_block_left >=
|
|
this.state.barAreaOffsetWidth -
|
|
parseInt(parseInt(this.state.blockSize.width) / 2) -
|
|
2,
|
|
);
|
|
if (
|
|
move_block_left >=
|
|
this.state.barAreaOffsetWidth -
|
|
parseInt(parseInt(this.state.blockSize.width) / 2) -
|
|
2
|
|
) {
|
|
console.log("第一个判断计算");
|
|
move_block_left =
|
|
this.state.barAreaOffsetWidth -
|
|
parseInt(parseInt(this.state.blockSize.width) / 2) -
|
|
2;
|
|
}
|
|
if (move_block_left <= 0) {
|
|
console.log("第二个判断计算");
|
|
move_block_left = parseInt(this.state.blockSize.width / 2);
|
|
}
|
|
let moveBlockLeft =
|
|
Math.floor(move_block_left - this.state.startLeft) + "px"; //计算移动块的左侧位置并取整
|
|
let leftBarWidth =
|
|
Math.floor(move_block_left - this.state.startLeft) + "px"; //计算左侧滑块的宽度并取整
|
|
console.log(moveBlockLeft, leftBarWidth); //打印移动块左侧位置和左侧滑块宽度
|
|
this.setState({
|
|
moveBlockLeft: moveBlockLeft, //更新移动块左侧位置
|
|
leftBarWidth: leftBarWidth, //更新左侧滑块宽度
|
|
});
|
|
}
|
|
};
|
|
|
|
end = () => {
|
|
console.log("鼠标放下");
|
|
let endMoveTime = +new Date();
|
|
if (this.state.status && !this.state.isEnd) {
|
|
let moveLeftDistance = parseInt(
|
|
(this.state.moveBlockLeft || "").replace("px", ""),
|
|
);
|
|
moveLeftDistance =
|
|
(moveLeftDistance * 310) / parseInt(this.state.setSize.imgWidth);
|
|
let data = {
|
|
captchaType: this.state.captchaType,
|
|
pointJson: this.state.secretKey
|
|
? aesEncrypt(
|
|
JSON.stringify({
|
|
x: moveLeftDistance,
|
|
y: 5.0,
|
|
}),
|
|
this.state.secretKey,
|
|
)
|
|
: JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
|
|
token: this.state.backToken,
|
|
clientUid: Taro.getStorageSync("slider"),
|
|
ts: Date.now(),
|
|
};
|
|
auth.captcha.check(data).then((res) => {
|
|
if (res.data.repCode === "0000") {
|
|
this.setState({
|
|
isEnd: true,
|
|
passFlag: true,
|
|
tipWords: `${((endMoveTime - this.state.startMoveTime) / 1000).toFixed(2)}s验证成功`,
|
|
});
|
|
setTimeout(() => {
|
|
this.setState({
|
|
tipWords: "",
|
|
});
|
|
const captchaVerification = this.state.secretKey
|
|
? aesEncrypt(
|
|
this.state.backToken +
|
|
"---" +
|
|
JSON.stringify({
|
|
x: moveLeftDistance,
|
|
y: 5.0,
|
|
}),
|
|
this.state.secretKey,
|
|
)
|
|
: this.state.backToken +
|
|
"---" +
|
|
JSON.stringify({ x: moveLeftDistance, y: 5.0 });
|
|
this.props.handleClick(captchaVerification);
|
|
this.refresh();
|
|
}, 1000);
|
|
} else {
|
|
this.setState({
|
|
isEnd: true,
|
|
moveBlockBackgroundColor: "#d9534f",
|
|
leftBarBorderColor: "#d9534f",
|
|
iconColor: "#fff",
|
|
// eslint-disable-next-line react/no-unused-state
|
|
iconClass: "icon-close",
|
|
passFlag: false,
|
|
tipWords: res.data.repMsg || "验证失败",
|
|
});
|
|
setTimeout(() => {
|
|
this.refresh();
|
|
this.setState({
|
|
tipWords: "",
|
|
});
|
|
}, 1000);
|
|
}
|
|
});
|
|
this.setState({
|
|
status: false,
|
|
});
|
|
}
|
|
};
|
|
|
|
render() {
|
|
const { vSpace, barSize, transitionWidth, finishText, transitionLeft } =
|
|
this.props;
|
|
return (
|
|
<View style={{ position: "relative" }} className="stop-user-select">
|
|
<View
|
|
className="verify-img-out"
|
|
style={{ height: parseInt(this.state.setSize.imgHeight) + vSpace }}
|
|
>
|
|
<View
|
|
className="verify-img-panel"
|
|
style={{
|
|
width: this.state.setSize.imgWidth,
|
|
height: this.state.setSize.imgHeight,
|
|
}}
|
|
>
|
|
<Image
|
|
src={"data:image/png;base64," + this.state.backImgBase}
|
|
mode="aspectFill"
|
|
style={{
|
|
width: "100%",
|
|
height: "100%",
|
|
}}
|
|
/>
|
|
{/* 其他内容 */}
|
|
<CSSTransition
|
|
in={this.state.tipWords.length > 0}
|
|
timeout={150}
|
|
classNames="tips"
|
|
unmountOnExit
|
|
>
|
|
<View
|
|
className={
|
|
this.state.passFlag
|
|
? `${"verify-tips"} ${"suc-bg"}`
|
|
: `${"verify-tips"} ${"err-bg"}`
|
|
}
|
|
>
|
|
{this.state.tipWords}
|
|
</View>
|
|
</CSSTransition>
|
|
</View>
|
|
</View>
|
|
|
|
<View
|
|
className="verify-bar-area"
|
|
style={{
|
|
width: this.state.setSize.imgWidth,
|
|
height: barSize.height,
|
|
lineHeight: barSize.height,
|
|
}}
|
|
ref={(ref) => (this.bararea = ref)}
|
|
>
|
|
<Text className="verify-msg">{this.state.text}</Text>
|
|
<View
|
|
className="verify-left-bar"
|
|
style={{
|
|
width:
|
|
this.state.leftBarWidth !== undefined
|
|
? this.state.leftBarWidth
|
|
: barSize.height,
|
|
height: barSize.height,
|
|
borderColor: this.state.leftBarBorderColor,
|
|
transaction: transitionWidth,
|
|
}}
|
|
>
|
|
<Text className="verify-msg">{finishText}</Text>
|
|
|
|
<View
|
|
className="verify-move-block"
|
|
onTouchStart={this.start}
|
|
onMouseDown={this.start}
|
|
onTouchMove={this.move}
|
|
onMouseMove={this.move}
|
|
onTouchEnd={this.end}
|
|
onMouseUp={this.end}
|
|
onMouseLeave={this.end}
|
|
style={{
|
|
width: barSize.height,
|
|
height: barSize.height,
|
|
backgroundColor: this.state.moveBlockBackgroundColor,
|
|
left: this.state.moveBlockLeft,
|
|
transition: transitionLeft,
|
|
}}
|
|
>
|
|
<Text
|
|
className="verify-icon iconfont icon-right"
|
|
style={{ color: this.state.iconColor }}
|
|
></Text>
|
|
<View
|
|
className="verify-sub-block"
|
|
style={{
|
|
width:
|
|
Math.floor(
|
|
(parseInt(this.state.setSize.imgWidth) * 47) / 310,
|
|
) + "px",
|
|
height: this.state.setSize.imgHeight,
|
|
top:
|
|
"-" +
|
|
(parseInt(this.state.setSize.imgHeight) + vSpace) +
|
|
"px",
|
|
backgroundSize:
|
|
this.state.setSize.imgWidth +
|
|
" " +
|
|
this.state.setSize.imgHeight,
|
|
}}
|
|
>
|
|
<Image
|
|
src={"data:image/png;base64," + this.state.blockBackImgBase}
|
|
alt=""
|
|
style={{ width: "100%", height: "100%", display: "block" }}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
Captcha.defaultProps = {
|
|
mode: "fixed",
|
|
vSpace: 5,
|
|
imgSize: {
|
|
width: "310px",
|
|
height: "200px",
|
|
},
|
|
barSize: {
|
|
width: "310px",
|
|
height: "40px",
|
|
},
|
|
setSize: {
|
|
imgHeight: 200,
|
|
imgWidth: 310,
|
|
barHeight: 0,
|
|
barWidth: 0,
|
|
},
|
|
};
|
|
|
|
export default Captcha;
|