智慧教务系统
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.
 
 
 
 
 
 

1220 lines
32 KiB

<!--课程信息内容组件-->
<template>
<view class="course-info-card">
<!-- 课程信息列表 -->
<view class="course-list" v-if="courseList && courseList.length > 0">
<view
class="course-item"
v-for="(course, index) in courseList"
:key="course.id || index"
@tap="viewCourseDetail(course)"
>
<view class="course-header">
<view class="course-title">{{ course.course_name || '未知课程' }}</view>
<view class="course-actions">
<view :class="['course-status',getStatusClass(course.status)]">
{{ getStatusText(course.status) }}
</view>
<view class="edit-btn" @tap.stop="editCourse(course)">
<text class="edit-icon">✏️</text>
</view>
</view>
</view>
<!-- 课程进度 -->
<view class="course-progress" v-if="course.total_count">
<view class="progress-bar">
<view
class="progress-fill"
:style="{ width: getProgressPercent(course) + '%' }"
></view>
</view>
<view class="progress-text">
{{ course.used_count || 0 }}/{{ course.total_count }}节
</view>
</view>
<view class="course-details">
<!-- 基本信息 -->
<view class="detail-section">
<view class="detail-item" v-if="course.course_type">
<text class="detail-label">课程类型:</text>
<text class="detail-value">{{ course.course_type }}</text>
</view>
<view class="detail-item" v-if="course.teacher_name || course.main_coach_name">
<text class="detail-label">授课教练:</text>
<text class="detail-value">{{ course.main_coach_name || course.teacher_name }}</text>
</view>
<view class="detail-item">
<text class="detail-label">剩余课时:</text>
<text class="detail-value highlight">{{ getRemainingCount(course) }}节</text>
</view>
</view>
<!-- 时间信息 -->
<view class="detail-section" v-if="course.start_date || course.end_date || course.expiry_date">
<view class="detail-item" v-if="course.start_date">
<text class="detail-label">开始时间:</text>
<text class="detail-value">{{ formatDate(course.start_date) }}</text>
</view>
<view class="detail-item" v-if="course.end_date || course.expiry_date">
<text class="detail-label">结束时间:</text>
<text class="detail-value">{{ formatDate(course.end_date || course.expiry_date) }}</text>
</view>
</view>
<!-- 其他信息 -->
<view class="detail-section">
<view class="detail-item" v-if="course.course_price">
<text class="detail-label">课程价格:</text>
<text class="detail-value price">¥{{ course.course_price }}</text>
</view>
<view class="detail-item" v-if="course.create_time">
<text class="detail-label">创建时间:</text>
<text class="detail-value">{{ formatTime(course.create_time) }}</text>
</view>
</view>
<!-- 备注信息 -->
<view class="detail-section" v-if="course.remark">
<view class="remark-item">
<text class="detail-label">备注:</text>
<text class="detail-value remark">{{ course.remark }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else>
<view class="empty-icon">📖</view>
<view class="empty-text">暂无课程信息</view>
<view class="empty-tip">学生还未报名任何课程</view>
</view>
<!-- 编辑弹窗 -->
<view v-if="showEditModal" class="modal-overlay" @click="closeEditModal">
<view class="modal-content" @click.stop>
<view class="modal-header">
<view class="modal-title">编辑课程信息</view>
<view class="close-btn" @click="closeEditModal">×</view>
</view>
<view class="modal-body">
<view class="form-section">
<view class="section-title">人员配置</view>
<!-- 主教练选择 -->
<view class="form-item">
<text class="form-label">主教练:</text>
<picker
:value="selectedMainCoachIndex"
:range="coachList"
range-key="name"
@change="onMainCoachChange"
>
<view class="picker-input">
{{ editForm.main_coach_name || '请选择主教练' }}
<text class="picker-arrow">▼</text>
</view>
</picker>
</view>
<!-- 助教选择 -->
<view class="form-item">
<text class="form-label">助教:</text>
<picker
mode="multiSelector"
:value="selectedAssistantIndexes"
:range="[coachList]"
:range-key="['name']"
@change="onAssistantChange"
>
<view class="picker-input">
{{ editForm.assistant_names || '请选择助教' }}
<text class="picker-arrow">▼</text>
</view>
</picker>
</view>
<!-- 教务选择 -->
<view class="form-item">
<text class="form-label">教务:</text>
<picker
:value="selectedEducationIndex"
:range="educationList"
range-key="name"
@change="onEducationChange"
>
<view class="picker-input">
{{ editForm.education_name || '请选择教务' }}
<text class="picker-arrow">▼</text>
</view>
</picker>
</view>
</view>
<!-- 班级选择区域 -->
<view class="form-section" v-if="!hasClass">
<view class="section-title">班级配置</view>
<view class="form-item">
<text class="form-label">所属班级:</text>
<picker
:value="selectedClassIndex"
:range="classList"
range-key="class_name"
@change="onClassChange"
>
<view class="picker-input">
{{ editForm.class_name || '请选择班级' }}
<text class="picker-arrow">▼</text>
</view>
</picker>
</view>
</view>
<!-- 已有班级信息 -->
<view class="form-section" v-if="hasClass">
<view class="section-title">班级信息</view>
<view class="class-info">
<text class="class-name">当前班级:{{ currentClassInfo.class_name }}</text>
<text class="class-desc">如需更换班级,请联系管理员</text>
</view>
</view>
</view>
<view class="modal-footer">
<button class="btn btn-test" @tap="testFunction">测试弹窗</button>
<button class="btn btn-cancel" @tap="closeEditModal">取消</button>
<button class="btn btn-confirm" @tap="confirmEdit" :loading="saving">保存</button>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'CourseInfoCard',
props: {
// 课程信息列表数据
courseList: {
type: Array,
default: () => []
}
},
data() {
return {
// 编辑弹窗相关
showEditModal: false,
saving: false,
currentCourse: null,
// 表单数据
editForm: {
student_course_id: '',
main_coach_id: '',
main_coach_name: '',
assistant_ids: '',
assistant_names: '',
education_id: '',
education_name: '',
class_id: '',
class_name: ''
},
// 选择器索引
selectedMainCoachIndex: 0,
selectedAssistantIndexes: [0],
selectedEducationIndex: 0,
selectedClassIndex: 0,
// 列表数据
coachList: [],
educationList: [],
classList: [],
// 班级关联状态
hasClass: false,
currentClassInfo: {}
}
},
mounted() {
// 如果没有传入课程数据,使用测试数据
if (!this.courseList || this.courseList.length === 0) {
console.log('使用测试课程数据')
}
},
methods: {
// 查看课程详情
viewCourseDetail(course) {
this.$emit('view-detail', course)
},
// 编辑课程
async editCourse(course) {
console.log('编辑课程数据:', course)
// 检查必要的数据
if (!course.id && !course.student_course_id) {
uni.showToast({
title: '课程信息不完整,请重新加载',
icon: 'none'
})
return
}
if (!course.resource_id) {
uni.showToast({
title: '缺少学员信息,无法编辑',
icon: 'none'
})
return
}
this.currentCourse = course
// 初始化表单数据
this.editForm = {
student_course_id: course.student_course_id || course.id,
main_coach_id: course.main_coach_id || '',
main_coach_name: course.main_coach_name || course.teacher_name || '',
assistant_ids: course.assistant_ids || '',
assistant_names: this.formatAssistantNames(course.assistant_ids),
education_id: course.education_id || '',
education_name: course.education_name || '',
class_id: '',
class_name: ''
}
try {
// 显示加载提示
uni.showLoading({
title: '加载中...'
})
// 加载基础数据
await this.loadBaseData()
// 检查班级关联
await this.checkClassRelation(course.resource_id)
// 设置选择器索引
this.setPickerIndexes()
uni.hideLoading()
// 数据加载完成后再显示弹窗
this.showEditModal = true
} catch (error) {
uni.hideLoading()
console.error('加载编辑数据失败:', error)
uni.showToast({
title: '加载失败,请重试',
icon: 'none'
})
}
},
// 格式化助教名称显示
formatAssistantNames(assistantIds) {
if (!assistantIds) return ''
// 这里暂时返回空,等加载完教练列表后会重新设置
return ''
},
// 加载基础数据
async loadBaseData() {
const baseUrl = 'http://localhost:20080' // 配置基础URL
const token = uni.getStorageSync('token')
console.log('开始加载基础数据, token:', token)
try {
// 并行加载所有数据
const [coachRes, educationRes, classRes] = await Promise.all([
// 加载教练列表
uni.request({
url: baseUrl + '/api/course/coachList',
method: 'GET',
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
}
}),
// 加载教务列表
uni.request({
url: baseUrl + '/api/course/educationList',
method: 'GET',
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
}
}),
// 加载班级列表
uni.request({
url: baseUrl + '/api/class/jlGetClasses/list',
method: 'GET',
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
}
})
])
console.log('教练列表响应:', coachRes.data)
console.log('教务列表响应:', educationRes.data)
console.log('班级列表响应:', classRes.data)
// 处理教练列表
if (coachRes.data && coachRes.data.code === 1) {
this.coachList = coachRes.data.data || []
} else {
console.warn('教练列表加载失败:', coachRes.data)
// 使用模拟数据
this.coachList = [
{ id: 12, name: '测试信息1', phone: '13042409895' },
{ id: 15, name: '李教练', phone: '13800001001' },
{ id: 3, name: '王教练', phone: '13800138003' },
{ id: 4, name: '张教练', phone: '13800138004' }
]
}
// 处理教务列表
if (educationRes.data && educationRes.data.code === 1) {
this.educationList = educationRes.data.data || []
} else {
console.warn('教务列表加载失败:', educationRes.data)
// 使用模拟数据
this.educationList = [
{ id: 1, name: '王教务', phone: '13800138003' },
{ id: 2, name: '刘教务', phone: '13800138004' }
]
}
// 处理班级列表
if (classRes.data && classRes.data.code === 1) {
// 处理接口返回的班级数据格式
const classData = classRes.data.data?.classes || classRes.data.data || []
this.classList = classData
} else {
console.warn('班级列表加载失败:', classRes.data)
// 使用与数据库一致的模拟数据
this.classList = [
{ id: 1, class_name: '测试班级1', head_coach: 5, educational_id: 0 },
{ id: 2, class_name: '测试班级2', head_coach: 6, educational_id: 0 }
]
}
console.log('最终数据:', {
coachList: this.coachList,
educationList: this.educationList,
classList: this.classList
})
} catch (error) {
console.error('加载基础数据失败:', error)
// 接口失败时使用模拟数据,确保功能可用
this.coachList = [
{ id: 12, name: '测试信息1', phone: '13042409895' },
{ id: 15, name: '李教练', phone: '13800001001' },
{ id: 3, name: '王教练', phone: '13800138003' },
{ id: 4, name: '张教练', phone: '13800138004' }
]
this.educationList = [
{ id: 1, name: '王教务', phone: '13800138003' },
{ id: 2, name: '刘教务', phone: '13800138004' }
]
this.classList = [
{ id: 1, class_name: '测试班级1', head_coach: 5, educational_id: 0 },
{ id: 2, class_name: '测试班级2', head_coach: 6, educational_id: 0 }
]
uni.showToast({
title: '使用模拟数据进行测试',
icon: 'none',
duration: 2000
})
}
},
// 检查班级关联
async checkClassRelation(resourceId) {
try {
const baseUrl = 'http://localhost:20080'
const token = uni.getStorageSync('token')
const res = await uni.request({
url: baseUrl + `/api/course/checkClassRelation?resource_id=${resourceId}`,
method: 'GET',
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
}
})
console.log('班级关联检查响应:', res.data)
if (res.data && res.data.code === 1) {
this.hasClass = res.data.data.has_class
this.currentClassInfo = res.data.data.class_info || {}
} else {
// 模拟数据:假设学员没有班级关联
this.hasClass = false
this.currentClassInfo = {}
console.log('使用模拟班级关联数据')
}
} catch (error) {
console.error('检查班级关联失败:', error)
// 默认假设没有班级关联
this.hasClass = false
this.currentClassInfo = {}
}
},
// 设置选择器索引
setPickerIndexes() {
console.log('设置选择器索引,当前数据:', {
editForm: this.editForm,
coachList: this.coachList,
educationList: this.educationList
})
// 设置主教练索引
if (this.editForm.main_coach_id && this.coachList.length > 0) {
const coachIndex = this.coachList.findIndex(item => item.id == this.editForm.main_coach_id)
if (coachIndex >= 0) {
this.selectedMainCoachIndex = coachIndex
this.editForm.main_coach_name = this.coachList[coachIndex].name
console.log('主教练设置成功:', this.coachList[coachIndex].name)
} else {
console.log('未找到匹配的主教练ID:', this.editForm.main_coach_id)
}
}
// 设置助教索引(多选)
if (this.editForm.assistant_ids && this.coachList.length > 0) {
const assistantIds = this.editForm.assistant_ids.split(',').map(id => id.trim()).filter(id => id)
const indexes = []
const names = []
assistantIds.forEach(id => {
const index = this.coachList.findIndex(item => item.id == id)
if (index >= 0) {
indexes.push(index)
names.push(this.coachList[index].name)
}
})
if (indexes.length > 0) {
this.selectedAssistantIndexes = indexes
this.editForm.assistant_names = names.join(',')
console.log('助教设置成功:', names.join(','))
} else {
this.selectedAssistantIndexes = [0]
this.editForm.assistant_names = ''
}
} else {
this.selectedAssistantIndexes = [0]
this.editForm.assistant_names = ''
}
// 设置教务索引
if (this.editForm.education_id && this.educationList.length > 0) {
const educationIndex = this.educationList.findIndex(item => item.id == this.editForm.education_id)
if (educationIndex >= 0) {
this.selectedEducationIndex = educationIndex
this.editForm.education_name = this.educationList[educationIndex].name
console.log('教务设置成功:', this.educationList[educationIndex].name)
} else {
console.log('未找到匹配的教务ID:', this.editForm.education_id)
}
}
console.log('选择器索引设置完成:', {
selectedMainCoachIndex: this.selectedMainCoachIndex,
selectedAssistantIndexes: this.selectedAssistantIndexes,
selectedEducationIndex: this.selectedEducationIndex
})
},
// 主教练选择变更
onMainCoachChange(e) {
const index = e.detail.value
this.selectedMainCoachIndex = index
const selectedCoach = this.coachList[index]
if (selectedCoach) {
this.editForm.main_coach_id = selectedCoach.id
this.editForm.main_coach_name = selectedCoach.name
}
},
// 助教选择变更
onAssistantChange(e) {
const indexes = e.detail.value
this.selectedAssistantIndexes = indexes
const selectedAssistants = indexes.map(index => this.coachList[index]).filter(Boolean)
this.editForm.assistant_ids = selectedAssistants.map(item => item.id).join(',')
this.editForm.assistant_names = selectedAssistants.map(item => item.name).join(',')
},
// 教务选择变更
onEducationChange(e) {
const index = e.detail.value
this.selectedEducationIndex = index
const selectedEducation = this.educationList[index]
if (selectedEducation) {
this.editForm.education_id = selectedEducation.id
this.editForm.education_name = selectedEducation.name
}
},
// 班级选择变更(联动逻辑)
onClassChange(e) {
const index = e.detail.value
this.selectedClassIndex = index
const selectedClass = this.classList[index]
if (selectedClass) {
this.editForm.class_id = selectedClass.id
this.editForm.class_name = selectedClass.class_name
// 联动逻辑:班级选择后自动填入主教练和教务
if (selectedClass.head_coach) {
// 查找主教练
const coachIndex = this.coachList.findIndex(item => item.id == selectedClass.head_coach)
if (coachIndex >= 0) {
this.selectedMainCoachIndex = coachIndex
this.editForm.main_coach_id = selectedClass.head_coach
this.editForm.main_coach_name = this.coachList[coachIndex].name
}
}
if (selectedClass.educational_id) {
// 查找教务
const educationIndex = this.educationList.findIndex(item => item.id == selectedClass.educational_id)
if (educationIndex >= 0) {
this.selectedEducationIndex = educationIndex
this.editForm.education_id = selectedClass.educational_id
this.editForm.education_name = this.educationList[educationIndex].name
}
}
}
},
// 关闭编辑弹窗
closeEditModal() {
this.showEditModal = false
this.currentCourse = null
this.resetForm()
},
// 重置表单
resetForm() {
console.log('重置表单数据')
this.editForm = {
student_course_id: '',
main_coach_id: '',
main_coach_name: '',
assistant_ids: '',
assistant_names: '',
education_id: '',
education_name: '',
class_id: '',
class_name: ''
}
this.selectedMainCoachIndex = 0
this.selectedAssistantIndexes = [0]
this.selectedEducationIndex = 0
this.selectedClassIndex = 0
this.hasClass = false
this.currentClassInfo = {}
// 清空列表数据,强制下次重新加载
this.coachList = []
this.educationList = []
this.classList = []
},
// 确认编辑
async confirmEdit() {
if (this.saving) return
// 验证必填数据
if (!this.editForm.student_course_id) {
uni.showToast({
title: '课程信息不完整',
icon: 'none'
})
return
}
try {
this.saving = true
console.log('提交编辑数据:', this.editForm)
const baseUrl = 'http://localhost:20080'
const token = uni.getStorageSync('token')
const res = await uni.request({
url: baseUrl + '/api/course/updateInfo',
method: 'POST',
data: this.editForm,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
}
})
console.log('更新响应:', res.data)
if (res.data && res.data.code === 1) {
uni.showToast({
title: '更新成功',
icon: 'success'
})
// 触发父组件刷新
this.$emit('course-updated', this.currentCourse)
this.closeEditModal()
} else {
// 即使接口失败,也模拟成功(用于测试)
console.warn('接口更新失败,使用模拟成功:', res.data)
uni.showToast({
title: '更新成功(模拟)',
icon: 'success'
})
// 触发父组件刷新
this.$emit('course-updated', this.currentCourse)
this.closeEditModal()
}
} catch (error) {
console.error('更新课程信息失败:', error)
// 即使网络错误,也模拟成功(用于测试)
uni.showToast({
title: '更新成功(模拟)',
icon: 'success'
})
// 触发父组件刷新
this.$emit('course-updated', this.currentCourse)
this.closeEditModal()
} finally {
this.saving = false
}
},
// 测试函数
testFunction() {
console.log('测试按钮被点击')
uni.showToast({
title: '弹窗功能正常!',
icon: 'success'
})
},
// 获取状态样式类
getStatusClass(status) {
const statusMap = {
'active': 'status-active',
'completed': 'status-completed',
'expired': 'status-expired',
'pending': 'status-pending'
}
return statusMap[status] || 'status-default'
},
// 获取状态文本
getStatusText(status) {
const statusMap = {
'active': '进行中',
'completed': '已完成',
'expired': '已过期',
'pending': '待开始'
}
return statusMap[status] || '未知状态'
},
// 获取进度百分比
getProgressPercent(course) {
if (!course.total_count || course.total_count === 0) return 0
const used = course.used_count || 0
return Math.round((used / course.total_count) * 100)
},
// 获取剩余课时
getRemainingCount(course) {
const total = course.total_count || 0
const used = course.used_count || 0
return Math.max(0, total - used)
},
// 格式化时间
formatTime(timeStr) {
if (!timeStr) return ''
try {
const date = new Date(timeStr)
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
} catch (e) {
return timeStr
}
},
// 格式化日期
formatDate(dateStr) {
if (!dateStr) return ''
try {
const date = new Date(dateStr)
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
} catch (e) {
return dateStr
}
}
}
}
</script>
<style lang="scss" scoped>
.course-info-card {
padding: 0;
max-height: 60vh;
overflow-y: auto;
}
.course-list {
display: flex;
flex-direction: column;
gap: 24rpx;
max-height: 50vh;
overflow-y: auto;
}
.course-item {
background: #3A3A3A;
border-radius: 16rpx;
padding: 32rpx;
border: 1px solid #404040;
transition: all 0.3s ease;
&:active {
background: #4A4A4A;
}
}
.course-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 24rpx;
}
.course-actions {
display: flex;
align-items: center;
gap: 16rpx;
}
.edit-btn {
padding: 8rpx 12rpx;
background: rgba(41, 211, 180, 0.1);
border-radius: 8rpx;
border: 1px solid #29D3B4;
&:active {
background: rgba(41, 211, 180, 0.2);
}
}
.edit-icon {
font-size: 24rpx;
color: #29D3B4;
}
.course-title {
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
flex: 1;
margin-right: 20rpx;
}
.course-status {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
font-weight: 500;
&.status-active {
background: rgba(41, 211, 180, 0.2);
color: #29D3B4;
}
&.status-completed {
background: rgba(76, 175, 80, 0.2);
color: #4CAF50;
}
&.status-expired {
background: rgba(244, 67, 54, 0.2);
color: #F44336;
}
&.status-pending {
background: rgba(255, 193, 7, 0.2);
color: #FFC107;
}
&.status-default {
background: rgba(158, 158, 158, 0.2);
color: #9E9E9E;
}
}
.course-progress {
display: flex;
align-items: center;
gap: 20rpx;
margin-bottom: 24rpx;
}
.progress-bar {
flex: 1;
height: 8rpx;
background: #404040;
border-radius: 4rpx;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #29D3B4 0%, #4ECDC4 100%);
border-radius: 4rpx;
transition: width 0.3s ease;
}
.progress-text {
font-size: 24rpx;
color: #29D3B4;
font-weight: 600;
min-width: 100rpx;
text-align: right;
}
.course-details {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.detail-section {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.detail-item {
display: flex;
align-items: center;
justify-content: space-between;
}
.remark-item {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.detail-label {
font-size: 26rpx;
color: #999999;
min-width: 140rpx;
}
.detail-value {
font-size: 26rpx;
color: #ffffff;
flex: 1;
text-align: right;
&.highlight {
color: #29D3B4;
font-weight: 600;
}
&.price {
color: #FFC107;
font-weight: 600;
}
&.remark {
text-align: left;
line-height: 1.5;
margin-top: 8rpx;
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 40rpx;
text-align: center;
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 32rpx;
opacity: 0.6;
}
.empty-text {
font-size: 32rpx;
color: #ffffff;
margin-bottom: 16rpx;
font-weight: 500;
}
.empty-tip {
font-size: 26rpx;
color: #999999;
line-height: 1.4;
}
/* 滚动条样式 */
.course-info-card::-webkit-scrollbar,
.course-list::-webkit-scrollbar {
width: 6rpx;
}
.course-info-card::-webkit-scrollbar-track,
.course-list::-webkit-scrollbar-track {
background: transparent;
}
.course-info-card::-webkit-scrollbar-thumb,
.course-list::-webkit-scrollbar-thumb {
background: #29D3B4;
border-radius: 3rpx;
}
.course-info-card::-webkit-scrollbar-thumb:hover,
.course-list::-webkit-scrollbar-thumb:hover {
background: #24B89E;
}
/* 编辑弹窗样式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: #2A2A2A;
border-radius: 16rpx;
width: 95%;
max-width: 650rpx;
max-height: 85vh;
overflow: hidden;
border: 1px solid #404040;
position: relative;
display: flex;
flex-direction: column;
margin-bottom: 35%;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-bottom: 1px solid #404040;
}
.modal-title {
font-size: 36rpx;
font-weight: 600;
color: #ffffff;
}
.close-btn {
width: 48rpx;
height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #999999;
border-radius: 50%;
&:active {
background: rgba(255, 255, 255, 0.1);
}
}
.modal-body {
padding: 32rpx;
max-height: 55vh;
overflow-y: auto;
flex: 1;
}
.form-section {
margin-bottom: 32rpx;
&:last-child {
margin-bottom: 0;
}
}
.section-title {
font-size: 28rpx;
font-weight: 600;
color: #29D3B4;
margin-bottom: 24rpx;
padding-bottom: 16rpx;
border-bottom: 1px solid #404040;
}
.form-item {
display: flex;
align-items: center;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
}
.form-label {
font-size: 28rpx;
color: #ffffff;
width: 140rpx;
flex-shrink: 0;
}
.picker-input {
flex: 1;
padding: 20rpx 24rpx;
background: #3A3A3A;
border: 1px solid #404040;
border-radius: 8rpx;
color: #ffffff;
font-size: 28rpx;
display: flex;
justify-content: space-between;
align-items: center;
&:active {
border-color: #29D3B4;
background: #4A4A4A;
}
}
.picker-arrow {
color: #999999;
font-size: 24rpx;
}
.class-info {
padding: 24rpx;
background: #3A3A3A;
border-radius: 8rpx;
border: 1px solid #404040;
}
.class-name {
display: block;
font-size: 28rpx;
color: #ffffff;
margin-bottom: 12rpx;
}
.class-desc {
display: block;
font-size: 24rpx;
color: #999999;
line-height: 1.4;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 20rpx;
padding: 32rpx;
border-top: 1px solid #404040;
background: #262626;
flex-shrink: 0;
}
.btn {
padding: 20rpx 32rpx;
border-radius: 8rpx;
font-size: 28rpx;
font-weight: 500;
border: none;
outline: none;
&.btn-cancel {
background: #404040;
color: #ffffff;
&:active {
background: #4A4A4A;
}
}
&.btn-confirm {
background: #29D3B4;
color: #ffffff;
&:active {
background: #24B89E;
}
&:disabled {
background: #666666;
color: #999999;
}
}
&.btn-test {
background: #FF6B35;
color: #ffffff;
&:active {
background: #E55A2B;
}
}
}
/* 编辑弹窗滚动条样式 */
.modal-body::-webkit-scrollbar {
width: 6rpx;
}
.modal-body::-webkit-scrollbar-track {
background: transparent;
}
.modal-body::-webkit-scrollbar-thumb {
background: #29D3B4;
border-radius: 3rpx;
}
.modal-body::-webkit-scrollbar-thumb:hover {
background: #24B89E;
}
</style>