Pārlūkot izejas kodu

Merge branch 'dev' of http://192.168.1.245:11111/shanming/onlineEducation-front into dev

zhangsq 7 mēneši atpakaļ
vecāks
revīzija
9bdc5664eb

+ 1 - 1
package.json

@@ -64,7 +64,7 @@
 		"vue": "3.2.44",
 		"vue-codemirror": "^6.1.1",
 		"vue-cropper": "1.0.5",
-		"vue-demi": "0.13.11",
+		"vue-demi": "0.14.6",
 		"vue-i18n": "9.2.2",
 		"vue-router": "4.1.6",
 		"vue-simple-uploader": "^1.0.3",

+ 27 - 12
pnpm-lock.yaml

@@ -49,13 +49,13 @@ importers:
         version: 5.0.0(vue@3.2.44)
       '@vue-office/docx':
         specifier: ^1.2.0
-        version: 1.2.0(vue-demi@0.13.11(vue@3.2.44))(vue@3.2.44)
+        version: 1.2.0(vue-demi@0.14.6(vue@3.2.44))(vue@3.2.44)
       '@vue-office/excel':
         specifier: 1.2.0
-        version: 1.2.0(vue-demi@0.13.11(vue@3.2.44))(vue@3.2.44)
+        version: 1.2.0(vue-demi@0.14.6(vue@3.2.44))(vue@3.2.44)
       '@vue-office/pdf':
         specifier: 1.2.0
-        version: 1.2.0(vue-demi@0.13.11(vue@3.2.44))(vue@3.2.44)
+        version: 1.2.0(vue-demi@0.14.6(vue@3.2.44))(vue@3.2.44)
       '@vueup/vue-quill':
         specifier: ^1.2.0
         version: 1.2.0(vue@3.2.44)
@@ -159,8 +159,8 @@ importers:
         specifier: 1.0.5
         version: 1.0.5
       vue-demi:
-        specifier: 0.13.11
-        version: 0.13.11(vue@3.2.44)
+        specifier: 0.14.6
+        version: 0.14.6(vue@3.2.44)
       vue-i18n:
         specifier: 9.2.2
         version: 9.2.2(vue@3.2.44)
@@ -3401,6 +3401,17 @@ packages:
       '@vue/composition-api':
         optional: true
 
+  vue-demi@0.14.6:
+    resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==}
+    engines: {node: '>=12'}
+    hasBin: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
   vue-eslint-parser@9.1.0:
     resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==}
     engines: {node: ^14.17.0 || >=16.0.0}
@@ -4514,20 +4525,20 @@ snapshots:
       vite: 4.2.1(less@4.1.3)(terser@5.42.0)
       vue: 3.2.44
 
-  '@vue-office/docx@1.2.0(vue-demi@0.13.11(vue@3.2.44))(vue@3.2.44)':
+  '@vue-office/docx@1.2.0(vue-demi@0.14.6(vue@3.2.44))(vue@3.2.44)':
     dependencies:
       vue: 3.2.44
-      vue-demi: 0.13.11(vue@3.2.44)
+      vue-demi: 0.14.6(vue@3.2.44)
 
-  '@vue-office/excel@1.2.0(vue-demi@0.13.11(vue@3.2.44))(vue@3.2.44)':
+  '@vue-office/excel@1.2.0(vue-demi@0.14.6(vue@3.2.44))(vue@3.2.44)':
     dependencies:
       vue: 3.2.44
-      vue-demi: 0.13.11(vue@3.2.44)
+      vue-demi: 0.14.6(vue@3.2.44)
 
-  '@vue-office/pdf@1.2.0(vue-demi@0.13.11(vue@3.2.44))(vue@3.2.44)':
+  '@vue-office/pdf@1.2.0(vue-demi@0.14.6(vue@3.2.44))(vue@3.2.44)':
     dependencies:
       vue: 3.2.44
-      vue-demi: 0.13.11(vue@3.2.44)
+      vue-demi: 0.14.6(vue@3.2.44)
 
   '@vue/babel-helper-vue-transform-on@1.4.0': {}
 
@@ -6505,7 +6516,7 @@ snapshots:
     dependencies:
       '@vue/devtools-api': 6.6.4
       vue: 3.2.44
-      vue-demi: 0.13.11(vue@3.2.44)
+      vue-demi: 0.14.6(vue@3.2.44)
     optionalDependencies:
       typescript: 4.9.5
 
@@ -7285,6 +7296,10 @@ snapshots:
     dependencies:
       vue: 3.2.44
 
+  vue-demi@0.14.6(vue@3.2.44):
+    dependencies:
+      vue: 3.2.44
+
   vue-eslint-parser@9.1.0(eslint@8.26.0):
     dependencies:
       debug: 4.4.1

+ 0 - 6
src/api/forum/forumApi.js

@@ -1,12 +1,6 @@
 import { baseRequest } from '@/utils/request'
 
 const request = (url, ...arg) => baseRequest(`/api/webapp/${url}`, ...arg)
