Procházet zdrojové kódy

1、根据测试账号进行限制视频播放
2、加入消息通用组件,可覆盖弹框
3、未登录状态默认跳转到登录页
4、更改打包配置

liyanbo před 9 měsíci
rodič
revize
5a9a3b8a43

+ 3 - 3
.env

@@ -2,10 +2,10 @@
 VITE_APP_TITLE=AI课程网
 
 # 请求路径
-# VITE_BASE_URL='http://59.110.91.129/admin-api'
+#VITE_BASE_URL='http://59.110.91.129/admin-api'
 VITE_BASE_URL='http://192.168.110.8:8080/admin-api'
 
 # 默认账户密码
 VITE_APP_DEFAULT_LOGIN_TENANT = 博雅智算
-VITE_APP_DEFAULT_LOGIN_USERNAME = admin
-VITE_APP_DEFAULT_LOGIN_PASSWORD = admin123
+VITE_APP_DEFAULT_LOGIN_USERNAME = aiTest
+VITE_APP_DEFAULT_LOGIN_PASSWORD = aiTest

+ 0 - 3
.vscode/extensions.json

@@ -1,3 +0,0 @@
-{
-  "recommendations": ["Vue.volar"]
-}

+ 58 - 0
src/utils/message/Message.js

@@ -0,0 +1,58 @@
+import { ElMessage, ElNotification } from 'element-plus'
+
+// 存储当前活动的消息或通知实例
+let activeInstance = null
+
+export const Message = () => {
+  // 显示弹框的通用方法
+  const show = (type, cover, content, showFn) => {
+    // 如果有活动实例,先关闭它
+    if (activeInstance && cover) {
+      activeInstance.close()
+    }
+    // 显示新的弹框
+    activeInstance = showFn(content)
+    activeInstance.onClose = () => {
+      activeInstance = null
+    }
+  }
+
+  return {
+    // 消息提示
+    info(content, cover = false) {
+      show('info', cover, content, (msg) => ElMessage.info(msg))
+    },
+    // 错误消息
+    error(content, cover = false) {
+      show('error', cover, content, (msg) => ElMessage.error(msg))
+    },
+    // 成功消息
+    success(content, cover = false) {
+      show('success', cover, content, (msg) => ElMessage.success(msg))
+    },
+    // 警告消息
+    warning(content, cover = false) {
+      show('warning', cover, content, (msg) => ElMessage.warning(msg))
+    },
+    // 通知提示
+    notify(content, cover = false) {
+      show('notify', cover, content, (msg) => ElNotification.info(msg))
+    },
+    // 错误通知
+    notifyError(content, cover = false) {
+      show('notifyError', cover, content, (msg) => ElNotification.error(msg))
+    },
+    // 成功通知
+    notifySuccess(content, cover = false) {
+      show('notifySuccess', cover, content, (msg) => ElNotification.success(msg))
+    },
+    // 警告通知
+    notifyWarning(content, cover = false) {
+      show('notifyWarning', cover, content, (msg) => ElNotification({
+        title: '系统提示',
+        message: msg,
+        type: 'warning',
+      }))
+    },
+  }
+}

+ 7 - 0
src/utils/request.js

