Files
jiapuapp/pages/auth/login.vue
T

85 lines
3.6 KiB
Vue

<template>
<view class="page-shell login-page">
<view class="ceremony-top">
<text class="seal-word"></text>
<text class="eyebrow">家脉绵延 · 生生不息</text>
<text class="brand-title">家谱</text>
<text class="brand-subtitle">为家族留存可传承的记忆</text>
</view>
<view class="login-paper">
<view class="section-title">登录家谱</view>
<text class="form-note">使用已注册手机号和密码登录</text>
<text class="form-label">手机号</text>
<input v-model="phone" class="line-input" type="number" maxlength="11" placeholder="请输入手机号" placeholder-class="placeholder" />
<text class="form-label">密码</text>
<input v-model="password" class="line-input" password placeholder="请输入密码" placeholder-class="placeholder" />
<text v-if="errorMessage" class="error-message">{{ errorMessage }}</text>
<button class="primary-button login-button" :loading="isSubmitting" :disabled="isSubmitting" @click="login">进入家谱</button>
<text class="login-tip">短信登录将在验证码服务联调后开放</text>
<text class="terms">登录即表示同意用户协议隐私政策</text>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { appApi } from '@/utils/api.js'
import { calcMD5 } from '@/utils/md5.js'
const phone = ref('')
const password = ref('')
const isSubmitting = ref(false)
const errorMessage = ref('')
const login = async () => {
if (!/^1\d{10}$/.test(phone.value)) {
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
return
}
if (!password.value) {
uni.showToast({ title: '请输入登录密码', icon: 'none' })
return
}
isSubmitting.value = true
errorMessage.value = ''
try {
await appApi.loginWithPassword({ phone: phone.value, passwordHash: calcMD5(password.value) })
uni.reLaunch({ url: '/pages/genealogy/index' })
} catch (error) {
errorMessage.value = error.message || '登录失败,请稍后重试'
} finally {
isSubmitting.value = false
}
}
</script>
<style scoped lang="scss">
.login-page { padding-top: 108rpx; }
.ceremony-top {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 40rpx 76rpx;
background: $brand-red;
color: #fff9ed;
}
.seal-word { display: flex; width: 66rpx; height: 66rpx; align-items: center; justify-content: center; box-sizing: border-box; border: 2rpx solid #f1d494; border-radius: 50%; color: #f5ddaa; font-family: serif; font-size: 37rpx; }
.eyebrow { margin-top: 24rpx; color: #f8dfad; font-size: 22rpx; letter-spacing: 5rpx; }
.brand-title { margin-top: 24rpx; font-family: serif; font-size: 68rpx; font-weight: 700; letter-spacing: 16rpx; }
.brand-subtitle { margin-top: 18rpx; color: #f5deae; font-size: 26rpx; letter-spacing: 3rpx; }
.login-paper { margin: -28rpx 28rpx 0; padding: 48rpx 42rpx; border: 1rpx solid $gold-soft; border-radius: 24rpx; background: $paper-white; }
.form-note { display: block; margin-top: 14rpx; color: $ink-muted; font-size: 23rpx; }
.form-label { display: block; margin-top: 40rpx; color: $ink-muted; font-size: 25rpx; }
.line-input { height: 78rpx; border-bottom: 1rpx solid $gold-soft; color: $ink; font-size: 30rpx; }
.placeholder { color: #b3a895; }
.error-message { display: block; margin-top: 22rpx; color: $brand-red; font-size: 23rpx; line-height: 1.5; }
.login-button { margin-top: 48rpx; }
.login-tip { display: block; margin-top: 24rpx; color: $ink-muted; font-size: 22rpx; text-align: center; }
.terms { display: block; margin-top: 16rpx; color: #a39785; font-size: 21rpx; text-align: center; }
</style>