Files
App.k12.Parents/pages/register/parent-account.uvue
T
2026-10-29 10:08:39 +08:00

497 lines
11 KiB
Plaintext

<template>
<view class="page-container">
<scroll-view class="content-scroll" direction="vertical">
<view class="page-back-action" @tap="handleBackToLogin">
<text class="page-back-text">返回登录</text>
</view>
<!-- 步骤进度 -->
<view class="step-indicator">
<view class="step-item">
<view class="step-dot step-dot-completed"></view>
<text class="step-text">扫码绑定孩子</text>
</view>
<view class="step-line step-line-completed"></view>
<view class="step-item step-active">
<view class="step-dot step-dot-active"></view>
<text class="step-text step-text-active">家长账号与密码</text>
</view>
<view class="step-line"></view>
<view class="step-item">
<view class="step-dot"></view>
<text class="step-text">注册完成</text>
</view>
</view>
<!-- 已绑定学生展示 -->
<view class="student-summary">
<view class="summary-title-row">
<text class="summary-title">已绑定学生</text>
<uni-badge-view :text="students.length.toString()" badge-class="student-count-badge"></uni-badge-view>
</view>
<view class="student-card" v-for="student in students" :key="student.id">
<image class="student-avatar-small" :src="student.avatar || '/static/avatar-default.png'" mode="aspectFill"></image>
<text class="student-name-small">{{ student.name }}</text>
</view>
</view>
<!-- 表单区域 -->
<view class="form-section">
<text class="form-title">设置家长账号信息</text>
<view class="form-group">
<text class="form-label">家长姓名</text>
<input
class="form-input"
type="text"
v-model="form.parentName"
placeholder="请输入家长姓名"
maxlength="10"
/>
</view>
<view class="form-group">
<text class="form-label">手机号</text>
<input
class="form-input"
type="number"
v-model="form.phone"
placeholder="请输入手机号"
maxlength="11"
/>
</view>
<view class="form-group">
<text class="form-label">与学生的关系</text>
<picker class="relationship-picker" :value="relationshipIndex" :range="relationshipOptions" @change="handleRelationshipChange">
<view class="relationship-picker-content">
<text :class="form.relationship ? 'relationship-value' : 'relationship-placeholder'">{{ form.relationship || '请选择与学生的关系' }}</text>
</view>
</picker>
</view>
<view class="form-group">
<text class="form-label">登录密码</text>
<view class="input-wrapper">
<input
class="form-input-inner"
type="password"
v-model="form.password"
placeholder="请设置登录密码(6-20位)"
maxlength="20"
/>
</view>
</view>
<view class="form-group">
<text class="form-label">确认密码</text>
<view class="input-wrapper">
<input
class="form-input-inner"
type="password"
v-model="form.confirmPassword"
placeholder="请再次输入密码"
maxlength="20"
/>
</view>
</view>
</view>
</scroll-view>
<!-- 底部按钮 -->
<view class="footer-button-container">
<view
class="footer-button"
:class="canSubmit ? '' : 'footer-button-disabled'"
@tap="handleSubmit"
>
<text class="footer-button-text" :class="canSubmit ? '' : 'footer-button-text-disabled'">完成注册</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { apiRegister } from '../../api/auth'
import { apiCheckPhone } from '../../api/student'
import { registrationStudents } from '../../store/registration'
import { setUserInfo } from '../../store/user'
type ParentAccountForm = {
parentName: string
phone: string
relationship: string
password: string
confirmPassword: string
}
const form = reactive<ParentAccountForm>({
parentName: '',
phone: '',
relationship: '',
password: '',
confirmPassword: ''
})
const students = registrationStudents
const relationshipOptions = ['爸爸', '妈妈', '爷爷', '奶奶', '外公', '外婆', '其他亲属']
const relationshipIndex = ref(0)
const canSubmit = computed((): boolean => {
const parentNameReg = /^[\u4e00-\u9fa5]{2,10}$/
const phoneReg = /^1[3-9]\d{9}$/
return (
parentNameReg.test(form.parentName) &&
phoneReg.test(form.phone) &&
form.relationship.length > 0 &&
form.password.length >= 6 &&
form.password.length <= 20 &&
form.password === form.confirmPassword
)
})
function handleRelationshipChange(event: UniPickerChangeEvent) {
const index = event.detail.value as number
relationshipIndex.value = index
form.relationship = relationshipOptions[index]
}
/**
* 提交注册
*/
async function handleSubmit() {
if (!canSubmit.value) {
if (!form.parentName) {
uni.showToast({ title: '请输入家长姓名', icon: 'none' })
} else if (!/^[\u4e00-\u9fa5]{2,10}$/.test(form.parentName)) {
uni.showToast({ title: '请输入正确的家长姓名(2-10个汉字)', icon: 'none' })
} else if (!form.phone) {
uni.showToast({ title: '请输入手机号', icon: 'none' })
} else if (!/^1[3-9]\d{9}$/.test(form.phone)) {
uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
} else if (!form.relationship) {
uni.showToast({ title: '请选择与学生的关系', icon: 'none' })
} else if (form.password.length < 6 || form.password.length > 20) {
uni.showToast({ title: '密码长度应为6-20位', icon: 'none' })
} else if (form.password !== form.confirmPassword) {
uni.showToast({ title: '两次输入的密码不一致', icon: 'none' })
}
return
}
// 检查手机号是否已注册
uni.showLoading({ title: '检查中...' })
const checkRes = await apiCheckPhone(form.phone)
uni.hideLoading()
if (checkRes && checkRes.data && checkRes.data.exists) {
uni.showToast({
title: '该手机号已注册,请直接登录',
icon: 'none'
})
return
}
// 提交注册
uni.showLoading({ title: '注册中...' })
const studentIds = students.value.map(s => s.id)
const res = await apiRegister({
parentName: form.parentName,
phone: form.phone,
relationship: form.relationship,
password: form.password,
students: studentIds
})
uni.hideLoading()
if (res && res.code === 0 && res.data) {
// 保存用户信息
setUserInfo(res.data)
// 跳转到注册完成页
uni.redirectTo({
url: '/pages/register/complete'
})
} else {
uni.showToast({
title: res?.message || '注册失败,请稍后重试',
icon: 'none'
})
}
}
function handleBackToLogin() {
uni.reLaunch({
url: '/pages/login/index'
})
}
</script>
<style>
.page-container {
display: flex;
flex-direction: column;
flex: 1;
background-color: #F5F5F5;
}
.content-scroll {
flex: 1;
padding: 16px 16px calc(96px + var(--uni-safe-area-inset-bottom));
}
.page-back-action {
align-self: flex-start;
padding: 8px 0;
margin-bottom: 8px;
}
.page-back-text {
font-size: 14px;
color: #6C63FF;
}
/* 步骤指示器 */
.step-indicator {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 16px;
background-color: #FFFFFF;
border-radius: 8px;
margin-bottom: 16px;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.step-dot {
width: 8px;
height: 8px;
border-radius: 4px;
background-color: #E5E5E5;
margin-bottom: 8px;
}
.step-dot-completed {
background-color: #10B981;
width: 8px;
height: 8px;
}
.step-dot-active {
background-color: #6C63FF;
width: 12px;
height: 12px;
border-radius: 6px;
}
.footer-button-text-disabled {
color: #999999;
}
.step-text {
font-size: 11px;
color: #999999;
text-align: center;
}
.step-text-active {
color: #6C63FF;
font-weight: 600;
}
.step-line {
flex: 1;
height: 2px;
background-color: #E5E5E5;
margin: 0 4px;
margin-bottom: 20px;
}
.step-line-completed {
background-color: #10B981;
}
/* 学生摘要 */
.student-summary {
display: flex;
flex-direction: column;
padding: 16px;
background-color: #FFFFFF;
border-radius: 8px;
margin-bottom: 16px;
}
.summary-title {
font-size: 14px;
font-weight: 600;
color: #333333;
margin-bottom: 12px;
}
.student-card {
display: flex;
flex-direction: row;
align-items: center;
padding: 8px;
background-color: #F8F9FA;
border-radius: 6px;
margin-bottom: 8px;
}
.student-avatar-small {
width: 32px;
height: 32px;
border-radius: 16px;
margin-right: 8px;
background-color: #E5E5E5;
}
.student-name-small {
font-size: 14px;
color: #333333;
}
/* 表单区域 */
.form-section {
display: flex;
flex-direction: column;
padding: 16px;
background-color: #FFFFFF;
border-radius: 8px;
margin-bottom: 16px;
}
.form-title {
font-size: 16px;
font-weight: 600;
color: #333333;
margin-bottom: 20px;
}
.form-group {
display: flex;
flex-direction: column;
margin-bottom: 16px;
}
.form-label {
font-size: 14px;
color: #333333;
margin-bottom: 8px;
font-weight: 500;
}
.form-input {
height: 44px;
padding: 0 16px;
background-color: #F8F9FA;
border-radius: 8px;
border: 1px solid #E5E5E5;
font-size: 14px;
color: #333333;
}
.relationship-picker {
height: 44px;
background-color: #F8F9FA;
border-radius: 8px;
border: 1px solid #E5E5E5;
}
.relationship-picker-content {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 44px;
padding: 0 16px;
}
.summary-title-row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.student-count-badge {
background-color: #6C63FF;
color: #FFFFFF;
}
.relationship-value {
font-size: 14px;
color: #333333;
}
.relationship-placeholder {
font-size: 14px;
color: #999999;
}
.relationship-picker-arrow {
font-size: 16px;
color: #999999;
}
.input-wrapper {
display: flex;
flex-direction: row;
align-items: center;
height: 44px;
background-color: #F8F9FA;
border-radius: 8px;
border: 1px solid #E5E5E5;
padding: 0 16px;
}
.form-input-inner {
flex: 1;
font-size: 14px;
color: #333333;
}
/* 底部按钮 */
.footer-button-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 16px 16px calc(16px + var(--uni-safe-area-inset-bottom));
background-color: #F5F5F5;
}
.footer-button {
height: 48px;
background-color: #6C63FF;
border-radius: 24px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.footer-button-disabled {
background-color: #E5E5E5;
}
.footer-button-text {
font-size: 16px;
color: #FFFFFF;
font-weight: 500;
}
.footer-button-text-disabled {
color: #999999;
}
</style>