@@ -30,6 +30,13 @@ request.interceptors.request.use(function (config) {
   // console.log(localStorage.getItem('token'));
   // 1. 先获取 token
   const token = localStorage.getItem('token')
+  //目前未登录状态是“undefined”,不对暂未找原因
+    if (token === "undefined") {
+    // token 无效,之后我们可以重新登录
+    localStorage.clear()
+    router.push('/login')
+    return
+  }
   // 2. 设置 token
   config.headers.token = token
   return config;

+ 66 - 71
src/views/AIDevelop.vue

@@ -90,6 +90,7 @@
             controlsList="nodownload"
             controls
             @timeupdate="handleTimeUpdate"
+            @play="checkVideoPermission"
             ref="videoRef"
           >
             <source :src="videoSrc" type="video/mp4" />
@@ -238,7 +239,7 @@ import {
   Setting
 } from '@element-plus/icons-vue'
 import { Search, ArrowLeftBold } from '@element-plus/icons-vue'
-import { valueEquals } from 'element-plus'
+import {ElMessage, ElMessageBox, ElNotification, valueEquals} from 'element-plus'
 // 引入视频
 // import video1 from '@/assets/02video/01video.mp4'
 // import video2 from '@/assets/02video/02video.mp4'
@@ -256,6 +257,7 @@ import { valueEquals } from 'element-plus'
 // import video14 from '@/assets/02video/14video.mp4'
 // import video15 from '@/assets/02video/15video.mp4'
 import {ClassType} from "@/api/class.js";
+import {Message} from "@/utils/message/Message.js";
 
 const router = useRouter() // 获取当前路由对象
 // 搜索框
@@ -288,11 +290,6 @@ const toggleDrawer = () => {
 //   '1-15': video15
 // }
 
-
-
-
-
-
 // 返回上一页
 const goBack = () => {
   router.go(-1)
@@ -387,6 +384,7 @@ const currentIndex = ref('1-1')
 // 渲染课程标题到视频上方
 const smallTitle = ref('课前回顾')
 const handleSelect = index => {
+
   const findTitle = items => {
     for (const item of items) {
       if (item.index === index) {
@@ -410,6 +408,9 @@ const handleSelect = index => {
     videoSrc.value = videoPathMap.value[index]
     currentIndex.value = index
   }
+
+  //测试账号禁用视频
+  if (disableVideo())return;
 }
 
 // 展平所有菜单项索引
@@ -430,6 +431,9 @@ const flattenMenuItems = () => {
 
 // 播放下一个视频
 const playNextVideo = () => {
+  //测试账号禁用视频
+  if (disableVideo())return;
+
   const allIndices = flattenMenuItems()
   const currentIndexInList = allIndices.indexOf(currentIndex.value)
   if (currentIndexInList !== -1 && currentIndexInList < allIndices.length - 1) {
@@ -451,6 +455,9 @@ const playNextVideo = () => {
 // 切换视频
 // 播放上一个视频
 const playPreviousVideo = () => {
+  //测试账号禁用视频
+  if (disableVideo())return;
+
   const allIndices = flattenMenuItems()
   const currentIndexInList = allIndices.indexOf(currentIndex.value)
   if (currentIndexInList > 0) {
@@ -466,6 +473,9 @@ const playPreviousVideo = () => {
 }
 // 尝试播放视频,处理浏览器自动播放限制
 const tryPlayVideo = () => {
+  //测试账号禁用视频
+  if (disableVideo())return;
+
   const playPromise = videoRef.value.play()
   if (playPromise !== undefined) {
     playPromise.catch(error => {
@@ -473,6 +483,55 @@ const tryPlayVideo = () => {
     })
   }
 }
+// 检查视频播放权限
+const checkVideoPermission = () => {
+  if (disableVideo()) {
+    if (videoRef.value) {
+      videoRef.value.pause();
+    }
+  }
+};
+
+//禁用视频
+const disableVideo = () => {
+  let dis = ["1-6","1-7","1-8","1-9","1-10","1-11","1-12","1-13","1-14","1-15"]
+
+  if (localStorage.getItem('userName') === "aiTest" &&
+      dis.indexOf(currentIndex.value) !== -1) {
+
+    if (videoRef.value) {
+      videoRef.value.pause()
+
+      // 禁用视频进度条拖拽功能
+      const handleSeeking = () => {
+        videoRef.value.pause()
+        Message().notifyWarning('您的账号并未开放此课程,禁止拖动进度条!', true)
+      }
+
+      // 添加 seeking 事件监听器
+      videoRef.value.addEventListener('seeking', handleSeeking)
+
+      // 当视频源改变时,移除事件监听器,避免影响其他视频
+      const removeListener = () => {
+        videoRef.value.removeEventListener('seeking', handleSeeking)
+        videoRef.value.removeEventListener('loadedmetadata', removeListener)
+      }
+      videoRef.value.addEventListener('loadedmetadata', removeListener)
+    }
+
+    //提示禁用// 显示消息框
+    Message().notifyWarning('您的账号并未开放此课程!', true)
+    return true
+  } else {
+    // 若不符合禁用条件,移除事件监听器
+    if (videoRef.value) {
+      videoRef.value.removeEventListener('seeking', (e) => {
+        e.preventDefault()
+      })
+    }
+    return false
+  }
+}
 
 // 视频 ref
 const videoRef = ref(null)
@@ -1113,41 +1172,6 @@ video::-webkit-media-controls-panel {
   }
 }
 
-// AI对话弹出框样式
-.ai-dialog {
-  .el-dialog__header {
-    background: linear-gradient(90deg, $primary-color, $secondary-color);
-    color: white;
-    padding: rpx(10) rpx(20);
-    border-radius: rpx(10) rpx(10) 0 0;
-
-    .el-dialog__title {
-      font-size: rpx(12);
-    }
-  }
-
-  .el-dialog__body {
-    padding: rpx(15);
-    background-color: white;
-  }
-
-  .el-dialog__footer {
-    border-top: none;
-    padding: rpx(10) rpx(20);
-    text-align: right;
-  }
-
-  .el-dialog {
-    border-radius: rpx(10);
-    // 修改半透明背景色
-    background-color: rgba(255, 255, 255, 0.95);
-    // 添加边框边晕效果
-    box-shadow: 0 0 20px 5px rgba($primary-color, 0.6),
-      0 0 40px 10px rgba($primary-color, 0.4),
-      0 0 60px 15px rgba($primary-color, 0.2);
-  }
-}
-
 // 问题标题样式
 .question-title {
   background: linear-gradient(
@@ -1307,36 +1331,6 @@ video::-webkit-media-controls-panel {
   }
 }
 
-// AI对话弹出框样式
-.ai-dialog {
-  .el-dialog__header {
-    background: linear-gradient(90deg, $primary-color, $secondary-color);
-    color: white;
-    padding: rpx(10) rpx(20);
-    border-radius: rpx(10) rpx(10) 0 0;
-
-    .el-dialog__title {
-      font-size: rpx(12);
-    }
-  }
-
-  .el-dialog__body {
-    padding: rpx(15);
-    background-color: white;
-  }
-
-  .el-dialog__footer {
-    border-top: none;
-    padding: rpx(10) rpx(20);
-    text-align: right;
-  }
-
-  .el-dialog {
-    border-radius: rpx(10);
-    box-shadow: 0 rpx(10) rpx(30) rgba(0, 0, 0, 0.15);
-  }
-}
-
 // AI消息样式
 .ai-message {
   display: flex;
@@ -1599,4 +1593,5 @@ video::-webkit-media-controls-panel {
     //background: linear-gradient(90deg, darken($primary-color, 5%), darken($secondary-color, 5%));
   }
 }
+
 </style>

+ 6 - 1
src/views/AIGeneralCourse.vue

@@ -114,7 +114,6 @@ import { ref, onMounted, computed, watch } from 'vue'
 
 import { ClassList, ClassOutline } from '@/api/class.js'
 // 添加 Element Plus 组件引入
-// import { ElDrawer, ElMenu, ElMenuItem, ElRow, ElCol } from 'element-plus'
 import {
   ArrowDown,
   ArrowRightBold,
@@ -193,6 +192,7 @@ const toggleDrawer = () => {
   drawerVisible.value = !drawerVisible.value
 }
 
+import {Message} from "@/utils/message/Message.js";
 
 
 // 搜索框
@@ -249,6 +249,11 @@ const goToAIExperience = outlineData => {
       path: '/ai-develop', // 跳转AI初体验
       query: { typeId: outlineData.id, typeName: outlineData.ctType }
     })
+  }else {
+    if (localStorage.getItem('userName') === "aiTest") {
+      //提示禁用
+      Message().notifyWarning('您的账号并未开放此课程!', true)
+    }
   }
 }
 

+ 2 - 1
src/views/Login.vue

@@ -128,7 +128,8 @@ const handleLogin = async params => {
           // 存储登录状态
           if (loginData.value.loginForm.rememberMe) {
             localStorage.setItem('isLoggedIn', 'true')
-            localStorage.setItem('token', res.data.token)
+            localStorage.setItem('token', res.data.accessToken)
+            localStorage.setItem('userName', loginData.value.loginForm.username)
             
             // 根据账号类型设置可查看的课程小节数
             if (loginData.value.loginForm.username === 'aiTest') {

+ 23 - 0
vite.config.js

@@ -32,6 +32,29 @@ export default defineConfig(({ mode }) => {
           changeOrigin: true
         }
       }
+    },
+    build: {
+      outDir: 'aiWeb',
+      rollupOptions: {
+        output: {
+          entryFileNames: `assets/[name]-[hash].js`,
+          chunkFileNames: `assets/[name]-[hash].js`,
+          // 保留原始目录结构
+          assetFileNames: ({ name }) => {
+            const extType = name.split('.').pop();
+            if (/png|jpe?g|gif|svg|webp/i.test(extType)) {
+              return `assets/images/[name].[ext]`;
+            }
+            if (/woff2?|ttf|otf|eot/i.test(extType)) {
+              return `assets/typeface/[name].[ext]`;
+            }
+            if (/mp4/i.test(extType)) {
+              return `assets/02video/[name].[ext]`;
+            }
+            return `assets/[name].[ext]`;
+          }
+        }
+      }
     }
   }
 })