-/**
- * 菜单
- *
- * @author yubaoshan
- * @date 2022-09-22 22:33:20
- */
 export default {
 	// 查询帖子列表接口
 	forumList(data) {

+ 49 - 0
src/api/student/classCentre.js

@@ -0,0 +1,49 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/api/webapp/disk/${url}`, ...arg)
+export default {
+	//课程中心-课程-详情(增加点击次数)
+	addViewCount(data) {
+		return request('coursecentry/addViewCount', data)
+	},
+	//课程中心-课程-详情(学生端)
+	courseDetail(data) {
+		return request('coursecentry/detail', data, 'get')
+	},
+	//课程中心-课程-详情(学生端)
+	coursechapterList(data) {
+		return request('coursecentry/coursechapterList', data, 'get')
+	},
+	//课程中心-课时-详情
+	courseTimeDetail(data) {
+		return request('coursecentry/hourDetail', data, 'get')
+	},
+	//课程中心-笔记/笔记本-添加
+	notesSubmitForm(data, edit = false) {
+		return request(`courseNotes/${edit ? 'edit' : 'add'}`, data)
+	},
+	//课程中心-笔记-分页列表
+	notesList(data) {
+		return request('courseNotes/page', data, 'get')
+	},
+	//课程中心-笔记-删除
+	notesEdit(data) {
+		return request('courseNotes/delete', data)
+	},
+	//课程中心-问答-添加/编辑
+	askSubmitForm(data, edit = false) {
+		return request(`answer/${edit ? 'edit' : 'add'}`, data)
+	},
+	//课程中心-问答-删除
+	askDel(data) {
+		return request('answer/delete', data)
+	},
+	//课程中心-问答-列表
+	askList(data) {
+		return request('answer/page', data, 'get')
+	},
+	//课程中心-问答-点赞/取消赞
+	askLike(data, like = false) {
+		return request(`answer/${edit ? 'giveCancel' : 'give'}`, data, 'get')
+	},
+}

+ 1 - 2
src/components/Editor/index.vue

@@ -44,7 +44,7 @@
 			default:
 				'undo redo |  forecolor backcolor bold italic underline strikethrough link | blocks fontfamily fontsize | \
 				alignleft aligncenter alignright alignjustify outdent indent lineheight | bullist numlist | \
-				image table  preview | code selectall | numberedline | kityformula-editor'
+				image table  preview | code selectall'
 		},
 		fileUploadFunction: {
 			type: Function,
@@ -74,7 +74,6 @@
 		automatic_uploads: false,
 		images_upload_handler(blobInfo, progress) {
 			return new Promise((resolve, reject) => {
-				console.log('调用上传文件回显:', blobInfo.blob)
 				const param = new FormData()
 				param.append('file', blobInfo.blob(), blobInfo.filename())
 				props

+ 2 - 1
src/router/index.js

@@ -8,6 +8,7 @@ import { afterEach, beforeEach } from './scrollBehavior'
 import whiteListRouters from './whiteList'
 import portal from './portal'
 import forum from './forum'
+import student from './student'
 import userRoutes from '@/config/route'
 import tool from '@/utils/tool'
 import fullPageTool from './fullPageTool'
@@ -27,7 +28,7 @@ const routes_404 = [
 	}
 ]
 // 系统路由
-const routes = [...systemRouter, ...whiteListRouters, ...routes_404,...forum]
+const routes = [...systemRouter, ...whiteListRouters, ...routes_404,...forum,...student]
 
 const router = createRouter({
 	history: createWebHistory(),

+ 11 - 0
src/router/student.js

@@ -0,0 +1,11 @@
+const forum = [
+	{
+		path: '/student/classCentre',
+		hide: true,
+		component: () => import('@/views/student/classCentre/index.vue'),
+		meta: {
+			title: '课程中心详情'
+		}
+	}
+]
+export default forum

+ 4 - 0
src/store/exam.js

@@ -20,6 +20,10 @@ const format = function (array, key) {
 
 export const useExamStore = defineStore('exam', {
 	state: () => ({
+		toolbar:
+			'undo redo |  forecolor backcolor bold italic underline strikethrough link | blocks fontfamily fontsize | \
+				alignleft aligncenter alignright alignjustify outdent indent lineheight | bullist numlist | \
+				image table  preview | code selectall | numberedline | kityformula-editor',
 		// 学科相关
 		subjects: [],
 		// 枚举相关

+ 0 - 2
src/utils/newRequest.js

@@ -1,4 +1,3 @@
- 
 // 统一的请求发送
 import axios from 'axios'
 import qs from 'qs'
@@ -69,7 +68,6 @@ const error = () => {
 // HTTP response 拦截器
 service.interceptors.response.use(
 	(response) => {
-		console.log(response, '文件')
 		// 配置了blob,不处理直接返回文件流
 		if (response.config.responseType === 'blob') {
 			if (response.status === 200) {

+ 4 - 1
src/utils/request.js

@@ -120,7 +120,10 @@ service.interceptors.response.use(
 				'saveDraft'
 			]
 			apiNameArray.forEach((apiName) => {
-				if (responseUrl.includes(apiName)) {
+				// if (responseUrl.includes(apiName)) {
+				// 	message.success(data.msg)
+				// }
+				if (responseUrl.substring(responseUrl.lastIndexOf('/') + 1) == apiName) {
 					message.success(data.msg)
 				}
 			})

+ 31 - 31
src/views/exm/examinationManagement/form.vue

@@ -12,13 +12,6 @@
 			<a-form-item label="考试标题" name="examName" :rules="rules.examName">
 				<a-input v-model:value="form.examName" placeholder="请输入考试标题" />
 			</a-form-item>
-			<a-form-item label="试卷类型" name="paperType">
-				<a-select v-model:value="paperPage.queryParam.paperType" placeholder="请选择试卷类型" @change="paperTypeChange">
-					<a-select-option v-for="item in paperTypeEnum" :key="item.key" :value="item.key">
-						{{ item.value }}
-					</a-select-option>
-				</a-select>
-			</a-form-item>
 			<a-form-item label="选择试卷" name="paperId" :rules="rules.paperId">
 				<a-input-group compact>
 					<a-input
@@ -30,9 +23,11 @@
 					<a-button type="primary" @click="addPaper" style="width: 100px">选择试卷</a-button>
 				</a-input-group>
 			</a-form-item>
-			<a-form-item label="章节" name="chapterId">
-				<a-select v-model:value="form.chapterId" placeholder="请选择章节(可选)" allowClear>
-					<!-- 预留章节选择位置 -->
+			<a-form-item label="课程" name="chapterId">
+				<a-select v-model:value="form.chapterId" placeholder="请选择课程(可选)" allowClear>
+					<a-select-option v-for="item in courseList" :key="item.courseId" :value="item.courseId">
+						{{ item.courseName }}
+					</a-select-option>
 				</a-select>
 			</a-form-item>
 			<a-form-item label="开始时间" name="startTime" :rules="rules.startTime">
@@ -42,6 +37,7 @@
 					format="YYYY-MM-DD HH:mm:ss"
 					placeholder="请选择开始时间"
 					style="width: 100%"
+					:disabledDate="disabledStartDate"
 				/>
 			</a-form-item>
 			<a-form-item label="结束时间" name="endTime" :rules="rules.endTime">
@@ -76,10 +72,14 @@
 			@cancel="() => (paperPage.showDialog = false)"
 		>
 			<a-form layout="inline">
-				<a-form-item label="学科">
-					<a-select v-model:value="paperPage.queryParam.subjectId" allowClear style="width: 200px">
-						<a-select-option v-for="item in paperPage.subjectFilter" :key="item.id" :value="item.id">
-							{{ item.name }} ( {{ item.levelName }} )
+				<a-form-item label="试卷类型">
+					<a-select
+						v-model:value="paperPage.queryParam.paperType"
+						placeholder="请选择试卷类型"
+						@change="paperTypeChange"
+					>
+						<a-select-option v-for="item in paperTypeEnum" :key="item.key" :value="item.key">
+							{{ item.value }}
 						</a-select-option>
 					</a-select>
 				</a-form-item>
@@ -123,6 +123,8 @@
 	import { useExamStore } from '@/store/exam.js'
 	import examManagerApi from '@/api/exam/paper/examManager.js'
 	import examPaperApi from '@/api/exam/paper/examPaperApi.js'
+	import resourceAuditApi from '@/api/resourceAudit.js'
+
 	import dayjs from 'dayjs'
 	const emit = defineEmits(['success'])
 	const props = defineProps({
@@ -136,6 +138,7 @@
 	const { subjectEnumFormat } = examStore
 	const paperTypeEnum = computed(() => examStore.paperTypeEnum)
 	const formLoading = ref(false)
+	const courseList = ref([])
 	const form = reactive({
 		id: null,
 		examName: '',
@@ -157,7 +160,7 @@
 
 	const modalColumns = [
 		{ title: 'Id', dataIndex: 'id', key: 'id', width: 90 },
-		{ title: '学科', dataIndex: 'subjectId', key: 'subjectId', width: 120 },
+		// { title: '学科', dataIndex: 'subjectId', key: 'subjectId', width: 120 },
 		{ title: '名称', dataIndex: 'name', key: 'name' },
 		{
 			title: '创建时间',
@@ -193,11 +196,10 @@
 		}
 	})
 
-	// 初始化学科
-	const initSubject = async (cb) => {
-		await examStore.initSubject()
-		paperPage.subjectFilter = examStore.subjects
-		if (cb) cb()
+	// 禁用开始时间大于今天的日期
+	const disabledStartDate = (current) => {
+		// 禁用大于今天的日期
+		return current && current < dayjs().endOf('day')
 	}
 
 	// 试卷类型变更
@@ -209,10 +211,6 @@
 
 	// 选择试卷
 	const addPaper = () => {
-		if (!paperPage.queryParam.paperType) {
-			message.warning('请先选择试卷类型')
-			return
-		}
 		paperPage.showDialog = true
 		search()
 	}
@@ -224,11 +222,12 @@
 		const params = {
 			...paperPage.queryParam,
 			current: paperPage.queryParam.pageIndex,
-			size: paperPage.queryParam.pageSize
+			size: paperPage.queryParam.pageSize,
+			paperType: paperPage.queryParam.paperType
 		}
 		delete params.pageIndex
 		delete params.pageSize
-		const data = await examPaperApi.taskExamPage(params)
+		const data = await examPaperApi.pageList(params)
 		const re = data
 		paperPage.tableData = re.records
 		paperPage.total = re.total
@@ -281,10 +280,9 @@
 				} else {
 					await examManagerApi.createExam(submitData)
 				}
-				message.success('操作成功')
 				emit('success')
 			} catch (e) {
-				message.error(e.msg || '操作失败')
+				console.error(e)
 			} finally {
 				formLoading.value = false
 			}
@@ -308,9 +306,6 @@
 
 	// 初始化
 	onMounted(() => {
-		initSubject(() => {
-			paperPage.subjectFilter = examStore.subjects
-		})
 		const id = props.id
 		if (id && parseInt(id) !== 0) {
 			formLoading.value = true
@@ -328,6 +323,11 @@
 				formLoading.value = false
 			})
 		}
+		resourceAuditApi.courseAllList().then((re) => {
+			if (re.code === 200) {
+				courseList.value = re.data
+			}
+		})
 	})
 </script>
 

+ 1 - 2
src/views/exm/examinationManagement/index.vue

@@ -2,7 +2,7 @@
 	<div class="task-container">
 		<a-form layout="inline" :model="queryParam">
 			<a-form-item label="考试标题:">
-				<a-input v-model:value="queryParam.examName" placeholder="请输入考试标题" style="min-width: 200px" />
+				<a-input v-model:value="queryParam.examName" placeholder="请输入考试标题" style="min-width: 200px" allowClear />
 			</a-form-item>
 			<a-form-item label="考试状态:">
 				<a-select style="min-width: 150px" v-model:value="queryParam.examStatus" allowClear placeholder="考试状态">
@@ -16,7 +16,6 @@
 				<a-button style="margin-left: 20px" type="primary" @click="createTask">创建考试</a-button>
 			</a-form-item>
 		</a-form>
-
 		<a-table
 			:loading="listLoading"
 			:data-source="tableData"

+ 2 - 1
src/views/exm/question/edit/gap-filling.vue

@@ -91,7 +91,7 @@
 			centered
 			destroy-on-close
 		>
-			<Editor v-model="richEditorContent" :height="300" />
+			<Editor v-model="richEditorContent" :toolbar="toolbar" :height="300" />
 			<div style="text-align: right; margin-top: 16px">
 				<a-button type="primary" @click="editorConfirm">确定</a-button>
 				<a-button @click="richEditor.dialogVisible = false">取消</a-button>
@@ -112,6 +112,7 @@
 	import Editor from '@/components/Editor/index.vue'
 	import '../style/common.less'
 	const bankTypeEnum = computed(() => examStore.getBankTypeEnum)
+	const toolbar = computed(() => examStore.toolbar)
 	const examStore = useExamStore()
 	const props = defineProps({
 		id: {

+ 2 - 2
src/views/exm/question/edit/multiple-choice.vue

@@ -95,7 +95,7 @@
 			centered
 			destroy-on-close
 		>
-			<Editor v-model="richEditorContent" :height="300" />
+			<Editor v-model="richEditorContent" :toolbar="toolbar" :height="300" />
 			<div style="text-align: right; margin-top: 16px">
 				<a-button type="primary" @click="editorConfirm">确定</a-button>
 				<a-button @click="richEditor.dialogVisible = false">取消</a-button>
@@ -116,7 +116,7 @@
 	import Editor from '@/components/Editor/index.vue'
 	import '../style/common.less'
 	const bankTypeEnum = computed(() => examStore.getBankTypeEnum)
-
+	const toolbar = computed(() => examStore.toolbar)
 	const examStore = useExamStore()
 	const props = defineProps({
 		id: {

+ 2 - 2
src/views/exm/question/edit/short-answer.vue

@@ -82,7 +82,7 @@
 			centered
 			destroy-on-close
 		>
-			<Editor v-model="richEditorContent" :height="300" />
+			<Editor v-model="richEditorContent" :toolbar="toolbar" :height="300" />
 			<div style="text-align: right; margin-top: 16px">
 				<a-button type="primary" @click="editorConfirm">确定</a-button>
 				<a-button @click="richEditor.dialogVisible = false">取消</a-button>
@@ -102,7 +102,7 @@
 	import Editor from '@/components/Editor/index.vue'
 	import '../style/common.less'
 	const bankTypeEnum = computed(() => examStore.getBankTypeEnum)
-
+	const toolbar = computed(() => examStore.toolbar)
 	const examStore = useExamStore()
 	const props = defineProps({
 		id: {

+ 2 - 2
src/views/exm/question/edit/single-choice.vue

@@ -95,7 +95,7 @@
 			centered
 			destroy-on-close
 		>
-			<Editor v-model="richEditorContent" :height="300" />
+			<Editor v-model="richEditorContent" :toolbar="toolbar" :height="300" />
 			<div style="text-align: right; margin-top: 16px">
 				<a-button type="primary" @click="editorConfirm">确定</a-button>
 				<a-button @click="richEditor.dialogVisible = false">取消</a-button>
@@ -116,7 +116,7 @@
 	import Editor from '@/components/Editor/index.vue'
 	import '../style/common.less'
 	const bankTypeEnum = computed(() => examStore.getBankTypeEnum)
-
+	const toolbar = computed(() => examStore.toolbar)
 	const examStore = useExamStore()
 	const props = defineProps({
 		id: {

+ 2 - 1
src/views/exm/question/edit/true-false.vue

@@ -93,7 +93,7 @@
 			centered
 			destroy-on-close
 		>
-			<Editor v-model="richEditorContent" :height="300" />
+			<Editor v-model="richEditorContent" :toolbar="toolbar" :height="300" />
 			<div style="text-align: right; margin-top: 16px">
 				<a-button type="primary" @click="editorConfirm">确定</a-button>
 				<a-button @click="richEditor.dialogVisible = false">取消</a-button>
@@ -113,6 +113,7 @@
 	import Editor from '@/components/Editor/index.vue'
 	import '../style/common.less'
 	const bankTypeEnum = computed(() => examStore.getBankTypeEnum)
+	const toolbar = computed(() => examStore.toolbar)
 	const examStore = useExamStore()
 	const props = defineProps({
 		id: {

+ 132 - 75
src/views/exm/questionnaireManagement/form.vue

@@ -9,33 +9,57 @@
 			:loading="formLoading"
 			layout="horizontal"
 		>
-			<a-form-item label="年级" name="gradeLevel" :rules="rules.gradeLevel">
-				<a-select v-model:value="form.gradeLevel" placeholder="请选择年级" @change="levelChange">
-					<a-select-option v-for="item in levelEnum" :key="item.key" :value="item.key">
-						{{ item.value }}
+			<a-form-item label="考试标题" name="examName" :rules="rules.examName">
+				<a-input v-model:value="form.examName" placeholder="请输入考试标题" />
+			</a-form-item>
+			<a-form-item label="选择试卷" name="paperId" :rules="rules.paperId">
+				<a-input-group compact>
+					<a-input
+						v-model:value="selectedPaperName"
+						placeholder="请选择试卷"
+						readonly
+						style="width: calc(100% - 100px)"
+					/>
+					<a-button type="primary" @click="addPaper" style="width: 100px">选择试卷</a-button>
+				</a-input-group>
+			</a-form-item>
+			<a-form-item label="课程" name="chapterId">
+				<a-select v-model:value="form.chapterId" placeholder="请选择课程(可选)" allowClear @change="chapterChange">
+					<a-select-option v-for="item in courseList" :key="item.courseId" :value="item.courseId">
+						{{ item.courseName }}
 					</a-select-option>
 				</a-select>
 			</a-form-item>
-			<a-form-item label="标题" name="title" :rules="rules.title">
-				<a-input v-model:value="form.title" placeholder="请输入任务标题" />
+			<a-form-item label="开始时间" name="startTime" :rules="rules.startTime">
+				<a-date-picker
+					v-model:value="form.startTime"
+					show-time
+					format="YYYY-MM-DD HH:mm:ss"
+					placeholder="请选择开始时间"
+					style="width: 100%"
+					:disabledDate="disabledStartDate"
+				/>
 			</a-form-item>
-			<a-form-item label="试卷">
-				<a-table :dataSource="form.paperItems" :columns="paperColumns" rowKey="id" bordered :pagination="false">
-					<template #bodyCell="{ column, record }">
-						<template v-if="column.key === 'subjectId'">
-							{{ subjectEnumFormat(record.subjectId) }}
-						</template>
-						<template v-else-if="column.key === 'action'">
-							<a-button type="link" danger @click="removePaper(record)">删除</a-button>
-						</template>
-					</template>
-				</a-table>
+			<a-form-item label="结束时间" name="endTime" :rules="rules.endTime">
+				<a-date-picker
+					v-model:value="form.endTime"
+					show-time
+					format="YYYY-MM-DD HH:mm:ss"
+					placeholder="请选择结束时间"
+					style="width: 100%"
+				/>
+			</a-form-item>
+			<a-form-item label="考试状态" name="examStatus">
+				<a-radio-group v-model:value="form.examStatus">
+					<a-radio :value="0">未开始</a-radio>
+					<a-radio :value="1">已开始</a-radio>
+					<a-radio :value="2">已结束</a-radio>
+				</a-radio-group>
 			</a-form-item>
 			<a-form-item>
 				<a-space>
 					<a-button type="primary" @click="submitForm">提交</a-button>
 					<a-button @click="resetForm">重置</a-button>
-					<a-button type="dashed" @click="addPaper">添加试卷</a-button>
 				</a-space>
 			</a-form-item>
 		</a-form>
@@ -48,10 +72,14 @@
 			@cancel="() => (paperPage.showDialog = false)"
 		>
 			<a-form layout="inline">
-				<a-form-item label="学科">
-					<a-select v-model:value="paperPage.queryParam.subjectId" allowClear style="width: 200px">
-						<a-select-option v-for="item in paperPage.subjectFilter" :key="item.id" :value="item.id">
-							{{ item.name }} ( {{ item.levelName }} )
+				<a-form-item label="试卷类型">
+					<a-select
+						v-model:value="paperPage.queryParam.paperType"
+						placeholder="请选择试卷类型"
+						@change="paperTypeChange"
+					>
+						<a-select-option v-for="item in paperTypeEnum" :key="item.key" :value="item.key">
+							{{ item.value }}
 						</a-select-option>
 					</a-select>
 				</a-form-item>
@@ -90,10 +118,14 @@
 </template>
 
 <script setup>
-	import { ref, reactive, onMounted } from 'vue'
+	import { ref, reactive, onMounted, computed } from 'vue'
+	import { message } from 'ant-design-vue'
 	import { useExamStore } from '@/store/exam.js'
-	import taskApi from '@/api/exam/paper/task.js'
+	import examManagerApi from '@/api/exam/paper/examManager.js'
 	import examPaperApi from '@/api/exam/paper/examPaperApi.js'
+	import resourceAuditApi from '@/api/resourceAudit.js'
+
+	import dayjs from 'dayjs'
 	const emit = defineEmits(['success'])
 	const props = defineProps({
 		id: {
@@ -104,30 +136,31 @@
 	const formRef = ref()
 	const examStore = useExamStore()
 	const { subjectEnumFormat } = examStore
-	const levelEnum = computed(() => examStore.getLevelEnum)
+	const paperTypeEnum = computed(() => examStore.paperTypeEnum)
 	const formLoading = ref(false)
+	const courseList = ref([])
 	const form = reactive({
 		id: null,
-		gradeLevel: null,
-		title: '',
-		paperItems: []
+		examName: '',
+		paperId: null,
+		chapterId: null,
+		startTime: null,
+		endTime: null,
+		examStatus: 0
 	})
 
 	const rules = {
-		gradeLevel: [{ required: true, message: '请选择年级', trigger: 'change' }],
-		title: [{ required: true, message: '请输入任务标题', trigger: 'blur' }]
+		examName: [{ required: true, message: '请输入考试标题', trigger: 'blur' }],
+		paperId: [{ required: true, message: '请选择试卷', trigger: 'change' }],
+		startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
+		endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
 	}
 
-	const paperColumns = [
-		{ title: '学科', dataIndex: 'subjectId', key: 'subjectId', width: 120 },
-		{ title: '名称', dataIndex: 'name', key: 'name' },
-		{ title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 160 },
-		{ title: '操作', key: 'action', width: 100 }
-	]
+	const selectedPaperName = ref('')
 
 	const modalColumns = [
 		{ title: 'Id', dataIndex: 'id', key: 'id', width: 90 },
-		{ title: '学科', dataIndex: 'subjectId', key: 'subjectId', width: 120 },
+		// { title: '学科', dataIndex: 'subjectId', key: 'subjectId', width: 120 },
 		{ title: '名称', dataIndex: 'name', key: 'name' },
 		{
 			title: '创建时间',
@@ -139,12 +172,11 @@
 
 	const paperPage = reactive({
 		subjectFilter: [],
-		multipleSelection: [],
+		selectedPaper: null,
 		showDialog: false,
 		queryParam: {
 			subjectId: null,
-			level: null,
-			paperType: 6,
+			paperType: null,
 			pageIndex: 1,
 			pageSize: 5
 		},
@@ -153,32 +185,35 @@
 		total: 0
 	})
 
-	// 试卷选择表格
+	// 试卷选择表格
 	const selectedRowKeys = ref([])
 	const rowSelection = reactive({
+		type: 'radio',
 		selectedRowKeys: selectedRowKeys,
 		onChange: (selectedRowKeysVal, selectedRows) => {
 			selectedRowKeys.value = selectedRowKeysVal
-			paperPage.multipleSelection = selectedRows
+			paperPage.selectedPaper = selectedRows[0] || null
 		}
 	})
+	const chapterChange = async () => {
+		form.chapterId = ''
+	}
 
-	// 初始化学科
-	const initSubject = async (cb) => {
-		await examStore.initSubject()
-		paperPage.subjectFilter = examStore.subjects
-		if (cb) cb()
+	// 禁用开始时间大于今天的日期
+	const disabledStartDate = (current) => {
+		// 禁用大于今天的日期
+		return current && current < dayjs().endOf('day')
 	}
 
-	// 年级变更
-	const levelChange = () => {
+	// 试卷类型变更
+	const paperTypeChange = () => {
 		paperPage.queryParam.subjectId = null
-		paperPage.subjectFilter = examStore.subjects.filter((data) => data.level === form.gradeLevel)
+		form.paperId = null
+		selectedPaperName.value = ''
 	}
 
-	// 添加试卷
+	// 选择试卷
 	const addPaper = () => {
-		paperPage.queryParam.level = form.gradeLevel
 		paperPage.showDialog = true
 		search()
 	}
@@ -190,11 +225,12 @@
 		const params = {
 			...paperPage.queryParam,
 			current: paperPage.queryParam.pageIndex,
-			size: paperPage.queryParam.pageSize
+			size: paperPage.queryParam.pageSize,
+			paperType: paperPage.queryParam.paperType
 		}
 		delete params.pageIndex
 		delete params.pageSize
-		const data = await examPaperApi.taskExamPage(params)
+		const data = await examPaperApi.pageList(params)
 		const re = data
 		paperPage.tableData = re.records
 		paperPage.total = re.total
@@ -204,11 +240,12 @@
 
 	// 确认选择试卷
 	const confirmPaperSelect = () => {
-		paperPage.multipleSelection.forEach((ep) => {
-			if (!form.paperItems.some((item) => item.id === ep.id)) {
-				form.paperItems.push(ep)
-			}
-		})
+		if (!paperPage.selectedPaper) {
+			message.warning('请选择一个试卷')
+			return
+		}
+		form.paperId = paperPage.selectedPaper.id
+		selectedPaperName.value = paperPage.selectedPaper.name
 		paperPage.showDialog = false
 		selectedRowKeys.value = []
 	}
@@ -230,21 +267,25 @@
 		search()
 	}
 
-	// 删除试卷
-	const removePaper = (row) => {
-		const idx = form.paperItems.findIndex((item) => item.id === row.id)
-		if (idx !== -1) form.paperItems.splice(idx, 1)
-	}
-
 	// 提交表单
 	const submitForm = () => {
 		formRef.value.validate().then(async () => {
 			formLoading.value = true
 			try {
-				await taskApi.edit(form)
+				const submitData = {
+					...form,
+					startTime: form.startTime ? form.startTime.format('YYYY-MM-DD HH:mm:ss') : null,
+					endTime: form.endTime ? form.endTime.format('YYYY-MM-DD HH:mm:ss') : null
+				}
+
+				if (form.id) {
+					await examManagerApi.edit(submitData)
+				} else {
+					await examManagerApi.createExam(submitData)
+				}
 				emit('success')
 			} catch (e) {
-				//
+				console.error(e)
 			} finally {
 				formLoading.value = false
 			}
@@ -256,24 +297,40 @@
 		const lastId = form.id
 		formRef.value.resetFields()
 		form.id = lastId
-		form.gradeLevel = null
-		form.title = ''
-		form.paperItems = []
+		form.examName = ''
+		form.paperId = null
+		form.chapterId = null
+		form.startTime = null
+		form.endTime = null
+		form.examStatus = 0
+		selectedPaperName.value = ''
+		paperPage.queryParam.paperType = null
 	}
 
 	// 初始化
 	onMounted(() => {
-		initSubject(() => {
-			paperPage.subjectFilter = examStore.subjects
-		})
 		const id = props.id
 		if (id && parseInt(id) !== 0) {
 			formLoading.value = true
-			taskApi.select(id).then((re) => {
-				Object.assign(form, re)
+			examManagerApi.select(id).then((re) => {
+				Object.assign(form, {
+					...re,
+					startTime: re.startTime ? dayjs(re.startTime) : null,
+					endTime: re.endTime ? dayjs(re.endTime) : null
+				})
+				// 如果有试卷ID,需要获取试卷名称显示
+				if (re.paperId) {
+					// 这里可以根据需要调用接口获取试卷名称
+					selectedPaperName.value = '已选择试卷'
+				}
 				formLoading.value = false
 			})
 		}
+		resourceAuditApi.courseAllList().then((re) => {
+			if (re.code === 200) {
+				courseList.value = re.data
+			}
+		})
 	})
 </script>
 

+ 35 - 17
src/views/exm/questionnaireManagement/index.vue

@@ -1,19 +1,21 @@
 <template>
 	<div class="task-container">
 		<a-form layout="inline" :model="queryParam">
-			<a-form-item label="年级:">
-				<a-select style="min-width: 150px" v-model:value="queryParam.gradeLevel" allowClear placeholder="年级">
-					<a-select-option v-for="item in levelEnum" :key="item.key" :value="item.key">
-						{{ item.value }}
-					</a-select-option>
+			<a-form-item label="考试标题:">
+				<a-input v-model:value="queryParam.examName" placeholder="请输入考试标题" style="min-width: 200px" allowClear />
+			</a-form-item>
+			<a-form-item label="考试状态:">
+				<a-select style="min-width: 150px" v-model:value="queryParam.examStatus" allowClear placeholder="考试状态">
+					<a-select-option :value="0">未开始</a-select-option>
+					<a-select-option :value="1">已开始</a-select-option>
+					<a-select-option :value="2">已结束</a-select-option>
 				</a-select>
 			</a-form-item>
 			<a-form-item>
 				<a-button type="primary" @click="submitForm">查询</a-button>
-				<a-button style="margin-left: 20px" type="primary" @click="createTask">创建任务</a-button>
+				<a-button style="margin-left: 20px" type="primary" @click="createTask">创建考试</a-button>
 			</a-form-item>
 		</a-form>
-
 		<a-table
 			:loading="listLoading"
 			:data-source="tableData"
@@ -23,9 +25,24 @@
 			style="margin-top: 16px"
 		>
 			<a-table-column title="Id" dataIndex="id" key="id" width="100" />
-			<a-table-column title="标题" dataIndex="title" key="title" />
-			<!-- <a-table-column title="学级" dataIndex="gradeLevel" key="gradeLevel" :customRender="levelFormatter" /> -->
-			<a-table-column title="发送人" dataIndex="createUserName" key="createUserName" width="100" />
+			<a-table-column title="考试标题" dataIndex="examName" key="examName" />
+			<a-table-column title="考试状态" dataIndex="examStatus" key="examStatus" width="120">
+				<template #default="{ record }">
+					<a-tag :color="record.examStatus === 0 ? 'default' : record.examStatus === 1 ? 'processing' : 'success'">
+						{{ record.examStatus === 0 ? '未开始' : record.examStatus === 1 ? '已开始' : '已结束' }}
+					</a-tag>
+				</template>
+			</a-table-column>
+			<a-table-column title="开始时间" dataIndex="startTime" key="startTime" width="160">
+				<template #default="{ record }">
+					{{ formatDateTime(record.startTime) }}
+				</template>
+			</a-table-column>
+			<a-table-column title="结束时间" dataIndex="endTime" key="endTime" width="160">
+				<template #default="{ record }">
+					{{ formatDateTime(record.endTime) }}
+				</template>
+			</a-table-column>
 			<a-table-column title="创建时间" dataIndex="createTime" key="createTime" width="160">
 				<template #default="{ record }">
 					{{ formatDateTime(record.createTime) }}
@@ -65,7 +82,7 @@
 <script setup>
 	import { ref, reactive, onMounted } from 'vue'
 	import { message, Modal } from 'ant-design-vue'
-	import taskApi from '@/api/exam/paper/task.js'
+	import examManagerApi from '@/api/exam/paper/examManager.js'
 	import TaskEdit from './form.vue'
 	import { useExamStore } from '@/store/exam.js'
 	import { storeToRefs } from 'pinia'
@@ -77,7 +94,8 @@
 	const editId = ref(null)
 
 	const queryParam = reactive({
-		gradeLevel: null,
+		examName: null,
+		examStatus: null,
 		pageIndex: 1,
 		pageSize: 10
 	})
@@ -95,7 +113,7 @@
 			}
 			delete params.pageIndex
 			delete params.pageSize
-			const data = await taskApi.pageList(params)
+			const data = await examManagerApi.pageList(params)
 			tableData.value = data.records || []
 			total.value = data.total || 0
 			queryParam.pageIndex = data.current || 1
@@ -126,16 +144,16 @@
 
 	const editTask = (record) => {
 		drawerVisible.value = true
-		drawerTitle.value = '编辑任务'
+		drawerTitle.value = '编辑考试'
 		editId.value = record.id
 	}
 
 	const deleteTask = (record) => {
 		Modal.confirm({
-			title: '确认删除该任务吗?',
+			title: '确认删除该考试吗?',
 			onOk: async () => {
 				try {
-					await taskApi.deleteTask(record.id)
+					await examManagerApi.deleteExam([{ id: record.id }])
 					message.success('删除成功')
 					fetchList()
 				} catch (e) {
@@ -150,7 +168,7 @@
 	}
 	const createTask = () => {
 		drawerVisible.value = true
-		drawerTitle.value = '创建任务'
+		drawerTitle.value = '创建考试'
 		editId.value = null
 	}
 	const onEditSuccess = () => {

+ 96 - 79
src/views/portal/components/Header.vue

@@ -1,14 +1,12 @@
 <template>
 	<div>
-<!--		//根据平台不同传不同参0后台(管理员)1老师2学生   eduIdentity -->
-		<!--		{{userInfo.eduIdentity}}-->
-<!--		管理员-->
+		<!-- //根据平台不同传不同参0后台(管理员)1老师2学生   eduIdentity
+		{{userInfo.eduIdentity}}
+		管理员 -->
 		<div v-if="userInfo.eduIdentity == 0" style="display: flex; width: 100vw; justify-content: space-between; align-items: center">
 			<div style="display: flex; padding-left: 10%">
 				<div style="width: 120px; height: 55px; background-color: brown" />
-
 				<a-menu v-model:selectedKeys="current" mode="horizontal" theme="light" style="line-height: 55px">
-<!--					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal">首页</a-menu-item>-->
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceManagement">资源管理</a-menu-item>
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceManagement">全院课程</a-menu-item>
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceManagement">全院开课</a-menu-item>
@@ -25,22 +23,10 @@
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceManagement">公告发布</a-menu-item>
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceManagement">管理员登录</a-menu-item>
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceManagement">密码找回</a-menu-item>
-<!--					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/personalResources">个人资源</a-menu-item>-->
-<!--					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseCenter">课程中心</a-menu-item>-->
-<!--					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement"-->
-<!--						>课程管理</a-menu-item-->
-<!--					>-->
 				</a-menu>
 			</div>
 
 			<div class="header-right">
-				<!-- <a-input-search placeholder="输入关键词搜索" style="width: 200px" /> -->
-				<!-- <div style="display: flex">
-					<SearchOutlined :style="{ fontSize: '16px', color: '#00000083' }" />
-					<div style="width: 5px"></div>
-					<span style="font-size: 12px; color: #00000083">搜索</span>
-				</div>
-				<div style="width: 20px"></div> -->
 				<div style="display: flex">
 					<UserOutlined :style="{ fontSize: '16px', color: '#00000083' }" />
 					<div style="width: 5px"></div>
@@ -50,15 +36,12 @@
 				<div style="display: flex">
 					<span style="font-size: 12px; color: #00000083">注册</span>
 				</div>
-				<!-- <a-button type="primary">登录</a-button>
-				<a-button>注册</a-button> -->
 			</div>
 		</div>
 		<div v-if="userInfo.eduIdentity == 1" style="display: flex; width: 100vw; justify-content: space-between; align-items: center">
 			<div style="display: flex; padding-left: 10%">
 				<div style="width: 120px; height: 55px; background-color: brown" />
 				<a-menu v-model:selectedKeys="current" mode="horizontal" theme="light" style="line-height: 55px">
-<!--					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal">首页</a-menu-item>-->
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceCenter">资源中心</a-menu-item>
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/personalResources">个人资源</a-menu-item>
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">课程管理</a-menu-item>
@@ -74,21 +57,10 @@
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">课程公告发布</a-menu-item>
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">登录</a-menu-item>
 					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">密码找回</a-menu-item>
-
-
-<!--					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceManagement">资源管理</a-menu-item>-->
-<!--					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseCenter">课程中心</a-menu-item>-->
 				</a-menu>
 			</div>
 
 			<div class="header-right">
-				<!-- <a-input-search placeholder="输入关键词搜索" style="width: 200px" /> -->
-				<!-- <div style="display: flex">
-					<SearchOutlined :style="{ fontSize: '16px', color: '#00000083' }" />
-					<div style="width: 5px"></div>
-					<span style="font-size: 12px; color: #00000083">搜索</span>
-				</div>
-				<div style="width: 20px"></div> -->
 				<div style="display: flex">
 					<UserOutlined :style="{ fontSize: '16px', color: '#00000083' }" />
 					<div style="width: 5px"></div>
@@ -98,50 +70,41 @@
 				<div style="display: flex">
 					<span style="font-size: 12px; color: #00000083">注册</span>
 				</div>
-				<!-- <a-button type="primary">登录</a-button>
-				<a-button>注册</a-button> -->
 			</div>
 		</div>
-		<div v-if="userInfo.eduIdentity == 2" style="display: flex; width: 100vw; justify-content: space-between; align-items: center">
-			<div style="display: flex; padding-left: 10%">
-				<div style="width: 120px; height: 55px; background-color: brown" />
-				<a-menu v-model:selectedKeys="current" mode="horizontal" theme="light" style="line-height: 55px">
-<!--					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal">首页</a-menu-item>-->
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceCenter">资源中心</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseCenter">课程中心</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceManagement">课程详情</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/personalResources">我的考试</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">我的作业</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/resourceCenter">调查问卷</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">个人中心</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">论坛</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">站内信</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">课程公告</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/personalResources">学习足迹</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">登录</a-menu-item>
-					<a-menu-item style="margin-left: 10px; margin-right: 10px" key="portal/courseManagement">密码找回</a-menu-item>
+		<div v-if="userInfo.eduIdentity == 2" class="fcbc">
+			<div class="headerBtn">
+				<div style="width: 120px; background-color: brown;" ></div>
+				<a-menu v-model:selectedKeys="current" mode="horizontal">
+					<a-menu-item key="portal/resourceCenter">资源中心</a-menu-item>
+					<a-menu-item key="portal/courseCenter">课程中心</a-menu-item>
+					<a-menu-item key="portal/resourceManagement">课程详情</a-menu-item>
+					<a-sub-menu key="myList">
+						<template #title>我的</template>
+						<a-menu-item key="portal/personalResources">我的考试</a-menu-item>
+						<a-menu-item key="portal/courseManagement">我的作业</a-menu-item>
+						<a-menu-item key="portal/courseManagement">调查问卷</a-menu-item>
+					</a-sub-menu>
 				</a-menu>
 			</div>
-
 			<div class="header-right">
-				<!-- <a-input-search placeholder="输入关键词搜索" style="width: 200px" /> -->
-				<!-- <div style="display: flex">
-					<SearchOutlined :style="{ fontSize: '16px', color: '#00000083' }" />
-					<div style="width: 5px"></div>
-					<span style="font-size: 12px; color: #00000083">搜索</span>
-				</div>
-				<div style="width: 20px"></div> -->
-				<div style="display: flex">
+				<a-dropdown>
 					<UserOutlined :style="{ fontSize: '16px', color: '#00000083' }" />
-					<div style="width: 5px"></div>
-					<span style="font-size: 12px; color: #00000083">登录</span>
-				</div>
-				<div style="width: 20px"></div>
-				<div style="display: flex">
-					<span style="font-size: 12px; color: #00000083">注册</span>
-				</div>
-				<!-- <a-button type="primary">登录</a-button>
-				<a-button>注册</a-button> -->
+					<template #overlay>
+						<a-menu>
+							<a-menu-item key="portal/courseManagement">个人中心</a-menu-item>
+							<a-menu-item key="portal/courseManagement">论坛</a-menu-item>
+							<a-menu-item key="portal/courseManagement">站内信</a-menu-item>
+							<a-menu-item key="portal/courseManagement">课程公告</a-menu-item>
+							<a-menu-item key="portal/personalResources">学习足迹</a-menu-item>
+							<a-menu-item key="portal/courseManagement">登录</a-menu-item>
+							<a-menu-item key="portal/courseManagement">密码找回</a-menu-item>
+							<a-menu-item key="outLogin" @click="handleUser('outLogin')">退出登陆</a-menu-item>
+						</a-menu>
+					</template>
+				</a-dropdown>
+				<div class="ml-2" @click="slogin">登录</div>
+				<div class="ml-2">注册</div>
 			</div>
 		</div>
 		<div class="line" style=""></div>
@@ -149,44 +112,98 @@
 </template>
 
 <script setup>
+	import { createVNode } from 'vue'
+	import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
+	import { Modal } from 'ant-design-vue'
+	import loginApi from '@/api/auth/loginApi'
+	import { message } from 'ant-design-vue'
 	import { ref } from 'vue'
 	import { useRouter, useRoute } from 'vue-router'
 	const router = useRouter()
-	const current = ref(['1']) // 默认选中“资源中心”
+	const route = useRoute()
+	const current = ref([route.path.slice(1)]) // 默认选中“资源中心”
 	import tool from '@/utils/tool'
 	const emit = defineEmits(['onChangeCurrent'])
 	const userInfo = tool.data.get('USER_INFO')
 	watch(
 		() => current.value,
 		(newVal, oldVal) => {
-			console.log('新的是', newVal, '旧的是', oldVal)
 			if (newVal != oldVal) {
 				emit('onChangeCurrent', newVal)
-
-				// router.push({
-				// 	path: '/' + newVal
-				// })
 			}
 		}
 	)
-
+	const slogin = () =>{
+		router.push({
+			path: '/slogin'
+		})
+	}
+	const handleUser = (key) => {
+		if (key === 'outLogin') {
+			Modal.confirm({
+				title: '提示',
+				content: '确认退出当前用户?',
+				icon: createVNode(ExclamationCircleOutlined),
+				maskClosable: false,
+				onOk() {
+					// 取得缓存中的token
+					const token = tool.data.get('TOKEN')
+					const param = {
+						token: token
+					}
+					message.loading('退出中...', 1)
+					loginApi
+						.logout(param)
+						.then(() => {
+							// 清理掉个人的一些信息
+							tool.data.remove('TOKEN')
+							tool.data.remove('USER_INFO')
+							tool.data.remove('MENU')
+							tool.data.remove('PERMISSIONS')
+							tool.cookie.remove('Token')
+							router.replace({ path: '/slogin' })
+						})
+						.catch(() => {
+							tool.data.clear()
+							router.replace({ path: '/slogin' })
+							location.reload()
+						})
+				},
+				onCancel() {}
+			})
+		}
+	}
 
 </script>
 
-<style scoped>
+<style scoped lang="less">
 	.header {
 		display: flex;
 		align-items: center;
 		justify-content: space-between;
 		padding: 0 50px;
 	}
+	.headerBtn {
+		display: flex;
+		padding-left: 10%;
+		flex:1;
+		:deep(.ant-menu-horizontal) {
+			line-height: 55px;
+		}
+	}
 	.header-right {
 		display: flex;
 		align-items: center;
-
 		margin-right: 20%;
 	}
-
+	.fcbc {
+		display: flex;
+		width: 100%;
+		justify-content: space-between;
+		align-items: center;
+		background-color: #fff;
+	}
+	
 	.line {
 		width: 100%;
 		height: 0.25px;

+ 137 - 0
src/views/student/classCentre/ask.vue

@@ -0,0 +1,137 @@
+<template>
+	<a-list
+		class="demo-loadmore-list"
+		:loading="initLoading"
+		item-layout="horizontal"
+		:data-source="listData"
+		:pagination="pagination"
+	>
+		<template #renderItem="{ item }">
+			<a-list-item>
+				<a-skeleton avatar :title="false" :loading="!!item.loading" active>
+					<div style="width: 100%">
+						<a-list-item-meta>
+							<template #title>{{ item.info }}</template>
+							<template #avatar v-if="item.avatar">
+								<a-avatar :src="item.avatar" />
+							</template>
+						</a-list-item-meta>
+						{{ item.remark }}
+						<div class="flc">
+							<div @click="editNote(item)">
+								<a-tooltip title="编辑" :getPopupContainer="(trigger) => trigger.parentElement">
+									<edit-outlined />
+								</a-tooltip>
+							</div>
+							<div class="ml-2" @click="delNote(item)">
+								<a-tooltip title="删除" :getPopupContainer="(trigger) => trigger.parentElement">
+									<delete-outlined />
+								</a-tooltip>
+							</div>
+							<!-- <div class="ml-2" @click="giveFun(item)">
+								<a-tooltip title="点赞" :getPopupContainer="(trigger) => trigger.parentElement">
+									<like-outlined :style="{ color: item.isLike == 1 ? '#fa6c8d' : '' }" />
+								</a-tooltip>
+							</div> -->
+						</div>
+					</div>
+				</a-skeleton>
+			</a-list-item>
+		</template>
+	</a-list>
+</template>
+
+<script setup>
+	import classCentre from '@/api/student/classCentre'
+	import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
+	import { createVNode } from 'vue'
+	import { Modal } from 'ant-design-vue'
+	const props = defineProps({
+		idsObj: {
+			type: [Array, Object],
+			required: () => {}
+		}
+	})
+	const count = 3
+	const emit = defineEmits({ edit: null })
+	const initLoading = ref(true)
+	const loading = ref(false)
+	const data = ref([])
+	const listData = ref([])
+	const itemNote = ref({})
+	onMounted(() => {
+		loading.value = false
+		initLoading.value = false
+		getList()
+	})
+	const pagination = ref({
+		current:1,
+		onChange: (page) => {
+			pagination.value.current = page
+			getList()
+		},
+		pageSize: 10
+	})
+	const getList = () => {
+		classCentre
+			.askList(
+				{
+					current:pagination.value.current,
+					size: pagination.value.pageSize,
+					...props.idsObj
+				},
+				itemNote.value.noteId
+			)
+			.then((data) => {
+				data.records = data.records.map((r) => {
+					return {
+						...r,
+						loading: false
+					}
+				})
+				listData.value = data.records
+				pagination.value.total = data.total
+				loading.value = false
+				initLoading.value = false
+			})
+	}
+
+	const editNote = (e) => {
+		itemNote.value = e.id
+		emit('edit', JSON.parse(JSON.stringify(e)))
+	}
+	const delNote = (e) => {
+		Modal.confirm({
+			title: '确定要删除笔记',
+			icon: createVNode(ExclamationCircleOutlined),
+			onOk() {
+				classCentre.askDel([{ noteId: e.noteId }]).then((data) => {
+					getList()
+				})
+			},
+			onCancel() {
+				console.log('Cancel')
+			}
+		})
+	}
+	const giveFun = (e) => {
+		classCentre.askLike({ giveId: e.id }, e.giveNumSelf).then(() => {
+			getList()
+		})
+	}
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		getList
+	})
+</script>
+<style scoped lang="less">
+	.flc {
+		display: flex;
+		align-items: center;
+	}
+	.fcbc {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+	}
+</style>

+ 192 - 0
src/views/student/classCentre/form.vue

@@ -0,0 +1,192 @@
+<template>
+	<xn-form-container
+		:width="500"
+		:get-container="false"
+		:visible="visible"
+		:destroy-on-close="true"
+		@close="onClose"
+		:mask="false"
+	>
+		<div v-if="itemObj.key == 2" style="height: 100%">
+			<vue-office-pdf :src="itemObj.url" style="width: 100%; height: 100%" />
+		</div>
+		<div v-if="itemObj.key == 3" style="height: 100%">
+			<div v-for="(item, idx) in itemObj.srtInfoList" :key="idx">
+				<div>{{ item.startTime }}~{{ item.endTime }}</div>
+				<div style="cursor: pointer; padding: 10px 0" @click="videoSpeed(item)">{{ item.text }}</div>
+			</div>
+		</div>
+		<div v-if="itemObj.type == 2 || itemObj.type == 4">
+			<a-card :bordered="false">
+				<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
+					<a-row :gutter="16">
+						<a-col :span="24">
+							<a-form-item name="noteContent" v-if="itemObj.type == 2">
+								<a-textarea v-model:value="formData.noteContent" placeholder="请输入内容" :rows="4" />
+							</a-form-item>
+							<div v-if="itemObj.type == 4">
+								<a-form-item name="info" label="问题">
+									<a-textarea v-model:value="formData.info" placeholder="请输入问题" :rows="4" />
+								</a-form-item>
+							</div>
+						</a-col>
+					</a-row>
+				</a-form>
+				<div class="frc">
+					<a-button type="primary" :loading="submitLoading" @click="onSubmit">保存</a-button>
+				</div>
+				<div class="noteBox">
+					<note v-if="itemObj.type == 2" :idsObj="idsObj" ref="noteRef" @edit="editForm"></note>
+					<askDiv v-if="itemObj.type == 4" :idsObj="idsObj" ref="noteRef" @edit="editForm"></askDiv>
+				</div>
+			</a-card>
+		</div>
+	</xn-form-container>
+</template>
+
+<script setup>
+	import classCentre from '@/api/student/classCentre'
+	import { required } from '@/utils/formRules'
+	import note from './note.vue'
+	import askDiv from './ask.vue'
+	import VueOfficePdf from '@vue-office/pdf'
+	import axios from 'axios'
+	const props = defineProps({
+		rightItem: {
+			type: [Array, Object],
+			required: () => {}
+		},
+		idsObj: {
+			type: [Array, Object],
+			required: () => {}
+		}
+	})
+	const itemObj = computed(() => {
+		if (props.rightItem?.url) {
+			GetSrtInfo(props.rightItem.url)
+		}
+		return props.rightItem
+	})
+	const noteRef = ref()
+	const submitLoading = ref(false)
+	// 表单数据,也就是默认给一些数据
+	const formData = ref({})
+	const formRef = ref()
+	//tabs
+	const activeKey = ref('1')
+	// 默认是关闭状态
+	const visible = ref(false)
+	const emit = defineEmits({ successful: null, videoSpeed: null })
+	// 打开抽屉
+	const onOpen = (edit) => {
+		visible.value = true
+		if (edit) {
+			if (itemObj.value.type == 2) {
+				formData.value.noteId = edit.noteId
+				formData.value.noteContent = edit.noteContent
+			} else {
+				formData.value.info = edit.info
+				formData.value.id = edit.id
+			}
+		}
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value?.resetFields()
+		formData.value.id = null
+		formData.value.noteId = null
+		visible.value = false
+	}
+	// 默认要校验的
+	const formRules = {
+		noteContent: [required('请输入内容')],
+		info: [required('请输入内容')]
+	}
+	// 提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				submitLoading.value = true
+				classCentre[itemObj.value.type == 2 ? 'notesSubmitForm' : 'askSubmitForm'](
+					{
+						...props.idsObj,
+						...formData.value
+					},
+					itemObj.value.type == 2 ? formData.value.noteId : formData.value.id
+				).then(() => {
+					emit('successful')
+					formRef.value.resetFields()
+					noteRef.value.getList()
+				})
+			})
+			.finally(() => {
+				submitLoading.value = false
+			})
+	}
+	const editForm = (e) => {
+		onOpen(e)
+		if (itemObj.value.type == 2) {
+			formData.value.noteId = e.noteId
+			formData.value.noteContent = e.noteContent
+		} else {
+			formData.value.info = e.info
+			formData.value.id = e.id
+		}
+	}
+	const videoSpeed = (e) => {
+		emit('videoSpeed', e)
+	}
+	//获取字幕内容,发送一个get请求
+	function GetSrtInfo(srtUrl) {
+		axios
+			.get(`${srtUrl}`)
+			.then(function (response) {
+				let textList = response.data
+					.split(/\n\s\n/)
+					.filter((item) => item != '')
+					.map((item, index) => {
+						let textItem = item.split(/\n/)
+						return {
+							index: index,
+							sort: textItem[0],
+							text: textItem[2],
+							startTime: ToSeconds(textItem[1].split(' --> ')[0]),
+							endTime: ToSeconds(textItem[1].split(' --> ')[1]),
+							timeLine: textItem[1]
+						}
+					})
+				itemObj.value.srtInfoList = textList
+				console.log('解析之后的字幕内容', textList)
+			})
+			.catch(function (error) {
+				console.log(error)
+			})
+	}
+	//将时间转化为秒
+	function ToSeconds(t) {
+		var s = 0.0
+		if (t) {
+			var p = t.split(':')
+			for (let i = 0; i < p.length; i++) {
+				s = s * 60 + parseFloat(p[i].replace(',', '.'))
+			}
+		}
+		return s
+	}
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>
+<style scoped lang="less">
+	.frc {
+		display: flex;
+		justify-content: flex-end;
+		align-items: center;
+	}
+	.noteBox {
+		height: 750px;
+		overflow-y: auto;
+	}
+</style>

+ 287 - 0
src/views/student/classCentre/index.vue

@@ -0,0 +1,287 @@
+<template>
+	<a-layout>
+		<a-layout-sider v-model:collapsed="collapsed" :trigger="null" collapsible collapsedWidth="0">
+			<div class="classTitle">
+				<div>{{ classDetail.courseName }}</div>
+			</div>
+			<a-menu v-model:openKeys="openKeys" v-model:selectedKeys="selectedKeys" theme="dark" mode="inline">
+				<template v-for="(item, idx) in classTimeList" :key="item.id">
+					<template v-if="!item.classHours">
+						<a-menu-item :key="item.id" v-for="(item, idx) in classTimeList">
+							<span class="mr-2">
+								<CheckSquareOutlined v-if="selectedKeys.includes(item.id)" />
+								<BorderOutlined v-else />
+							</span>
+							<span>{{ item.name }}</span>
+						</a-menu-item>
+					</template>
+					<template v-else>
+						<a-sub-menu :key="String(idx + 1)" v-for="(item, idx) in classTimeList">
+							<template #title>{{ item.name }}</template>
+							<a-menu-item :key="itemc.id" v-for="(itemc, idxc) in item.classHours">
+								<span class="mr-2">
+									<CheckSquareOutlined v-if="selectedKeys.includes(itemc.id)" />
+									<BorderOutlined v-else />
+								</span>
+								<span>{{ itemc.name }}</span>
+							</a-menu-item>
+						</a-sub-menu>
+					</template>
+				</template>
+			</a-menu>
+		</a-layout-sider>
+		<a-layout>
+			<a-layout-header style="padding: 0 20px">
+				<div class="flex-between">
+					<div>
+						<menu-unfold-outlined
+							v-if="collapsed"
+							class="trigger"
+							@click="() => (collapsed = !collapsed)"
+							style="font-size: 30px; color: #fff"
+						/>
+						<menu-fold-outlined
+							v-else
+							class="trigger"
+							@click="() => (collapsed = !collapsed)"
+							style="font-size: 30px; color: #fff"
+						/>
+					</div>
+				</div>
+			</a-layout-header>
+			<a-layout-content style="overflow-y: auto">
+				<a-card :bordered="false">
+					<video
+						style="height: 900px; width: 100%; background-color: #000"
+						ref="videoRef"
+						controls
+						Controlslist="nodownload noplaybackrate noremoteplayback disablePictureInPicture"
+						@timeupdate="timeUpdate"
+						:currentTime="currentTimenew"
+					>
+						<source :src="videoUrl" type="video/mp4" />
+						<source :src="videoUrl" type="video/ogg" />
+						您的浏览器不支持 HTML5 video 标签。
+					</video>
+					<rightMenu :idsObj="idsObj" :dataList="classTimeData" ref="rightNenuRef" @videoSpeed="videoSpeed"></rightMenu>
+				</a-card>
+				<div style="display: flex;justify-content: center;">
+					<a-card :bordered="false" class="mt-2" style="max-width: 1200px">
+						<a-tabs v-model:activeKey="tabsActiveKey">
+							<a-tab-pane key="1" tab="讲义">
+								<div style="height: 900px;">
+									<vue-office-pdf :src="itemObj.url" style="width: 100%; height: 100%" />
+								</div>
+							</a-tab-pane>
+							<a-tab-pane key="2" tab="字幕">
+								<div style="height: 900px; overflow-y: auto">
+									<div v-for="(item, idx) in danmuObj.srtInfoList" :key="idx">
+										<div>{{ item.startTime }}~{{ item.endTime }}</div>
+										<div style="cursor: pointer; padding: 10px 0" @click="videoSpeed(item)">{{ item.text }}</div>
+									</div>
+								</div>
+							</a-tab-pane>
+							<a-tab-pane key="3" tab="笔记">
+								<note :idsObj="idsObj" ref="noteRef" @edit="noteEdit"></note>
+							</a-tab-pane>
+							<a-tab-pane key="4" tab="问答">
+								<askDiv :idsObj="idsObj" ref="askDivRef" @edit="askEdit"></askDiv>
+							</a-tab-pane>
+						</a-tabs>
+					</a-card>
+				</div>
+			</a-layout-content>
+		</a-layout>
+	</a-layout>
+</template>
+
+<script setup name="classCentre">
+	import classCentre from '@/api/student/classCentre'
+	import rightMenu from './rightMenu.vue'
+	import { useRoute, useRouter } from 'vue-router'
+	import sysConfig from '@/config/index'
+	import note from './note.vue'
+	import askDiv from './ask.vue'
+	import VueOfficePdf from '@vue-office/pdf'
+	import axios from 'axios'
+	const route = useRoute()
+	const router = useRouter()
+	const classDetail = ref({})
+	const classTimeList = ref([])
+	const classTimeData = ref([])
+	const openKeys = ref(['1'])
+	const selectedKeys = ref([])
+	const collapsed = ref(false)
+	const videoRef = ref()
+	const currentTimenew = ref()
+	const idsObj = computed(() => {
+		let item = findNodeByKey(classTimeList.value, selectedKeys.value[0])
+		return {
+			courseId: route.query.id,
+			chapterId: item?.chapterId,
+			hourId: selectedKeys.value[0]
+		}
+	})
+	function findNodeByKey(list, id) {
+		for (const item of list) {
+			if (item.id === id) {
+				return item
+			}
+			if (item.classHours && Array.isArray(item.classHours)) {
+				const found = findNodeByKey(item.classHours, id)
+				if (found) return found
+			}
+		}
+		return null
+	}
+	const getClassData = () => {
+		classCentre.addViewCount({ courseId: route.query.id })
+		classCentre.courseDetail({ courseId: route.query.id }).then((data) => {
+			classDetail.value = data
+			classCentre.coursechapterList({ courseId: data.courseId }).then((data) => {
+				classTimeList.value = data
+				selectedKeys.value = [data[0]?.classHours[0].id]
+				if (selectedKeys.value[0]) {
+					classCentre.courseTimeDetail({ id: selectedKeys.value[0] }).then((data) => {
+						classTimeData.value = data.courseRelates.map((r) => {
+							return {
+								...r,
+								url: sysConfig.FILE_URL + r.url
+							}
+						})
+						videoRef.value.src = classTimeData.value.filter((r) => r.funcType == 1)[0]?.url
+					})
+				}
+			})
+		})
+	}
+	getClassData()
+
+	const tabsActiveKey = ref('1')
+	const noteRef = ref()
+	const askDivRef = ref()
+	const itemObj = computed(() => {
+		let item = classTimeData.value.length > 0 ? classTimeData.value.filter((r) => r.funcType == 2)[0] : { url: '' }
+		return item
+	})
+	const danmuObj = computed(() => {
+		let item = classTimeData.value.length > 0 ? classTimeData.value.filter((r) => r.funcType == 3)[0] : { url: '' }
+		console.log('🚀 ~ item:', item)
+		if (item?.url) {
+			GetSrtInfo(item.url)
+		}
+		return item
+	})
+	const videoUrl = ''
+	const newsschedule = ref(0)
+	const currTime = ref(null)
+	const maxTime = ref(0)
+	const initialtime = ref(0)
+	const videoContext = ref()
+	const timeStamp1 = ref()
+	const allTime = ref()
+	const biNum = ref()
+	const currentTime = ref()
+	const videoStart = () => {
+		videoContext.value = videoRef.value
+		if (initialtime.value > 0) {
+			videoContext.value.currentTime = initialtime.value
+			timeStamp1.value = initialtime.value
+		}
+	}
+	nextTick(() => {
+		videoStart()
+	})
+	const timeUpdate = (e) => {
+		//获取当前播放时间 durationinitialtime
+		if (timeStamp1.value != parseInt(e.target.currentTime)) {
+			allTime.value = parseInt(e.target.duration) //视频总时长(秒)
+			//对播放的时间进行整
+			timeStamp1.value = parseInt(e.target.currentTime) //播放进度 (秒)
+			biNum.value = Math.floor((timeStamp1.value / allTime.value) * 10000) / 100 //暂时没用到
+			currentTime.value = e.target.currentTime
+			if (newsschedule.value < 90) {
+				//newsschedule.value 请求接口获取的参数就是判断当前观看的时长是否小于90 小于90就禁止拖动进度条
+
+				if (e.srcElement.currentTime - currTime.value > 3) {
+					//这里拖动进度条时间 - 当前观看的时长 > 3秒 这边判定它为拖动进度条
+					e.srcElement.currentTime = currTime.value > maxTime.value ? currTime.value : maxTime.value
+					let newsVal = initialtime.value
+					if (newsVal) {
+						videoContext.value.currentTime = newsVal
+					}
+					//当前用户记录观看时间 大于 实施播放时间
+					if (currTime.value > newsVal) {
+						videoContext.value.currentTime = currTime.value
+					}
+					console.log('快进了')
+				}
+				currTime.value = e.srcElement.currentTime
+				maxTime.value = currTime.value > maxTime.value ? currTime.value : maxTime.value
+			}
+		}
+	}
+
+	const rightNenuRef = ref()
+	const noteEdit = (e) => {
+		rightNenuRef.value.selectBtn({ key: 6, type: 2 }, e)
+	}
+	const askEdit = (e) => {
+		rightNenuRef.value.selectBtn({ key: 7, type: 4 }, e)
+	}
+	const videoSpeed = (e) => {
+		currentTimenew.value = e.startTime
+	}
+	//获取字幕内容,发送一个get请求
+	function GetSrtInfo(srtUrl) {
+		axios
+			.get(`${srtUrl}`)
+			.then(function (response) {
+				let textList = response.data
+					.split(/\n\s\n/)
+					.filter((item) => item != '')
+					.map((item, index) => {
+						let textItem = item.split(/\n/)
+						return {
+							index: index,
+							sort: textItem[0],
+							text: textItem[2],
+							startTime: ToSeconds(textItem[1].split(' --> ')[0]),
+							endTime: ToSeconds(textItem[1].split(' --> ')[1]),
+							timeLine: textItem[1]
+						}
+					})
+				danmuObj.value.srtInfoList = textList
+				console.log('解析之后的字幕内容', textList)
+			})
+			.catch(function (error) {
+				console.log(error)
+			})
+	}
+	//将时间转化为秒
+	function ToSeconds(t) {
+		var s = 0.0
+		if (t) {
+			var p = t.split(':')
+			for (let i = 0; i < p.length; i++) {
+				s = s * 60 + parseFloat(p[i].replace(',', '.'))
+			}
+		}
+		return s
+	}
+</script>
+<style scoped lang="less">
+	.classTitle {
+		height: 64px;
+		width: 200px;
+		font-size: 18px;
+		color: #ccc;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+	.flex-between {
+		display: flex;
+		justify-content: space-between;
+	}
+</style>

+ 126 - 0
src/views/student/classCentre/note.vue

@@ -0,0 +1,126 @@
+<template>
+	<a-list
+		class="demo-loadmore-list"
+		:loading="initLoading"
+		item-layout="horizontal"
+		:data-source="listData"
+		:pagination="pagination"
+	>
+		<template #renderItem="{ item }">
+			<a-list-item>
+				<a-skeleton avatar :title="false" :loading="!!item.loading" active>
+					<div style="width: 100%">
+						<a-list-item-meta>
+							<template #title>{{ item.courseName }}</template>
+							<template #avatar v-if="item.avatar">
+								<a-avatar :src="item.avatar" />
+							</template>
+						</a-list-item-meta>
+						{{ item.noteContent }}
+						<div class="flc">
+							<div @click="editNote(item)">
+								<a-tooltip title="编辑" :getPopupContainer="(trigger) => trigger.parentElement">
+									<edit-outlined />
+								</a-tooltip>
+							</div>
+							<div class="ml-2" @click="delNote(item)">
+								<a-tooltip title="删除" :getPopupContainer="(trigger) => trigger.parentElement">
+									<delete-outlined />
+								</a-tooltip>
+							</div>
+						</div>
+					</div>
+				</a-skeleton>
+			</a-list-item>
+		</template>
+	</a-list>
+</template>
+
+<script setup>
+	import classCentre from '@/api/student/classCentre'
+	import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
+	import { createVNode } from 'vue'
+	import { Modal } from 'ant-design-vue'
+	const props = defineProps({
+		idsObj: {
+			type: [Array, Object],
+			required: () => {}
+		}
+	})
+	const count = 3
+	const emit = defineEmits({ edit: null })
+	const initLoading = ref(true)
+	const loading = ref(false)
+	const data = ref([])
+	const listData = ref([])
+	const itemNote = ref({})
+	onMounted(() => {
+		loading.value = false
+		initLoading.value = false
+		getList()
+	})
+	const pagination = ref({
+		current: 1,
+		onChange: (page) => {
+			pagination.value.current = page
+			getList()
+		},
+		pageSize: 10
+	})
+	const getList = () => {
+		classCentre
+			.notesList(
+				{
+					current: pagination.value.current,
+					size: pagination.value.pageSize,
+					...props.idsObj
+				},
+				itemNote.value.noteId
+			)
+			.then((data) => {
+				data.records = data.records.map((r) => {
+					return {
+						...r,
+						loading: false
+					}
+				})
+				listData.value = data.records
+				pagination.value.total = data.total
+				loading.value = false
+				initLoading.value = false
+			})
+	}
+	const editNote = (e) => {
+		itemNote.value = e.noteId
+		emit('edit', JSON.parse(JSON.stringify(e)))
+	}
+	const delNote = (e) => {
+		Modal.confirm({
+			title: '确定要删除笔记',
+			icon: createVNode(ExclamationCircleOutlined),
+			onOk() {
+				classCentre.notesEdit([{ noteId: e.noteId }]).then((data) => {
+					getList()
+				})
+			},
+			onCancel() {
+				console.log('Cancel')
+			}
+		})
+	}
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		getList
+	})
+</script>
+<style scoped lang="less">
+	.flc {
+		display: flex;
+		align-items: center;
+	}
+	.fcbc {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+	}
+</style>

+ 132 - 0
src/views/student/classCentre/rightMenu.vue

@@ -0,0 +1,132 @@
+<template>
+	<div class="rightBtn">
+		<a-menu v-model:selectedKeys="selectedKeys" style="width: 60px" mode="inline" theme="dark">
+			<a-menu-item :key="item.key" class="btnItem" v-for="(item, idx) in listBtn" @click="selectBtn(item)">
+				<div class="fcc">
+					<component :is="item.icon"></component>
+				</div>
+				<div class="fcc">{{ item.title }}</div>
+			</a-menu-item>
+		</a-menu>
+		<rightContent ref="formRef" :idsObj="props.idsObj" :rightItem="rightItem" @videoSpeed="videoSpeed"></rightContent>
+	</div>
+</template>
+
+<script setup name="rightMenu">
+	import classCentre from '@/api/student/classCentre'
+	import rightContent from './form.vue'
+	import { useRoute, useRouter } from 'vue-router'
+	const route = useRoute()
+	const router = useRouter()
+	const emit = defineEmits({ videoSpeed: null })
+	const props = defineProps({
+		dataList: {
+			type: [Array, Object],
+			default: () => []
+		},
+		idsObj: {
+			type: [Array, Object],
+			required: () => {}
+		}
+	})
+	import {
+		PlaySquareOutlined,
+		FileTextOutlined,
+		AlignCenterOutlined,
+		SnippetsOutlined,
+		CopyOutlined,
+		ReadOutlined,
+		QuestionCircleOutlined
+	} from '@ant-design/icons-vue'
+	const formRef = ref()
+	const rightItem = ref({})
+	const selectedKeys = ref([''])
+	const btnList = ref([
+		// {
+		// 	title: '视频',
+		// 	key: '1',
+		// 	icon: PlaySquareOutlined,
+		// 	type: 1
+		// },
+		{
+			title: '讲义',
+			key: '2',
+			icon: FileTextOutlined,
+			type: 1
+		},
+		{
+			title: '字幕',
+			key: '3',
+			icon: AlignCenterOutlined,
+			type: 1
+		},
+		{
+			title: '作业',
+			key: '4',
+			icon: SnippetsOutlined,
+			type: 3,
+			routerUrl: ''
+		},
+		{
+			title: '测验',
+			key: '5',
+			icon: CopyOutlined,
+			type: 3,
+			routerUrl: ''
+		},
+		{
+			title: '笔记',
+			key: '6',
+			icon: ReadOutlined,
+			type: 2
+		},
+		{
+			title: '问答',
+			key: '7',
+			icon: QuestionCircleOutlined,
+			type: 4
+		}
+	])
+	const selectBtn = (event, edit) => {
+		if (event.type == 3) {
+			router.push({
+				path: event.routerUrl,
+				query: {}
+			})
+		} else {
+			rightItem.value = event
+			formRef.value.onOpen(edit ? edit : '')
+		}
+	}
+	const listBtn = computed(() => {
+		return btnList.value.map((r) => {
+			const match = props.dataList.length && props.dataList.find((e) => r.key == e.funcType)
+			return match ? { ...r, ...match } : r
+		})
+	})
+	
+	const videoSpeed = (e) => {
+		emit('videoSpeed', e)
+	}
+	defineExpose({
+		selectBtn
+	})
+</script>
+<style scoped lang="less">
+	.rightBtn {
+		position: fixed;
+		right: 0;
+		top: 100px;
+		z-index: 99999;
+	}
+	.fcc {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+	:deep(.btnItem) {
+		height: auto;
+		line-height: normal;
+		padding: 10px 0 !important;
+	}
+</style>

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 146
stats.html


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels