fix: 采用弹窗的方式显示验证码,解决验证码在低分辨率下被掩盖的Bug

This commit is contained in:
RockYang 2023-09-05 16:47:40 +08:00
parent 46a551df16
commit bf3f68fa19
11 changed files with 354 additions and 499 deletions

View File

@ -70,7 +70,6 @@ func (h *UserHandler) Register(c *gin.Context) {
var code int var code int
err := h.leveldb.Get(key, &code) err := h.leveldb.Get(key, &code)
if err != nil || code != data.Code { if err != nil || code != data.Code {
logger.Info(code)
resp.ERROR(c, "短信验证码错误") resp.ERROR(c, "短信验证码错误")
return return
} }

View File

@ -21,11 +21,11 @@ ALTER TABLE `chatgpt_chat_models`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7; MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
INSERT INTO `chatgpt_chat_models` (`id`, `platform`, `name`, `value`, `sort_num`, `enabled`, `created_at`, `updated_at`) VALUES INSERT INTO `chatgpt_chat_models` (`id`, `platform`, `name`, `value`, `sort_num`, `enabled`, `created_at`, `updated_at`) VALUES
(1, 'OpenAI', 'Bot GPT-3.5', 'gpt-3.5-turbo', 0, 1, '2023-08-23 12:06:36', '2023-09-02 16:49:36'), (1, 'OpenAI', 'GPT-3.5', 'gpt-3.5-turbo', 0, 1, '2023-08-23 12:06:36', '2023-09-02 16:49:36'),
(2, 'Azure', 'Bot Azure-3.5', 'gpt-3.5-turbo', 0, 1, '2023-08-23 12:15:30', '2023-09-02 16:49:46'), (2, 'Azure', 'Azure-3.5', 'gpt-3.5-turbo', 0, 1, '2023-08-23 12:15:30', '2023-09-02 16:49:46'),
(3, 'ChatGML', 'ChatGML-Pro', 'chatglm_pro', 3, 1, '2023-08-23 13:35:45', '2023-08-29 11:41:29'), (3, 'ChatGLM', 'ChatGML-Pro', 'chatglm_pro', 3, 1, '2023-08-23 13:35:45', '2023-08-29 11:41:29'),
(5, 'ChatGML', 'ChatGLM-Std', 'chatglm_std', 2, 1, '2023-08-24 15:05:38', '2023-08-29 11:41:28'), (5, 'ChatGLM', 'ChatGLM-Std', 'chatglm_std', 2, 1, '2023-08-24 15:05:38', '2023-08-29 11:41:28'),
(6, 'ChatGML', 'ChatGLM-Lite', 'chatglm_lite', 4, 1, '2023-08-24 15:06:15', '2023-08-29 11:41:29'); (6, 'ChatGLM', 'ChatGLM-Lite', 'chatglm_lite', 4, 1, '2023-08-24 15:06:15', '2023-08-29 11:41:29');
ALTER TABLE `chatgpt_users` ALTER TABLE `chatgpt_users`
DROP `username`, DROP `username`,

View File

@ -12,15 +12,15 @@ npm run build
cd ../docker cd ../docker
# remove docker image if exists # remove docker image if exists
docker rmi -f registry.cn-hangzhou.aliyuncs.com/geekmaster/chatgpt-plus-go:$version docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-api:$version
# build docker image for chatgpt-plus-go # build docker image for chatgpt-plus-go
docker build -t registry.cn-hangzhou.aliyuncs.com/geekmaster/chatgpt-plus-go:$version -f dockerfile-api-go ../ docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-api:$version -f dockerfile-api-go ../
# build docker image for chatgpt-plus-vue # build docker image for chatgpt-plus-vue
docker rmi -f registry.cn-hangzhou.aliyuncs.com/geekmaster/chatgpt-plus-vue:$version docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-web:$version
docker build --platform linux/amd64 -t registry.cn-hangzhou.aliyuncs.com/geekmaster/chatgpt-plus-vue:$version -f dockerfile-vue ../ docker build --platform linux/amd64 -t registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-web:$version -f dockerfile-vue ../
if [ "$2" = "push" ];then if [ "$2" = "push" ];then
docker push registry.cn-hangzhou.aliyuncs.com/geekmaster/chatgpt-plus-vue:$version docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-api:$version
docker push registry.cn-hangzhou.aliyuncs.com/geekmaster/chatgpt-plus-go:$version docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-web:$version
fi fi

View File

@ -2,7 +2,7 @@ version: '3'
services: services:
# 后端 API 程序 # 后端 API 程序
chatgpt-plus-api: chatgpt-plus-api:
image: registry.cn-hangzhou.aliyuncs.com/geekmaster/chatgpt-plus-api:v3.0.7.4 image: registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-api:v3.1.0
container_name: chatgpt-plus-api container_name: chatgpt-plus-api
restart: always restart: always
environment: environment:
@ -18,9 +18,9 @@ services:
- ./static:/var/www/app/static - ./static:/var/www/app/static
# 前端应用 # 前端应用
chatgpt-plus-vue: chatgpt-plus-web:
image: registry.cn-hangzhou.aliyuncs.com/geekmaster/chatgpt-plus-vue:v3.0.7.4 image: registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-web:v3.1.0
container_name: chatgpt-plus-vue container_name: chatgpt-plus-web
restart: always restart: always
ports: ports:
- "8080:8080" - "8080:8080"

View File

@ -4,5 +4,5 @@ VUE_APP_USER=18575670125
VUE_APP_PASS=12345678 VUE_APP_PASS=12345678
VUE_APP_ADMIN_USER=admin VUE_APP_ADMIN_USER=admin
VUE_APP_ADMIN_PASS=admin123 VUE_APP_ADMIN_PASS=admin123
VUE_APP_KEY_PREFIX=ChatPLUS_ VUE_APP_KEY_PREFIX=ChatPLUS_DEV_
VUE_APP_TITLE="ChatGPT-PLUS V3" VUE_APP_TITLE="ChatGPT-PLUS V3"

View File

@ -1,257 +1,265 @@
<template> <template>
<div class="wg-cap-wrap" :style="{width: width}"> <div class="wg-cap-wrap" :style="{width: width}">
<div class="wg-cap-wrap__header"> <div class="wg-cap-wrap__header">
<span>请在下图<em>依次</em>点击</span> <span>请在下图<em>依次</em>点击</span>
<img class="wg-cap-wrap__thumb" v-if="thumbBase64Code" :src="thumbBase64Code" alt=" "> <img class="wg-cap-wrap__thumb" v-if="thumbBase64Code" :src="thumbBase64Code" alt=" ">
</div> </div>
<div class="wg-cap-wrap__body"> <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__picture" v-if="imageBase64Code" :src="imageBase64Code" alt=" "
<img class="wg-cap-wrap__loading" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBzdHlsZT0ibWFyZ2luOiBhdXRvOyBiYWNrZ3JvdW5kOiByZ2JhKDI0MSwgMjQyLCAyNDMsIDApOyBkaXNwbGF5OiBibG9jazsgc2hhcGUtcmVuZGVyaW5nOiBhdXRvOyIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIj4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjM2LjgxMDEiIHI9IjEzIiBmaWxsPSIjM2U3Y2ZmIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN5IiBkdXI9IjFzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgY2FsY01vZGU9InNwbGluZSIga2V5U3BsaW5lcz0iMC40NSAwIDAuOSAwLjU1OzAgMC40NSAwLjU1IDAuOSIga2V5VGltZXM9IjA7MC41OzEiIHZhbHVlcz0iMjM7Nzc7MjMiPjwvYW5pbWF0ZT4KICA8L2NpcmNsZT4KPC9zdmc+" alt="正在加载中..."> @click="handleClickPos($event)">
<div v-for="(dot, key) in dots" :key="key" class="wg-cap-wrap__dot" :style="`top: ${dot.y}px; left:${dot.x}px;`"> <img class="wg-cap-wrap__loading"
<span>{{ dot.index }}</span> src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBzdHlsZT0ibWFyZ2luOiBhdXRvOyBiYWNrZ3JvdW5kOiByZ2JhKDI0MSwgMjQyLCAyNDMsIDApOyBkaXNwbGF5OiBibG9jazsgc2hhcGUtcmVuZGVyaW5nOiBhdXRvOyIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIj4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjM2LjgxMDEiIHI9IjEzIiBmaWxsPSIjM2U3Y2ZmIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImN5IiBkdXI9IjFzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgY2FsY01vZGU9InNwbGluZSIga2V5U3BsaW5lcz0iMC40NSAwIDAuOSAwLjU1OzAgMC40NSAwLjU1IDAuOSIga2V5VGltZXM9IjA7MC41OzEiIHZhbHVlcz0iMjM7Nzc7MjMiPjwvYW5pbWF0ZT4KICA8L2NpcmNsZT4KPC9zdmc+"
</div> alt="正在加载中...">
</div> <div v-for="(dot, key) in dots" :key="key" class="wg-cap-wrap__dot" :style="`top: ${dot.y}px; left:${dot.x}px;`">
<div class="wg-cap-wrap__footer"> <span>{{ dot.index }}</span>
<div class="wg-cap-wrap__ico">
<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">
<button @click="handleConfirmEvent">确认</button>
</div>
</div> </div>
</div> </div>
<div class="wg-cap-wrap__footer">
<div class="wg-cap-wrap__ico">
<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> </template>
<script> <script>
export default { export default {
name: 'CaptchaPlus', name: 'CaptchaPlus',
mounted() { 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') this.$emit('refresh')
}, },
props: { /**
value: Boolean, * @Description: 处理确认事件
width: { */
type: String, handleConfirmEvent() {
default: '300px' this.$emit('confirm', this.dots)
},
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() { /**
* @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 (this.calcPosType === 'screen') {
mouseX = (navigator.vendor === 'Netscape') ? e.clientX : e.x
mouseY = (navigator.vendor === 'Netscape') ? e.clientY : e.y
}
//
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 { return {
dots: [], domX: x,
imageBase64Code: '', domY: y
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 (this.calcPosType === 'screen') {
mouseX = (navigator.vendor === 'Netscape') ? e.clientX : e.x
mouseY = (navigator.vendor === 'Netscape') ? e.clientY : e.y
}
//
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> </script>
<style> <style scoped lang="stylus">
.wg-cap-wrap{ .wg-cap-wrap {
background: #ffffff; background: #ffffff;
-webkit-border-radius: 10px; -webkit-border-radius: 10px;
-moz-border-radius: 10px; -moz-border-radius: 10px;
border-radius: 10px; border-radius: 10px;
-webkit-touch-callout: none; -webkit-touch-callout: none;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
}
.wg-cap-wrap__header{ .wg-cap-wrap__header {
height: 50px; height: 50px;
width: 100%; width: 100%;
font-size: 15px; font-size: 15px;
display:-webkit-box; display: -webkit-box;
display:-webkit-flex; display: -webkit-flex;
display:-ms-flexbox; display: -ms-flexbox;
display:flex; display: flex;
-webkit-box-align:center; -webkit-box-align: center;
-webkit-align-items:center; -webkit-align-items: center;
-ms-flex-align:center; -ms-flex-align: center;
align-items: center; align-items: center;
}
.wg-cap-wrap__header span{ span {
padding-right: 5px; padding-right: 5px;
}
.wg-cap-wrap__header span em{ em {
padding: 0 3px; padding: 0 3px;
font-weight: bold; font-weight: bold;
color: #3e7cff; color: #3e7cff;
font-style: normal; font-style: normal;
} }
.wg-cap-wrap__header .wg-cap-wrap__image{ }
-webkit-border-radius: 5px;
-moz-border-radius: 5px; .wg-cap-wrap__image {
border-radius: 5px; -webkit-border-radius: 5px;
overflow: hidden; -moz-border-radius: 5px;
text-align: center; border-radius: 5px;
line-height: 1; overflow: hidden;
} text-align: center;
.wg-cap-wrap__header .wg-cap-wrap__thumb{ line-height: 1;
min-width: 150px; }
text-align: center;
line-height: 1; .wg-cap-wrap__thumb {
max-height: 100%; min-width: 150px;
} text-align: center;
.wg-cap-wrap__header .wg-cap-wrap__thumb.wg-cap-wrap__hidden{ line-height: 1;
display: none; max-height: 100%;
}
.wg-cap-wrap__thumb.wg-cap-wrap__hidden {
display: none;
}
} }
.wg-cap-wrap__body{ .wg-cap-wrap__body {
position: relative; position: relative;
display: -webkit-box; display: -webkit-box;
display: -moz-box; display: -moz-box;
@ -264,106 +272,78 @@
-moz-border-radius: 5px; -moz-border-radius: 5px;
border-radius: 5px; border-radius: 5px;
overflow: hidden; overflow: hidden;
}
.wg-cap-wrap__body .wg-cap-wrap__picture{
position: relative;
z-index: 10;
width: 100%;
/*height: 100%;*/ .wg-cap-wrap__picture {
/*max-width: 100%;*/ position: relative;
/*max-height: 100%;*/ z-index: 10;
/*object-fit: cover;*/ width: 100%;
/*text-align: center;*/ }
}
.wg-cap-wrap__body .wg-cap-wrap__picture.wg-cap-wrap__hidden{ .wg-cap-wrap__picture.wg-cap-wrap__hidden {
display: none; display: none;
} }
.wg-cap-wrap__body .wg-cap-wrap__loading{
position: absolute; .wg-cap-wrap__loading {
z-index: 9; position: absolute;
top: 50%; z-index: 9;
left: 50%; top: 50%;
width: 68px; left: 50%;
height: 68px; width: 68px;
margin-left: -34px; height: 68px;
margin-top: -34px; margin-left: -34px;
line-height: 68px; margin-top: -34px;
text-align: center; line-height: 68px;
} text-align: center;
.wg-cap-wrap__body .wg-cap-wrap__dot{ }
position: absolute;
z-index: 10; .wg-cap-wrap__dot {
width: 22px; position: absolute;
height: 22px; z-index: 10;
color: #cedffe; width: 22px;
background: #3e7cff; height: 22px;
border: 2px solid #f7f9fb; color: #cedffe;
line-height: 20px; background: #3e7cff;
text-align: center; border: 2px solid #f7f9fb;
-webkit-border-radius: 22px; line-height: 20px;
-moz-border-radius: 22px; text-align: center;
border-radius: 22px; -webkit-border-radius: 22px;
cursor: default; -moz-border-radius: 22px;
border-radius: 22px;
cursor: default;
}
} }
.wg-cap-wrap__footer { .wg-cap-wrap__footer {
width: 100%; width: 100%;
height: 40px; height: 40px;
color: #34383e; color: #34383e;
display:-webkit-box; display: -webkit-box;
display:-webkit-flex; display: -webkit-flex;
display:-ms-flexbox; display: -ms-flexbox;
display:flex; display: flex;
-webkit-box-align:center; -webkit-box-align: center;
-webkit-align-items:center; -webkit-align-items: center;
-ms-flex-align:center; -ms-flex-align: center;
align-items: center; align-items: center;
padding-top: 15px; 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
}
} }
.wg-cap-wrap__footer .wg-cap-wrap__ico{ }
flex: 1;
}
.wg-cap-wrap__footer .wg-cap-wrap__ico img{
width: 24px;
height: 24px;
color: #34383e;
margin: 0 5px;
cursor: pointer;
}
.wg-cap-wrap__footer .wg-cap-wrap__btn{
width: 120px;
height: 40px;
}
.wg-cap-wrap__footer .wg-cap-wrap__btn button{
width: 100%;
height: 40px;
letter-spacing: 2px;
text-align: center;
padding: 9px 15px;
font-size: 15px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
color: #fff;
background-color: #409eff;
border: 1px solid #409eff;
-webkit-appearance: none;
box-sizing: border-box;
outline: none;
margin: 0;
transition: .1s;
font-weight: 500;
-moz-user-select: none;
-webkit-user-select: none;
}
.wg-cap-wrap__footer .wg-cap-wrap__btn button:hover {
background: #66b1ff;
border-color: #66b1ff;
color: #fff;
}
</style> </style>

