This commit is contained in:
Wxp 2025-08-22 16:42:02 +08:00
commit 1193c017ee
9 changed files with 579 additions and 26 deletions

View File

@ -38,7 +38,7 @@
</form> </form>
<div class="form-footer"> <div class="form-footer">
<p>登录即代表同意我们的 <a href="#" class="link">服务协议隐私政策</a></p> <p>登录即代表同意我们的 <a @click="goToServiceAgreement" class="link">服务协议隐私政策</a></p>
</div> </div>
</div> </div>
</div> </div>
@ -48,6 +48,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed } from 'vue' import { ref, reactive, computed } from 'vue'
import { useMessage } from 'naive-ui' import { useMessage } from 'naive-ui'
import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { AuthApi } from '@/api' import { AuthApi } from '@/api'
@ -64,6 +65,7 @@ const props = defineProps<Props>()
const emit = defineEmits<Emits>() const emit = defineEmits<Emits>()
const message = useMessage() const message = useMessage()
const router = useRouter()
const userStore = useUserStore() const userStore = useUserStore()
const showModal = computed({ const showModal = computed({
@ -197,6 +199,14 @@ const handleLogin = async () => {
isLoading.value = false isLoading.value = false
} }
} }
//
const goToServiceAgreement = () => {
//
closeModal()
//
router.push('/service-agreement')
}
</script> </script>
export default { export default {

View File

@ -46,7 +46,7 @@
</form> </form>
<div class="form-footer"> <div class="form-footer">
<p>注册即代表同意我们的 <a href="#" class="link">服务协议隐私政策</a></p> <p>注册即代表同意我们的 <a @click="goToServiceAgreement" class="link">服务协议隐私政策</a></p>
</div> </div>
</div> </div>
</div> </div>
@ -56,6 +56,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed } from 'vue' import { ref, reactive, computed } from 'vue'
import { useMessage } from 'naive-ui' import { useMessage } from 'naive-ui'
import { useRouter } from 'vue-router'
import { AuthApi } from '@/api' import { AuthApi } from '@/api'
interface Props { interface Props {
@ -71,6 +72,7 @@ const props = defineProps<Props>()
const emit = defineEmits<Emits>() const emit = defineEmits<Emits>()
const message = useMessage() const message = useMessage()
const router = useRouter()
const showModal = computed({ const showModal = computed({
get: () => props.show, get: () => props.show,
@ -209,6 +211,14 @@ const sendVerificationCode = async () => {
} }
} }
} }
//
const goToServiceAgreement = () => {
//
closeModal()
//
router.push('/service-agreement')
}
</script> </script>
export default { export default {

View File

@ -88,18 +88,51 @@ const loadDPlayer = (): Promise<void> => {
return return
} }
// CSS // CDN
const cssLink = document.createElement('link') const cdnSources = [
cssLink.rel = 'stylesheet' {
cssLink.href = 'https://cdn.jsdelivr.net/npm/dplayer@1.27.1/dist/DPlayer.min.css' css: 'https://unpkg.com/dplayer@1.27.1/dist/DPlayer.min.css',
document.head.appendChild(cssLink) js: 'https://unpkg.com/dplayer@1.27.1/dist/DPlayer.min.js'
},
{
css: 'https://cdn.bootcdn.net/ajax/libs/dplayer/1.27.1/DPlayer.min.css',
js: 'https://cdn.bootcdn.net/ajax/libs/dplayer/1.27.1/DPlayer.min.js'
},
{
css: 'https://cdn.jsdelivr.net/npm/dplayer@1.27.1/dist/DPlayer.min.css',
js: 'https://cdn.jsdelivr.net/npm/dplayer@1.27.1/dist/DPlayer.min.js'
}
]
// JS let currentIndex = 0
const script = document.createElement('script')
script.src = 'https://cdn.jsdelivr.net/npm/dplayer@1.27.1/dist/DPlayer.min.js' const tryLoadFromCDN = () => {
script.onload = () => resolve() if (currentIndex >= cdnSources.length) {
script.onerror = () => reject(new Error('Failed to load DPlayer')) reject(new Error('All CDN sources failed to load DPlayer'))
document.head.appendChild(script) return
}
const source = cdnSources[currentIndex]
// CSS
const cssLink = document.createElement('link')
cssLink.rel = 'stylesheet'
cssLink.href = source.css
document.head.appendChild(cssLink)
// JS
const script = document.createElement('script')
script.src = source.js
script.onload = () => resolve()
script.onerror = () => {
console.warn(`Failed to load DPlayer from ${source.js}, trying next CDN...`)
currentIndex++
tryLoadFromCDN()
}
document.head.appendChild(script)
}
tryLoadFromCDN()
}) })
} }
@ -278,12 +311,13 @@ onUnmounted(() => {
position: relative; position: relative;
width: 100%; width: 100%;
height: 578px; height: 578px;
background: #000; background: transparent;
} }
.dplayer-wrapper { .dplayer-wrapper {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: transparent;
} }
.video-placeholder { .video-placeholder {
@ -298,7 +332,7 @@ onUnmounted(() => {
background-size: cover; background-size: cover;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-image: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: transparent;
color: white; color: white;
cursor: pointer; cursor: pointer;
} }
@ -310,8 +344,9 @@ onUnmounted(() => {
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background: rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.3);
z-index: 1; z-index: 1;
border-radius: 12px;
} }
.placeholder-content { .placeholder-content {

View File

@ -196,6 +196,14 @@ const routes: RouteRecordRaw[] = [
}, },
// 首页与课程 // 首页与课程
{
path: '/service-agreement',
name: 'ServiceAgreement',
component: () => import('@/views/ServiceAgreement.vue'),
meta: {
title: '服务协议与隐私政策'
}
},
{ {
path: '/', path: '/',
name: 'Home', name: 'Home',

View File

@ -2818,4 +2818,242 @@ button:active {
line-height: 18px; line-height: 18px;
margin-top: 25px; margin-top: 25px;
} }
/* 响应式设计 */
@media (max-width: 1200px) {
.page {
width: 100% !important;
max-width: 1200px;
margin: 0 auto;
}
.block_1 {
width: 100% !important;
height: auto !important;
}
.section_1 {
width: 100% !important;
height: auto !important;
padding: 0 20px;
}
.text-wrapper_2 {
text-align: center;
margin-bottom: 30px;
}
.group_3 {
flex-wrap: wrap;
justify-content: center;
gap: 10px;
margin-bottom: 30px;
}
.group_6 {
flex-direction: column;
gap: 20px;
}
.group_27 {
flex-direction: column;
gap: 20px;
}
}
@media (max-width: 1024px) {
.section_1 {
padding: 0 20px;
}
.group_6 {
flex-direction: column;
gap: 20px;
}
.group_12 {
flex-direction: column;
gap: 20px;
}
.group_21 {
flex-direction: column;
gap: 20px;
}
/* 调整卡片宽度 */
.box_5, .group_13, .group_18, .group_20, .group_22, .group_23 {
width: 100% !important;
}
}
@media (max-width: 768px) {
.section_1 {
padding: 0 15px;
}
.text_13 {
font-size: 28px !important;
}
.text_14 {
font-size: 16px !important;
}
.group_3 {
flex-direction: column;
align-items: center;
gap: 15px;
}
.group_6 {
gap: 15px;
}
.box_5 {
width: 100% !important;
}
.group_27 {
flex-direction: column;
gap: 15px;
}
.box_18, .box_20, .box_21, .box_22 {
width: 100% !important;
margin-bottom: 15px;
}
/* 调整图片尺寸 */
.image_7, .image_8, .image_9, .image_10, .image_11,
.image_12, .image_13, .image_14, .image_15, .image_16,
.image_17, .image_18 {
width: 100% !important;
height: auto !important;
max-height: 200px;
object-fit: cover;
}
/* 左侧导航区域调整 */
.group_5 {
flex-direction: column;
align-items: center;
gap: 10px;
margin-bottom: 20px;
}
/* 项目卡片调整 */
.group_12 {
flex-direction: column;
gap: 20px;
}
.group_13 {
width: 100% !important;
}
.group_18, .group_20 {
width: 100% !important;
margin-bottom: 20px;
}
/* 底部导航调整 */
.group_21 {
flex-direction: column;
gap: 20px;
}
.group_22, .group_23 {
width: 100% !important;
}
}
@media (max-width: 480px) {
.section_1 {
padding: 0 10px;
}
.text_13 {
font-size: 24px !important;
}
.text_14 {
font-size: 14px !important;
}
.group_3 {
gap: 10px;
}
.group_6, .group_27 {
gap: 10px;
}
/* 文字大小调整 */
.text_19, .text_20, .text_32, .text_35, .text_38, .text_41 {
font-size: 14px !important;
white-space: normal !important;
word-wrap: break-word;
}
.text_33, .text_36, .text_39, .text_42, .text_47, .text_50, .text_53, .text_56 {
font-size: 12px !important;
line-height: 1.4 !important;
white-space: normal !important;
word-wrap: break-word;
}
/* 按钮和交互元素调整 */
.image-text_1, .image-text_2, .image-text_3, .image-text_15, .image-text_16 {
padding: 8px !important;
min-height: 40px;
}
/* 卡片内边距调整 */
.box_5, .box_11, .box_13, .box_18, .box_19, .box_20, .box_21, .box_22 {
padding: 15px !important;
}
/* 标题文字换行 */
.paragraph_1, .paragraph_2, .paragraph_3, .paragraph_4 {
white-space: normal !important;
word-wrap: break-word;
line-height: 1.3 !important;
}
}
/* 通用响应式修复 */
@media (max-width: 1920px) {
/* 修复所有固定宽度元素 */
.page, .block_1, .section_1, .group_1, .group_2 {
width: 100% !important;
max-width: 100vw;
}
/* 确保内容不会溢出 */
* {
max-width: 100%;
box-sizing: border-box;
}
/* 图片响应式 */
img {
max-width: 100%;
height: auto;
}
/* 文本容器响应式 */
.text-wrapper_2, .text-group_1, .text-group_2, .text-group_3,
.text-group_13, .text-group_14, .text-group_15, .text-group_16,
.text-group_19, .text-group_20, .text-group_21, .text-group_22,
.text-group_23, .text-group_24, .text-group_25, .text-group_26,
.text-group_27, .text-group_28, .text-group_29, .text-group_30,
.text-group_31, .text-group_32, .text-group_33, .text-group_34,
.text-group_35, .text-group_36, .text-group_37, .text-group_38,
.text-group_39, .text-group_40, .text-group_41, .text-group_42,
.text-group_43 {
width: auto !important;
max-width: 100%;
}
}
</style> </style>

View File

@ -38,13 +38,15 @@
<div class="faculty-grid"> <div class="faculty-grid">
<div v-for="teacher in paginatedTeachers" :key="teacher.id" class="faculty-card"> <div v-for="teacher in paginatedTeachers" :key="teacher.id" class="faculty-card">
<div class="card-header"> <div class="card-header">
<div class="avatar-container" @mouseenter="toggleCourseInfo(teacher.id)"> <div class="avatar-container"
@mouseenter="showCourseInfo(teacher.id)"
@mouseleave="hideCourseInfo(teacher.id)">
<!-- 师资头像 --> <!-- 师资头像 -->
<img :src="teacher.avatar" :alt="teacher.name" class="teacher-avatar" /> <img :src="teacher.avatar" :alt="teacher.name" class="teacher-avatar" />
<div v-if="teacher.featured" class="featured-badge"></div> <div v-if="teacher.featured" class="featured-badge"></div>
<!-- 课程介绍盒子 --> <!-- 课程介绍盒子 -->
<div v-if="expandedTeacherId === teacher.id" class="course-info-box" :class="{ active: expandedTeacherId === teacher.id }"> <div class="course-info-box" :class="{ active: expandedTeacherId === teacher.id }">
<div class="course-info-content"> <div class="course-info-content">
<h4 class="course-title">课程介绍</h4> <h4 class="course-title">课程介绍</h4>
<p class="course-description">主讲课程为Python语言程序设计动画原理与实现虚拟现实技术与应用</p> <p class="course-description">主讲课程为Python语言程序设计动画原理与实现虚拟现实技术与应用</p>
@ -135,6 +137,16 @@ const toggleCourseInfo = (teacherId: number) => {
} }
} }
//
const showCourseInfo = (teacherId: number) => {
expandedTeacherId.value = teacherId
}
//
const hideCourseInfo = (teacherId: number) => {
expandedTeacherId.value = null
}
// //
const filterTabs = ref([ const filterTabs = ref([
{ id: 'all', name: '全部' }, { id: 'all', name: '全部' },
@ -586,11 +598,13 @@ height: 37px;
/* box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); */ /* box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); */
z-index: 10; z-index: 10;
transform: translateY(100%); transform: translateY(100%);
transition: transform 0.3s ease-out; opacity: 0;
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
} }
.course-info-box.active { .course-info-box.active {
transform: translateY(0); transform: translateY(0);
opacity: 1;
} }
.course-info-content { .course-info-content {
@ -627,10 +641,7 @@ height: 37px;
box-shadow: -2px -2px 4px rgba(0, 0, 0, 0.1); box-shadow: -2px -2px 4px rgba(0, 0, 0, 0.1);
} */ } */
.faculty-card:hover .course-info-box, /* 移除冲突的hover样式改用JavaScript控制 */
.course-info-box:hover {
transform: translateY(0);
}
/* 箭头按钮旋转动画 */ /* 箭头按钮旋转动画 */
/* .card-arrow { /* .card-arrow {

View File

@ -6,7 +6,7 @@
<div class="hero-content"> <div class="hero-content">
<h2>中小学教师人工智能素养<span>提升</span>在线学习平台</h2> <h2>中小学教师人工智能素养<span>提升</span>在线学习平台</h2>
<p>为教师量身定制帮助教师快速掌握AI技术教学应用与伦理规范赋能智慧课堂</p> <p>为教师量身定制帮助教师快速掌握AI技术教学应用与伦理规范赋能智慧课堂</p>
<button>立即学习</button> <button @click="goToCoursesPage">立即学习</button>
</div> </div>
</section> </section>
@ -538,7 +538,25 @@ const router = useRouter()
const courseStore = useCourseStore() const courseStore = useCourseStore()
// @ts-ignore // @ts-ignore
const userStore = useUserStore() const userStore = useUserStore()
const { loginModalVisible, registerModalVisible, handleAuthSuccess } = useAuth() const { loginModalVisible, registerModalVisible, handleAuthSuccess: originalHandleAuthSuccess } = useAuth()
//
const handleAuthSuccess = () => {
originalHandleAuthSuccess()
//
router.push('/profile')
}
//
const goToCoursesPage = () => {
if (userStore.isLoggedIn) {
//
router.push('/profile')
} else {
//
loginModalVisible.value = true
}
}
// //
const goToCourseDetail = async (courseId: string) => { const goToCourseDetail = async (courseId: string) => {

View File

@ -2290,7 +2290,7 @@ const isMessageTab = computed(() => activeTab.value === 'message')
// //
const handleMenuSelect = (key: TabType) => { const handleMenuSelect = (key: TabType) => {
activeTab.value = key activeTab.value = key
message.info(`切换到${getTabTitle(key)}`) // message.info(`${getTabTitle(key)}`)
} }
// //
@ -4280,6 +4280,44 @@ onActivated(() => {
padding: 16px; padding: 16px;
min-height: 240px; min-height: 240px;
} }
/* 考试标题在小屏幕上给分数留出空间并允许换行 */
.exam-title {
padding-right: 80px; /* 给分数徽章留出空间 */
word-wrap: break-word;
word-break: break-word;
white-space: normal;
}
/* 分数徽章保持在右上角不换行 */
.exam-score-badge {
position: absolute;
top: 16px;
right: 16px;
white-space: nowrap;
}
}
/* 更小屏幕的适配 */
@media (max-width: 480px) {
.exam-card {
padding: 12px;
min-height: 220px;
}
.exam-title {
padding-right: 70px; /* 给分数徽章留出更多空间 */
font-size: 14px;
}
.exam-score-badge {
top: 12px;
right: 12px;
}
.score-text {
font-size: 16px;
}
} }
/* 简化的作业卡片样式 */ /* 简化的作业卡片样式 */

