智慧教务系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

474 lines
15 KiB

<!--学生信息编辑弹窗组件-->
<template>
<view>
<uni-popup ref="popup" type="center">
<view class="popup-container student-edit-popup">
<view class="popup-header">
<view class="popup-title">{{ isEditing ? '编辑学生信息' : '添加学生信息' }}</view>
<view class="popup-close" @click="close"></view>
</view>
<scroll-view class="student-form-container" scroll-y="true">
<view class="form-section">
<!-- 基本信息 -->
<view class="form-group">
<view class="form-group-title">基本信息</view>
<view class="form-item">
<view class="form-label required">姓名</view>
<view class="form-input">
<input type="text" v-model="studentData.name" placeholder="请输入学生姓名" maxlength="20" />
</view>
</view>
<view class="form-item">
<view class="form-label required">性别</view>
<view class="form-input">
<picker :range="genderOptions" range-key="label" @change="onGenderChange">
<view class="picker-display">
{{ $util.formatGender(studentData.gender) || '请选择性别' }}
<text class="picker-arrow">›</text>
</view>
</picker>
</view>
</view>
<view class="form-item">
<view class="form-label required">出生日期</view>
<view class="form-input">
<picker mode="date" :value="studentData.birthday" @change="onBirthdayChange">
<view class="picker-display">
{{ studentData.birthday || '请选择出生日期' }}
<text class="picker-arrow">›</text>
</view>
</picker>
</view>
</view>
<view class="form-item">
<view class="form-label">联系电话</view>
<view class="form-input">
<input type="number" v-model="studentData.contact_phone" placeholder="请输入联系电话" maxlength="20" />
</view>
</view>
<view class="form-item">
<view class="form-label">紧急联系人</view>
<view class="form-input">
<input type="text" v-model="studentData.emergency_contact" placeholder="请输入紧急联系人" maxlength="255" />
</view>
</view>
</view>
<!-- 学员信息 -->
<view class="form-group">
<view class="form-group-title">学员信息</view>
<view class="form-item">
<view class="form-label">学员标签</view>
<view class="form-input">
<view class="tag-selector" @click="showTagSelector">
<view class="selected-tags" v-if="selectedTagNames.length > 0">
<view class="tag-item" v-for="tagName in selectedTagNames" :key="tagName">
{{ tagName }}
</view>
</view>
<view class="placeholder" v-else>请选择学员标签</view>
<text class="picker-arrow"></text>
</view>
</view>
</view>
<!-- <view class="form-item">-->
<!-- <view class="form-label">体验课次数</view>-->
<!-- <view class="form-input">-->
<!-- <input type="number" v-model="studentData.trial_class_count" placeholder="体验课次数" />-->
<!-- </view>-->
<!-- </view>-->
<view class="form-item">
<view class="form-label">学员状态</view>
<view class="form-input">
<picker :range="statusOptions" range-key="label" @change="onStatusChange">
<view class="picker-display">
{{ studentData.status === 1 ? '有效' : '无效' }}
<text class="picker-arrow">›</text>
</view>
</picker>
</view>
</view>
</view>
<!-- 其他信息 -->
<view class="form-group">
<view class="form-group-title">其他信息</view>
<view class="form-item">
<view class="form-label">备注</view>
<view class="form-textarea">
<textarea v-model="studentData.note" placeholder="请输入备注信息" maxlength="200"></textarea>
</view>
</view>
</view>
</view>
</scroll-view>
<view class="popup-footer">
<view class="popup-btn cancel-btn" @click="close">取消</view>
<view class="popup-btn confirm-btn" @click="confirm">保存</view>
</view>
</view>
</uni-popup>
<!-- 标签选择器弹窗 -->
<uni-popup ref="tagPopup" type="bottom">
<view class="tag-popup">
<view class="tag-popup-header">
<view class="tag-popup-title">选择学员标签</view>
<view class="tag-popup-close" @click="closeTagSelector">✕</view>
</view>
<view class="tag-list">
<view
class="tag-option"
v-for="tag in studentTagOptions"
:key="tag.label_id"
:class="{ 'selected': isTagSelected(tag.label_id) }"
@click="toggleTag(tag.label_id)"
>
<view class="tag-checkbox">
<text v-if="isTagSelected(tag.label_id)">✓</text>
</view>
<view class="tag-name">{{ tag.label_name }}</view>
</view>
</view>
<view class="tag-popup-footer">
<view class="tag-popup-btn cancel" @click="closeTagSelector">取消</view>
<view class="tag-popup-btn confirm" @click="confirmTagSelection">确定</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js'
export default {
name: 'StudentEditPopup',
props: {
// 客户资源ID,用于关联学生
resourceId: {
type: [String, Number],
default: 0
}
},
data() {
return {
isEditing: false, // 是否为编辑模式
// 学生数据
studentData: {
id: null,
name: '',
gender: 0, // 性别: 0未知, 1男, 2女
age: 0.00, // 年龄,支持小数表示,例如3.11表示3岁11个月
birthday: null,
user_id: 0, // 资源ID
campus_id: null,
class_id: null,
note: '', // 备注信息
status: 1, // 学员状态: 0无效, 1有效
emergency_contact: '', // 紧急联系人
contact_phone: '', // 联系人电话
member_label: '', // 会员标签
consultant_id: null,
coach_id: null,
trial_class_count: 2, // 体验课次数|默认2(新增学员赠送)
actionsExpanded: false // 操作面板展开状态
},
// 选项数据
genderOptions: [
{ label: '未知', value: 0 },
{ label: '男', value: 1 },
{ label: '女', value: 2 }
],
statusOptions: [
{ label: '无效', value: 0 },
{ label: '有效', value: 1 }
],
// 学员标签相关数据
studentTagOptions: [], // 所有可选的学员标签
selectedTagIds: [], // 当前选中的标签ID数组
selectedTagNames: [] // 当前选中的标签名称数组
}
},
mounted() {
this.loadStudentTagOptions()
},
watch: {
// 监听标签选项加载完成,解析已有标签数据
studentTagOptions: {
handler(newOptions) {
if (newOptions.length > 0 && this.studentData.member_label) {
this.parseExistingTags()
}
},
immediate: true
}
},
methods: {
// 打开弹窗 - 新增模式
openAdd() {
this.isEditing = false
this.resetStudentData()
this.studentData.user_id = this.resourceId
this.$refs.popup.open()
},
// 打开弹窗 - 编辑模式
openEdit(student) {
this.isEditing = true
this.studentData = {
id: student.id,
name: student.name,
gender: student.gender,
age: student.age,
birthday: student.birthday,
user_id: student.user_id,
campus_id: student.campus_id,
class_id: student.class_id,
note: student.note,
status: student.status,
emergency_contact: student.emergency_contact,
contact_phone: student.contact_phone,
member_label: student.member_label,
consultant_id: student.consultant_id,
coach_id: student.coach_id,
trial_class_count: student.trial_class_count,
actionsExpanded: student.actionsExpanded || false
}
// 解析已有的标签数据
this.parseExistingTags()
this.$refs.popup.open()
},
// 关闭弹窗
close() {
this.$refs.popup.close()
this.resetStudentData()
this.$emit('close')
},
// 重置学生数据
resetStudentData() {
this.studentData = {
id: null,
name: '',
gender: 0,
age: 0.00,
birthday: null,
user_id: this.resourceId || 0,
campus_id: null,
class_id: null,
note: '',
status: 1,
emergency_contact: '',
contact_phone: '',
member_label: '',
consultant_id: null,
coach_id: null,
trial_class_count: 2,
actionsExpanded: false
}
// 重置标签选择状态
this.selectedTagIds = []
this.selectedTagNames = []
},
// 性别选择器变化事件
onGenderChange(e) {
const selectedGender = this.genderOptions[e.detail.value]
this.studentData.gender = selectedGender.value
},
// 出生日期选择器变化事件
onBirthdayChange(e) {
this.studentData.birthday = e.detail.value
// 计算年龄(精确到月)
if (this.studentData.birthday) {
const today = new Date()
const birthday = new Date(this.studentData.birthday)
let years = today.getFullYear() - birthday.getFullYear()
let months = today.getMonth() - birthday.getMonth()
if (today.getDate() < birthday.getDate()) {
months--
}
if (months < 0) {
years--
months += 12
}
// 转换为小数表示:例如3岁11个月 = 3.11
const ageDecimal = years + (months / 100)
this.studentData.age = ageDecimal > 0 ? parseFloat(ageDecimal.toFixed(2)) : 0
}
},
// 学员状态选择器变化事件
onStatusChange(e) {
this.studentData.status = this.statusOptions[e.detail.value].value
},
// 确认保存
confirm() {
// 表单验证
if (!this.studentData.name.trim()) {
uni.showToast({
title: '请输入学生姓名',
icon: 'none'
})
return
}
if (this.studentData.gender === 0) {
uni.showToast({
title: '请选择性别',
icon: 'none'
})
return
}
if (!this.studentData.birthday) {
uni.showToast({
title: '请选择出生日期',
icon: 'none'
})
return
}
// 构建保存参数
const params = {
name: this.studentData.name,
gender: this.studentData.gender,
age: this.studentData.age,
birthday: this.studentData.birthday,
user_id: this.resourceId, // 关联到当前客户资源ID
campus_id: this.studentData.campus_id,
class_id: this.studentData.class_id,
note: this.studentData.note,
status: this.studentData.status,
emergency_contact: this.studentData.emergency_contact,
contact_phone: this.studentData.contact_phone,
member_label: this.studentData.member_label,
consultant_id: this.studentData.consultant_id,
coach_id: this.studentData.coach_id,
trial_class_count: this.studentData.trial_class_count
}
if (this.isEditing) {
params.id = this.studentData.id
}
// 触发确认事件,将数据传递给父组件处理
this.$emit('confirm', {
isEditing: this.isEditing,
studentData: params
})
},
// 加载学员标签选项
async loadStudentTagOptions() {
try {
const res = await apiRoute.getAllStudentLabels()
if (res.code === 1) {
this.studentTagOptions = res.data || []
} else {
console.warn('获取学员标签失败:', res.msg)
// 使用模拟数据作为后备
this.studentTagOptions = [
{ label_id: 1, label_name: '标签一' },
{ label_id: 2, label_name: '标签二' },
{ label_id: 3, label_name: '意向用户' }
]
}
} catch (error) {
console.error('加载学员标签失败:', error)
// 使用模拟数据作为后备
this.studentTagOptions = [
{ label_id: 1, label_name: '优秀学员' },
{ label_id: 2, label_name: '积极参与' },
{ label_id: 3, label_name: '需要关注' }
]
}
},
// 显示标签选择器
showTagSelector() {
this.$refs.tagPopup.open()
},
// 关闭标签选择器
closeTagSelector() {
this.$refs.tagPopup.close()
},
// 判断标签是否被选中
isTagSelected(tagId) {
return this.selectedTagIds.includes(tagId)
},
// 切换标签选择状态
toggleTag(tagId) {
const index = this.selectedTagIds.indexOf(tagId)
if (index > -1) {
// 如果已选中,则取消选择
this.selectedTagIds.splice(index, 1)
} else {
// 如果未选中,则添加选择
this.selectedTagIds.push(tagId)
}
},
// 确认标签选择
confirmTagSelection() {
// 更新选中的标签名称数组
this.selectedTagNames = this.selectedTagIds.map(tagId => {
const tag = this.studentTagOptions.find(t => t.label_id === tagId)
return tag ? tag.label_name : ''
}).filter(name => name)
// 更新 member_label 字段为逗号分割的ID字符串
this.studentData.member_label = this.selectedTagIds.join(',')
this.closeTagSelector()
},
// 解析已有的标签数据
parseExistingTags() {
if (this.studentData.member_label) {
// 将逗号分割的字符串转换为ID数组
this.selectedTagIds = this.studentData.member_label.split(',')
.map(id => parseInt(id.trim()))
.filter(id => !isNaN(id))
// 更新选中的标签名称数组
this.selectedTagNames = this.selectedTagIds.map(tagId => {
const tag = this.studentTagOptions.find(t => t.label_id === tagId)
return tag ? tag.label_name : ''
}).filter(name => name)
} else {
this.selectedTagIds = []
this.selectedTagNames = []
}
}
}
}
</script>
<style lang="less" scoped>
@import './student-edit-popup.less';
</style>