547 lines
10 KiB
Plaintext
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>
|