chore: update auth and portal

This commit is contained in:
2026-01-14 11:29:17 +08:00
parent fb0a1c2f84
commit 3bcee7efc2
42 changed files with 5969 additions and 3014 deletions

View File

@@ -1,93 +1,144 @@
<script setup>
import { ref } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useToast } from 'primevue/usetoast';
import Toast from 'primevue/toast';
import { authApi } from '../../api/auth';
import { tenantPath } from '../../utils/tenant';
import { ref } from "vue";
import { useRouter, useRoute } from "vue-router";
import { useToast } from "primevue/usetoast";
import Toast from "primevue/toast";
import { authApi } from "../../api/auth";
import { tenantPath } from "../../utils/tenant";
const router = useRouter();
const route = useRoute();
const toast = useToast();
const step = ref(1);
const phone = ref('');
const otpCode = ref('');
const phone = ref("");
const otpCode = ref("");
const agreed = ref(false);
const getOTP = async () => {
if(!agreed.value) return;
try {
await authApi.sendOTP(phone.value);
step.value = 2;
toast.add({ severity: 'success', summary: '发送成功', detail: '验证码已发送', life: 3000 });
} catch (e) {
toast.add({ severity: 'error', summary: '错误', detail: e.message, life: 3000 });
}
if (!agreed.value) return;
try {
await authApi.sendOTP(phone.value);
step.value = 2;
toast.add({
severity: "success",
summary: "发送成功",
detail: "验证码已发送",
life: 3000,
});
} catch (e) {
toast.add({
severity: "error",
summary: "错误",
detail: e.message,
life: 3000,
});
}
};
const login = async () => {
try {
const res = await authApi.login(phone.value, otpCode.value);
localStorage.setItem('token', res.token);
localStorage.setItem('user', JSON.stringify(res.user));
toast.add({ severity: 'success', summary: '登录成功', detail: '欢迎回来', life: 1000 });
setTimeout(() => {
router.push(tenantPath('/', route));
}, 1000);
} catch (e) {
toast.add({ severity: 'error', summary: '登录失败', detail: e.message, life: 3000 });
}
try {
const res = await authApi.login(phone.value, otpCode.value);
localStorage.setItem("token", res.token);
localStorage.setItem("user", JSON.stringify(res.user));
toast.add({
severity: "success",
summary: "登录成功",
detail: "欢迎回来",
life: 1000,
});
setTimeout(() => {
router.push(tenantPath("/", route));
}, 1000);
} catch (e) {
toast.add({
severity: "error",
summary: "登录失败",
detail: e.message,
life: 3000,
});
}
};
</script>
<template>
<div class="bg-white rounded-2xl shadow-xl w-full max-w-4xl overflow-hidden flex min-h-[550px]">
<div
class="bg-white rounded-2xl shadow-xl w-full max-w-4xl overflow-hidden flex min-h-[550px]"
>
<!-- Left Brand Area -->
<div class="hidden md:flex w-1/2 bg-slate-900 relative p-12 flex-col justify-between text-white">
<div
class="hidden md:flex w-1/2 bg-slate-900 relative p-12 flex-col justify-between text-white"
>
<!-- Decor/Bg could be here -->
<div class="z-10">
<div class="flex items-center gap-2 mb-8">
<div class="w-8 h-8 bg-white/20 rounded flex items-center justify-center font-bold">Q</div>
<span class="text-xl font-bold">Quyun</span>
<div
class="w-8 h-8 bg-white/20 rounded flex items-center justify-center font-bold"
>
Q
</div>
<span class="text-xl font-bold">Quyun</span>
</div>
<h1 class="text-4xl font-bold leading-tight mb-4">探索戏曲的<br>无限可能</h1>
<p class="text-slate-400">专业的租户管理与内容交付平台连接创作者与用户</p>
<h1 class="text-4xl font-bold leading-tight mb-4">
探索戏曲的<br />无限可能
</h1>
<p class="text-slate-400">
专业的租户管理与内容交付平台连接创作者与用户
</p>
</div>
<div class="text-xs text-slate-500 z-10">
© 2025 Quyun. All rights reserved.
</div>
<div class="text-xs text-slate-500 z-10">© 2025 Quyun. All rights reserved.</div>
</div>
<!-- Right Form Area -->
<div class="w-full md:w-1/2 p-8 sm:p-12 flex flex-col justify-center bg-white relative">
<div
class="w-full md:w-1/2 p-8 sm:p-12 flex flex-col justify-center bg-white relative"
>
<div v-if="step === 1">
<h2 class="text-2xl font-bold text-slate-900 mb-2">欢迎回来</h2>
<p class="text-sm text-slate-500 mb-8">未注册的手机号验证后将自动创建账号</p>
<p class="text-sm text-slate-500 mb-8">
未注册的手机号验证后将自动创建账号
</p>
<form @submit.prevent="getOTP">
<div class="mb-6">
<label class="block text-sm font-medium text-slate-700 mb-2">手机号码</label>
<label class="block text-sm font-medium text-slate-700 mb-2"
>手机号码</label
>
<div class="flex">
<span class="inline-flex items-center px-4 rounded-l-lg border border-r-0 border-slate-300 bg-slate-50 text-slate-500 text-sm">+86</span>
<input
<span
class="inline-flex items-center px-4 rounded-l-lg border border-r-0 border-slate-300 bg-slate-50 text-slate-500 text-sm"
>+86</span
>
<input
v-model="phone"
type="tel"
class="flex-1 block w-full rounded-r-lg border border-slate-300 focus:border-primary-500 focus:ring-primary-500 sm:text-lg py-3 px-4"
type="tel"
class="flex-1 block w-full rounded-r-lg border border-slate-300 focus:border-primary-500 focus:ring-primary-500 sm:text-lg py-3 px-4"
placeholder="请输入手机号"
required
>
/>
</div>
</div>
<div class="flex items-start mb-6">
<div class="flex items-center h-5">
<input id="terms" v-model="agreed" type="checkbox" class="w-5 h-5 rounded border-slate-300 text-primary-600 focus:ring-primary-500">
<input
id="terms"
v-model="agreed"
type="checkbox"
class="w-5 h-5 rounded border-slate-300 text-primary-600 focus:ring-primary-500"
/>
</div>
<label for="terms" class="ml-3 text-sm text-slate-500">
我已阅读并同意 <a href="#" class="text-primary-600 hover:underline">用户协议</a> <a href="#" class="text-primary-600 hover:underline">隐私政策</a>
我已阅读并同意
<a href="#" class="text-primary-600 hover:underline">用户协议</a>
<a href="#" class="text-primary-600 hover:underline">隐私政策</a>
</label>
</div>
<button
type="submit"
<button
type="submit"
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-lg font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="!agreed || !phone"
>
@@ -105,38 +156,55 @@ const login = async () => {
</div>
</div>
<div class="mt-6 flex justify-center gap-6">
<button class="w-10 h-10 rounded-full bg-slate-50 border border-slate-200 flex items-center justify-center hover:bg-slate-100 text-green-600"><i class="pi pi-wechat text-xl"></i></button>
<button class="w-10 h-10 rounded-full bg-slate-50 border border-slate-200 flex items-center justify-center hover:bg-slate-100 text-slate-800"><i class="pi pi-github text-xl"></i></button>
<button
class="w-10 h-10 rounded-full bg-slate-50 border border-slate-200 flex items-center justify-center hover:bg-slate-100 text-green-600"
>
<i class="pi pi-wechat text-xl"></i>
</button>
<button
class="w-10 h-10 rounded-full bg-slate-50 border border-slate-200 flex items-center justify-center hover:bg-slate-100 text-slate-800"
>
<i class="pi pi-github text-xl"></i>
</button>
</div>
</div>
</div>
<div v-else-if="step === 2">
<button @click="step = 1" class="absolute top-8 left-8 text-slate-400 hover:text-slate-600"><i class="pi pi-arrow-left mr-1"></i> 返回</button>
<h2 class="text-2xl font-bold text-slate-900 mb-2">输入验证码</h2>
<p class="text-sm text-slate-500 mb-8">验证码已发送至 +86 {{ phone }}</p>
<button
@click="step = 1"
class="absolute top-8 left-8 text-slate-400 hover:text-slate-600"
>
<i class="pi pi-arrow-left mr-1"></i> 返回
</button>
<h2 class="text-2xl font-bold text-slate-900 mb-2">输入验证码</h2>
<p class="text-sm text-slate-500 mb-8">
验证码已发送至 +86 {{ phone }}
</p>
<div class="mb-8">
<input
v-model="otpCode"
type="text"
maxlength="4"
class="w-full h-14 px-4 text-center text-3xl font-bold border border-slate-300 rounded-lg focus:border-primary-500 focus:ring-4 focus:ring-primary-100 outline-none tracking-[1.5em] pl-[1.5em]"
placeholder="0000"
@input="otpCode = otpCode.replace(/\D/g, '')"
>
</div>
<div class="mb-8">
<input
v-model="otpCode"
type="text"
maxlength="4"
class="w-full h-14 px-4 text-center text-3xl font-bold border border-slate-300 rounded-lg focus:border-primary-500 focus:ring-4 focus:ring-primary-100 outline-none tracking-[1.5em] pl-[1.5em]"
placeholder="0000"
@input="otpCode = otpCode.replace(/\D/g, '')"
/>
</div>
<div class="text-center mb-8">
<button class="text-sm text-slate-500 hover:text-primary-600">59s 后重新获取</button>
</div>
<button
@click="login"
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-lg font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
>
登录 / 注册
<div class="text-center mb-8">
<button class="text-sm text-slate-500 hover:text-primary-600">
59s 后重新获取
</button>
</div>
<button
@click="login"
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-lg font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
>
登录 / 注册
</button>
</div>
</div>
<Toast />