596 lines
13 KiB
Vue
Raw Normal View History

2025-10-11 21:11:16 +08:00
<template>
<view class="add-member-page">
<!-- 页面标题 -->
<view class="page-header">
<view class="header-content">
<view class="header-title-wrapper">
<text class="header-icon">👥</text>
<view class="header-text">
<text class="header-title">新增课程成员</text>
<text class="header-subtitle">{{ courseName }}</text>
</view>
</view>
</view>
</view>
<!-- 表单内容 -->
<view class="form-container">
<view class="form-card">
<view class="form-section">
<view class="section-title">
<text class="title-icon">📋</text>
<text class="title-text">成员信息</text>
</view>
<view class="form-item">
<view class="form-label">
<text class="required-asterisk">*</text>
<text class="label-text">分组名称</text>
</view>
<uni-easyinput
type="text"
v-model="formData.fzmc"
placeholder="请输入分组名称"
:inputBorder="true"
class="input-field"
:maxlength="20"
></uni-easyinput>
</view>
<view class="form-item">
<text class="form-label">课程成员</text>
<!-- 新增模式显示教师选择器 -->
<template v-if="!isReassign">
<BasicJsPicker
v-if="!loading"
:defaultValue="selectedTeachers"
:multiple="true"
@change="onChangeTeacher"
placeholder="请选择课程成员"
ref="teacherPickerRef"
/>
</template>
<!-- 已选教师列表 -->
<view class="teacher-list" v-if="selectedTeacherList.length > 0">
<view class="teacher-grid">
<view
v-for="teacher in selectedTeacherList"
:key="teacher.value"
class="teacher-item bg-white r-md p-12"
:class="{ 'readonly-item': isReassign }"
>
<view class="flex-row items-center">
<view class="avatar-container mr-8">
<text class="teacher-avatar">{{ teacher.label?.charAt(0) || '?' }}</text>
</view>
<view class="flex-1 overflow-hidden">
<view class="teacher-name">
<text class="font-14 cor-333">{{ teacher.label }}</text>
</view>
</view>
<!-- 重新分组模式下不显示删除按钮 -->
<BasicIcon
v-if="!isReassign"
type="clear"
size="24"
color="#ff4d4f"
@click="removeTeacher(teacher.value)"
class="remove-btn"
/>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 底部操作按钮 -->
<view class="bottom-actions">
<view class="action-button cancel-button" @click="goBack">
<text class="action-icon"></text>
<text class="action-text">取消</text>
</view>
<view class="action-button confirm-button" @click="handleSubmit">
<text class="action-icon"></text>
<text class="action-text">确认添加</text>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, onMounted } from "vue";
import { onLoad as onPageLoad } from "@dcloudio/uni-app";
import { kccySaveApi } from "@/api/base/kccyApi";
import BasicJsPicker from '@/components/BasicJsPicker/Picker.vue';
import BasicIcon from '@/components/BasicIcon/Icon.vue';
// 定义接口
interface TeacherItem {
id: string;
jsxm: string;
jsId: string;
}
// 响应式数据
const courseId = ref('');
const courseName = ref('');
const selectedTeachers = ref<string[]>([]);
const selectedTeacherList = ref<any[]>([]);
const loading = ref(false);
const teacherPickerRef = ref<any>(null);
const isReassign = ref(false); // 是否为重新分组模式
const memberId = ref(''); // 成员ID用于重新分组
const formData = ref({
fzmc: '',
jsIds: [] as string[]
});
// 页面加载
onPageLoad((options: any) => {
console.log('新增成员页面接收到的参数:', options);
if (options.kcjbId) {
courseId.value = options.kcjbId;
}
if (options.kcmc) {
courseName.value = decodeURIComponent(options.kcmc);
}
// 判断是否为重新分组模式
if (options.isReassign === 'true') {
isReassign.value = true;
memberId.value = options.memberId || '';
// 设置分组名称
if (options.fzmc) {
formData.value.fzmc = decodeURIComponent(options.fzmc);
}
// 设置成员信息(只读)
if (options.jsId && options.jsxm) {
const teacher = {
value: options.jsId,
label: decodeURIComponent(options.jsxm)
};
selectedTeacherList.value = [teacher];
selectedTeachers.value = [options.jsId];
}
}
});
// 教师选择变化
const onChangeTeacher = (teachers: any[]) => {
selectedTeacherList.value = teachers.map(teacher => ({
...teacher
}));
selectedTeachers.value = teachers.map(teacher => teacher.value);
};
// 移除已选教师
const removeTeacher = (teacherId: string) => {
selectedTeacherList.value = selectedTeacherList.value.filter((teacher: any) => teacher.value !== teacherId);
selectedTeachers.value = selectedTeacherList.value.map((teacher: any) => teacher.value);
if (teacherPickerRef.value) {
teacherPickerRef.value.setValue(selectedTeachers.value);
}
};
// 表单验证
const validateForm = () => {
if (!formData.value.fzmc.trim()) {
uni.showToast({
title: '请输入分组名称',
icon: 'none'
});
return false;
}
if (selectedTeachers.value.length === 0) {
uni.showToast({
title: '请选择至少一位成员',
icon: 'none'
});
return false;
}
return true;
};
// 提交表单
const handleSubmit = async () => {
if (!validateForm()) {
return;
}
try {
loading.value = true;
if (isReassign.value) {
// 重新分组模式:只更新分组名称
const submitData = {
id: memberId.value,
kcjbId: courseId.value,
fzmc: formData.value.fzmc.trim(),
jsId: [selectedTeacherList.value[0].value],
jsxm: [selectedTeacherList.value[0].label]
};
console.log('重新分组提交数据:', submitData);
// 调用API保存
await kccySaveApi(submitData);
uni.showToast({
title: '重新分组成功',
icon: 'success'
});
} else {
// 新增模式:批量添加成员
const jsIds = selectedTeacherList.value.map(teacher => teacher.value);
const jsxms = selectedTeacherList.value.map(teacher => teacher.label);
const submitData = {
kcjbId: courseId.value,
fzmc: formData.value.fzmc.trim(),
jsId: jsIds, // 教师ID数组
jsxm: jsxms // 教师姓名数组
};
console.log('新增成员提交数据:', submitData);
console.log('教师ID数组:', jsIds);
console.log('教师姓名数组:', jsxms);
// 调用API保存
await kccySaveApi(submitData);
uni.showToast({
title: '添加成功',
icon: 'success'
});
}
// 触发刷新成员列表事件
uni.$emit('refreshMemberList');
// 延迟返回上一页
setTimeout(() => {
uni.navigateBack();
}, 1500);
} catch (error) {
console.error('操作失败:', error);
uni.showToast({
title: isReassign.value ? '重新分组失败' : '添加失败,请重试',
icon: 'error'
});
} finally {
loading.value = false;
}
};
// 返回上一页
const goBack = () => {
uni.navigateBack();
};
</script>
<style scoped>
.add-member-page {
min-height: 100vh;
background: linear-gradient(180deg, #f0f5ff 0%, #f5f7fa 100%);
padding-bottom: 80px;
}
/* 页面标题 */
.page-header {
background: linear-gradient(135deg, #4e73df 0%, #2e59d9 100%);
padding: 20px 16px;
box-shadow: 0 4px 12px rgba(78, 115, 223, 0.3);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-title-wrapper {
display: flex;
align-items: center;
gap: 12px;
flex: 1;
}
.header-icon {
font-size: 32px;
line-height: 1;
}
.header-text {
display: flex;
flex-direction: column;
gap: 4px;
}
.header-title {
font-size: 20px;
font-weight: bold;
color: #ffffff;
line-height: 1.2;
}
.header-subtitle {
font-size: 13px;
color: rgba(255, 255, 255, 0.9);
letter-spacing: 1px;
}
/* 表单容器 */
.form-container {
padding: 16px;
}
.form-card {
background: linear-gradient(135deg, #ffffff 0%, #f7f9fc 100%);
border-radius: 16px;
box-shadow: 0 4px 16px rgba(78, 115, 223, 0.1);
border: 1px solid rgba(78, 115, 223, 0.15);
overflow: hidden;
}
.form-section {
padding: 20px;
}
.section-title {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(78, 115, 223, 0.2);
}
.title-icon {
font-size: 18px;
line-height: 1;
}
.title-text {
font-size: 16px;
font-weight: 600;
color: #2c3e50;
}
/* 表单项 */
.form-item {
margin-bottom: 20px;
}
.form-label {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.required-asterisk {
color: #ff4757;
font-size: 16px;
font-weight: bold;
margin-right: 4px;
}
.label-text {
font-size: 16px;
font-weight: 600;
color: #333;
}
.input-field {
font-size: 16px;
color: #333;
:deep(.uni-easyinput__content) {
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 12px;
min-height: 44px;
}
:deep(.uni-easyinput__content-input) {
color: #333;
font-size: 16px;
}
:deep(.uni-easyinput__placeholder-class) {
color: #999;
font-size: 16px;
}
:deep(.uni-easyinput__content):focus-within {
border-color: #4e73df;
box-shadow: 0 0 0 2px rgba(78, 115, 223, 0.1);
}
}
/* 教师列表 */
.teacher-list {
margin-top: 16px;
}
.teacher-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 12px;
}
.teacher-item {
position: relative;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(78, 115, 223, 0.1);
transition: all 0.2s ease;
border: 1px solid rgba(78, 115, 223, 0.15);
}
.teacher-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(78, 115, 223, 0.15);
}
.teacher-item.readonly-item {
background: #f5f7fa;
cursor: not-allowed;
opacity: 0.85;
}
.teacher-item.readonly-item:hover {
transform: none;
box-shadow: 0 2px 8px rgba(78, 115, 223, 0.1);
}
.remove-btn {
position: absolute;
right: 6px;
top: 6px;
cursor: pointer;
}
.avatar-container {
width: 32px;
height: 32px;
border-radius: 50%;
padding: 2px;
background: linear-gradient(135deg, #4e73df 0%, #2e59d9 100%);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 6px rgba(78, 115, 223, 0.3);
margin-right: 8px;
}
.teacher-avatar {
width: 28px;
height: 28px;
border-radius: 50%;
background: #ffffff;
color: #4e73df;
font-size: 12px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}
.flex-row {
display: flex;
flex-direction: row;
}
.items-center {
align-items: center;
}
.flex-1 {
flex: 1;
}
.overflow-hidden {
overflow: hidden;
}
.bg-white {
background-color: #ffffff;
}
.r-md {
border-radius: 12px;
}
.p-12 {
padding: 12px;
}
.font-14 {
font-size: 14px;
}
.cor-333 {
color: #333333;
}
.mr-8 {
margin-right: 8px;
}
/* 底部操作按钮 */
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
border-top: 1px solid #e8ecf1;
padding: 12px 16px;
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08);
z-index: 1000;
display: flex;
gap: 12px;
}
.action-button {
flex: 1;
height: 46px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.2s ease;
font-weight: 600;
}
.cancel-button {
background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%);
color: #6b7280;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.cancel-button:active {
transform: translateY(1px);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
}
.confirm-button {
background: linear-gradient(135deg, #4e73df 0%, #2e59d9 100%);
color: #fff;
box-shadow: 0 3px 12px rgba(78, 115, 223, 0.35);
}
.confirm-button:active {
transform: translateY(1px);
box-shadow: 0 2px 8px rgba(78, 115, 223, 0.4);
}
.action-icon {
font-size: 18px;
font-weight: bold;
line-height: 1;
}
.action-text {
font-size: 15px;
font-weight: 600;
letter-spacing: 0.5px;
}
</style>