Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/Aida_Purchaser_Front

This commit is contained in:
2026-04-22 16:11:17 +08:00
11 changed files with 318 additions and 237 deletions

3
src/assets/icons/dui.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.7269 0.156104C19.5188 -0.0520347 19.1025 -0.0520347 18.8944 0.156104L7.13454 11.9159C6.97843 12.0721 6.66622 12.0721 6.51012 11.9159L0.994441 6.40027C0.786302 6.19213 0.370025 6.19213 0.161886 6.40027C-0.0462532 6.60841 0.00578167 6.66044 0.00578167 6.81655C0.00578167 6.97265 0.0578162 7.12876 0.161886 7.23283L6.40605 13.477C6.51012 13.5811 6.66622 13.6331 6.82233 13.6331C6.97843 13.6331 7.13454 13.5811 7.23861 13.477L19.7269 0.98866C19.831 0.88459 19.883 0.728486 19.883 0.572382C19.883 0.416278 19.831 0.260174 19.7269 0.156104Z" fill="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 669 B

View File

@@ -7,9 +7,11 @@ export default {
name: 'Name',
email: 'Email',
password: 'Password',
passwordConfirmation: 'Password Confirmation',
enterName: 'Enter your name',
enterEmail: 'Enter your email',
enterPassword: 'Enter your password',
enterPasswordAgain: 'Enter your password again',
forgotPassword: 'Forget password?',
pleaseInputName: 'Please input the name',
nameLengthError: 'Name length must be between {min} and {max} characters',
@@ -28,7 +30,7 @@ export default {
havenAccountToLogin: `Already have an account? <span onclick="onClickLogin()">Log in</span>`,
verifyEmail: 'Verify your email address',
verifyCodeHasSent: 'A verification code has been sent to<br><span>{email}</span>',
verify: 'Verify',
verify: 'VERIFY',
resendCode: 'Resend Code',
resendCodeIn: 'Resend Code in {time}',
orContinueWith: 'or continue with',

View File

@@ -8,9 +8,11 @@ export default {
name: '姓名',
email: '邮箱',
password: '密码',
passwordConfirmation: '密码确认',
enterName: '请输入姓名',
enterEmail: '请输入邮箱',
enterPassword: '请输入密码',
enterPasswordAgain: '请输入密码确认',
forgotPassword: '忘记密码?',
pleaseInputName: '请输入姓名',
nameLengthError: '姓名长度必须在 {min} 到 {max} 个字符之间',

View File

@@ -38,7 +38,7 @@
--el-input-border-radius: 0;
--el-input-text-color: #232323;
--el-border-color: #C4C4C4;
font-size: 1.4rem;
font-size: 1rem;
}
.retrieve-password:deep(.el-form) .el-input::placeholder,
.register:deep(.el-form) .el-input::placeholder,

View File

@@ -1,5 +1,5 @@
<template>
<div class="visible-code">
<div class="email-verify">
<div class="tip" v-html="$t('Login.verifyCodeHasSent', { email: props.email })"></div>
<input-code @submit="onVerify" v-model="code" ref="inputCodeRef" />
<p class="time" v-if="time > 0">{{ $t('Login.resendCodeIn', { time: timeStr }) }}</p>
@@ -7,7 +7,7 @@
<span @click="onResend">{{ $t('Login.resendCode') }}</span>
</p>
<button class="verify" custom="black" @click="onVerify">{{ $t('Login.verify') }}</button>
<other-login />
<other-login v-if="isShowOtherLogin" />
</div>
</template>
@@ -29,7 +29,8 @@
type: String as () => 'LOGIN' | 'REGISTER' | 'FORGOT_PWD',
required: true
},
password: { type: String, default: '' }
password: { type: String, default: '' },
isShowOtherLogin: { type: Boolean, default: true }
})
const code = ref('')
const time = ref(60)
@@ -96,7 +97,7 @@
</script>
<style lang="less" scoped>
.visible-code {
.email-verify {
width: 100%;
display: flex;
flex-direction: column;
@@ -123,17 +124,15 @@
--button-font-size: 1.4rem;
}
> .time {
font-family: KaiseiOpti-Regular;
user-select: none;
margin-top: 2.4rem;
font-size: 1.6rem;
font-size: 1.2rem;
color: #666;
font-family: Regular;
> span {
color: #ff7a50;
color: #232323;
text-decoration: underline;
cursor: pointer;
font-weight: 500;
font-family: Medium;
}
}
> .other-login {

View File

@@ -1,117 +0,0 @@
<template>
<div class="index">
<div class="header">
<span class="tip">{{ $t('AlphaVersion') }}</span>
<img src="@/assets/images/logo-1.png" class="logo" />
<p class="split"></p>
<button class="login" @click="onLogin">{{ $t('Login.login') }}</button>
<button class="register" @click="onRegister">{{ $t('Login.register') }}</button>
</div>
<img src="@/assets/images/login/index-title.png" class="title" draggable="false" />
<img src="@/assets/images/login/index-zhuangshi.png" class="zhuangshi" draggable="false" />
<div class="tip">{{ $t('Login.indexTip') }}</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const onLogin = () => {
router.push({ name: 'login' })
}
const onRegister = () => {
router.push({ name: 'register' })
}
</script>
<style lang="less" scoped>
.index {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
background-image: url('@/assets/images/login/index-bg.png');
background-size: cover;
background-position: center;
> .header {
position: absolute;
top: 3rem;
left: 0;
width: 100%;
display: flex;
justify-content: center;
> * {
position: relative;
z-index: 1;
}
> .tip {
position: absolute;
width: 100%;
top: 0;
left: 0;
font-size: 3rem;
text-align: center;
font-family: Regular;
color: #fff;
z-index: 0;
}
> .logo {
width: auto;
height: 2.5rem;
margin-left: 3.8rem;
}
> .split {
margin: 0 auto;
}
> button {
margin-right: 3rem;
width: 20rem;
height: 5.2rem;
border-radius: 5rem;
border: none;
outline: none;
font-size: 2.2rem;
font-weight: 600;
font-family: SemiBold;
border: 0.2rem solid #fff;
&:active {
opacity: 0.8;
}
}
> .login {
background-color: #fff;
color: #713e1f;
}
> .register {
background-color: transparent;
color: #ffffff;
backdrop-filter: blur(10px);
}
}
> .zhuangshi,
> .title,
> .tip {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
> .title {
// width: 55%;
width: 105.6rem;
height: auto;
top: 20.5rem;
}
> .zhuangshi {
width: 21.5rem;
height: auto;
bottom: 13.4rem;
}
> .tip {
font-size: 2.8rem;
font-family: Regular;
color: #fff;
bottom: 8rem;
}
}
</style>

View File

@@ -32,7 +32,7 @@
--el-input-border-radius: 0;
--el-input-text-color: #232323;
--el-border-color: #C4C4C4;
font-size: 1.4rem;
font-size: 1rem;
&::placeholder {
color: #9F9F9F;

View File

@@ -10,7 +10,6 @@
:close-on-click-modal="false"
>
<div class="login-dialog-content">
<img class="bg" src="@/assets/images/login/bg.jpg" />
<img class="logo" src="@/assets/images/logo.png" />
<div class="close" @click="show = false"><svg-icon name="close" /></div>
<div class="content" v-if="curentTabInfo">
@@ -22,8 +21,10 @@
<div class="nav" v-show="!curentTabInfo.title">
<div
class="item"
:class="{ active: currentTab === TabNames.sign_up }"
@click="currentTab = TabNames.sign_up"
:class="{
active: [TabNames.register, TabNames.register_success].includes(currentTab)
}"
@click="currentTab = TabNames.register"
>
SIGN UP
</div>
@@ -42,6 +43,7 @@
@login="onLogin"
@register="onRegister"
@submit-email-code="onSubmitEmailCode"
@back="onBack"
:name="data.name"
:email="data.email"
:password="data.password"
@@ -58,6 +60,8 @@
import login from './login.vue'
import register from './register.vue'
import emailVerify from './email-verify.vue'
import registerSuccess from './register-success.vue'
import retrievePassword from './retrieve-password.vue'
import myEvent from '@/utils/myEvent'
const data = ref({
name: '',
@@ -65,11 +69,11 @@
password: '',
type: ''
})
const show = ref(false)
const TabNames = {
login: 'login',
sign_up: 'sign_up',
register: 'register',
email_verify: 'email_verify',
register_success: 'register_success',
retrieve_password: 'retrieve_password'
}
const tabList = markRaw([
@@ -78,7 +82,7 @@
component: login
},
{
name: TabNames.sign_up,
name: TabNames.register,
component: register
},
{
@@ -89,15 +93,20 @@
{
name: TabNames.retrieve_password,
title: 'RETRIEVE PASSWORD',
component: login
component: retrievePassword
},
{
name: TabNames.register_success,
component: registerSuccess
}
])
const show = ref(false)
const currentTab = ref(TabNames.login)
const curentTabInfo = computed(() => tabList.find((v) => v.name === currentTab.value))
const lastTab = ref('')
watch(currentTab, (v, o) => (lastTab.value = o))
const onBack = () => {
if (lastTab.value) currentTab.value = lastTab.value
currentTab.value = lastTab.value || TabNames.login
}
const open = (type?: string) => {
currentTab.value = TabNames[type] || TabNames.login
@@ -118,13 +127,17 @@
}
const onRegister = (res: any) => {
data.value = res
data.value.type = TabNames.sign_up
data.value.type = TabNames.register
currentTab.value = TabNames.email_verify
}
const onSubmitEmailCode = (code: string) => {
// data.value.code = code
console.log(code)
show.value = false
if (data.value.type === TabNames.login) {
console.log('登录', code)
show.value = false
} else {
console.log('注册', code)
currentTab.value = TabNames.register_success
}
}
</script>
@@ -147,14 +160,16 @@
height: 100%;
overflow: hidden;
position: relative;
background: url('@/assets/images/login/bg.jpg') no-repeat center center / 100% 100%;
> *:not(.content) {
position: absolute;
}
> .bg {
width: 100%;
height: 100%;
display: block;
}
> * {
position: absolute;
}
> .logo {
width: auto;
height: 4rem;
@@ -170,9 +185,10 @@
}
> .content {
width: 34rem;
top: 5rem;
right: 6rem;
height: calc(100% - 10rem);
margin: 5rem 6rem auto auto;
display: flex;
flex-direction: column;
> .header {
--padding-bottom: 1.2rem;
padding-bottom: var(--padding-bottom);

View File

@@ -40,15 +40,15 @@
.password-tip {
background: #404040;
color: #fff;
font-size: 1.4rem;
padding: 2rem;
border-radius: 2rem;
font-size: 1.1rem;
padding: 1.5rem;
border-radius: 1.5rem;
line-height: normal;
> div {
display: flex;
align-items: center;
margin-bottom: 0.5rem;
margin-bottom: 1rem;
&:last-child {
margin-bottom: 0;
}

View File

@@ -0,0 +1,113 @@
<template>
<div class="register-success">
<div class="icon"><svg-icon name="dui" size="20" /></div>
<div class="title">Welcome to Stylish Parade!</div>
<div class="title">Please switch to the Login tab to log in.</div>
<div class="footer">
<div class="title">
<span class="text">What awaits you in Stylish Parade</span>
<span class="icon"><svg-icon name="arrow_right" size="11" /></span>
</div>
<div class="content">
<div>
<div class="title">Behind the design</div>
<div class="tip">
Discover how designers bring ideas to life with AiDA from first sketch to final look.
</div>
</div>
<div>
<div class="title">Creative digital works</div>
<div class="tip">
Unlock a growing library of inspiring digital works to refresh your creative mind.
</div>
</div>
<div>
<div class="title">A fashion community</div>
<div class="tip">
Join a space where fashion speaks exchange ideas and connect with creators worldwide.
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
</script>
<style lang="less" scoped>
.register-success {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
margin-top: 7.7rem;
> .icon {
width: 4rem;
height: 4rem;
border-radius: 50%;
background-color: #fff;
border: 0.1rem solid #e8e8e8;
margin-bottom: 1.8rem;
}
> .title {
font-size: 1.6rem;
line-height: 2.4rem;
text-align: center;
color: #232323;
}
> .footer {
position: absolute;
width: 100%;
left: 0;
bottom: 7rem;
padding: 0 6rem;
> .title {
font-family: KaiseiOpti-Regular;
font-size: 1.4rem;
line-height: 2.4rem;
color: #666;
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: 1.2rem;
> .icon {
margin-left: 1.2rem;
transform: rotate(90deg);
}
}
> .content {
display: flex;
align-items: center;
justify-content: center;
gap: 1.8rem;
> div {
padding: 2.4rem 1.5rem 0;
height: 14.8rem;
background: rgba(255, 255, 255, 0.9);
box-shadow: 3px 4px 8px 0px rgba(0, 0, 0, 0.11);
flex: 1;
&:first-child {
flex: 0.84;
}
> .title {
font-family: KaiseiOpti-Bold;
font-size: 1.6rem;
line-height: 2.4rem;
margin-bottom: 1.2rem;
color: #232323;
}
> .tip {
font-family: KaiseiOpti-Regular;
font-size: 1.2rem;
line-height: 1.7rem;
color: #585858;
}
}
}
}
}
</style>

View File

@@ -1,120 +1,183 @@
<template>
<div class="retrieve-password">
<div class="left">
<img class="bg" src="@/assets/images/login/left-bg.png" />
<img class="logo" src="@/assets/images/logo-1.png" />
</div>
<div class="right">
<div class="top">
<button class="back" @click="onBack">
<svg-icon name="arrow-left" size="37" />
<el-form
:model="formData"
:rules="ruleForm"
label-position="top"
ref="form1Ref"
v-show="index === 0"
>
<div class="title">Please enter your email address below to verify your identity.</div>
<el-form-item :label="$t('Login.email')" prop="email">
<el-input v-model="formData.email" :placeholder="$t('Login.enterEmail')" name="email" />
</el-form-item>
<el-form-item class="submit-item">
<button class="submit" type="submit" custom="black" @click.prevent="onSubmit1">
SUBMIT
</button>
</div>
<div class="box">
<img src="@/assets/images/login/elephant.png" />
<template v-if="!isVisible">
<div class="title">{{ $t('Login.retrievePassword') }}</div>
<el-form :model="formData" :rules="ruleForm" label-position="top" ref="formRef">
<el-form-item :label="$t('Login.email')" prop="email">
<el-input
v-model="formData.email"
:placeholder="$t('Login.enterEmail')"
name="email"
/>
</el-form-item>
<el-form-item :label="$t('Login.password')" prop="password">
<password-tip :value="formData.password" v-show="showPasswordTip" />
<el-input
v-model="formData.password"
:placeholder="$t('Login.enterPassword')"
type="password"
show-password
name="password"
@blur="showPasswordTip = false"
@focus="showPasswordTip = true"
/>
</el-form-item>
<br />
<br />
<el-form-item>
<el-button class="submit" type="primary" @click="onSubmit">{{
$t('submit')
}}</el-button>
</el-form-item>
</el-form>
</template>
<!-- <visible-code
v-show="isVisible"
type="FORGOT_PWD"
ref="visibleCodeRef"
:email="formData.email"
@submit="onVerifyCode"
/> -->
<other-login />
</div>
</el-form-item>
</el-form>
<div class="verify-box" v-if="index === 1">
<email-verify
type="FORGOT_PWD"
:email="formData.email"
@submit-email-code="onVerifyCode"
:is-show-other-login="false"
/>
</div>
<el-form
:model="formData"
:rules="ruleForm"
label-position="top"
ref="form2Ref"
v-show="index === 2"
>
<div class="title">
Enter a new password for <br />
<span>{{ formData.email }}</span>
</div>
<el-form-item :label="$t('Login.password')" prop="password">
<password-tip :value="formData.password" v-show="showPasswordTip" />
<el-input
v-model="formData.password"
:placeholder="$t('Login.enterPassword')"
type="password"
show-password
name="password"
@blur="showPasswordTip = false"
@focus="showPasswordTip = true"
/>
</el-form-item>
<div class="password-warning">
<span class="icon"><svg-icon name="warning" size="12" /></span>
<span class="label">You must satisfy ALL password conditions to register.</span>
</div>
<el-form-item :label="$t('Login.passwordConfirmation')" prop="confirmPassword">
<el-input
v-model="formData.confirmPassword"
:placeholder="$t('Login.enterPasswordAgain')"
type="password"
show-password
name="password"
/>
</el-form-item>
<el-form-item class="submit-item">
<button class="submit" type="submit" custom="black" @click.prevent="onSubmit2">
SUBMIT
</button>
</el-form-item>
</el-form>
</div>
</template>
<script setup lang="ts">
import md5 from 'md5'
import { ForgotPassword } from '@/api/user'
import { computed, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { validateEmail, validatePass } from './tools'
import OtherLogin from './other-login.vue'
import emailVerify from './email-verify.vue'
import PasswordTip from './password-tip.vue'
import { useUserInfoStore } from '@/stores'
const userInfoStore = useUserInfoStore()
const router = useRouter()
import EmailVerify from './email-verify.vue'
const emit = defineEmits(['back'])
const validateConfirmPassword = (rule: any, value: string, callback: any) => {
if (value !== formData.password) {
callback(new Error('Passwords do not match'))
} else {
callback()
}
}
const index = ref(0)
const ruleForm = reactive({
email: [{ validator: validateEmail, trigger: 'change' }],
password: [{ validator: validatePass, trigger: 'change' }]
password: [{ validator: validatePass, trigger: 'change' }],
confirmPassword: [{ validator: validateConfirmPassword, trigger: 'change' }]
})
const isVisible = ref(false)
const showPasswordTip = ref(false)
const formData = reactive({
email: '',
password: ''
code: '',
password: '',
confirmPassword: ''
})
const formRef = ref(null)
const onBack = () => {
if (isVisible.value) {
isVisible.value = false
} else {
router.back()
}
}
const form1Ref = ref(null)
const visibleCodeRef = ref(null)
const onSubmit = () => {
formRef.value?.validate?.((valid) => {
const form2Ref = ref(null)
const onSubmit1 = () => {
form1Ref.value?.validate?.((valid) => {
if (valid) {
// console.log('submit!')
visibleCodeRef.value?.onSendCode().then(() => {
isVisible.value = true
})
index.value = 1
} else {
console.warn('error submit!')
}
})
}
const onSubmit2 = () => {
form2Ref.value?.validate?.((valid) => {
if (valid) {
const data = {
email: formData.email,
code: formData.code,
password: md5(formData.password)
}
console.log(data)
emit('back')
} else {
console.warn('error submit!')
}
})
}
const onVerifyCode = (code: string) => {
// console.log(code)
ForgotPassword({
email: formData.email,
newPassword: md5(formData.password),
verificationCode: code
})
.then((res) => {
if (res) router.push({ name: 'login' })
})
.catch((error) => {
console.warn(error)
})
if (!code) return
formData.code = code
index.value = 2
}
</script>
<style lang="less" scoped>
@import './less/style.less';
.retrieve-password {
flex: 1;
&:deep(.el-form) {
height: 100%;
display: flex;
flex-direction: column;
.el-form-item.submit-item {
margin-top: auto;
}
.el-input {
--el-input-height: 4.8rem;
}
.el-form-item:nth-last-child(2) {
margin-bottom: 10rem;
}
> .title {
font-family: KaiseiOpti-Regular;
font-size: 1.6rem;
line-height: 2.4rem;
text-align: center;
color: #585858;
margin-top: auto;
margin-bottom: 3rem;
> span {
font-family: KaiseiOpti-Medium;
color: #252727;
}
}
}
> .verify-box {
width: 100%;
height: 100%;
display: flex;
&:deep(.email-verify) {
> .tip {
margin-top: 8rem;
}
> .input-code {
margin-top: 3rem;
}
> .verify {
margin-top: auto;
}
}
}
}
</style>