596 lines
13 KiB
Vue
596 lines
13 KiB
Vue
<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>
|