128 lines
4.2 KiB
Vue
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>
|
|
|