新增规则触发人机验证和加载失败提示

This commit is contained in:
eoao
2025-07-29 20:00:00 +08:00
parent d03b7ca21d
commit 3a40cfc63b
25 changed files with 632 additions and 273 deletions

View File

@@ -13,11 +13,12 @@
<span class="form-title">{{settingStore.settings.title}}</span>
<span class="form-desc" v-if="show === 'login'">{{$t('loginTitle')}}</span>
<span class="form-desc" v-else>{{$t('regTitle')}}</span>
<div v-if="show === 'login'">
<div v-show="show === 'login'">
<el-input class="email-input" v-model="form.email" type="text" :placeholder="$t('emailAccount')" autocomplete="off">
<template #append>
<div @click.stop="openSelect">
<el-select
v-if="show === 'login'"
ref="mySelect"
v-model="suffix"
:placeholder="$t('select')"
@@ -43,11 +44,12 @@
>{{$t('loginBtn')}}
</el-button>
</div>
<div v-else>
<div v-show="show !== 'login'">
<el-input class="email-input" v-model="registerForm.email" type="text" :placeholder="$t('emailAccount')" autocomplete="off">
<template #append>
<div @click.stop="openSelect">
<el-select
v-if="show !== 'login'"
ref="mySelect"
v-model="suffix"
:placeholder="$t('select')"
@@ -75,7 +77,12 @@
class="register-turnstile"
:data-sitekey="settingStore.settings.siteKey"
data-callback="onTurnstileSuccess"
></div>
data-error-callback="onTurnstileError"
data-after-interactive-callback="loadAfter"
data-before-interactive-callback="loadBefore"
>
<span style="font-size: 12px;color: #F56C6C" v-if="botJsError">人机验证模块加载失败,请刷新浏览器</span>
</div>
<el-button class="btn" type="primary" @click="submitRegister" :loading="registerLoading"
>{{$t('regBtn')}}
</el-button>
@@ -129,15 +136,30 @@ suffix.value = domainList[0]
const verifyShow = ref(false)
let verifyToken = ''
let turnstileId = null
let botJsError = ref(false)
window.onTurnstileSuccess = (token) => {
verifyToken = token;
setTimeout(() => {
verifyShow.value = false
}, 2000)
};
window.onTurnstileError = (e) => {
console.log('人机验加载失败')
nextTick(() => {
if (!turnstileId) {
turnstileId = window.turnstile.render('.register-turnstile')
} else {
window.turnstile.reset(turnstileId);
}
})
};
window.loadAfter = (e) => {
console.log('loadAfter')
}
window.loadBefore = (e) => {
console.log('loadBefore')
}
const loginOpacity = computed(() => {
return `rgba(255, 255, 255, ${settingStore.settings.loginOpacity})`
@@ -158,7 +180,6 @@ const openSelect = () => {
mySelect.value.toggleMenu()
}
const submit = () => {
if (!form.email) {
@@ -267,15 +288,26 @@ function submitRegister() {
}
if (!verifyToken && settingStore.settings.registerVerify === 0) {
verifyShow.value = true
if (!turnstileId) {
if (!verifyToken && (settingStore.settings.registerVerify === 0 || (settingStore.settings.registerVerify === 2 && settingStore.settings.regVerifyOpen))) {
if (!verifyShow.value) {
verifyShow.value = true
nextTick(() => {
turnstileId = window.turnstile.render('.register-turnstile')
if (!turnstileId) {
try {
turnstileId = window.turnstile.render('.register-turnstile')
} catch (e) {
botJsError.value = true
console.log('人机验证js加载失败')
}
} else {
window.turnstile.reset('.register-turnstile')
}
})
} else {
nextTick(() => {
window.turnstile.reset(turnstileId);
} else if (!botJsError.value) {
ElMessage({
message: t('botVerifyMsg'),
type: "error",
plain: true
})
}
return;
@@ -290,27 +322,38 @@ function submitRegister() {
code: registerForm.code
}
register(form).then(() => {
register(form).then(({regVerifyOpen}) => {
show.value = 'login'
registerForm.email = ''
registerForm.password = ''
registerForm.confirmPassword = ''
registerForm.code = ''
registerLoading.value = false
turnstileId = null
verifyToken = ''
settingStore.settings.regVerifyOpen = regVerifyOpen
verifyShow.value = false
ElMessage({
message: t('regSuccessMsg'),
type: 'success',
plain: true,
})
}).catch(res => {
registerLoading.value = false
if (res.code === 400) {
verifyToken = ''
window.turnstile.reset(turnstileId)
settingStore.settings.regVerifyOpen = true
if (turnstileId) {
window.turnstile.reset(turnstileId)
} else {
nextTick(() => {
turnstileId = window.turnstile.render('.register-turnstile')
})
}
verifyShow.value = true
}
registerLoading.value = false
});
}

View File

@@ -230,15 +230,39 @@
<div class="setting-item">
<div><span>{{$t('signUpVerification')}}</span></div>
<div>
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
v-model="setting.registerVerify"/>
<el-button class="opt-button" size="small" type="primary" @click="openRegVerifyCount">
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
</el-button>
<el-select
@change="change"
:style="`width: ${ locale === 'en' ? 100 : 80 }px;`"
v-model="setting.registerVerify"
placeholder="Select"
class="bot-verify-select"
>
<el-option key="1" :value="0" :label="$t('enable')" />
<el-option key="1" :value="1" :label="$t('disable')" />
<el-option key="1" :value="2" :label="$t('rulesVerify')" />
</el-select>
</div>
</div>
<div class="setting-item">
<div><span>{{$t('addEmailVerification')}}</span></div>
<div>
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
v-model="setting.addEmailVerify"/>
<el-button class="opt-button" size="small" type="primary" @click="openAddVerifyCount">
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
</el-button>
<el-select
@change="change"
:style="`width: ${ locale === 'en' ? 100 : 80 }px;`"
v-model="setting.addEmailVerify"
placeholder="Select"
class="bot-verify-select"
>
<el-option key="1" :value="0" :label="$t('enable')" />
<el-option key="1" :value="1" :label="$t('disable')" />
<el-option key="1" :value="2" :label="$t('rulesVerify')" />
</el-select>
</div>
</div>
<div class="setting-item">
@@ -450,6 +474,19 @@
<el-table-column :width="tokenColumnWidth" property="value" label="Token" fixed="right" :show-overflow-tooltip="true" />
</el-table>
</el-dialog>
<el-dialog v-model="showRegVerifyCount" :title="$t('rulesVerifyTitle',{count: regVerifyCount})" @closed="regVerifyCount = setting.regVerifyCount" >
<form>
<el-input-number type="text" v-model="regVerifyCount" :min="1" >
</el-input-number>
<el-button type="primary" :loading="settingLoading" @click="saveRegVerifyCount">{{$t('save')}}</el-button>
</form>
</el-dialog>
<el-dialog v-model="showAddVerifyCount" :title="$t('rulesVerifyTitle',{count: addVerifyCount})" @closed="addVerifyCount = setting.addVerifyCount">
<form>
<el-input-number type="text" v-model="addVerifyCount" :min="1"/>
<el-button type="primary" :loading="settingLoading" @click="saveAddVerifyCount">{{$t('save')}}</el-button>
</form>
</el-dialog>
</el-scrollbar>
</div>
</template>
@@ -497,7 +534,11 @@ const loginOpacity = ref(0)
const backgroundUrl = ref('')
let backgroundFile = {}
const showSetBackground = ref(false)
let regVerifyCount = ref(1)
let addVerifyCount = ref(1)
let backup = '{}'
const showAddVerifyCount = ref(false)
const showRegVerifyCount = ref(false)
const resendTokenForm = reactive({
domain: '',
token: '',
@@ -542,8 +583,20 @@ settingQuery().then(settingData => {
backgroundUrl.value = setting.value.background?.startsWith('http') ? setting.value.background : ''
editTitle.value = setting.value.title
r2DomainInput.value = setting.value.r2Domain
addVerifyCount.value = setting.value.addVerifyCount
regVerifyCount.value = setting.value.regVerifyCount
})
function openAddVerifyCount() {
if (settingLoading.value) return
showAddVerifyCount.value = true
}
function openRegVerifyCount() {
if (settingLoading.value) return
showRegVerifyCount.value = true
}
const resendList = computed(() => {
let list = Object.keys(setting.value.resendTokens).map(key => {
@@ -566,6 +619,20 @@ const resendList = computed(() => {
return list;
});
function saveAddVerifyCount() {
if(!addVerifyCount.value) {
addVerifyCount.value = 1
}
editSetting({addVerifyCount: addVerifyCount.value})
}
function saveRegVerifyCount() {
if (!regVerifyCount.value) {
regVerifyCount.value = 1
}
editSetting({regVerifyCount: regVerifyCount.value})
}
const compareByLengthAndUpperCase = (a, b, key) => {
const getUpperCaseCount = (str) => (str.match(/[A-Z]/g) || []).length;
if (a[key].length === b[key].length) {
@@ -742,9 +809,9 @@ async function saveBackground() {
if (localUpShow.value) {
image = await fileToBase64(backgroundFile,true);
} else {
if (!backgroundUrl.value.startsWith('http')) {
if (backgroundUrl.value && !backgroundUrl.value.startsWith('http')) {
ElMessage({
message: '图片地址不正确',
message: t('imageLinkErrorMsg'),
type: "error",
plain: true
})
@@ -872,8 +939,9 @@ function editSetting(settingForm, refreshStatus = true) {
tgSettingShow.value = false
thirdEmailShow.value = false
forwardRulesShow.value = false
showAddVerifyCount.value = false
showRegVerifyCount.value = false
}).catch((e) => {
console.error(e)
loginOpacity.value = setting.value.loginOpacity
setting.value = {...setting.value, ...JSON.parse(backup)}
}).finally(() => {
@@ -941,7 +1009,9 @@ function editSetting(settingForm, refreshStatus = true) {
flex-direction: column;
}
.bot-verify-select {
margin-left: 10px;
}
.settings-card {
background-color: #fff;