2025-10-11 21:11:16 +08:00

596 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>