backend/src/views/login/forget.vue
2026-04-01 10:12:52 +08:00

128 lines
4.2 KiB
Vue

<template>
<div class="page">
<div class="card">
<h2>忘记密码</h2>
<p class="desc">通过租户账号和手机号重置密码</p>
<input v-model="form.tenant_name" class="input" placeholder="租户名称" />
<input v-model="form.account" class="input" placeholder="账号" />
<input v-model="form.phone" class="input" placeholder="手机号" />
<div class="code-row">
<input v-model="form.sms_code" class="input code-input" placeholder="短信验证码" />
<button class="code-btn" :disabled="codeLoading || countdown > 0" @click="handleSendCode">
{{ countdown > 0 ? `${countdown}s` : (codeLoading ? "发送中..." : "发送验证码") }}
</button>
</div>
<input v-model="form.new_password" class="input" type="password" placeholder="新密码" />
<input v-model="form.confirm_password" class="input" type="password" placeholder="确认新密码" />
<div v-if="errorMsg" class="error">{{ errorMsg }}</div>
<button class="btn" :disabled="loading" @click="handleSubmit">
{{ loading ? "提交中..." : "重置密码" }}
</button>
<a class="back" @click.prevent="router.push('/login')">返回登录</a>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onUnmounted } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import { resetPassword, sendResetCode } from "@/api/login";
const router = useRouter();
const loading = ref(false);
const codeLoading = ref(false);
const countdown = ref(0);
let timer = null;
const errorMsg = ref("");
const form = reactive({
tenant_name: "",
account: "",
phone: "",
sms_code: "",
new_password: "",
confirm_password: "",
});
const startCountdown = () => {
countdown.value = 60;
if (timer) clearInterval(timer);
timer = setInterval(() => {
countdown.value -= 1;
if (countdown.value <= 0) {
clearInterval(timer);
timer = null;
}
}, 1000);
};
const handleSendCode = async () => {
if (codeLoading.value || countdown.value > 0) return;
if (!form.tenant_name || !form.account || !form.phone) {
errorMsg.value = "请先填写租户名称、账号和手机号";
return;
}
codeLoading.value = true;
errorMsg.value = "";
try {
const res = await sendResetCode({
tenant_name: form.tenant_name,
account: form.account,
phone: form.phone,
});
if (res.code === 200) {
ElMessage.success("验证码已发送");
startCountdown();
} else {
errorMsg.value = res.msg || "验证码发送失败";
}
} catch {
errorMsg.value = "验证码发送失败,请稍后重试";
} finally {
codeLoading.value = false;
}
};
const handleSubmit = async () => {
errorMsg.value = "";
if (loading.value) return;
loading.value = true;
try {
const res = await resetPassword(form);
if (res.code === 200) {
ElMessage.success("密码重置成功,请重新登录");
router.push("/login");
} else {
errorMsg.value = res.msg || "重置失败";
}
} catch {
errorMsg.value = "重置失败,请稍后重试";
} finally {
loading.value = false;
}
};
onUnmounted(() => {
if (timer) clearInterval(timer);
});
</script>
<style scoped>
.page { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: #f4f8ff; }
.card { width: 420px; background: #fff; border-radius: 12px; padding: 24px; box-shadow: 0 8px 24px rgba(0,0,0,.08); }
.desc { color: #6b7b93; margin-bottom: 12px; }
.input { width: 100%; margin-bottom: 10px; padding: 10px; border: 1px solid #dbe3ee; border-radius: 6px; box-sizing: border-box; }
.code-row { display: flex; gap: 8px; margin-bottom: 10px; }
.code-input { margin-bottom: 0; flex: 1; }
.code-btn { width: 120px; border: 1px solid #dbe3ee; border-radius: 6px; background: #fff; cursor: pointer; }
.code-btn:disabled { opacity: .6; cursor: not-allowed; }
.btn { width: 100%; margin-top: 8px; padding: 10px; border: none; border-radius: 6px; background: #3f8cff; color: #fff; cursor: pointer; }
.btn:disabled { opacity: .65; cursor: not-allowed; }
.error { color: #e34d4d; font-size: 13px; margin: 6px 0; }
.back { display: inline-block; margin-top: 10px; color: #3f8cff; cursor: pointer; }
</style>