View File

@ -0,0 +1,185 @@
<template>
<div class="service-agreement-page">
<div class="container">
<!-- 协议内容 -->
<div class="agreement-content">
<!-- 用户协议部分 -->
<div class="agreement-section">
<h2 class="section-title">用户协议</h2>
<p class="update-info">更新日期2025年8月31日</p>
<div class="content-text">
<p>在注册中小学教师人工智能素养提升在线学习平台下称"平台"或使用平台的任何页面之前包括访问任何课程资料讨论区或使用其他电子服务之前请务必审慎阅读充分理解本用户协议下称"本服务条款"隐私政策诚信守则和Cookie政策的内容特别是免除或限制责任的条款限制免责条款可能以黑体加粗形式提示您注意</p>
<p>本服务条款隐私政策诚信守则和Cookie政策是您与平台之间的协议下称"本协议"不论您是否为注册用户您使用本产品软件即视为您已阅读并同意接受本协议的约束如果您不希望受本协议条款的约束我们将无法为您提供服务</p>
<p>如果您未满18周岁请在法定监护人的监护指导下阅读本协议和使用本产品</p>
<p>平台保留在不事先通知您的情况下随时修改本服务条款的权利本服务条款的任何修改在本页面上发布后立即生效并附更新后的生效日期在本产品进行任何修改之后您继续访问本产品即视为您已接受修改后的服务条款和本产品的其他全部修改请定期访问本页面以了解本服务条款的最新版本</p>
</div>
</div>
<!-- 隐私政策部分 -->
<div class="agreement-section">
<h2 class="section-title">隐私政策</h2>
<p class="update-info">更新日期2025年8月31日</p>
<div class="content-text">
<p>在注册中小学教师人工智能素养提升在线学习平台下称"平台"或使用平台的任何页面之前包括访问任何课程资料讨论区或使用其他电子服务之前请务必审慎阅读充分理解本用户协议下称"本服务条款"隐私政策诚信守则和Cookie政策的内容特别是免除或限制责任的条款限制免责条款可能以黑体加粗形式提示您注意</p>
<p>本服务条款隐私政策诚信守则和Cookie政策是您与平台之间的协议下称"本协议"不论您是否为注册用户您使用本产品软件即视为您已阅读并同意接受本协议的约束如果您不希望受本协议条款的约束我们将无法为您提供服务</p>
<p>如果您未满18周岁请在法定监护人的监护指导下阅读本协议和使用本产品</p>
<p>平台保留在不事先通知您的情况下随时修改本服务条款的权利本服务条款的任何修改在本页面上发布后立即生效并附更新后的生效日期在本产品进行任何修改之后您继续访问本产品即视为您已接受修改后的服务条款和本产品的其他全部修改请定期访问本页面以了解本服务条款的最新版本</p>
</div>
<h3 class="subsection-title">平台简述</h3>
<div class="content-text">
<p>平台为用户提供线上学习支持包括但不限于1包括图文视频讨论作业等学习单元的学习2视频在线观看3公告区与讨论区师生同学间互动4线上考试</p>
</div>
<h3 class="subsection-title">网络行为准则</h3>
<div class="content-text">
<p>您同意对您使用本产品和您的用户发布内容承担责任"用户发布内容"是指您和本产品的其他用户在本产品上提交发布发表和分发的所有内容包括但不限于所有帖子评论视频和上传的文件您同意您将根据本服务条款诚信守则和所有适用的法律法规使用本产品不得利用本产品从事违法违规行为您同意尊重当地的社会公德道德和风俗习惯避免因使用本产品而使平台卷入任何政治和公共事件如果您的行为违反本条约定您应当为此承担全部法律及相关行政责任相关国家机关或机构可能会对您提起诉讼罚款或采取其他制裁措施并要求平台给予协助您对由此给平台或他人造成损害的您应依法予以赔偿平台不承担任何责任</p>
<p>作为您使用平台服务的条件之一您不得以任何试图损坏禁用超载或损害平台服务器及连接平台服务器的网站或影响他人使用本产品的方式使用本产品您不得试图通过黑客手段密码破解或任何其他手段未经授权地访问本产品其他帐号软硬件系统和连接平台服务器的网络您不得通过任何不当手段获取或者试图获取存储在本产品其服务器和相关计算机上的资料和信息</p>
<p><strong>本产品严格禁止以下内容</strong>诽谤骚扰或威胁他人的内容讨论试图从事的非法活动的内容侵犯他人知识产权及其他与知识产权有关的合法权利的内容粗俗色情淫秽下流暴力和非法的内容广告或者其他形式的商业宣传与政治活动相关的内容病毒木马蠕虫数据包炸弹篡改的文件恶意软件间谍软件或者其他类似的可能损害他人计算机运行或财产的内容恶意虚构或者故意误导他人的内容</p>
<p>此外您同意不抓取和批量下载本产品的内容包括但不限于系统上的用户列表或目录在线教材用户发布内容或用户信息在使用本产品时您不得冒用或者试图冒用他人身份</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.service-agreement-page {
min-height: 100vh;
background: #f8f9fa;
display: flex;
justify-content: center;
padding: 40px 0;
}
.container {
width: 1420px;
margin: 0 auto;
padding: 0;
}
.agreement-content {
width: 1420px;
height: auto;
background: #FFFFFF;
border-radius: 0;
padding: 0 120px;
box-shadow: none;
margin: 0 auto;
}
.agreement-section {
margin-bottom: 40px;
}
.section-title {
font-size: 28px;
font-weight: 600;
color: #333;
margin: 60px 0 24px 0;
text-align: center;
}
.update-info {
text-align: center;
color: #999;
font-size: 14px;
margin-bottom: 40px;
}
.subsection-title {
font-size: 20px;
font-weight: 600;
color: #444;
margin: 32px 0 16px 0;
}
.content-text {
font-size: 16px;
line-height: 1.8;
color: #555;
margin-bottom: 24px;
}
.content-text p {
margin-bottom: 16px;
text-align: justify;
}
.content-text ul {
margin: 16px 0;
padding-left: 24px;
}
.content-text li {
margin-bottom: 8px;
list-style-type: disc;
}
.update-date {
text-align: center;
color: #999;
font-size: 14px;
margin-top: 40px !important;
}
/* 响应式设计 */
@media (max-width: 1440px) {
.container {
width: 95%;
max-width: 1420px;
}
.agreement-content {
padding: 0 60px 60px 60px;
}
}
@media (max-width: 768px) {
.service-agreement-page {
padding: 24px 16px;
}
.container {
width: 100%;
}
.agreement-content {
padding: 0 24px 40px 24px;
}
.section-title {
font-size: 22px;
margin: 40px 0 20px 0;
}
.subsection-title {
font-size: 18px;
}
}
</style>