View File

@ -1,29 +1,26 @@
<template> <template>
<el-container> <el-container class="captcha-box">
<el-popover <el-button type="primary" class="send-btn" :size="props.size" :disabled="!canSend" @click="loadCaptcha" plain>
:visible="showCaptcha" {{ btnText }}
:hide-after="0" </el-button>
placement="top"
:width="325" <el-dialog
trigger="click" v-model="showCaptcha"
content="this is content, this is content, this is content" :close-on-click-modal="true"
:show-close="false"
style="width:90%;max-width: 360px;"
> >
<captcha-plus <captcha-plus
v-if="showCaptcha"
:max-dot="maxDot" :max-dot="maxDot"
:image-base64="imageBase64" :image-base64="imageBase64"
:thumb-base64="thumbBase64" :thumb-base64="thumbBase64"
width="300"
@close="showCaptcha = false" @close="showCaptcha = false"
@refresh="handleRequestCaptCode" @refresh="handleRequestCaptCode"
@confirm="handleConfirm" @confirm="handleConfirm"
/> />
<template #reference> </el-dialog>
<el-button type="primary" :size="props.size" :disabled="!canSend" @click="loadCaptcha" plain>
{{ btnText }}
</el-button>
</template>
</el-popover>
</el-container> </el-container>
</template> </template>
@ -75,6 +72,7 @@ const handleConfirm = (dots) => {
dots: dots.value, dots: dots.value,
key: captKey.value key: captKey.value
}).then(() => { }).then(() => {
// ElMessage.success('')
showCaptcha.value = false showCaptcha.value = false
sendMsg() sendMsg()
}).catch(() => { }).catch(() => {
@ -120,6 +118,21 @@ const sendMsg = () => {
</script> </script>
<style scoped> <style lang="stylus">
.captcha-box {
.send-btn {
width: 100%;
}
.el-dialog {
.el-dialog__header {
padding: 0;
}
.el-dialog__body {
//padding 0
}
}
}
</style> </style>

View File

@ -18,7 +18,7 @@
placeholder="请输入短信验证码" placeholder="请输入短信验证码"
> >
<template #button> <template #button>
<send-msg-mobile size="small" :mobile="form.mobile"/> <send-msg size="small" :mobile="form.mobile"/>
</template> </template>
</van-field> </van-field>
</van-cell-group> </van-cell-group>
@ -30,7 +30,7 @@ import {computed, ref} from "vue";
import {httpPost} from "@/utils/http"; import {httpPost} from "@/utils/http";
import {validateMobile} from "@/utils/validate"; import {validateMobile} from "@/utils/validate";
import {showNotify} from "vant"; import {showNotify} from "vant";
import SendMsgMobile from "@/components/mobile/SendMsgMobile.vue"; import SendMsg from "@/components/SendMsg.vue";
const props = defineProps({ const props = defineProps({
show: Boolean, show: Boolean,

View File

@ -1,137 +0,0 @@
<template>
<el-container class="captcha-box">
<el-button type="primary" class="send-btn" :size="props.size" :disabled="!canSend" @click="loadCaptcha" plain>
{{ btnText }}
</el-button>
<el-dialog
v-model="showCaptcha"
:close-on-click-modal="true"
:show-close="false"
style="width:90%;max-width: 800px;"
>
<captcha-plus
:max-dot="maxDot"
:image-base64="imageBase64"
:thumb-base64="thumbBase64"
width="100%"
@close="showCaptcha = false"
@refresh="handleRequestCaptCode"
@confirm="handleConfirm"
/>
</el-dialog>
</el-container>
</template>
<script setup>
//
import {ref} from "vue";
import lodash from 'lodash'
import {validateMobile} from "@/utils/validate";
import {ElMessage} from "element-plus";
import {httpGet, httpPost} from "@/utils/http";
import CaptchaPlus from "@/components/CaptchaPlus.vue";
const props = defineProps({
mobile: String,
size: String,
});
const btnText = ref('发送验证码')
const canSend = ref(true)
const showCaptcha = ref(false)
const maxDot = ref(5)
const imageBase64 = ref('')
const thumbBase64 = ref('')
const captKey = ref('')
const dots = ref(null)
const handleRequestCaptCode = () => {
httpGet('/api/captcha/get').then(res => {
const data = res.data
imageBase64.value = data.image
thumbBase64.value = data.thumb
captKey.value = data.key
}).catch(e => {
ElMessage.error('获取人机验证数据失败:' + e.message)
})
}
const handleConfirm = (dots) => {
if (lodash.size(dots) <= 0) {
return ElMessage.error('请进行人机验证再操作')
}
let dotArr = []
lodash.forEach(dots, (dot) => {
dotArr.push(dot.x, dot.y)
})
dots.value = dotArr.join(',')
httpPost('/api/captcha/check', {
dots: dots.value,
key: captKey.value
}).then(() => {
showCaptcha.value = false
sendMsg()
}).catch(() => {
ElMessage.error('人机验证失败')
handleRequestCaptCode()
})
}
const loadCaptcha = () => {
if (!validateMobile(props.mobile)) {
return ElMessage.error("请输入合法的手机号")
}
showCaptcha.value = true
handleRequestCaptCode() //
}
const sendMsg = () => {
if (!canSend.value) {
return
}
canSend.value = false
httpPost('/api/sms/code', {mobile: props.mobile, key: captKey.value, dots: dots.value}).then(() => {
ElMessage.success('短信发送成功')
let time = 120
btnText.value = time
const handler = setInterval(() => {
time = time - 1
if (time <= 0) {
clearInterval(handler)
btnText.value = '重新发送'
canSend.value = true
} else {
btnText.value = time
}
}, 1000)
}).catch(e => {
canSend.value = true
ElMessage.error('短信发送失败:' + e.message)
})
}
</script>
<style lang="stylus">
.captcha-box {
.send-btn {
width: 100%;
}
.el-dialog {
.el-dialog__header {
padding: 0;
}
.el-dialog__body {
//padding 0
}
}
}
</style>

View File

@ -9,7 +9,7 @@
<div class="header">{{ title }}</div> <div class="header">{{ title }}</div>
<div class="content"> <div class="content">
<div class="block"> <div class="block">
<el-input placeholder="手机号" size="large" v-model="username" autocomplete="off"> <el-input placeholder="手机号" size="large" maxlength="11" v-model="username" autocomplete="off">
<template #prefix> <template #prefix>
<el-icon> <el-icon>
<UserFilled/> <UserFilled/>
@ -57,6 +57,7 @@ import FooterBar from "@/components/FooterBar.vue";
import {isMobile} from "@/utils/libs"; import {isMobile} from "@/utils/libs";
import {checkSession} from "@/action/session"; import {checkSession} from "@/action/session";
import {setUserToken} from "@/store/session"; import {setUserToken} from "@/store/session";
import {validateMobile} from "@/utils/validate";
const router = useRouter(); const router = useRouter();
const title = ref('ChatGPT-PLUS 用户登录'); const title = ref('ChatGPT-PLUS 用户登录');
@ -81,8 +82,8 @@ onMounted(() => {
}) })
const login = function () { const login = function () {
if (username.value === '') { if (!validateMobile(username.value)) {
return ElMessage.error('请输入用户名'); return ElMessage.error('请输入合法的手机号');
} }
if (password.value.trim() === '') { if (password.value.trim() === '') {
return ElMessage.error('请输入密码'); return ElMessage.error('请输入密码');

View File

@ -64,8 +64,7 @@
</el-input> </el-input>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<send-msg-mobile size="large" :mobile="formData.mobile" v-if="isMobile()"/> <send-msg size="large" :mobile="formData.mobile"/>
<send-msg size="large" :mobile="formData.mobile" v-else/>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
@ -109,7 +108,7 @@ import FooterBar from "@/components/FooterBar.vue";
import SendMsg from "@/components/SendMsg.vue"; import SendMsg from "@/components/SendMsg.vue";
import {validateMobile} from "@/utils/validate"; import {validateMobile} from "@/utils/validate";
import {isMobile} from "@/utils/libs"; import {isMobile} from "@/utils/libs";
import SendMsgMobile from "@/components/mobile/SendMsgMobile.vue"; import SendMsgMobile from "@/components/SendMsg.vue";
const router = useRouter(); const router = useRouter();
const title = ref('ChatGPT-PLUS 用户注册'); const title = ref('ChatGPT-PLUS 用户注册');