| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- <!--
- Description: 课程详情页面
- Author: BaiQi
- Created Date: 2024-7-1
- -->
- <template>
- <div class="course-details">
- <div class="course-details-info">
- <div>
- <img class="course-details-image" :src="convertImageUrl(dataForm.courseImages[0], dataForm.courseName)" :alt="dataForm.courseName">
- </div>
- <div class="course-details-text">
- <div class="title">
- {{dataForm.courseName}}
- </div>
- <div class="item">
- <span class="item-title">任课老师:</span>
- {{dataForm.teacherNames?.filter(Boolean).join(",")}}
- </div>
- <div class="item">
- <span class="item-title">目标学生:</span>
- {{dataForm.targetGrade}}
- </div>
- <div class="item">
- <span class="item-title">起止时间:</span>
- {{new Date(Date.parse(dataForm.startTime)).toLocaleString()}} - {{new Date(Date.parse(dataForm.endTime)).toLocaleString()}}
- </div>
- <div>
- <template v-if="showCode && role == 'student'">
- <el-input v-model="enrollCode" placeholder="输入邀请码" type="text" style="width: 150px;margin-right: 20px"></el-input>
- <el-button color="#597d3b" @click="enroll">加入课程</el-button>
- </template>
- <template v-if="showEdit && role == 'teachers'">
- <el-button color="#597d3b" @click="visible = true; fileList[0] = {name: '壁纸', url: newCourseInfo.courseImages[0]}">编辑课程</el-button>
- </template>
- </div>
- </div>
- </div>
- <div class="course-tabs">
- <el-tabs v-model="activeTab" >
- <el-tab-pane v-for="item in items" :key="item.key" :name="item.key" :label="item.label">
- <template v-if="activeTab == '课程简介'">
- <div>
- {{dataForm.description}}
- </div>
- </template>
- <template v-else>
- <CourseHomeworkTable :disabled="isDisabled()" :columns="columns" :tableData="homeworkList" :role="role" :courseId="dataForm.courseId" :isShowNewHomework="showEdit && role == 'teachers'"/>
- </template>
- </el-tab-pane>
- </el-tabs>
- </div>
- </div>
- <el-dialog v-model="visible" title="编辑课程" >
- <el-form :model="newCourseInfo" label-width="auto" :rules="editCourseRules" ref="formRef">
- <!-- <el-form-item label="课程图片">-->
- <el-form-item label="课程名称" prop="courseName">
- <el-input v-model="newCourseInfo.courseName" placeholder="请输入名称"></el-input>
- </el-form-item>
- <el-form-item label="面向年级" prop="courseName">
- <el-input v-model="newCourseInfo.targetGrade" placeholder="请输入年级"></el-input>
- </el-form-item>
- <el-form-item label="开始时间" prop="startTime">
- <el-date-picker v-model="newCourseInfo.startTime" type="datetime" placeholder="截止时间" style="width: 180px"/>
- </el-form-item>
- <el-form-item label="截止时间" prop="endTime">
- <el-date-picker v-model="newCourseInfo.endTime" type="datetime" placeholder="截止时间" style="width: 180px"/>
- </el-form-item>
- <el-form-item label="选课码" prop="enrollCode">
- <el-input v-model="newCourseInfo.enrollCode" placeholder="请输入选课码"></el-input>
- </el-form-item>
- <!-- <el-form-item label="课程图片">-->
- <!-- <el-input v-model="newCourseInfo.courseImages[0]" placeholder="请输入图片链接"></el-input>-->
- <!-- </el-form-item>-->
- <el-form-item label="课程图片">
- <el-upload
- ref="uploadRef"
- drag
- action="/api/file"
- multiple
- v-model:file-list="fileList"
- :limit="1"
- :on-success="handleSuccess"
- :on-error="handleError"
- :on-exceed="handleExceed"
- :on-change="handleChange"
- list-type="picture-card"
- accept=".png, .jpg"
- :auto-upload="false"
- >
- <el-icon class="el-icon--upload"><upload-filled /></el-icon>
- <div class="el-upload__text">
- 拖拽上传或<em>点击此处上传</em>
- </div>
- <template #tip>
- <div class="el-upload__tip">
- jpg/png 文件不大于 500kb
- </div>
- </template>
- </el-upload>
- </el-form-item>
- <el-form-item label="描述" prop="description">
- <el-input v-model="newCourseInfo.description" placeholder="请输入描述" type="textarea" :rows="3"></el-input>
- </el-form-item>
- </el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="visible = false">取消</el-button>
- <el-button type="primary" @click="submitForm(formRef)">确定</el-button>
- </div>
- </template>
- </el-dialog>
- </template>
- <script setup lang="ts">
- import {onMounted, reactive, ref} from 'vue';
- import {
- ElTabs,
- ElTabPane,
- ElMessage,
- ElNotification,
- type UploadFiles, type UploadFile, type FormRules, type FormInstance
- } from 'element-plus';
- import './index.scss'
- import type {CourseDetailVO, CourseVO, IAssignment, IAssignmentTableColumn, IAssignmentTableItem} from "@/types/vo";
- import {useRoute} from "vue-router";
- import useApis from "@/apis";
- import CourseHomeworkTable from "@/views/CourseSquare/component/CourseDetails/component/CourseHomeworkTable.vue";
- import type {AssignmentQuery} from "@/types/query";
- import {useStore} from "@/store";
- import type {CourseUpdateDTO, EnrollDTO} from "@/types/dto";
- import {UploadFilled} from "@element-plus/icons-vue"
- import type { UploadProps, UploadUserFile } from 'element-plus'
- import { convertImageUrl } from '@/utils/convertUtil';
- const apis = useApis()
- const store = useStore()
- const route = useRoute()
- const role:'teachers' | 'student' = store.user.role === 'STUDENT' ? 'student':'teachers';
- const courseId = +route.params.courseId
- let showCode = ref(true)
- let enrollCode = ref(null)
- let showEdit = ref(false)
- let visible = ref(false)
- let uploadRef = ref()
- // 用于展示课程信息
- let dataForm = reactive<CourseDetailVO>({
- courseId: null,
- courseName: '',
- teacherIds: [],
- teacherNames: [],
- courseImages: [''],
- targetGrade: '',
- startTime: '',
- endTime: '',
- createTime: '',
- description: '',
- })
- let newCourseInfo = reactive<CourseUpdateDTO>({
- courseName: '',
- teacherIds: [],
- courseImages: [''],
- targetGrade: '',
- startTime: '',
- endTime: '',
- description: '',
- enrollCode: '',
- })
- //用于查询课程包含的作业
- let query = reactive<AssignmentQuery>({
- assignmentId:null,
- teacherId:null,
- courseId:courseId,
- assignmentName:null,
- status:null,
- type:null
- })
- const editCourseRules = reactive<FormRules>({
- courseName: [
- {
- required: true,
- message: "课程名称不能为空",
- trigger: 'blur'
- }
- ],
- targetGrade: [
- {
- required: true,
- message: "面向年级不能为空",
- trigger: 'blur'
- }
- ],
- startTime: [
- {
- type: 'date',
- required: true,
- message: "开始时间不能为空",
- trigger: 'blur'
- }
- ],
- endTime: [
- {
- type: 'date',
- required: true,
- message: "截止时间不能为空",
- trigger: 'blur'
- }
- ],
- enrollCode: [
- {
- required: true,
- message: "选课码不能为空",
- trigger: 'blur'
- }
- ],
- description: [
- {
- required: true,
- message: "描述不能为空",
- trigger: 'blur'
- }
- ]
- })
- const activeTab = ref('课程简介');
- const items = ref([
- { label: '课程简介', key: '课程简介'},
- { label: '作业列表', key: '作业列表'},
- ]);
- const columns = ref<IAssignmentTableColumn[]>([
- {
- title: '作业名称',
- dataIndex: 'desc',
- key: 'desc',
- width: 200
- },
- {
- title: '类型',
- dataIndex: 'type',
- key: 'type',
- width: 80
- },
- {
- title: '截止日期',
- dataIndex: 'endDate',
- key: 'endDate',
- width: 170
- },
- {
- title: '发布人',
- dataIndex: 'publisher',
- key: 'publisher',
- width: 100
- },
- ]);
- // 用于展示课程所含的作业
- let homeworkList = ref<IAssignmentTableItem[]>([]);
- // 用于编辑课程展示预览图片
- const fileList = ref<UploadUserFile[]>([])
- const handleSuccess: UploadProps['onSuccess'] = (response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
- console.log("上传成功", response.data)
- newCourseInfo.courseImages[0] = response.data
- }
- const handleError: UploadProps['onError'] = (error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
- console.log("上传失败", error)
- console.log(error, uploadFile, uploadFiles)
- }
- const handleExceed = (files, fileList) => {
- // console.log(files)
- uploadRef.value.clearFiles() //清空上传文件(限制一个,所以直接清空即可)
- const file = files[0]
- uploadRef.value.handleStart(file) //重新上传
- }
- //选择文件
- const handleChange = (file,fileList) => {
- // console.log(file)
- const fileImgType = file.name.match(/\.([^\.]+)$/)[1]; //匹配文件格式(最后一个'.'后的格式)或者匹配file.raw.type
- const isImageType = ['png','jpg'];
- const isLt5KB = file.size / 1024 < 500; //判断图片格式与大小
- if (isImageType.indexOf(fileImgType)==-1) {
- ElMessage.error('文件仅支持png、jpg格式') //限制文件类型
- uploadRef.value.clearFiles() //清空上传文件
- return false;
- }
- if (!isLt5KB) {
- ElMessage.error('文件不能超过500kb!') //限制文件大小
- uploadRef.value.clearFiles() //清空上传文件
- return false;
- }
- apis.uploadFile(file.raw)
- .then((res) => {
- newCourseInfo.courseImages[0] = res.data.data
- })
- .catch((err) => {
- console.log(err)
- })
- }
- /*
- 用于处理课程图片
- */
- const processCourses = () => {
- dataForm.courseImages[0] = dataForm.courseImages[0].split("?")[0]
- }
- const getCourse = () => {
- // 获取课程信息
- apis.getCourseByCourseId(courseId)
- .then((res: any) => {
- let data = res.data.data.records[0]
- Object.assign(dataForm, {...data})
- processCourses()
- getNewCourseInfo(dataForm)
- })
- .catch((err: any) => {
- console.error(err);
- })
- .finally(() => {});
- }
- const getNewCourseInfo = (dataForm: CourseVO) => {
- apis.getEnrollCode(store.user.id, courseId)
- .then((res: any) => {
- Object.assign(newCourseInfo, {
- courseName: dataForm.courseName,
- teacherIds: dataForm.teacherIds,
- courseImages: dataForm.courseImages,
- targetGrade: dataForm.targetGrade,
- startTime: dataForm.startTime,
- endTime: dataForm.endTime,
- description: dataForm.description,
- enrollCode: res.data.data
- })
- })
- .catch((err: any) => {
- Object.assign(newCourseInfo, {
- courseName: dataForm.courseName,
- teacherIds: dataForm.teacherIds,
- courseImages: dataForm.courseImages,
- targetGrade: dataForm.targetGrade,
- startTime: dataForm.startTime,
- endTime: dataForm.endTime,
- description: dataForm.description,
- enrollCode: ''
- })
- console.error(err);
- })
- }
- const getHomeworkList = () => {
- apis.getAssignmentByQuery(query)
- .then((res: any) => {
- let data = res.data.data.records
- homeworkList.value = convertHomeworkList(data)
- for(let i = 0; i < homeworkList.value.length; i++){
- apis.findUserById(+homeworkList.value[i].publisher)
- .then((res: any) => {
- homeworkList.value[i].publisher = res.data.data.records[0].name
- })
- }
- })
- .catch((err: any) => {
- console.error(err);
- })
- .finally(() => {
- });
- }
- /*
- 学生,是否显示选课码输入框选课
- */
- const isShowCode = () => {
- apis.getCourseByStudentId()
- .then((res: any) => {
- let data = res.data.data.records
- for(let i = 0 ; i < data.length; i++){
- if(data[i].courseId == courseId){
- showCode.value = false;
- break;
- }
- }
- })
- .catch((err: any) => {
- console.error(err);
- })
- .finally(() => {});
- }
- let enrollDTO = reactive<EnrollDTO>({
- studentId: null,
- courseId: courseId,
- enrollCode: enrollCode.value
- })
- /*
- 学生,加入课程
- */
- const enroll = () => {
- enrollDTO.enrollCode = enrollCode.value
- apis.enrollCourse(enrollDTO)
- .then((res:any) => {
- if(res.data.code == 400){
- ElMessage({
- message: '选课码错误',
- type: 'error',
- })
- }else {
- showCode.value = false
- ElMessage({
- message: '选课成功',
- type: 'success',
- })
- }
- })
- }
- /*
- 教师,是否显示编辑课程按钮
- */
- const isShowEdit = () => {
- apis.getCourseByTeacherId()
- .then((res: any) => {
- let total = res.data.data.total
- apis.getCourseByTeacherId(1, total)
- .then((res: any) => {
- let data = res.data.data.records
- for(let i = 0 ; i < data.length; i++){
- if(data[i].courseId == courseId){
- showEdit.value = true;
- break;
- }
- }
- })
- .catch((err: any) => {
- console.error(err);
- })
- .finally(() => {});
- })
- }
- /*
- 将后端传来的作业数据进行整形,便于前端展示
- */
- const convertHomeworkList = (data:IAssignment[]) => {
- let newHomeworkList: IAssignmentTableItem[] = ([])
- for(let i = 0; i < data.length; i++){
- let item:IAssignmentTableItem = {
- desc: data[i].assignmentName,
- type: data[i].type,
- publisher: data[i].teacherId.toString(),
- startDate: formatDate(data[i].startTime),
- endDate: formatDate(data[i].endTime),
- assignmentId: data[i].assignmentId,
- descriptionFile: data[i].descriptionFile != null ? data[i].descriptionFile: null,
- teacherId: data[i].teacherId.toString(),
- }
- newHomeworkList.push(item)
- }
- return newHomeworkList
- }
- /*
- 时间格式转换方法
- */
- function formatDate(date:string) {
- const d = new Date(date);
- const year = d.getFullYear();
- const month = String(d.getMonth() + 1).padStart(2, '0');
- const day = String(d.getDate()).padStart(2, '0');
- const hours = String(d.getHours()).padStart(2, '0');
- const minutes = String(d.getMinutes()).padStart(2, '0');
- const seconds = String(d.getSeconds()).padStart(2, '0');
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
- }
- let formRef = ref<FormInstance>()
- const submitForm = async (formEl: FormInstance | undefined) => {
- if (!formEl) return
- await formEl.validate((valid, fields) => {
- if (valid) {
- console.log('submit!')
- handleSubmit()
- } else {
- console.log('error submit!', fields)
- }
- })
- }
- const handleSubmit = () => {
- newCourseInfo.endTime = formatDate(newCourseInfo.endTime)
- newCourseInfo.startTime = formatDate(newCourseInfo.startTime)
- if(new Date(newCourseInfo.endTime) < new Date(newCourseInfo.startTime)){
- ElMessage.error('截止时间不能早于开始时间!');
- checkTimeAndAdjust();
- return;
- }
- // uploadRef.value!.submit()
- console.log(newCourseInfo)
- // TODO: 更新图片需要更新两次,不知道为什么
- apis.updateCourse(dataForm.courseId, newCourseInfo)
- .then((res:any) => {
- if(res.data.code===200) {
- ElNotification({
- title: '修改成功',
- type: 'success',
- })
- visible.value = false
- getCourse();
- // apis.getCourseByCourseId(dataForm.courseId)
- // .then((res:any) => {
- // console.log(res.data.data.records[0])
- // })
- // setTimeout(() => apis.updateCourse(dataForm.courseId, newCourseInfo), 500)
- }else {
- ElNotification({
- title: '修改失败',
- type: 'error',
- })
- visible.value = false
- }
- })
- .catch((err:any) => {
- console.log(err)
- })
- }
- const checkTimeAndAdjust = () => {
- const startTime = new Date(dataForm.startTime);
- let endTime = new Date(dataForm.endTime);
- // 检查截止时间是否早于开始时间
- if (endTime < startTime) {
- // 将截止时间设置为开始时间的下一天
- endTime.setDate(startTime.getDate() + 1);
- newCourseInfo.endTime = formatDate(endTime.toISOString()); // 更新格式
- ElMessage.warning('先前时间逻辑有误,截止时间已自动调整为开始时间的下一天!');
- }
- }
- function isDisabled(){
- if(role == "student") return showCode.value;
- if(role == "teachers") return !showEdit.value;
- }
- onMounted(() => {
- getCourse()
- getHomeworkList()
- if(role == 'student') isShowCode()
- if(role == 'teachers') isShowEdit()
- //检查并自动调整时间逻辑
- checkTimeAndAdjust();
- })
- </script>
- <style scoped>
- ::v-deep .el-tabs__item {
- color:#597D3B;
- opacity: 1;
- }
- ::v-deep .el-tabs__item:hover {
- cursor: pointer;
- opacity: 0.7;
- }
- /*去下划线 */
- ::v-deep .el-tabs__nav-wrap::after {
- position: static !important;
- }
- /* 下划线颜色 */
- ::v-deep .el-tabs__active-bar {
- background-color: #597D3B;
- }
- :deep(.el-upload-dragger) {
- padding: 0px 0px;
- }
- </style>
|