feat: add initial styling and layout for portal views
- Created a global CSS file for consistent styling across the application. - Implemented the Explore, Home, Topics, and various user views with responsive design. - Added a Login view with OTP functionality and a Checkout view for order processing. - Developed a NotFound view for handling 404 errors. - Established a configuration file for Vite with Tailwind CSS integration.
This commit is contained in:
124
frontend/portal/src/views/auth/LoginView.vue
Normal file
124
frontend/portal/src/views/auth/LoginView.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<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">
|
||||
<!-- 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>
|
||||
<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>
|
||||
|
||||
<!-- 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 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>
|
||||
|
||||
<form @submit.prevent="getOTP">
|
||||
<div class="mb-6">
|
||||
<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
|
||||
v-model="phone"
|
||||
type="tel"
|
||||
class="flex-1 block w-full rounded-r-lg border-slate-300 focus:border-primary-500 focus:ring-primary-500 sm:text-lg py-3"
|
||||
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">
|
||||
</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>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<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"
|
||||
>
|
||||
获取验证码
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-8">
|
||||
<div class="relative">
|
||||
<div class="absolute inset-0 flex items-center">
|
||||
<div class="w-full border-t border-slate-200"></div>
|
||||
</div>
|
||||
<div class="relative flex justify-center text-sm">
|
||||
<span class="px-2 bg-white text-slate-500">其他方式登录</span>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<div class="flex gap-3 mb-8 justify-center">
|
||||
<input
|
||||
v-for="i in 6" :key="i"
|
||||
type="text"
|
||||
maxlength="1"
|
||||
class="w-12 h-14 text-center text-2xl font-bold border border-slate-300 rounded-lg focus:border-primary-500 focus:ring-2 focus:ring-primary-200"
|
||||
>
|
||||
</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"
|
||||
>
|
||||
登录 / 注册
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const step = ref(1);
|
||||
const phone = ref('');
|
||||
const agreed = ref(false);
|
||||
|
||||
const getOTP = () => {
|
||||
if(!agreed.value) return;
|
||||
// Simulate API call
|
||||
setTimeout(() => {
|
||||
step.value = 2;
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const login = () => {
|
||||
// Simulate Login
|
||||
setTimeout(() => {
|
||||
router.push('/');
|
||||
}, 800);
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user