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.
475 lines
16 KiB
475 lines
16 KiB
<!--学生信息编辑弹窗组件-->
|
|
<template>
|
|
<view class="student-edit-popup-wrapper">
|
|
<!-- 主编辑弹窗 -->
|
|
<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>
|