feat: require invitation code for registration

This commit is contained in:
richjohnrambo
2026-10-15 17:31:42 +08:00
parent 05d6ec26ad
commit b6a0f819b8
6 changed files with 32 additions and 9 deletions
+1
View File
@@ -9,6 +9,7 @@ const reasonMessages: Record<string, string> = {
FORBIDDEN: '当前账户无权执行此操作',
INVALID_CREDENTIALS: '手机号或密码错误',
ACCOUNT_ALREADY_REGISTERED: '该账户或手机号已经注册',
REFERRAL_CODE_INVALID: '邀请码无效,请检查后重新输入',
RATE_LIMITED: '操作过于频繁,请稍后再试',
TASK_DISABLED: '领取功能暂未开放',
OUTSIDE_BUSINESS_HOURS: '当前不在营业时间内',
+24 -3
View File
@@ -48,7 +48,8 @@
<input
v-model.trim="form.referral_code"
maxlength="32"
placeholder="请输入推荐码(选填)"
autocomplete="off"
placeholder="请输入邀请码(必填)"
/>
</label>
<router-link v-if="mode === 'login'" class="m-forgot-link" to="/app/password/verify"
@@ -99,15 +100,32 @@
const error = ref('');
const form = reactive({ username: '', phone: '', password: '', referral_code: '' });
watch(mode, () => {
function referralCodeFromLink() {
const value =
route.query.inviteCode ?? route.query.invite_code ?? route.query.referral_code;
return typeof value === 'string' ? value.trim() : '';
}
watch(mode, (value) => {
form.username = '';
form.phone = '';
form.password = '';
form.referral_code = '';
form.referral_code = value === 'register' ? referralCodeFromLink() : '';
accepted.value = true;
error.value = '';
});
watch(
() => [route.query.inviteCode, route.query.invite_code, route.query.referral_code],
() => {
const referralCode = referralCodeFromLink();
if (!referralCode) return;
mode.value = 'register';
form.referral_code = referralCode;
},
{ immediate: true }
);
async function submit() {
error.value = '';
if (!/^1\d{10}$/.test(form.phone)) return void (error.value = '请输入正确的手机号码');
@@ -116,6 +134,9 @@
if (mode.value === 'register' && !/^[\p{L}\p{M}·]{1,10}$/u.test(form.username)) {
return void (error.value = '名字须为 1 至 10 个字符,支持汉字');
}
if (mode.value === 'register' && !/^[A-Za-z0-9]{1,32}$/.test(form.referral_code)) {
return void (error.value = '请输入有效的邀请码');
}
submitting.value = true;
try {
const data = await request<AuthResponse>({
+3 -3
View File
@@ -45,7 +45,7 @@
<n-modal v-model:show="showResult" :mask-closable="false">
<section class="m-claim-result" :class="`is-${resultState}`">
<template v-if="resultState === 'matching'">
<h2>正在匹配任务</h2>
<h2>正在匹配订单</h2>
<p>{{ matchingHint }}</p>
<div class="m-match-visual">
<img class="orbit" :src="mobileAssets.claimSpinner" alt="" />
@@ -54,7 +54,7 @@
<div v-if="isAutoMatching" class="m-match-progress"><i :style="{ width: `${matchProgress}%` }"></i></div>
<strong v-if="isAutoMatching" class="m-match-percent">{{ matchProgress }}%</strong>
<ol class="m-match-steps m-match-steps--single">
<li class="active"><span>✓</span>正在匹配任务</li>
<li class="active"><span>✓</span>正在匹配订单</li>
</ol>
<button class="m-result-secondary" @click="openOrders">稍后去记录查看</button>
</template>
@@ -141,7 +141,7 @@
const matchingHint = computed(() => {
if (order.value?.dispatch_mode === 'manual') return '订单已领取,正在等待后台匹配...';
if (matchRemainingSeconds.value > 0) return `系统正在派单... ${formatCountdown(matchRemainingSeconds.value)}`;
return pollFinished.value ? '任务仍在匹配,可稍后前往记录查看' : '系统正在为您匹配任务,请稍等...';
return pollFinished.value ? '订单仍在匹配,可稍后前往记录查看' : '系统正在为您匹配订单,请稍等...';
});
const resultIcon = computed(() => resultState.value === 'success' ? mobileAssets.claimSuccess : resultState.value === 'failed' ? mobileAssets.claimFailed : mobileAssets.claimCancelled);
const resultTitle = computed(() => resultState.value === 'success' ? '任务获取成功' : resultState.value === 'failed' ? '领取失败' : '已取消任务');