Переглянути джерело

1.论坛展示(课程名-章节名-课时名)
2.收藏课程样式调整
3.添加通知/公告图标提示
4.课程公告样式调整
5.站内信样式调整

canghailong 6 місяців тому
батько
коміт
9229906dd1

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

@@ -74,6 +74,10 @@ export default {
 	classNotice(data) {
 		return request('notice/page', data, 'get')
 	},
+	//课程公告-详情
+	classNoticeDetail(data) {
+		return request('notice/detail', data, 'get')
+	},
 	//学习足迹埋点
 	footprintClassAdd(data) {
 		return request('courselog/add', data)

+ 0 - 13
src/views/courseCenter/components/ResourceList.vue

@@ -58,19 +58,6 @@
 						<span style="font-size: 16px; font-weight: bold">{{ item.courseName || '-' }}</span>
 						<span style="font-size: 12px">{{ item.majorIdName || '-' }}</span>
 						<span style="font-size: 12px">{{ item.teacherIdName || '-' }}</span>
-						<!-- <div style="display: flex; justify-content: space-between">
-							<div style="display: flex; justify-content: center; align-items: center">
-								<FieldTimeOutlined />
-								<div style="width: 5px"></div>
-								<span style="font-size: 12px">{{ item.uploadTime }}</span>
-							</div>
-
-							<div style="display: flex; justify-content: center; align-items: center">
-								<EyeOutlined />
-								<div style="width: 5px"></div>
-								<span style="font-size: 12px">{{ item.viewCount }}</span>
-							</div>
-						</div> -->
 					</div>
 				</div>
 			</a-col>

+ 29 - 8
src/views/forum/addForum.vue

@@ -3,12 +3,17 @@
 		<a-card :bordered="false" style="width: 1200px">
 			<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
 				<a-row :gutter="16">
+					<a-col :span="8">
+						<a-form-item label="类型:">
+							<span>{{ forumTitle }}</span>
+						</a-form-item>
+					</a-col>
 					<a-col :span="8">
 						<a-form-item label="标题:" name="postTitle">
 							<a-input v-model:value="formData.postTitle" placeholder="请输入标题" allow-clear />
 						</a-form-item>
 					</a-col>
-					<a-col :span="8" v-if="formData.postType==0">
+					<a-col :span="8" v-if="formData.postType == 0">
 						<a-form-item label="分类:" name="typeId">
 							<a-select
 								v-model:value="formData.typeId"
@@ -74,6 +79,14 @@
 	// 模块ID
 	const moduleId = ref('')
 	const usertypeOptions = ref([])
+	const forumTitle = computed(() => {
+		if (route.query.postType == 1) {
+			return '技术支持'
+		}
+		if (route.query.postType == 2) {
+			return '内容纠错'
+		}
+	})
 	const filterOption = (input, option) => {
 		return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
 	}
@@ -136,13 +149,13 @@
 				if (route.query.postType == 1) {
 					params = {
 						...formData.value,
-						...browserObj.value,
+						...browserObj.value
 					}
 				}
 				if (route.query.postType == 2) {
 					params = {
 						...formData.value,
-						...errorVal.value,
+						...errorVal.value
 					}
 				}
 				forumApi.submitForm(params).then(() => {
@@ -154,6 +167,17 @@
 				submitLoading.value = false
 			})
 	}
+	const editContent = () => {
+		let videoUrl = route.query?.videoUrl ? decodeURIComponent(atob(route.query.videoUrl)) : ''
+		let html = `<p>${route.query.courseName ? route.query.courseName + '-' : ''}${route.query.title}${
+			route.query.chapterName ? '-' + route.query.chapterName : ''
+		}</p>${
+			videoUrl
+				? `<video controls name="media" style="width:100%; height: auto;"><source src="${videoUrl}"></video>`
+				: ''
+		}`
+		formData.value.postContent = html
+	}
 	//获取操作系统/浏览器等信息
 	const browserObj = ref({})
 	function getBower() {
@@ -168,6 +192,7 @@
 				deviceType: 0
 			}
 		}
+		editContent()
 	}
 	//纠错
 	const errorVal = ref({})
@@ -178,10 +203,6 @@
 				resourceId: route.query.id
 			}
 		}
