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

547 lines
10 KiB
Plaintext

<template>
<view class="page-container">
<scroll-view class="content-scroll" direction="vertical">
<!-- 步骤进度 -->
<view class="step-indicator">
<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 class="step-line"></view>
<view class="step-item">
<view class="step-dot"></view>
<text class="step-text">注册完成</text>
</view>
</view>
<!-- 提示信息 -->
<view class="info-banner">
<text class="info-text">先扫描孩子的学生二维码,或输入学生账号完成绑定。再设置家长账号,最多可绑定 3 个孩子。</text>
</view>
<!-- 切换按钮 -->
<view class="toggle-buttons">
<view class="toggle-btn toggle-btn-inactive toggle-btn-first" @tap="handleTabChange('scan')">
<text class="toggle-btn-text toggle-btn-text-inactive">扫码绑定</text>
</view>
<view class="toggle-btn toggle-btn-active" @tap="handleTabChange('input')">
<text class="toggle-btn-text toggle-btn-text-active">输入学生账号</text>
</view>
</view>
<!-- 表单区域 -->
<view class="form-section">
<view class="form-header">
<text class="form-title">填写要绑定的学生信息</text>
<text class="form-description">学生账号与姓名需与学生信息保持一致,校验通过后即可加入下方已绑定列表。</text>
</view>
<view class="form-group">
<text class="form-label">学生账号</text>
<input
class="form-input"
type="text"
v-model="studentForm.account"
placeholder="输入学生的账号或账号"
/>
</view>
<view class="form-group">
<text class="form-label">学生姓名</text>
<input
class="form-input"
type="text"
v-model="studentForm.name"
placeholder="需与学生本人姓名一致"
/>
</view>
<view
class="add-student-btn"
@tap="handleAddStudent"
>
<text class="add-student-btn-text">确认添加学生</text>
</view>
</view>
<!-- 已绑定学生列表 -->
<view class="student-list" v-if="studentList.length > 0">
<view class="list-title-row">
<text class="list-title">已绑定学生</text>
<uni-badge-view :text="studentList.length + '/3'" badge-class="student-count-badge"></uni-badge-view>
</view>
<view class="student-item" v-for="(student, index) in studentList" :key="student.id">
<image class="student-avatar" :src="student.avatar || '/static/avatar-default.png'" mode="aspectFill"></image>
<view class="student-info">
<text class="student-name">{{ student.name }}</text>
<text class="student-detail">{{ student.grade }} {{ student.class }} | {{ student.account }}</text>
</view>
<button class="remove-btn" @tap="handleRemoveStudent(index)">移除</button>
</view>
</view>
</scroll-view>
<!-- 底部按钮 -->
<view class="footer-button-container">
<view
class="footer-button"
@tap="handleNext"
>
<text class="footer-button-text">下一步</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { apiVerifyStudentAccount } from '../../api/student'
import { registrationStudents } from '../../store/registration'
type StudentForm = {
account: string
name: string
}
const studentForm = reactive<StudentForm>({
account: '',
name: ''
})
const studentList = registrationStudents
const MAX_STUDENTS = 3
const canAdd = computed((): boolean => {
return studentForm.account.trim().length > 0 && studentForm.name.trim().length > 0
})
const canNext = computed((): boolean => {
return studentList.value.length > 0
})
/**
* 切换标签页
*/
function handleTabChange(tab: string) {
if (tab === 'scan') {
uni.redirectTo({
url: '/pages/register/scan-student'
})
}
}
/**
* 添加学生
*/
async function handleAddStudent() {
if (!canAdd.value) return
const account = studentForm.account.trim()
const name = studentForm.name.trim()
// 检查是否达到上限
if (studentList.value.length >= MAX_STUDENTS) {
uni.showToast({
title: `最多只能绑定${MAX_STUDENTS}个孩子`,
icon: 'none'
})
return
}
if (studentList.value.some(student => student.account === account)) {
uni.showToast({
title: '该学生已添加',
icon: 'none'
})
return
}
// 验证姓名格式
const nameReg = /^[\u4e00-\u9fa5]{2,10}$/
if (!nameReg.test(name)) {
uni.showToast({
title: '请输入正确的学生姓名(2-10个汉字)',
icon: 'none'
})
return
}
uni.showLoading({ title: '验证中...' })
const res = await apiVerifyStudentAccount({
account,
name
})
uni.hideLoading()
if (res && res.code === 0 && res.data) {
// 检查是否重复绑定
const exists = studentList.value.find(s => s.account === res.data!.account)
if (exists) {
uni.showToast({
title: '该学生已添加',
icon: 'none'
})
return
}
// 添加到列表
studentList.value.push(res.data)
// 清空表单
studentForm.account = ''
studentForm.name = ''
uni.showToast({
title: '添加成功',
icon: 'success'
})
} else {
uni.showToast({
title: res?.message || '学生信息验证失败,请检查账号和姓名是否正确',
icon: 'none'
})
}
}
/**
* 移除学生
*/
function handleRemoveStudent(index: number) {
uni.showModal({
title: '提示',
content: '确定要移除该学生吗?',
success: (res) => {
if (res.confirm) {
studentList.value.splice(index, 1)
uni.showToast({
title: '已移除',
icon: 'success'
})
}
}
})
}
/**
* 下一步
*/
function handleNext() {
if (!canNext.value) {
uni.showToast({
title: '请至少绑定一个学生',
icon: 'none'
})
return
}
uni.navigateTo({
url: '/pages/register/parent-account'
})
}
</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));
}
/* 步骤指示器 - 复用扫码页样式 */
.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-active {
background-color: #6C63FF;
width: 12px;
height: 12px;
border-radius: 6px;
}
.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;
}
/* 提示信息 */
.info-banner {
display: flex;
flex-direction: row;
align-items: flex-start;
padding: 12px;
background-color: #EEF2FF;
border-radius: 8px;
margin-bottom: 16px;
}
.info-icon {
font-size: 16px;
margin-right: 8px;
flex-shrink: 0;
}
.info-text {
flex: 1;
font-size: 13px;
color: #4F46E5;
line-height: 1.6;
}
/* 切换按钮 */
.toggle-buttons {
display: flex;
flex-direction: row;
margin-bottom: 24px;
}
.toggle-btn-first {
margin-right: 12px;
}
.toggle-btn {
flex: 1;
height: 40px;
border-radius: 20px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.toggle-btn-active {
background-color: #6C63FF;
}
.toggle-btn-inactive {
background-color: #FFFFFF;
border: 1px solid #E5E5E5;
}
.toggle-btn-text {
font-size: 14px;
}
.toggle-btn-text-active {
color: #FFFFFF;
}
.toggle-btn-text-inactive {
color: #666666;
}
/* 表单区域 */
.form-section {
display: flex;
flex-direction: column;
padding: 16px;
background-color: #FFFFFF;
border-radius: 8px;
margin-bottom: 16px;
}
.form-header {
display: flex;
flex-direction: column;
margin-bottom: 20px;
}
.form-title {
font-size: 16px;
font-weight: 600;
color: #333333;
margin-bottom: 8px;
}
.form-description {
font-size: 13px;
color: #666666;
line-height: 1.6;
}
.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;
}
.add-student-btn {
height: 40px;
background-color: #EEF2FF;
border-radius: 8px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-top: 8px;
}
.add-student-btn-text {
font-size: 14px;
color: #6C63FF;
}
/* 学生列表 */
.student-list {
display: flex;
flex-direction: column;
margin-bottom: 16px;
}
.list-title {
font-size: 14px;
font-weight: 600;
color: #333333;
margin-bottom: 12px;
}
.list-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;
}
.student-item {
display: flex;
flex-direction: row;
align-items: center;
padding: 12px;
background-color: #FFFFFF;
border-radius: 8px;
margin-bottom: 8px;
}
.student-avatar {
width: 48px;
height: 48px;
border-radius: 24px;
margin-right: 12px;
background-color: #F5F5F5;
}
.student-info {
display: flex;
flex-direction: column;
flex: 1;
}
.student-name {
font-size: 16px;
font-weight: 600;
color: #333333;
margin-bottom: 4px;
}
.student-detail {
font-size: 12px;
color: #999999;
}
.remove-btn {
width: 56px;
height: 32px;
padding: 0;
font-size: 12px;
color: #EF4444;
background-color: #FEF2F2;
}
/* 底部按钮 */
.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-text {
font-size: 16px;
color: #FFFFFF;
font-weight: 500;
}
</style>