-		let videoUrl = route.query.videoUrl ? decodeURIComponent(atob(route.query.videoUrl)) : ''
-		if (videoUrl) {
-			let html = `<p>${route.query.title}</p><video controls name="media" style="width:100%; height: auto;"><source src="${videoUrl}"></video>`
-			formData.value.postContent = html
-		}
+		editContent()
 	}
 </script>

+ 1 - 0
src/views/forum/detail.vue

@@ -140,6 +140,7 @@
 				chapterId: route.query.chapterId,
 				courseName: route.query.courseName,
 				chapterName: route.query.chapterName,
+				courseName:`${route.query.courseName}-${route.query.title}-${route.query.chapterName}`,
 				...pagination.value
 			})
 			.then((data) => {

+ 3 - 1
src/views/portal/components/Header.vue

@@ -35,7 +35,7 @@
 						<a-menu-item key="4" @click="jump('/classNotice')">课程公告</a-menu-item>
 						<a-menu-item key="5" @click="jump('/learningFootprint')">学习足迹</a-menu-item>
 						<a-menu-item key="7" @click="jump('/passwordRetrieve')">密码找回</a-menu-item>
-						<a-menu-item key="8" @click="outLogin()">退出登</a-menu-item>
+						<a-menu-item key="8" @click="outLogin()">退出登</a-menu-item>
 					</a-menu>
 				</template>
 			</a-dropdown>
@@ -43,6 +43,7 @@
 				<UserOutlined :style="{ fontSize: '16px', color: '#00000083' }" />
 				<div class="ml-2 cur" @click="jump('/slogin')">登录</div>
 			</div>
+			<headerIcon></headerIcon>
 		</div>
 	</div>
 	<div class="line"></div>
@@ -57,6 +58,7 @@
 	import { ref } from 'vue'
 	import { useRouter, useRoute } from 'vue-router'
 	import tool from '@/utils/tool'
+	import headerIcon from './headerIcon.vue'
 	const router = useRouter()
 	const route = useRoute()
 	const current = ref([route.path.slice(1)]) // 默认选中“资源中心”

+ 50 - 0
src/views/portal/components/headerIcon.vue

@@ -0,0 +1,50 @@
+<template>
+	<a-badge :count="bellNum" class="ml-3 cur" @click="jump('/inSsiteMessage')">
+		<bell-outlined style="font-size: 20px" />
+	</a-badge>
+	<a-badge :count="notiNum" class="ml-3 cur" @click="jump('/classNotice')">
+		<notification-outlined style="font-size: 20px" />
+	</a-badge>
+</template>
+
+<script setup>
+	import userCenterApi from '@/api/sys/userCenterApi'
+	import classCentre from '@/api/student/classCentre'
+	import { useRouter, useRoute } from 'vue-router'
+	const router = useRouter()
+	const route = useRoute()
+	const bellNum = ref(0)
+	const notiNum = ref(0)
+	function getList() {
+		userCenterApi
+			.userLoginUnreadMessagePage({
+				current: 1,
+				size: 9999
+			})
+			.then((res) => {
+				bellNum.value = res.records.filter((r) => !r.read).length
+			})
+		classCentre
+			.classNotice({
+				current: 1,
+				size: 9999
+			})
+			.then((res) => {
+				notiNum.value = res.records.filter((r) => !r.read).length
+			})
+	}
+	const jump = (url) => {
+		router.push({
+			path: url
+		})
+	}
+	onMounted(() => {
+		getList()
+	})
+</script>
+
+<style scoped lang="less">
+	.cur {
+		cursor: pointer;
+	}
+</style>

+ 88 - 0
src/views/student/In-site-message/detail.vue

@@ -0,0 +1,88 @@
+<template>
+	<xn-form-container title="详情" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
+		<a-descriptions :column="1" size="middle" bordered class="mb-2">
+			<a-descriptions-item label="主题">{{ formData.subject }}</a-descriptions-item>
+			<a-descriptions-item label="发送时间">{{ formData.createTime }}</a-descriptions-item>
+		</a-descriptions>
+		<a-form ref="formRef" :model="formData" layout="vertical">
+			<a-form-item label="内容:" name="content">
+				<span>{{ formData.content }}</span>
+			</a-form-item>
+			<a-form-item label="查收情况:" name="receiveInfoList">
+				<s-table
+					ref="table"
+					:columns="columns"
+					:data="loadData"
+					:alert="false"
+					:showPagination="false"
+					bordered
+					:row-key="(record) => record.id"
+					:scroll="{x:'auto'}"
+				>
+					<template #bodyCell="{ column, record }">
+						<template v-if="column.dataIndex === 'read'">
+							<span v-if="record.read" style="color: #d9d9d9">已读</span>
+							<span v-else style="color: #ff4d4f">未读</span>
+						</template>
+					</template>
+				</s-table>
+			</a-form-item>
+		</a-form>
+	</xn-form-container>
+</template>
+
+<script setup name="inSiteMessageDetail">
+	import userCenterApi from '@/api/sys/userCenterApi'
+	const emits = defineEmits(['refresh'])
+	const receiveInfoList = ref([])
+
+	// 默认是关闭状态
+	let visible = $ref(false)
+	const formRef = ref()
+	// 表单数据
+	const formData = ref({})
+	const table = ref()
+	const columns = [
+		{
+			title: '姓名',
+			dataIndex: 'receiveUserName'
+		},
+		{
+			title: '是否已读',
+			dataIndex: 'read',
+			width: 120
+		}
+	]
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible = true
+		getMessageList(record)
+	}
+	// 获取站内信列表
+	const getMessageList = (record) => {
+		const param = {
+			id: record.id
+		}
+		userCenterApi.userLoginUnreadMessageDetail(param).then((data) => {
+			Object.assign(record, data)
+			formData.value = record
+			receiveInfoList.value = data.receiveInfoList
+			table.value.refresh(true)
+		})
+	}
+	const loadData = () => {
+		return new Promise((resolve) => {
+			resolve(receiveInfoList.value)
+		})
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		receiveInfoList.value = []
+		visible = false
+		emits('refresh')
+	}
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>

+ 63 - 106
src/views/student/In-site-message/index.vue

@@ -1,133 +1,90 @@
 <template>
-	<a-card title="站内信" :bordered="false" :bodyStyle="miniMessageBodyStyle">
-		<div class="index-message-list">
-			<a-list :data-source="messageList" size="small" :loading="miniMessageLoading">
-				<template #renderItem="{ item }">
-					<a-list-item>
-						<a-list-item-meta :description="formatDateTime(item.createTime)">
-							<template #title>
-								<a @click="messageDetail(item)">{{ item.subject }}</a>
-							</template>
-						</a-list-item-meta>
-					</a-list-item>
-				</template>
-			</a-list>
-		</div>
-		<xn-form-container title="详情" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
-			<a-form ref="formRef" :model="formData" layout="vertical">
-				<a-form-item label="主题:" name="subject">
-					<span>{{ formData.subject }}</span>
-				</a-form-item>
-				<a-form-item label="发送时间:" name="createTime">
-					<span>{{ formatDateTime(formData.createTime)}}</span>
-				</a-form-item>
-				<a-form-item label="内容:" name="content">
-					<span>{{ formData.content }}</span>
-				</a-form-item>
-				<a-form-item label="查收情况:" name="receiveInfoList">
-					<s-table
-						ref="table"
-						:columns="columns"
-						:data="loadData"
-						:alert="false"
-						:showPagination="false"
-						bordered
-						:row-key="(record) => record.id"
-						:scroll="{x:'auto'}"
-					>
+	<a-card>
+		<a-row :gutter="10">
+			<a-col :span="4">
+				<a-menu id="userMessage" v-model:selected-keys="selectedKeys" mode="inline" @click="handleClick">
+					<a-menu-item :key="messageCategory.value" v-for="messageCategory in messageCategoryList">{{
+						messageCategory.label
+					}}</a-menu-item>
+				</a-menu>
+			</a-col>
+			<a-col :span="20">
+				<div style="margin-top: -16px;">
+					<s-table ref="table" :columns="columns" :data="loadData" bordered :row-key="(record) => record.id">
 						<template #bodyCell="{ column, record }">
+							<template v-if="column.dataIndex === 'subject'">
+								<ellipsis :length="40" tooltip>
+									{{ record.subject }}
+								</ellipsis>
+							</template>
+							<template v-if="column.dataIndex == 'createTime'">{{ formatDateTime(record.createTime) }}</template>
 							<template v-if="column.dataIndex === 'read'">
 								<span v-if="record.read" style="color: #d9d9d9">已读</span>
 								<span v-else style="color: #ff4d4f">未读</span>
 							</template>
+							<template v-if="column.dataIndex === 'action'">
+								<a-space>
+									<a @click="detailRef.onOpen(record)">详情</a>
+								</a-space>
+							</template>
 						</template>
 					</s-table>
-				</a-form-item>
-			</a-form>
-		</xn-form-container>
+				</div>
+			</a-col>
+			<detail ref="detailRef" @refresh="refresh" />
+		</a-row>
 	</a-card>
 </template>
 
-<script setup name="miniMessage">
-	import indexApi from '@/api/sys/indexApi'
-	import { onMounted } from 'vue'
-	import router from '@/router'
+<script setup name="inSiteMessage">
+	import detail from './detail.vue'
+	import userCenterApi from '@/api/sys/userCenterApi'
+	import tool from '@/utils/tool'
+	import { nextTick } from 'vue'
 	import { parseTime } from '@/utils/exam'
-	const formatDateTime = (val) => {
+	function formatDateTime(val) {
 		if (!val) return ''
 		return parseTime(val, '{y}-{m}-{d} {h}:{i}:{s}')
 	}
-	const miniMessageLoading = ref(false)
-	const messageList = ref([])
-	const miniMessageBodyStyle = ref({
-		'padding-top': '10px'
-	})
-	onMounted(() => {
-		// 进来后执行查询
-		getMessageList()
-	})
-	// 获取站内信列表
-	const getMessageList = () => {
-		miniMessageLoading.value = true
-		indexApi
-			.indexMessageList()
-			.then((data) => {
-				messageList.value = data
-			})
-			.finally(() => {
-				miniMessageLoading.value = false
-			})
-	}
-	// 跳转,用于点击更多按钮
-	const leaveFor = (url = '/') => {
-		router.replace({ path: url, query: { tab: 'userMessage' } })
-	}
-	// 点击详情
-	const messageDetail = (message) => {
-		visible.value = true
-		const param = {
-			id: message.id
-		}
-		indexApi.indexMessageDetail(param).then((data) => {
-			Object.assign(message, data)
-			formData.value = message
-			receiveInfoList.value = data.receiveInfoList
-			table.value.refresh(true)
-		})
-	}
 
-	const loadData = () => {
-		return new Promise((resolve) => {
-			resolve(receiveInfoList.value)
-		})
-	}
-	// 以下部分是抽屉的
-	const visible = ref(false)
-	const formRef = ref()
-	const receiveInfoList = ref([])
-	const formData = ref({})
+	const messageCategoryList = tool.dictList('MESSAGE_CATEGORY')
+	const selectedKeys = ref(new Array(messageCategoryList[0].value))
 	const table = ref()
+	const detailRef = ref()
 	const columns = [
 		{
-			title: '姓名',
-			dataIndex: 'receiveUserName'
+			title: '主题',
+			dataIndex: 'subject'
+		},
+		{
+			title: '发送时间',
+			dataIndex: 'createTime',
 		},
 		{
 			title: '是否已读',
 			dataIndex: 'read',
-			width: 120
+			width: '100px'
+		},
+		{
+			title: '操作',
+			dataIndex: 'action',
+			width: '100px'
 		}
 	]
-	// 关闭抽屉
-	const onClose = () => {
-		visible.value = false
-		formData.value = {}
-		receiveInfoList.value = []
+	const loadData = (parameter) => {
+		parameter.category = selectedKeys.value[0]
+		return userCenterApi.userLoginUnreadMessagePage(parameter).then((data) => {
+			return data
+		})
 	}
-</script>
-
-<style scoped>
-	.index-message-list {
-		overflow: auto;
+	const refresh = () => {
+		table.value.refresh(false)
 	}
-</style>
+	// 点击左侧菜单切换数据查询
+	const handleClick = () => {
+		// 先让上面的变量赋值,咱在查询
+		nextTick(() => {
+			table.value.refresh(true)
+		})
+	}
+</script>

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

@@ -109,6 +109,8 @@
 					},
 					itemObj.value.type == 2 ? formData.value.noteId : formData.value.id
 				).then(() => {
+					formData.value.noteId = ''
+					formData.value.id = ''
 					emit('successful')
 					formRef.value.resetFields()
 					noteRef.value.getList()

+ 14 - 10
src/views/student/classCentre/index.vue

@@ -67,7 +67,7 @@
 				<div style="display: flex; justify-content: center">
 					<a-card :bordered="false" class="mt-2" style="width: 1200px">
 						<a-tabs v-model:activeKey="tabsActiveKey">
-							<a-tab-pane key="1" tab="讲义" style="height: 900px;">
+							<a-tab-pane key="1" tab="讲义" style="height: 900px">
 								<handouts :itemObj="itemObj" :hourId="classHourData.id" v-if="classHourData"></handouts>
 							</a-tab-pane>
 							<a-tab-pane key="2" tab="字幕">
@@ -109,7 +109,7 @@
 	const videoRef = ref()
 	const currentTimenew = ref(0)
 	const idsObj = computed(() => {
-		let item = findNodeByKey(classTimeList.value, selectedKeys.value[0])
+		let item = findNodeByKey(classTimeList.value, selectedKeys.value[0],classTimeList.value[0])
 		return {
 			courseId: route.query.id,
 			chapterId: selectedKeys.value[0],
@@ -117,19 +117,23 @@
 			...item
 		}
 	})
-	function findNodeByKey(list, id) {
+	function findNodeByKey(list, id, parent = null) {
 		for (const item of list) {
-			if (item.id === id) {
-				return item
+			const itemWithParent = {
+				...item,
+				parent: parent
+			}
+			if (itemWithParent.id === id) {
+				return itemWithParent
 			}
 			if (item.classHours && Array.isArray(item.classHours)) {
-				const found = findNodeByKey(item.classHours, id)
+				const found = findNodeByKey(item.classHours, id, itemWithParent)
 				if (found) return found
 			}
 		}
 		return null
 	}
-	const videoPoster = computed(()=>{
+	const videoPoster = computed(() => {
 		return classTimeData.value.filter((r) => r.funcType == 0)[0]?.url
 	})
 	const getClassData = () => {
@@ -147,7 +151,7 @@
 	}
 	const classHourData = ref()
 	const menuClick = (e) => {
-		rightNenuRef.value.onClose();
+		rightNenuRef.value.onClose()
 		classCentre.courseTimeDetail({ id: e?.key ? e.key : selectedKeys.value[0] }).then((data) => {
 			classHourData.value = data
 			classTimeData.value = data.courseRelates.map((r) => {
@@ -249,13 +253,13 @@
 	}
 
 	const forumData = computed(() => {
-		let item = findNodeByKey(classTimeList.value, selectedKeys.value[0]) ?? {}
+		let item = findNodeByKey(classTimeList.value, selectedKeys.value[0],classTimeList.value[0]) ?? {}
 		if (!item.src) {
 			item.src = classTimeData.value.filter((r) => r.funcType == 1)[0]?.url
 		}
 		return {
 			id: classHourData.value?.id,
-			title: item?.name,
+			title: item.parent?.name,
 			videoUrl: btoa(encodeURIComponent(videoRef.value?.src ? videoRef.value.src : item.src)),
 			courseId: route.query.id,
 			chapterId: selectedKeys.value[0],

+ 98 - 26
src/views/student/classCollect/index.vue

@@ -1,43 +1,77 @@
 <template>
 	<a-card>
-		<a-list item-layout="vertical" size="large" :pagination="pagination" :data-source="listData">
-			<template #renderItem="{ item }">
-				<a-list-item key="item.title">
-					<a-list-item-meta @click="jump(item)" style="cursor: pointer;">
-						<template #title>{{ item.courseName }}</template>
-						<template #description>
-							<div v-html="item.courseDesc"></div>
-						</template>
-					</a-list-item-meta>
-					<div class="flc">
-						<div>院系名称:{{ item.collegeAllIdName }}</div>
-						<div class="ml-4">老师姓名:{{ item.teacherIdName }}</div>
-						<div class="ml-4">观看次数:{{ item.viewCount }}</div>
-						<div class="ml-4">课时数量:{{ item.hourCount }}</div>
+		<a-row :gutter="[16, 16]">
+			<a-col :span="8" v-for="(item, index) in listData" :key="index">
+				<div
+					style="border-radius: 10px 10px 5px 5px; border: 1px solid #dcdcdc; position: relative"
+					@click="jump(item)"
+				>
+					<div style="display: flex; position: relative">
+						<div
+							class="resource"
+							:style="{
+								backgroundSize: 'cover',
+								backgroundPosition: 'center',
+								backgroundImage:
+									'url(' +
+									(item.coverImagePath != '' && sysConfig.FILE_URL + item.coverImagePath
+										? sysConfig.FILE_URL + item.coverImagePath
+										: '') +
+									')'
+							}"
+						>
+							<PlayCircleOutlined
+								:style="{ fontSize: '40px', color: 'white' }"
+								style="position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%)"
+							/>
+							<div
+								style="
+									position: absolute;
+									bottom: 0;
+									right: 0;
+									background-color: #40a0ff;
+									color: white;
+									padding: 5px 20px;
+									border-radius: 4px;
+								"
+							>
+								共{{ item.hourCount }}节课
+							</div>
+						</div>
 					</div>
-				</a-list-item>
-			</template>
-		</a-list>
+					<div style="display: flex; flex-direction: column; padding: 5px 10px">
+						<span style="font-size: 16px; font-weight: bold">{{ item.courseName || '-' }}</span>
+						<span style="font-size: 12px">{{ item.majorIdName || '-' }}</span>
+						<span style="font-size: 12px">{{ item.teacherIdName || '-' }}</span>
+					</div>
+				</div>
+			</a-col>
+		</a-row>
+		<div style="display: flex; width: 100%; align-items: center; justify-content: center">
+			<a-pagination
+				v-model:current="pagination.current"
+				v-model:pageSize="pagination.size"
+				:total="pagination.total"
+				@change="onChange"
+			/>
+		</div>
 	</a-card>
 </template>
-<script setup>
+<script setup name="classCollect">
 	import classCentre from '@/api/student/classCentre'
 	import { useRoute, useRouter } from 'vue-router'
+	import sysConfig from '@/config/index'
 	const router = useRouter()
 	const listData = ref([])
 	const pagination = ref({
 		current: 1,
-		onChange: (page) => {
-			pagination.value.current = page
-			pagination.value.pageSize = size
-			getList(page)
-		},
-		pageSize: 10
+		pageSize: 10,
+		total: 0
 	})
 	const getList = () => {
 		classCentre
 			.classCollectList({
-				current:pagination.value.current,
+				current: pagination.value.current,
 				size: pagination.value.pageSize
 			})
 			.then((data) => {
@@ -45,6 +79,11 @@
 				pagination.value.total = data.total
 			})
 	}
+	const onChange = (page, pageSize) => {
+		pagination.value.current = page
+		pagination.value.pageSize = pageSize
+		getList()
+	}
 	const jump = (t) => {
 		router.push({
 			path: '/student/classCentre',
@@ -57,7 +96,7 @@
 		getList()
 	})
 </script>
-<style scoped>
+<style scoped lang="less">
 	.index-message-list {
 		overflow: auto;
 	}
@@ -65,4 +104,37 @@
 		display: flex;
 		justify-content: space-between;
 	}
+	.resource {
+		width: 100%;
+		height: 150px;
+		position: relative;
+		background: #00000011;
+		border-radius: 10px 10px 0 0;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		cursor: pointer;
+		position: relative;
+		overflow: hidden;
+	}
+	.resource::before {
+		content: '';
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		background-color: transparent;
+		transition: background-color 0.6s ease;
+		z-index: 1;
+	}
+
+	.resource:hover::before {
+		background-color: rgba(0, 0, 0, 0.4); /* 悬停变暗 */
+	}
+
+	.resource > * {
+		position: relative;
+		z-index: 2;
+	}
 </style>

+ 48 - 0
src/views/student/classNotice/detail.vue

@@ -0,0 +1,48 @@
+<template>
+	<xn-form-container title="详情" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
+		<a-descriptions :column="1" size="middle" bordered class="mb-2">
+			<a-descriptions-item label="标题">{{ formData.title }}</a-descriptions-item>
+			<a-descriptions-item label="发送时间">{{ formData.createTime }}</a-descriptions-item>
+			<a-descriptions-item label="是否已读">
+				<span v-if="formData.read" style="color: #d9d9d9">已读</span>
+				<span v-else style="color: #ff4d4f">未读</span>
+			</a-descriptions-item>
+			<a-descriptions-item label="内容">
+				<div style="width: 450px;">{{ formData.content }}</div>
+			</a-descriptions-item>
+		</a-descriptions>
+	</xn-form-container>
+</template>
+
+<script setup name="inSiteMessageDetail">
+	import classCentre from '@/api/student/classCentre'
+	const emits = defineEmits(['refresh'])
+
+	// 默认是关闭状态
+	let visible = $ref(false)
+	const formData = ref({})
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible = true
+		getMessageList(record)
+	}
+	// 获取站内信列表
+	const getMessageList = (record) => {
+		const param = {
+			noticeId: record.noticeId
+		}
+		classCentre.classNoticeDetail(param).then((data) => {
+			Object.assign(record, data)
+			formData.value = record
+		})
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		visible = false
+		emits('refresh')
+	}
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>

+ 64 - 44
src/views/student/classNotice/index.vue

@@ -1,57 +1,77 @@
 <template>
 	<a-card>
-		<a-list item-layout="vertical" size="large" :pagination="pagination" :data-source="listData" class="listBox">
-			<template #renderItem="{ item }">
-				<a-list-item style="padding:5px 24px;">
-					<a-list-item-meta style="margin-bottom: 5px;" :description="platformType(item.platform)">
-						<template #title>{{ item.title }}</template>
-					</a-list-item-meta>
-					{{ item.content }}
-				</a-list-item>
+		<s-table ref="table" :columns="columns" :data="loadData" bordered :row-key="(record) => record.id">
+			<template #bodyCell="{ column, record }">
+				<template v-if="column.dataIndex == 'platform'">{{ platformType(record.platform) }}</template>
+				<template v-if="column.dataIndex === 'read'">
+					<span v-if="record.read" style="color: #d9d9d9">已读</span>
+					<span v-else style="color: #ff4d4f">未读</span>
+				</template>
+				<template v-if="column.dataIndex === 'content'">
+					<ellipsis :length="40" tooltip>
+						{{ record.content }}
+					</ellipsis>
+				</template>
+				<template v-if="column.dataIndex === 'action'">
+					<a-space>
+						<a @click="detailRef.onOpen(record)">详情</a>
+					</a-space>
+				</template>
 			</template>
-		</a-list>
+		</s-table>
+		<detail ref="detailRef" @refresh="refresh" />
 	</a-card>
 </template>
-<script setup>
+
+<script setup name="classNotice">
+	import detail from './detail.vue'
 	import classCentre from '@/api/student/classCentre'
-	const listData = ref([])
-	const pagination = ref({
-		current: 1,
-		onChange: (page) => {
-			pagination.value.current = page
-			pagination.value.pageSize = size
-			getList()
+	const table = ref()
+	const detailRef = ref()
+	const columns = [
+		{
+			title: '标题',
+			dataIndex: 'title'
 		},
-		pageSize: 10
-	})
-	const platformType = (t)=>{
-		if(t== 1){
+		{
+			title: '类型',
+			dataIndex: 'platform'
+		},
+		{
+			title: '时间',
+			dataIndex: 'createTime'
+		},
+		{
+			title: '内容',
+			dataIndex: 'content'
+		},
+		{
+			title: '是否已读',
+			dataIndex: 'read'
+		},
+		{
+			title: '操作',
+			dataIndex: 'action',
+			width: '100px'
+		}
+	]
+	const loadData = (parameter) => {
+		return classCentre.classNotice(parameter).then((data) => {
+			return data
+		})
+	}
+	const refresh = () => {
+		table.value.refresh(false)
+	}
+	const platformType = (t) => {
+		if (t == 1) {
 			return '课程'
 		}
-		if(t== 2){
+		if (t == 2) {
 			return '考试'
 		}
+		if (t == 3) {
+			return '系统'
+		}
 	}
-	const getList = () => {
-		classCentre
-			.classNotice({
-				current:pagination.value.current,
-				size: pagination.value.pageSize
-			})
-			.then((data) => {
-				listData.value = data.records
-				pagination.value.total = data.total
-			})
-	}
-	onMounted(() => {
-		getList()
-	})
 </script>
-<style scoped>
-	.index-message-list {
-		overflow: auto;
-	}
-	:deep(.listBox .ant-list-item-meta-title ){
-		margin-bottom: 5px!important;
-	}
-</style>

+ 6 - 0
src/views/student/exam/paper/edit.vue

@@ -172,6 +172,12 @@
 			})
 		}
 	})
+	const forumData = computed(() => {
+		return {
+			id: route.query.id,
+			title: form.name
+		}
+	})
 </script>
 
 <style lang="less" scoped>

+ 1 - 1
src/views/student/forumBtn/index.vue

@@ -82,7 +82,7 @@
 	.redressBox {
 		position: fixed;
 		right: 10px;
-		bottom: 150px;
+		top: 150px + 435px + 5px;
 	}
 	.addBox {
 		display: flex;

+ 43 - 43
src/views/student/learningFootprint/index.vue

@@ -1,47 +1,55 @@
 <template>
 	<a-card>
-		<a-list item-layout="vertical" size="large" :pagination="pagination" :data-source="listData" class="listBox">
-			<template #renderItem="{ item }">
-				<a-list-item style="padding:5px 24px;">
-					<a-list-item-meta :description="item.chapterName" style="margin-bottom: 5px;">
-						<template #title>
-							<div @click="jumpDetail(item)" style="cursor: pointer">{{ item.courseName }}</div>
-						</template>
-					</a-list-item-meta>
-					{{ item.fileName }}
-				</a-list-item>
+		<s-table ref="table" :columns="columns" :data="loadData" bordered :row-key="(record) => record.id">
+			<template #bodyCell="{ column, record }">
+				<template v-if="column.dataIndex == 'createTime'">{{ formatDateTime(record.createTime) }}</template>
+				<template v-if="column.dataIndex === 'action'">
+					<a-space>
+						<a @click="jumpDetail(record)">详情</a>
+					</a-space>
+				</template>
 			</template>
-		</a-list>
+		</s-table>
 	</a-card>
 </template>
-<script setup>
+
+<script setup name="learningFootprint">
 	import classCentre from '@/api/student/classCentre'
-	import { useRouter, useRoute } from 'vue-router'
+	import { useRouter } from 'vue-router'
+	import { parseTime } from '@/utils/exam'
+	function formatDateTime(val) {
+		if (!val) return ''
+		return parseTime(val, '{y}-{m}-{d} {h}:{i}:{s}')
+	}
 	const router = useRouter()
-	const listData = ref([])
-	const pagination = ref({
-		current: 1,
-		onChange: (page,size) => {
-			pagination.value.current = page
-			pagination.value.pageSize = size
-			getList()
+	const columns = [
+		{
+			title: '课程名',
+			dataIndex: 'courseName'
+		},
+		{
+			title: '章节名',
+			dataIndex: 'chapterName'
+		},
+		{
+			title: '时间',
+			dataIndex: 'createTime'
+		},
+		{
+			title: '文件名',
+			dataIndex: 'fileName'
 		},
-		pageSize: 10
-	})
-	const getList = () => {
-		classCentre
-			.footprintClassList({
-				current:pagination.value.current,
-				size: pagination.value.pageSize
-			})
-			.then((data) => {
-				listData.value = data.records
-				pagination.value.total = data.total
-			})
+		{
+			title: '操作',
+			dataIndex: 'action',
+			width: '100px'
+		}
+	]
+	const loadData = (parameter) => {
+		return classCentre.footprintClassList(parameter).then((data) => {
+			return data
+		})
 	}
-	onMounted(() => {
-		getList()
-	})
 	const jumpDetail = (item) => {
 		router.push({
 			path: '/student/classCentre',
@@ -51,11 +59,3 @@
 		})
 	}
 </script>
-<style scoped>
-	.index-message-list {
-		overflow: auto;
-	}
-	:deep(.listBox .ant-list-item-meta-title ){
-		margin-bottom: 5px!important;
-	}
-</style>