Compare commits
243 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fa88b3c164 | ||
![]() |
4f9f8d0a88 | ||
![]() |
d5bbc38ddf | ||
![]() |
e66b21a871 | ||
![]() |
412a796ecb | ||
![]() |
5455490811 | ||
![]() |
5de4c9596f | ||
![]() |
6a8078e272 | ||
![]() |
b093dcd630 | ||
![]() |
3ff9e43877 | ||
![]() |
42d84ee2fa | ||
![]() |
3262b8c979 | ||
![]() |
949d5f439d | ||
![]() |
49a55c6c26 | ||
![]() |
68f3652935 | ||
![]() |
5b125c15b2 | ||
![]() |
8d2b4037d4 | ||
![]() |
00ae37c216 | ||
![]() |
3bd9e1fac3 | ||
![]() |
dd1c8de407 | ||
![]() |
4c43226c91 | ||
![]() |
d9c455d946 | ||
![]() |
a7d8b00fe9 | ||
![]() |
080ce9e724 | ||
![]() |
fed060545a | ||
![]() |
b52a954e86 | ||
![]() |
a55bf916c2 | ||
![]() |
a5bb4de1fe | ||
![]() |
d5346675ae | ||
![]() |
cb0de218fa | ||
![]() |
41cccd9f7a | ||
![]() |
8297d62258 | ||
![]() |
97d8e99689 | ||
![]() |
8a8fd09137 | ||
![]() |
03f9d1b423 | ||
![]() |
82ae528785 | ||
![]() |
6b3846ea50 | ||
![]() |
17ed40fc23 | ||
![]() |
978713b316 | ||
![]() |
ce6a0d41eb | ||
![]() |
2a7da14df5 | ||
![]() |
0e176e8d3c | ||
![]() |
5c14a13f5e | ||
![]() |
75fd712a32 | ||
![]() |
254fb72d0d | ||
![]() |
ce54a41f4a | ||
![]() |
7aa5257cbd | ||
![]() |
fbe2060425 | ||
![]() |
52e57b7cf3 | ||
![]() |
9b6ad60913 | ||
![]() |
0de7531c1a | ||
![]() |
5519a849a3 | ||
![]() |
9aa5fbcea0 | ||
![]() |
2e3b6a6cf7 | ||
![]() |
f152497cab | ||
![]() |
f80fcb7025 | ||
![]() |
b2ec1e2015 | ||
![]() |
62143affd5 | ||
![]() |
3e1f1fdc67 | ||
![]() |
52b9e9a475 | ||
![]() |
bf6496b755 | ||
![]() |
6d6ded74b4 | ||
![]() |
50adc24979 | ||
![]() |
b5db02d95c | ||
![]() |
dd73e28b08 | ||
![]() |
23c54eaf40 | ||
![]() |
e687fa8ebd | ||
![]() |
061a67a5e8 | ||
![]() |
23f6924711 | ||
![]() |
935c68ac6d | ||
![]() |
aa87b0e8e4 | ||
![]() |
b20cc50f44 | ||
![]() |
8de56bd07c | ||
![]() |
3c3c2063b0 | ||
![]() |
e086442a2d | ||
![]() |
8a2a8add39 | ||
![]() |
a4cd04429a | ||
![]() |
bff716f6b6 | ||
![]() |
568d8fdf47 | ||
![]() |
3425097b15 | ||
![]() |
7e540664e2 | ||
![]() |
68c64a96c1 | ||
![]() |
27b68d2abc | ||
![]() |
ef49c7b6d3 | ||
![]() |
ad2ec33c6a | ||
![]() |
b287cf73b9 | ||
![]() |
760290bfb0 | ||
![]() |
ed8e688422 | ||
![]() |
1721ab50fc | ||
![]() |
3461499661 | ||
![]() |
d9d80c8337 | ||
![]() |
2031fa4ba4 | ||
![]() |
c35f54fdd9 | ||
![]() |
155db7a1e4 | ||
![]() |
e645a190dd | ||
![]() |
59c4d9ebc5 | ||
![]() |
21845cb21a | ||
![]() |
0c638147f2 | ||
![]() |
7551571f0a | ||
![]() |
7911565249 | ||
![]() |
7fb049d31d | ||
![]() |
ec1a19d005 | ||
![]() |
2a3e6ccd91 | ||
![]() |
b283f4e3c7 | ||
![]() |
9b601f9117 | ||
![]() |
75d722440f | ||
![]() |
2c1fb5ab7f | ||
![]() |
7de929ac18 | ||
![]() |
eb4c8504b1 | ||
![]() |
21b76b9683 | ||
![]() |
e39010c484 | ||
![]() |
f4991929c6 | ||
![]() |
e1b32c2c3c | ||
![]() |
3d5d34b660 | ||
![]() |
764064bd80 | ||
![]() |
6b685501dd | ||
![]() |
71ea0bbfb4 | ||
![]() |
81c0556559 | ||
![]() |
580e32d69c | ||
![]() |
a30e23ab07 | ||
![]() |
dd11e9aa2d | ||
![]() |
177bdbc009 | ||
![]() |
7b993f0648 | ||
![]() |
a0cb27afe4 | ||
![]() |
39020bd14c | ||
![]() |
9a37ff49bc | ||
![]() |
44e734b50c | ||
![]() |
739ad411c1 | ||
![]() |
f888b0b458 | ||
![]() |
adeaf086c4 | ||
![]() |
f0320a021d | ||
![]() |
5f199931ca | ||
![]() |
2aac753e3a | ||
![]() |
46df63e7fd | ||
![]() |
96f433c745 | ||
![]() |
5621903f34 | ||
![]() |
27f41332d4 | ||
![]() |
02dfa15e75 | ||
![]() |
b3ed88602f | ||
![]() |
8e146666f7 | ||
![]() |
590af0951f | ||
![]() |
57eb57d0ce | ||
![]() |
7df60c4ea4 | ||
![]() |
714de2f19b | ||
![]() |
1714221e70 | ||
![]() |
48d80ef281 | ||
![]() |
537a7f8fe6 | ||
![]() |
06dfacc074 | ||
![]() |
2aab0f7152 | ||
![]() |
8000bde1b9 | ||
![]() |
39fff08a0d | ||
![]() |
027253f7b4 | ||
![]() |
7bb632f9a9 | ||
![]() |
3d4fa1abb4 | ||
![]() |
cea9929ebc | ||
![]() |
4f90499ada | ||
![]() |
7c6c19d8f9 | ||
![]() |
d841224536 | ||
![]() |
dc912c3842 | ||
![]() |
597b9a0d3f | ||
![]() |
0d3184104f | ||
![]() |
8427f0ec82 | ||
![]() |
2c27fe8730 | ||
![]() |
adf90b1390 | ||
![]() |
be64951358 | ||
![]() |
788df9a92c | ||
![]() |
1172193733 | ||
![]() |
a5a802c4a6 | ||
![]() |
829660dbda | ||
![]() |
068fc262ab | ||
![]() |
541886b6e6 | ||
![]() |
638e939fe5 | ||
![]() |
cc7c4ec23a | ||
![]() |
2d0dd00fc4 | ||
![]() |
37198b3b37 | ||
![]() |
510b2b1b0c | ||
![]() |
3293384d8e | ||
![]() |
7be3eca61e | ||
![]() |
16ee40e020 | ||
![]() |
4d9b5eec2b | ||
![]() |
844d5721b7 | ||
![]() |
5e34c71b14 | ||
![]() |
8bb211fc33 | ||
![]() |
59faaa25cb | ||
![]() |
2d8339ed4e | ||
![]() |
56f5e7077e | ||
![]() |
e75ffdf1e8 | ||
![]() |
c4e96ea899 | ||
![]() |
b1a906f176 | ||
![]() |
bc5d21fc76 | ||
![]() |
e18d5f610f | ||
![]() |
4e983b923c | ||
![]() |
8ce7c66fea | ||
![]() |
8cd22653aa | ||
![]() |
1193c017ee | ||
![]() |
1a11290c97 | ||
![]() |
ad5b5b5de0 | ||
![]() |
f6142edd7d | ||
![]() |
2f13f5da8b | ||
![]() |
36be89dac5 | ||
![]() |
ebae7ab2dd | ||
![]() |
7ab20755b4 | ||
![]() |
4e58477135 | ||
![]() |
b2f00f4801 | ||
![]() |
9889b6c57d | ||
![]() |
fea00e8c1a | ||
![]() |
7a8ebd903b | ||
![]() |
c6bcb136ad | ||
![]() |
3fed1249c4 | ||
![]() |
fa3514dd55 | ||
![]() |
34738a29e1 | ||
![]() |
a4832dc8fc | ||
![]() |
5b82a9b044 | ||
![]() |
2ddc067ebc | ||
![]() |
f06aef9913 | ||
![]() |
d6e76b7c73 | ||
![]() |
0fe429cd79 | ||
![]() |
b37cdd3ccc | ||
![]() |
7cc9a80297 | ||
![]() |
bf03f35a35 | ||
![]() |
9201cc44e5 | ||
![]() |
eff83cfdc3 | ||
![]() |
713b81c4e1 | ||
![]() |
fbf783582d | ||
![]() |
699c0e733c | ||
![]() |
6e1ad5ea07 | ||
![]() |
94ee525ce6 | ||
![]() |
7e3e22ee4c | ||
![]() |
6bdd7d6999 | ||
![]() |
f4a5f6f782 | ||
![]() |
0e1a73192f | ||
![]() |
13113e1bbc | ||
![]() |
a428d2b36b | ||
![]() |
90ffb5a444 | ||
![]() |
4bfd6f2351 | ||
![]() |
d4db394b58 | ||
![]() |
7e5809afde | ||
![]() |
495d6a2f9e | ||
87cce63ae8 | |||
853dd3d7ec | |||
a532924668 | |||
87478f82f2 | |||
![]() |
c0dbb2c97d |
@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
type: "manual"
|
type: "always_apply"
|
||||||
|
description: "Example description"
|
||||||
---
|
---
|
||||||
|
|
||||||
1、在接下来的每一个步骤当中,请帮我实现对页面的响应式设计
|
1、在接下来的每一个步骤当中,请帮我实现对页面的响应式设计
|
||||||
2、必须严格执行我给你的指令,一步一步执行,不得有缩减
|
2、必须严格执行我给你的指令,一步一步执行,不得有缩减
|
||||||
|
3、我们用的是naive UI组件 ,TS,vue3
|
||||||
|
2
.env
@ -1,5 +1,5 @@
|
|||||||
# API配置
|
# API配置
|
||||||
VITE_API_BASE_URL=http://110.42.96.65:55510/api
|
VITE_API_BASE_URL=http://103.40.14.23:25526/jeecgboot
|
||||||
|
|
||||||
# Mock配置 - 禁用Mock,使用真实API
|
# Mock配置 - 禁用Mock,使用真实API
|
||||||
VITE_ENABLE_MOCK=false
|
VITE_ENABLE_MOCK=false
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
|
|
||||||
# API配置
|
# API配置
|
||||||
VITE_API_BASE_URL=http://110.42.96.65:55510/api
|
VITE_API_BASE_URL=http://103.40.14.23:25526/jeecgboot
|
||||||
|
|
||||||
# Mock配置
|
# Mock配置
|
||||||
# 设置为 true 使用Mock数据,false 使用真实API
|
# 设置为 true 使用Mock数据,false 使用真实API
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
|
|
||||||
# API配置
|
# API配置
|
||||||
VITE_API_BASE_URL=http://110.42.96.65:55510/api
|
VITE_API_BASE_URL=http://103.40.14.23:25526/jeecgboot
|
||||||
|
|
||||||
# Mock配置 - 生产环境禁用Mock,使用真实API
|
# Mock配置 - 生产环境禁用Mock,使用真实API
|
||||||
VITE_ENABLE_MOCK=false
|
VITE_ENABLE_MOCK=false
|
||||||
|
@ -6,6 +6,6 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
ports:
|
ports:
|
||||||
- "55514:80"
|
- "25527:80"
|
||||||
container_name: vue3-nginx
|
container_name: vue3-nginx
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
320
docs/DPlayer-Integration-Guide.md
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
# DPlayer 集成指南
|
||||||
|
|
||||||
|
## 什么是 DPlayer?
|
||||||
|
|
||||||
|
**DPlayer** 是由 [DIYGod](https://github.com/DIYGod) 开发的一个开源的 HTML5 视频播放器,具有以下特点:
|
||||||
|
|
||||||
|
- 🎨 **界面美观**:现代化的设计风格
|
||||||
|
- 🎯 **轻量级**:体积小,加载快
|
||||||
|
- 🌏 **中文友好**:由中国开发者开发,中文文档完善
|
||||||
|
- 🎮 **功能丰富**:支持弹幕、快捷键、倍速播放等
|
||||||
|
- 📱 **移动端适配**:响应式设计,支持移动设备
|
||||||
|
|
||||||
|
## 主要功能特性
|
||||||
|
|
||||||
|
### 基础功能
|
||||||
|
- ✅ 播放/暂停控制
|
||||||
|
- ✅ 音量控制
|
||||||
|
- ✅ 进度条拖拽
|
||||||
|
- ✅ 全屏切换
|
||||||
|
- ✅ 倍速播放 (0.5x - 2x)
|
||||||
|
|
||||||
|
### 高级功能
|
||||||
|
- 🎯 键盘快捷键支持
|
||||||
|
- 🎨 自定义主题色
|
||||||
|
- 📝 右键菜单自定义
|
||||||
|
- 🎵 音频可视化
|
||||||
|
- 📱 移动端手势支持
|
||||||
|
|
||||||
|
### 格式支持
|
||||||
|
- MP4
|
||||||
|
- WebM
|
||||||
|
- Ogg
|
||||||
|
- HLS (.m3u8)
|
||||||
|
- FLV
|
||||||
|
- 更多格式通过插件支持
|
||||||
|
|
||||||
|
## 安装和集成
|
||||||
|
|
||||||
|
### 方法1:CDN 引入(推荐用于快速测试)
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- 在 index.html 中引入 -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/dplayer@1.27.1/dist/DPlayer.min.css">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/dplayer@1.27.1/dist/DPlayer.min.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法2:NPM 安装(推荐用于生产环境)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install dplayer
|
||||||
|
```
|
||||||
|
|
||||||
|
然后在组件中导入:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import DPlayer from 'dplayer'
|
||||||
|
import 'dplayer/dist/DPlayer.min.css'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 基础使用
|
||||||
|
|
||||||
|
### 创建播放器
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const player = new DPlayer({
|
||||||
|
container: document.getElementById('dplayer'),
|
||||||
|
video: {
|
||||||
|
url: 'video.mp4',
|
||||||
|
type: 'auto'
|
||||||
|
},
|
||||||
|
autoplay: false,
|
||||||
|
theme: '#007bff',
|
||||||
|
lang: 'zh-cn'
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 事件监听
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
player.on('play', () => {
|
||||||
|
console.log('视频开始播放')
|
||||||
|
})
|
||||||
|
|
||||||
|
player.on('pause', () => {
|
||||||
|
console.log('视频暂停')
|
||||||
|
})
|
||||||
|
|
||||||
|
player.on('ended', () => {
|
||||||
|
console.log('视频播放结束')
|
||||||
|
})
|
||||||
|
|
||||||
|
player.on('error', () => {
|
||||||
|
console.log('播放出错')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置选项
|
||||||
|
|
||||||
|
### 基础配置
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const options = {
|
||||||
|
container: document.getElementById('dplayer'), // 容器元素
|
||||||
|
video: {
|
||||||
|
url: 'video.mp4', // 视频地址
|
||||||
|
type: 'auto', // 视频类型:auto, normal, hls, flv
|
||||||
|
defaultQuality: 0, // 默认画质
|
||||||
|
pic: 'poster.jpg', // 封面图
|
||||||
|
thumbnails: 'thumbnails.jpg' // 缩略图
|
||||||
|
},
|
||||||
|
autoplay: false, // 自动播放
|
||||||
|
theme: '#007bff', // 主题色
|
||||||
|
lang: 'zh-cn', // 语言:zh-cn, en
|
||||||
|
hotkey: true, // 启用快捷键
|
||||||
|
preload: 'auto', // 预加载:auto, metadata, none
|
||||||
|
volume: 0.8, // 默认音量
|
||||||
|
playbackSpeed: [0.5, 0.75, 1, 1.25, 1.5, 2], // 倍速选项
|
||||||
|
contextmenu: [ // 右键菜单
|
||||||
|
{
|
||||||
|
text: '关于 DPlayer',
|
||||||
|
link: 'https://github.com/DIYGod/DPlayer'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 高级配置
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const advancedOptions = {
|
||||||
|
// 弹幕配置
|
||||||
|
danmaku: {
|
||||||
|
id: 'dplayer-danmaku',
|
||||||
|
api: 'https://api.prprpr.me/dplayer/',
|
||||||
|
token: 'token',
|
||||||
|
maximum: 1000,
|
||||||
|
addition: ['https://api.prprpr.me/dplayer/bilibili?aid=4157142'],
|
||||||
|
user: 'DIYGod',
|
||||||
|
bottom: '15%',
|
||||||
|
unlimited: true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 字幕配置
|
||||||
|
subtitle: {
|
||||||
|
url: 'subtitle.vtt',
|
||||||
|
type: 'webvtt',
|
||||||
|
fontSize: '20px',
|
||||||
|
bottom: '10%',
|
||||||
|
color: '#fff'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 画质切换
|
||||||
|
video: {
|
||||||
|
url: [
|
||||||
|
{
|
||||||
|
name: '1080P',
|
||||||
|
url: 'video-1080p.mp4'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '720P',
|
||||||
|
url: 'video-720p.mp4'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
defaultQuality: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 在 Vue 项目中使用
|
||||||
|
|
||||||
|
### 创建 DPlayer 组件
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div class="video-player-wrapper">
|
||||||
|
<div ref="dplayerContainer" class="dplayer-container"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
|
|
||||||
|
const dplayerContainer = ref()
|
||||||
|
let player = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 确保 DPlayer 已加载
|
||||||
|
if (window.DPlayer) {
|
||||||
|
initPlayer()
|
||||||
|
} else {
|
||||||
|
loadDPlayer().then(() => {
|
||||||
|
initPlayer()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadDPlayer = () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const cssLink = document.createElement('link')
|
||||||
|
cssLink.rel = 'stylesheet'
|
||||||
|
cssLink.href = 'https://cdn.jsdelivr.net/npm/dplayer@1.27.1/dist/DPlayer.min.css'
|
||||||
|
document.head.appendChild(cssLink)
|
||||||
|
|
||||||
|
const script = document.createElement('script')
|
||||||
|
script.src = 'https://cdn.jsdelivr.net/npm/dplayer@1.27.1/dist/DPlayer.min.js'
|
||||||
|
script.onload = resolve
|
||||||
|
document.head.appendChild(script)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const initPlayer = () => {
|
||||||
|
player = new window.DPlayer({
|
||||||
|
container: dplayerContainer.value,
|
||||||
|
video: {
|
||||||
|
url: '/video/first.mp4',
|
||||||
|
type: 'auto'
|
||||||
|
},
|
||||||
|
autoplay: false,
|
||||||
|
theme: '#007bff',
|
||||||
|
lang: 'zh-cn',
|
||||||
|
hotkey: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (player) {
|
||||||
|
player.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.video-player-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dplayer-container {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 16/9;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 样式定制
|
||||||
|
|
||||||
|
### 自定义主题色
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* 修改播放器主题色 */
|
||||||
|
.dplayer {
|
||||||
|
--dplayer-theme: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义进度条颜色 */
|
||||||
|
.dplayer .dplayer-bar-wrap .dplayer-bar .dplayer-played {
|
||||||
|
background: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义控制按钮颜色 */
|
||||||
|
.dplayer .dplayer-icons .dplayer-icon {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dplayer .dplayer-icons .dplayer-icon:hover {
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 响应式设计
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* 移动端适配 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.dplayer {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dplayer .dplayer-icons .dplayer-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快捷键支持
|
||||||
|
|
||||||
|
DPlayer 默认支持以下快捷键:
|
||||||
|
|
||||||
|
- `空格键` - 播放/暂停
|
||||||
|
- `←` - 后退 10 秒
|
||||||
|
- `→` - 前进 10 秒
|
||||||
|
- `↑` - 音量 +10%
|
||||||
|
- `↓` - 音量 -10%
|
||||||
|
- `F` - 全屏切换
|
||||||
|
- `M` - 静音切换
|
||||||
|
|
||||||
|
## 与 CKPlayer 对比
|
||||||
|
|
||||||
|
| 特性 | CKPlayer | DPlayer |
|
||||||
|
|------|----------|---------|
|
||||||
|
| 界面美观度 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||||
|
| 功能丰富度 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||||
|
| 移动端支持 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||||
|
| 中文支持 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||||
|
| 社区活跃度 | ⭐⭐ | ⭐⭐⭐⭐ |
|
||||||
|
| 文档质量 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||||
|
| 学习成本 | ⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
**DPlayer 是一个优秀的视频播放器选择**,特别适合:
|
||||||
|
|
||||||
|
- 🎯 需要美观界面的项目
|
||||||
|
- 🌏 中文用户群体
|
||||||
|
- 📱 重视移动端体验
|
||||||
|
- 🎨 需要自定义主题的项目
|
||||||
|
- ⚡ 追求轻量级解决方案
|
||||||
|
|
||||||
|
相比当前的 CKPlayer,DPlayer 提供了更好的用户体验和更丰富的功能,是升级视频播放器的理想选择。
|
523
docs/ExamInfo-API-Usage.md
Normal file
@ -0,0 +1,523 @@
|
|||||||
|
# 考试信息API使用文档
|
||||||
|
|
||||||
|
## 接口概述
|
||||||
|
|
||||||
|
`/aiol/aiolExam/getExamInfo` 接口用于获取教师名下的考试信息列表。
|
||||||
|
|
||||||
|
## 接口定义
|
||||||
|
|
||||||
|
### 请求方式
|
||||||
|
- **方法**: GET
|
||||||
|
- **路径**: `/aiol/aiolExam/getExamInfo`
|
||||||
|
- **参数**: `userId` (查询参数)
|
||||||
|
|
||||||
|
### 请求参数
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必选 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| userId | string | 是 | 教师用户ID |
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
"success": boolean,
|
||||||
|
"message": string,
|
||||||
|
"code": number,
|
||||||
|
"result": ExamInfo[],
|
||||||
|
"timestamp": number
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ExamInfo 数据结构
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ExamInfo {
|
||||||
|
id: string // 考试ID
|
||||||
|
name: string // 考试名称
|
||||||
|
paperId: string // 试卷ID
|
||||||
|
startTime: string // 开始时间
|
||||||
|
endTime: string // 结束时间
|
||||||
|
totalTime: number // 考试时长(分钟)
|
||||||
|
type: number // 考试类型:0=练习,1=考试
|
||||||
|
status: number // 状态:0=未发布,1=发布中,2=已结束
|
||||||
|
createBy: string // 创建人
|
||||||
|
createTime: string // 创建时间
|
||||||
|
updateBy: string // 更新人
|
||||||
|
updateTime: string // 更新时间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 1. 在API模块中调用
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ExamApi } from '@/api/modules/exam'
|
||||||
|
|
||||||
|
// 获取教师考试信息
|
||||||
|
const response = await ExamApi.getExamInfo('teacher_user_id')
|
||||||
|
console.log('考试信息:', response.data)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 在Vue组件中使用
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ExamApi } from '@/api/modules/exam'
|
||||||
|
import { useUserStore } from '@/stores/user'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const loadExamInfo = async () => {
|
||||||
|
if (!userStore.user?.id) {
|
||||||
|
console.error('请先登录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await ExamApi.getExamInfo(userStore.user.id.toString())
|
||||||
|
return response.data || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载考试信息失败:', error)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadExamInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 在试卷管理页面中使用
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 在 ExamLibrary.vue 中
|
||||||
|
import { ExamApi } from '@/api/modules/exam'
|
||||||
|
import { useUserStore } from '@/stores/user'
|
||||||
|
import type { ExamInfo } from '@/api/types'
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const loadExamInfo = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const currentUser = userStore.user
|
||||||
|
if (!currentUser?.id) {
|
||||||
|
message.error('请先登录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await ExamApi.getExamInfo(currentUser.id.toString())
|
||||||
|
|
||||||
|
if (response.data && Array.isArray(response.data)) {
|
||||||
|
// 数据映射和显示逻辑
|
||||||
|
const mappedList = response.data.map((item: ExamInfo) => {
|
||||||
|
// 映射逻辑...
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
// ... 其他字段映射
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
examData.value = mappedList
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载考试信息失败:', error)
|
||||||
|
message.error('加载考试信息失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据映射说明
|
||||||
|
|
||||||
|
### 状态映射
|
||||||
|
```typescript
|
||||||
|
const statusMap: { [key: number]: string } = {
|
||||||
|
0: '未发布',
|
||||||
|
1: '发布中',
|
||||||
|
2: '已结束'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 类型映射
|
||||||
|
```typescript
|
||||||
|
const categoryMap: { [key: number]: string } = {
|
||||||
|
0: '练习',
|
||||||
|
1: '考试'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 难度映射
|
||||||
|
```typescript
|
||||||
|
const difficultyMap: { [key: number]: string } = {
|
||||||
|
0: '易',
|
||||||
|
1: '中',
|
||||||
|
2: '难'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
接口可能返回以下错误:
|
||||||
|
|
||||||
|
1. **401 Unauthorized**: 用户未登录或token过期
|
||||||
|
2. **403 Forbidden**: 没有权限访问
|
||||||
|
3. **500 Internal Server Error**: 服务器内部错误
|
||||||
|
|
||||||
|
建议在调用时添加适当的错误处理:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
const response = await ExamApi.getExamInfo(userId)
|
||||||
|
// 处理成功响应
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response?.status === 401) {
|
||||||
|
// 处理认证错误
|
||||||
|
console.error('登录已过期,请重新登录')
|
||||||
|
} else {
|
||||||
|
// 处理其他错误
|
||||||
|
console.error('获取考试信息失败:', error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 调用此接口前需要确保用户已登录
|
||||||
|
2. 只有教师用户才能调用此接口
|
||||||
|
3. 返回的考试信息按创建时间倒序排列
|
||||||
|
4. 建议在组件挂载时调用此接口加载数据
|
||||||
|
5. 可以根据需要添加分页、搜索等参数(需要后端支持)
|
||||||
|
|
||||||
|
## 相关文件
|
||||||
|
|
||||||
|
- API实现: `src/api/modules/exam.ts`
|
||||||
|
- 类型定义: `src/api/types.ts`
|
||||||
|
- 使用示例: `src/api/examples/getExamInfo-example.ts`
|
||||||
|
- 页面实现: `src/views/teacher/ExamPages/ExamLibrary.vue`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 创建试卷API使用文档
|
||||||
|
|
||||||
|
## 接口概述
|
||||||
|
|
||||||
|
`POST /aiol/aiolPaper/add` 接口用于创建新的试卷。
|
||||||
|
|
||||||
|
## 接口定义
|
||||||
|
|
||||||
|
### 请求方式
|
||||||
|
- **方法**: POST
|
||||||
|
- **路径**: `/aiol/aiolPaper/add`
|
||||||
|
- **Content-Type**: `application/json`
|
||||||
|
|
||||||
|
### 请求参数
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必选 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| title | string | 是 | 试卷标题 |
|
||||||
|
| generateMode | number | 否 | 组卷模式:0=固定试卷组,1=随机抽题组卷 |
|
||||||
|
| rules | string | 否 | 组卷规则(随机抽题时使用) |
|
||||||
|
| repoId | string | 否 | 题库ID(随机抽题时使用) |
|
||||||
|
| totalScore | number | 是 | 试卷总分 |
|
||||||
|
| passScore | number | 否 | 及格分数(默认总分的60%) |
|
||||||
|
| requireReview | number | 否 | 是否需要批阅:0=不需要,1=需要 |
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
"success": boolean,
|
||||||
|
"message": string,
|
||||||
|
"code": number,
|
||||||
|
"result": string, // 试卷ID
|
||||||
|
"timestamp": number
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 1. 在API模块中调用
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ExamApi } from '@/api/modules/exam'
|
||||||
|
|
||||||
|
// 创建固定试卷组
|
||||||
|
const response = await ExamApi.createExamPaper({
|
||||||
|
title: '数学期末考试试卷',
|
||||||
|
generateMode: 0,
|
||||||
|
totalScore: 100,
|
||||||
|
passScore: 60,
|
||||||
|
requireReview: 0
|
||||||
|
})
|
||||||
|
console.log('试卷ID:', response.data)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 在Vue组件中使用
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 在 AddExam.vue 中
|
||||||
|
const saveExam = async () => {
|
||||||
|
try {
|
||||||
|
const apiData = {
|
||||||
|
title: examForm.title,
|
||||||
|
generateMode: examForm.type === 1 ? 0 : 1,
|
||||||
|
rules: '',
|
||||||
|
repoId: '',
|
||||||
|
totalScore: examForm.totalScore,
|
||||||
|
passScore: examForm.passScore || Math.floor(examForm.totalScore * 0.6),
|
||||||
|
requireReview: examForm.useAIGrading ? 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await ExamApi.createExamPaper(apiData)
|
||||||
|
console.log('创建试卷成功:', response.data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建试卷失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 不同组卷模式示例
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 固定试卷组
|
||||||
|
const fixedPaper = {
|
||||||
|
title: '固定试卷组示例',
|
||||||
|
generateMode: 0,
|
||||||
|
rules: '',
|
||||||
|
repoId: '',
|
||||||
|
totalScore: 100,
|
||||||
|
passScore: 60,
|
||||||
|
requireReview: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机抽题组卷
|
||||||
|
const randomPaper = {
|
||||||
|
title: '随机抽题组卷示例',
|
||||||
|
generateMode: 1,
|
||||||
|
rules: '{"difficulty": [1, 2, 3], "types": [0, 1, 2], "count": 20}',
|
||||||
|
repoId: 'repo_123',
|
||||||
|
totalScore: 100,
|
||||||
|
passScore: 60,
|
||||||
|
requireReview: 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 相关文件
|
||||||
|
|
||||||
|
- API实现: `src/api/modules/exam.ts`
|
||||||
|
- 页面实现: `src/views/teacher/ExamPages/AddExam.vue`
|
||||||
|
- 使用示例: `src/api/examples/createPaper-example.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 删除试卷API使用文档
|
||||||
|
|
||||||
|
## 接口概述
|
||||||
|
|
||||||
|
删除试卷相关的API接口包括单个删除和批量删除功能。
|
||||||
|
|
||||||
|
## 接口定义
|
||||||
|
|
||||||
|
### 1. 删除单个试卷
|
||||||
|
|
||||||
|
- **方法**: DELETE
|
||||||
|
- **路径**: `/aiol/aiolPaper/delete?id={paperId}`
|
||||||
|
- **参数**: `id` (查询参数) - 试卷ID
|
||||||
|
|
||||||
|
### 2. 批量删除试卷
|
||||||
|
|
||||||
|
- **实现方式**: 循环调用单个删除接口(因为后端可能不支持批量删除接口)
|
||||||
|
- **方法**: 内部调用多个 `DELETE /aiol/aiolPaper/delete?id={paperId}`
|
||||||
|
- **参数**: `ids: string[]` - 试卷ID数组
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
|
||||||
|
**单个删除响应**:
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
"success": boolean,
|
||||||
|
"message": string,
|
||||||
|
"code": number,
|
||||||
|
"result": string,
|
||||||
|
"timestamp": number
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**批量删除响应**:
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
"success": boolean,
|
||||||
|
"message": string,
|
||||||
|
"code": number,
|
||||||
|
"result": {
|
||||||
|
"success": number, // 成功删除的数量
|
||||||
|
"failed": number, // 失败删除的数量
|
||||||
|
"total": number, // 总数量
|
||||||
|
"errors": string[] // 错误信息数组
|
||||||
|
},
|
||||||
|
"timestamp": number
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 1. 删除单个试卷
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ExamApi } from '@/api/modules/exam'
|
||||||
|
|
||||||
|
// 删除单个试卷
|
||||||
|
const response = await ExamApi.deleteExamPaper('1962379646322384897')
|
||||||
|
console.log('删除结果:', response.data)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 批量删除试卷
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 批量删除试卷
|
||||||
|
const paperIds = ['1962379646322384897', '1966450638717292545']
|
||||||
|
const response = await ExamApi.batchDeleteExamPapers(paperIds)
|
||||||
|
console.log('批量删除结果:', response.data)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 在Vue组件中使用(使用 Naive UI 对话框组件)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 在 ExamLibrary.vue 中
|
||||||
|
import { useDialog, useMessage } from 'naive-ui'
|
||||||
|
|
||||||
|
const dialog = useDialog()
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const handleDeletePaper = async (row: any) => {
|
||||||
|
try {
|
||||||
|
// 使用 Naive UI 对话框组件
|
||||||
|
dialog.warning({
|
||||||
|
title: '确认删除',
|
||||||
|
content: `确定要删除试卷"${row.name}"吗?此操作不可撤销。`,
|
||||||
|
positiveText: '确定删除',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
try {
|
||||||
|
// 调用删除API
|
||||||
|
const response = await ExamApi.deleteExamPaper(row.id)
|
||||||
|
|
||||||
|
// 显示成功消息
|
||||||
|
message.success('试卷删除成功!')
|
||||||
|
|
||||||
|
// 重新加载试卷列表
|
||||||
|
await loadExamPaperList()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除试卷失败:', error)
|
||||||
|
message.error('删除试卷失败,请重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除试卷失败:', error)
|
||||||
|
message.error('删除试卷失败,请重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除示例
|
||||||
|
const handleBatchDelete = async () => {
|
||||||
|
if (checkedRowKeys.value.length === 0) {
|
||||||
|
message.warning('请先选择要删除的试卷')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用 Naive UI 对话框组件
|
||||||
|
dialog.error({
|
||||||
|
title: '确认批量删除',
|
||||||
|
content: `确定要删除选中的 ${checkedRowKeys.value.length} 个试卷吗?此操作不可撤销。`,
|
||||||
|
positiveText: '确定删除',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
try {
|
||||||
|
// 调用批量删除API
|
||||||
|
const response = await ExamApi.batchDeleteExamPapers(checkedRowKeys.value as string[])
|
||||||
|
|
||||||
|
// 显示成功消息
|
||||||
|
message.success(`成功删除 ${checkedRowKeys.value.length} 个试卷!`)
|
||||||
|
|
||||||
|
// 清空选中状态
|
||||||
|
checkedRowKeys.value = []
|
||||||
|
|
||||||
|
// 重新加载试卷列表
|
||||||
|
await loadExamPaperList()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('批量删除试卷失败:', error)
|
||||||
|
message.error('批量删除试卷失败,请重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('批量删除试卷失败:', error)
|
||||||
|
message.error('批量删除试卷失败,请重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 错误处理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const deletePaperWithErrorHandling = async (paperId: string) => {
|
||||||
|
try {
|
||||||
|
const response = await ExamApi.deleteExamPaper(paperId)
|
||||||
|
|
||||||
|
if (response.data === 'success') {
|
||||||
|
return { success: true, message: '删除成功' }
|
||||||
|
} else {
|
||||||
|
return { success: false, message: '删除失败,请重试' }
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.response?.status === 404) {
|
||||||
|
return { success: false, message: '试卷不存在' }
|
||||||
|
} else if (error.response?.status === 403) {
|
||||||
|
return { success: false, message: '没有权限删除此试卷' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, message: '删除失败,请检查网络连接' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
### 单个删除
|
||||||
|
- 支持删除单个试卷
|
||||||
|
- 使用 Naive UI 警告对话框组件
|
||||||
|
- 删除成功后自动刷新列表
|
||||||
|
- 完整的错误处理
|
||||||
|
|
||||||
|
### 批量删除
|
||||||
|
- 支持同时删除多个试卷
|
||||||
|
- 循环调用单个删除接口(避免后端接口不存在的问题)
|
||||||
|
- 逐个删除,避免对服务器造成过大压力
|
||||||
|
- 详细的删除结果反馈(成功/失败数量)
|
||||||
|
- 使用 Naive UI 错误对话框组件(更醒目的警告)
|
||||||
|
- 删除后清空选中状态
|
||||||
|
|
||||||
|
### 用户体验
|
||||||
|
- **美观的确认对话框**: 使用 Naive UI 组件,样式统一美观
|
||||||
|
- **不同类型的对话框**: 单个删除使用 warning,批量删除使用 error
|
||||||
|
- **成功/失败消息提示**: 使用 Naive UI 的 message 组件
|
||||||
|
- **按钮状态管理**: 批量删除按钮在未选中时禁用
|
||||||
|
- **实时更新选中数量显示**: 动态显示选中的试卷数量
|
||||||
|
- **异步操作处理**: 在对话框确认后才执行删除操作
|
||||||
|
|
||||||
|
## 相关文件
|
||||||
|
|
||||||
|
- API实现: `src/api/modules/exam.ts`
|
||||||
|
- 页面实现: `src/views/teacher/ExamPages/ExamLibrary.vue`
|
||||||
|
- 使用示例: `src/api/examples/deletePaper-example.ts`
|
148
docs/ai-model-api-debug.md
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
# AI模型字典API调试指南
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
在调用AI模型字典API时遇到错误:
|
||||||
|
```
|
||||||
|
Sign签名校验失败,时间戳为空!
|
||||||
|
```
|
||||||
|
|
||||||
|
## 已实施的修复措施
|
||||||
|
|
||||||
|
### 1. **添加时间戳参数到URL**
|
||||||
|
```typescript
|
||||||
|
// 修改前
|
||||||
|
return request.get('/sys/dict/getDictItems/airag_model%20where%20model_type%20=%20\'LLM\',name,id')
|
||||||
|
|
||||||
|
// 修改后
|
||||||
|
const timestamp = Date.now()
|
||||||
|
return request.get(`/sys/dict/getDictItems/airag_model%20where%20model_type%20=%20'LLM',name,id?_t=${timestamp}`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **增强请求头时间戳**
|
||||||
|
```typescript
|
||||||
|
// 添加多种时间戳格式
|
||||||
|
const timestamp = Date.now().toString()
|
||||||
|
config.headers['X-Request-Time'] = timestamp
|
||||||
|
config.headers['timestamp'] = timestamp
|
||||||
|
config.headers['X-Timestamp'] = timestamp
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **增强错误调试**
|
||||||
|
- 在AiAppSetting.vue中添加详细的调试日志
|
||||||
|
- 在AiModelTest.vue中添加直接请求测试功能
|
||||||
|
- 显示完整的错误信息和响应数据
|
||||||
|
|
||||||
|
## 测试步骤
|
||||||
|
|
||||||
|
### 1. 访问测试页面
|
||||||
|
```
|
||||||
|
http://localhost:5173/ai-model-test
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 检查登录状态
|
||||||
|
- 确保用户已登录并有有效的token
|
||||||
|
- 在测试页面中查看"用户Token"状态
|
||||||
|
|
||||||
|
### 3. 测试API调用
|
||||||
|
1. 点击"加载AI模型列表"按钮(使用封装的API)
|
||||||
|
2. 点击"直接请求测试"按钮(使用原生axios)
|
||||||
|
3. 查看控制台日志和响应数据
|
||||||
|
|
||||||
|
### 4. 检查AI应用设置
|
||||||
|
1. 访问 `/ai/app` 页面
|
||||||
|
2. 点击任意AI应用卡片
|
||||||
|
3. 切换到"模型配置"选项卡
|
||||||
|
4. 查看控制台日志
|
||||||
|
|
||||||
|
## 调试信息
|
||||||
|
|
||||||
|
### 请求信息
|
||||||
|
- **URL**: `/sys/dict/getDictItems/airag_model%20where%20model_type%20=%20'LLM',name,id?_t={timestamp}`
|
||||||
|
- **方法**: GET
|
||||||
|
- **Base URL**: `/jeecgboot` (或环境变量配置)
|
||||||
|
|
||||||
|
### 请求头
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Access-Token': '{user_token}',
|
||||||
|
'X-Request-Time': '{timestamp}',
|
||||||
|
'timestamp': '{timestamp}',
|
||||||
|
'X-Timestamp': '{timestamp}'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 预期响应格式
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "",
|
||||||
|
"code": 0,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"value": "1890232564262739969",
|
||||||
|
"text": "OpenAI",
|
||||||
|
"color": null,
|
||||||
|
"jsonObject": null,
|
||||||
|
"label": "OpenAI",
|
||||||
|
"title": "OpenAI"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1759136645858
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可能的解决方案
|
||||||
|
|
||||||
|
### 1. **检查后端签名算法**
|
||||||
|
如果问题仍然存在,可能需要:
|
||||||
|
- 检查后端期望的签名算法
|
||||||
|
- 添加必要的签名参数
|
||||||
|
- 确认时间戳格式要求
|
||||||
|
|
||||||
|
### 2. **检查接口权限**
|
||||||
|
- 确认该接口是否需要特定的用户权限
|
||||||
|
- 检查token是否有效
|
||||||
|
- 验证用户角色权限
|
||||||
|
|
||||||
|
### 3. **检查接口路径**
|
||||||
|
- 确认接口路径是否正确
|
||||||
|
- 检查URL编码是否正确
|
||||||
|
- 验证查询参数格式
|
||||||
|
|
||||||
|
## 备用方案
|
||||||
|
|
||||||
|
如果API调用仍然失败,系统会自动使用备用数据:
|
||||||
|
```typescript
|
||||||
|
modelOptions.value = [
|
||||||
|
{ label: 'GPT-3.5 Turbo', value: 'gpt-3.5-turbo' },
|
||||||
|
{ label: 'GPT-4', value: 'gpt-4' },
|
||||||
|
{ label: 'Claude-3', value: 'claude-3' }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 下一步调试
|
||||||
|
|
||||||
|
1. **查看网络请求**
|
||||||
|
- 打开浏览器开发者工具
|
||||||
|
- 查看Network选项卡
|
||||||
|
- 检查实际发送的请求和响应
|
||||||
|
|
||||||
|
2. **检查后端日志**
|
||||||
|
- 查看后端服务器日志
|
||||||
|
- 确认请求是否到达后端
|
||||||
|
- 检查签名验证逻辑
|
||||||
|
|
||||||
|
3. **联系后端开发**
|
||||||
|
- 确认接口的正确调用方式
|
||||||
|
- 获取签名算法详细信息
|
||||||
|
- 确认必需的请求参数
|
||||||
|
|
||||||
|
## 文件修改记录
|
||||||
|
|
||||||
|
- `src/api/modules/system.ts`: 添加字典API和时间戳参数
|
||||||
|
- `src/api/request.ts`: 增强时间戳请求头
|
||||||
|
- `src/views/Ai/component/AiAppSetting.vue`: 添加详细调试日志
|
||||||
|
- `src/views/AiModelTest.vue`: 创建专门的测试页面
|
||||||
|
- `src/router/index.ts`: 添加测试页面路由
|
234
docs/ai-model-dict-api.md
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
# AI模型字典API集成
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本文档说明如何在AI应用设置弹窗中集成真实的AI模型字典API,替换原有的模拟数据。
|
||||||
|
|
||||||
|
## API接口信息
|
||||||
|
|
||||||
|
### 获取AI模型字典
|
||||||
|
- **接口地址**: `/sys/dict/getDictItems/airag_model%20where%20model_type%20=%20'LLM',name,id`
|
||||||
|
- **请求方法**: GET
|
||||||
|
- **接口说明**: 获取LLM类型的AI模型字典数据
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "",
|
||||||
|
"code": 0,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"value": "1890232564262739969",
|
||||||
|
"text": "OpenAI",
|
||||||
|
"color": null,
|
||||||
|
"jsonObject": null,
|
||||||
|
"label": "OpenAI",
|
||||||
|
"title": "OpenAI"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "1897481367743143938",
|
||||||
|
"text": "deepseek",
|
||||||
|
"color": null,
|
||||||
|
"jsonObject": null,
|
||||||
|
"label": "deepseek",
|
||||||
|
"title": "deepseek"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "1897883052995006466",
|
||||||
|
"text": "智谱",
|
||||||
|
"color": null,
|
||||||
|
"jsonObject": null,
|
||||||
|
"label": "智谱",
|
||||||
|
"title": "智谱"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "1970031008335876097",
|
||||||
|
"text": "测试",
|
||||||
|
"color": null,
|
||||||
|
"jsonObject": null,
|
||||||
|
"label": "测试",
|
||||||
|
"title": "测试"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1759136645858
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实现的功能
|
||||||
|
|
||||||
|
### 1. **API模块扩展** (`src/api/modules/system.ts`)
|
||||||
|
|
||||||
|
添加了字典相关的API接口:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 字典项接口
|
||||||
|
export interface DictItem {
|
||||||
|
value: string
|
||||||
|
text: string
|
||||||
|
color: string | null
|
||||||
|
jsonObject: any | null
|
||||||
|
label: string
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SystemApi = {
|
||||||
|
// 获取字典项
|
||||||
|
getDictItems(dictCode: string, params?: string): Promise<ApiResponse<DictItem[]>>
|
||||||
|
|
||||||
|
// 获取AI模型字典项(LLM类型)
|
||||||
|
getAiModelDict(): Promise<ApiResponse<DictItem[]>>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **AI应用设置组件更新** (`src/views/Ai/component/AiAppSetting.vue`)
|
||||||
|
|
||||||
|
修改了 `loadModelOptions` 函数:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 加载模型选项
|
||||||
|
const loadModelOptions = async () => {
|
||||||
|
loadingModels.value = true
|
||||||
|
try {
|
||||||
|
const response = await SystemApi.getAiModelDict()
|
||||||
|
if (response.data.code === 200 || response.data.code === 0) {
|
||||||
|
modelOptions.value = response.data.data.map(item => ({
|
||||||
|
label: item.text,
|
||||||
|
value: item.value
|
||||||
|
}))
|
||||||
|
console.log('✅ AI模型列表加载成功:', modelOptions.value)
|
||||||
|
} else {
|
||||||
|
throw new Error(response.data.message || '获取模型列表失败')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('❌ 加载模型列表失败:', error)
|
||||||
|
message.error(error.message || '加载模型列表失败')
|
||||||
|
|
||||||
|
// 失败时使用备用数据
|
||||||
|
modelOptions.value = [
|
||||||
|
{ label: 'GPT-3.5 Turbo', value: 'gpt-3.5-turbo' },
|
||||||
|
{ label: 'GPT-4', value: 'gpt-4' },
|
||||||
|
{ label: 'Claude-3', value: 'claude-3' }
|
||||||
|
]
|
||||||
|
} finally {
|
||||||
|
loadingModels.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **测试页面** (`src/views/AiModelTest.vue`)
|
||||||
|
|
||||||
|
创建了专门的测试页面来验证API调用:
|
||||||
|
- 实时加载AI模型数据
|
||||||
|
- 显示API响应状态
|
||||||
|
- 测试选择器功能
|
||||||
|
- 查看原始数据
|
||||||
|
|
||||||
|
## 使用流程
|
||||||
|
|
||||||
|
### 1. 点击AI应用卡片
|
||||||
|
在AI应用列表页面,点击任意应用卡片:
|
||||||
|
```vue
|
||||||
|
<n-card
|
||||||
|
class="app-card"
|
||||||
|
hoverable
|
||||||
|
@click="handleEditApp(item)"
|
||||||
|
>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 打开设置弹窗
|
||||||
|
系统会调用 `handleEditApp` 函数,打开应用设置弹窗:
|
||||||
|
```typescript
|
||||||
|
const handleEditApp = (app: AiApp) => {
|
||||||
|
isEditMode.value = true
|
||||||
|
currentApp.value = { ...app }
|
||||||
|
showSettingModal.value = true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 自动加载模型数据
|
||||||
|
弹窗打开时,会自动调用 `loadModelOptions()` 函数加载AI模型数据:
|
||||||
|
```typescript
|
||||||
|
onMounted(() => {
|
||||||
|
loadModelOptions() // 自动加载模型选项
|
||||||
|
loadFlowOptions()
|
||||||
|
loadSelectedKnowledges()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 显示模型选择器
|
||||||
|
在"模型配置"选项卡中,用户可以看到从后端加载的真实AI模型列表:
|
||||||
|
- OpenAI
|
||||||
|
- deepseek
|
||||||
|
- 智谱
|
||||||
|
- 测试
|
||||||
|
|
||||||
|
## 测试方法
|
||||||
|
|
||||||
|
### 1. 访问测试页面
|
||||||
|
```
|
||||||
|
http://localhost:5173/ai-model-test
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 测试功能
|
||||||
|
- 点击"加载AI模型列表"按钮
|
||||||
|
- 查看加载状态和结果
|
||||||
|
- 测试选择器功能
|
||||||
|
- 查看原始API响应数据
|
||||||
|
|
||||||
|
### 3. 验证AI应用设置
|
||||||
|
- 访问 `/ai/app` 页面
|
||||||
|
- 点击任意AI应用卡片
|
||||||
|
- 在弹窗中切换到"模型配置"选项卡
|
||||||
|
- 验证AI模型下拉选择器是否显示真实数据
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
系统包含完整的错误处理机制:
|
||||||
|
|
||||||
|
1. **网络请求失败**: 显示错误消息并使用备用数据
|
||||||
|
2. **API响应错误**: 解析错误信息并提示用户
|
||||||
|
3. **数据格式错误**: 容错处理,确保界面正常显示
|
||||||
|
4. **加载状态**: 显示加载指示器,提升用户体验
|
||||||
|
|
||||||
|
## 数据映射
|
||||||
|
|
||||||
|
API返回的字典项会被映射为选择器选项:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// API数据格式
|
||||||
|
{
|
||||||
|
"value": "1890232564262739969",
|
||||||
|
"text": "OpenAI",
|
||||||
|
"label": "OpenAI",
|
||||||
|
"title": "OpenAI"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 映射为选择器选项
|
||||||
|
{
|
||||||
|
label: "OpenAI", // 显示文本
|
||||||
|
value: "1890232564262739969" // 选择值
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 扩展说明
|
||||||
|
|
||||||
|
### 添加其他字典类型
|
||||||
|
可以在 `SystemApi` 中添加其他字典类型的获取方法:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 获取其他类型的字典
|
||||||
|
getOtherDict(): Promise<ApiResponse<DictItem[]>> {
|
||||||
|
return request.get('/sys/dict/getDictItems/other_dict_code')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义字典查询
|
||||||
|
使用通用的 `getDictItems` 方法:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 自定义查询条件
|
||||||
|
const response = await SystemApi.getDictItems('dict_code', 'custom_params')
|
||||||
|
```
|
||||||
|
|
||||||
|
这样就完成了AI模型字典API的集成,用户在AI应用设置弹窗中可以看到真实的AI模型数据了!
|
197
docs/dynamic-menu-routes.md
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
# 动态菜单路由系统
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本系统实现了基于后端接口的动态菜单路由加载功能,支持从后端获取菜单配置并自动生成前端路由。
|
||||||
|
|
||||||
|
## 接口说明
|
||||||
|
|
||||||
|
### 获取首页菜单
|
||||||
|
- **接口**: `/aiol/aiolMenu/getIndexMenus`
|
||||||
|
- **方法**: GET
|
||||||
|
- **返回格式**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "",
|
||||||
|
"code": 200,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "首页",
|
||||||
|
"path": "/",
|
||||||
|
"type": "index",
|
||||||
|
"icon": null,
|
||||||
|
"parentId": null,
|
||||||
|
"sortOrder": 99,
|
||||||
|
"izVisible": 1,
|
||||||
|
"permissionKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1759134778382
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取学生菜单
|
||||||
|
- **接口**: `/aiol/aiolMenu/getStudentMenus`
|
||||||
|
- **方法**: GET
|
||||||
|
- **返回格式**: 同上,type为"student"
|
||||||
|
|
||||||
|
## 核心文件
|
||||||
|
|
||||||
|
### 1. API模块 (`src/api/modules/menu.ts`)
|
||||||
|
```typescript
|
||||||
|
export interface MenuItem {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
type: string
|
||||||
|
icon: string | null
|
||||||
|
parentId: string | null
|
||||||
|
sortOrder: number
|
||||||
|
izVisible: number
|
||||||
|
permissionKey: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MenuApi {
|
||||||
|
static async getIndexMenus(): Promise<ApiResponse<MenuItem[]>>
|
||||||
|
static async getStudentMenus(): Promise<ApiResponse<MenuItem[]>>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 路由工具 (`src/utils/routeUtils.ts`)
|
||||||
|
```typescript
|
||||||
|
// 根据菜单数据生成路由配置
|
||||||
|
export function generateRoutesFromMenus(menus: MenuItem[]): RouteRecordRaw[]
|
||||||
|
|
||||||
|
// 生成导航菜单数据
|
||||||
|
export function generateNavMenus(menus: MenuItem[])
|
||||||
|
|
||||||
|
// 检查路由是否存在于菜单中
|
||||||
|
export function isRouteInMenus(path: string, menus: MenuItem[]): boolean
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 状态管理 (`src/stores/menu.ts`)
|
||||||
|
```typescript
|
||||||
|
export const useMenuStore = defineStore('menu', () => {
|
||||||
|
// 状态
|
||||||
|
const indexMenus = ref<MenuItem[]>([])
|
||||||
|
const studentMenus = ref<MenuItem[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref<string | null>(null)
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
const navMenus = computed(() => generateNavMenus(indexMenus.value))
|
||||||
|
const visibleIndexMenus = computed(() => ...)
|
||||||
|
|
||||||
|
// 方法
|
||||||
|
const fetchIndexMenus = async () => { ... }
|
||||||
|
const fetchStudentMenus = async () => { ... }
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 路由配置 (`src/router/index.ts`)
|
||||||
|
在路由守卫中自动加载动态菜单:
|
||||||
|
```typescript
|
||||||
|
router.beforeEach(async (to, from, next) => {
|
||||||
|
// 加载动态菜单路由(仅在首次访问时)
|
||||||
|
if (!isMenuLoaded) {
|
||||||
|
await loadMenuRoutes()
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 1. 在组件中使用菜单状态
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useMenuStore } from '@/stores/menu'
|
||||||
|
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
|
||||||
|
// 获取菜单数据
|
||||||
|
await menuStore.fetchIndexMenus()
|
||||||
|
|
||||||
|
// 使用菜单数据
|
||||||
|
console.log(menuStore.navMenus)
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 使用动态导航组件
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<DynamicNavigation />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import DynamicNavigation from '@/components/layout/DynamicNavigation.vue'
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 检查路由权限
|
||||||
|
```typescript
|
||||||
|
import { useMenuStore } from '@/stores/menu'
|
||||||
|
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
|
||||||
|
// 检查路由是否可见
|
||||||
|
const isVisible = menuStore.isRouteVisible('/courses', 'index')
|
||||||
|
```
|
||||||
|
|
||||||
|
## 路径映射
|
||||||
|
|
||||||
|
当前支持的路径映射:
|
||||||
|
|
||||||
|
| 菜单名称 | 路径 | 组件 |
|
||||||
|
|---------|------|------|
|
||||||
|
| 首页 | / | Home |
|
||||||
|
| 热门好课 | /courses | Courses |
|
||||||
|
| 专题训练 | /special-training | SpecialTraining |
|
||||||
|
| 师资力量 | /faculty | Faculty |
|
||||||
|
| 精选资源 | /resources | Resources |
|
||||||
|
| 活动 | /activities | Activities |
|
||||||
|
| AI体验 | /ai/app | AiAppList |
|
||||||
|
|
||||||
|
## 测试页面
|
||||||
|
|
||||||
|
访问 `/menu-test` 可以查看动态菜单系统的测试页面,包含:
|
||||||
|
- 菜单状态监控
|
||||||
|
- 菜单数据展示
|
||||||
|
- 路由跳转测试
|
||||||
|
- 动态导航组件演示
|
||||||
|
|
||||||
|
## 响应式设计
|
||||||
|
|
||||||
|
动态导航组件支持响应式设计:
|
||||||
|
- 桌面端:垂直菜单布局
|
||||||
|
- 移动端:水平滚动菜单布局
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
系统包含完整的错误处理机制:
|
||||||
|
- 网络请求失败重试
|
||||||
|
- 加载状态显示
|
||||||
|
- 错误信息提示
|
||||||
|
- 手动重试功能
|
||||||
|
|
||||||
|
## 扩展说明
|
||||||
|
|
||||||
|
### 添加新的路径映射
|
||||||
|
在 `src/utils/routeUtils.ts` 中的 `componentMap` 添加新的路径映射:
|
||||||
|
```typescript
|
||||||
|
const componentMap: Record<string, () => Promise<any>> = {
|
||||||
|
'/new-path': () => import('@/views/NewComponent.vue'),
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义菜单图标
|
||||||
|
在 `DynamicNavigation.vue` 中的 `iconMap` 添加新的图标映射:
|
||||||
|
```typescript
|
||||||
|
const iconMap: Record<string, any> = {
|
||||||
|
'new-icon': NewIconComponent,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
@ -2,13 +2,15 @@
|
|||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/favicon.ico">
|
<!-- <link rel="icon" href="/logo/logo1.png"> -->
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>在线学习平台</title>
|
<title>在线学习平台</title>
|
||||||
<meta name="description" content="专业的在线学习平台,提供优质的编程和技术课程">
|
<meta name="description" content="专业的在线学习平台,提供优质的编程和技术课程">
|
||||||
<meta name="keywords" content="在线学习,编程课程,技术培训,Vue.js,React,Node.js">
|
<meta name="keywords" content="在线学习,编程课程,技术培训,Vue.js,React,Node.js">
|
||||||
<!-- CKPlayer CSS -->
|
<!-- CKPlayer CSS -->
|
||||||
<link rel="stylesheet" href="/ckplayer/css/ckplayer.css">
|
<link rel="stylesheet" href="/ckplayer/css/ckplayer.css">
|
||||||
|
<!-- HLS.js for m3u8 playback in CKPlayer -->
|
||||||
|
<script src="/ckplayer/hls.js/hls.min.js"></script>
|
||||||
<!-- CKPlayer JS -->
|
<!-- CKPlayer JS -->
|
||||||
<script src="/ckplayer/js/ckplayer.js"></script>
|
<script src="/ckplayer/js/ckplayer.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
2255
package-lock.json
generated
18
package.json
@ -13,18 +13,34 @@
|
|||||||
"test:ui": "vitest --ui"
|
"test:ui": "vitest --ui"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/sortablejs": "^1.15.8",
|
||||||
|
"@vicons/antd": "^0.13.0",
|
||||||
"@vicons/ionicons5": "^0.13.0",
|
"@vicons/ionicons5": "^0.13.0",
|
||||||
|
"@wangeditor/editor": "^5.1.23",
|
||||||
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
|
"@wangeditor/plugin-upload-attachment": "^1.1.0",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"ckplayer": "^3.1.2",
|
"ckplayer": "^3.1.2",
|
||||||
|
"dplayer": "^1.27.1",
|
||||||
|
"echarts": "5.6.0",
|
||||||
|
"marked": "^16.4.0",
|
||||||
"naive-ui": "^2.42.0",
|
"naive-ui": "^2.42.0",
|
||||||
|
"naive-ui-editor": "^1.0.6",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
|
"quill": "^2.0.3",
|
||||||
"vue": "^3.5.17",
|
"vue": "^3.5.17",
|
||||||
|
"vue-echarts": "7.0.3",
|
||||||
"vue-i18n": "^9.14.5",
|
"vue-i18n": "^9.14.5",
|
||||||
"vue-router": "^4.5.1"
|
"vue-quill-editor": "^3.0.6",
|
||||||
|
"vue-router": "^4.5.1",
|
||||||
|
"vuedraggable": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/dplayer": "^1.25.5",
|
||||||
"@types/node": "^24.0.15",
|
"@types/node": "^24.0.15",
|
||||||
"@vitejs/plugin-vue": "^6.0.0",
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
|
"less": "^4.4.2",
|
||||||
|
"sass-embedded": "^1.93.2",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.0.0",
|
"vite": "^7.0.0",
|
||||||
"vite-plugin-vue-devtools": "^7.7.7",
|
"vite-plugin-vue-devtools": "^7.7.7",
|
||||||
|
916
pnpm-lock.yaml
generated
BIN
public/analysis/icon/分组 106.png
Normal file
After Width: | Height: | Size: 625 B |
BIN
public/analysis/icon/分组 86.png
Normal file
After Width: | Height: | Size: 806 B |
BIN
public/analysis/icon/分组 93.png
Normal file
After Width: | Height: | Size: 821 B |
BIN
public/analysis/icon/切片 21.png
Normal file
After Width: | Height: | Size: 562 B |
BIN
public/analysis/icon/路径 10.png
Normal file
After Width: | Height: | Size: 775 B |
BIN
public/analysis/icon/路径 11.png
Normal file
After Width: | Height: | Size: 585 B |
BIN
public/analysis/icon/路径 18.png
Normal file
After Width: | Height: | Size: 917 B |
BIN
public/analysis/icon/路径 19.png
Normal file
After Width: | Height: | Size: 834 B |
BIN
public/analysis/切片 28.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
public/analysis/切片 29.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
public/analysis/切片 30.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/analysis/切片 31.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
public/analysis/切片 32.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/analysis/切片 33.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/analysis/切片 34.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
public/analysis/切片 35.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
public/banners/banner8.png
Normal file
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1011 B After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 812 B After Width: | Height: | Size: 908 B |
Before Width: | Height: | Size: 944 B After Width: | Height: | Size: 1.4 KiB |
BIN
public/images/Help-center/course-active.png
Normal file
After Width: | Height: | Size: 844 B |
BIN
public/images/Help-center/disposition.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 224 KiB |
BIN
public/images/advertising/advertising3.png
Normal file
After Width: | Height: | Size: 175 KiB |
BIN
public/images/advertising/advertising4.png
Normal file
After Width: | Height: | Size: 368 KiB |
BIN
public/images/aiAssistant/AI助教1.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
public/images/aiAssistant/AI助教2-active.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
public/images/aiAssistant/AI助教2.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
public/images/aiAssistant/bg.png
Normal file
After Width: | Height: | Size: 16 MiB |
BIN
public/images/aiAssistant/points.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
public/images/aiAssistant/upload.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
public/images/aiAssistant/多维度分析.png
Normal file
After Width: | Height: | Size: 295 KiB |
BIN
public/images/aiAssistant/失败icon.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
public/images/aiAssistant/教学内容分析.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
public/images/aiAssistant/教学动作分析.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
public/images/aiAssistant/教学情感分析.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
public/images/aiAssistant/教学语言分析.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
public/images/aiAssistant/教师-女.png
Normal file
After Width: | Height: | Size: 4.1 MiB |
BIN
public/images/aiAssistant/教师-男.png
Normal file
After Width: | Height: | Size: 3.3 MiB |
BIN
public/images/aiAssistant/数据支持.png
Normal file
After Width: | Height: | Size: 263 KiB |
BIN
public/images/aiAssistant/智能评估.png
Normal file
After Width: | Height: | Size: 352 KiB |
BIN
public/images/aiAssistant/编组.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
public/images/aiCompanion/@@2x.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
public/images/aiCompanion/AI伴学标签@2x.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
public/images/aiCompanion/AI小助手@2x.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
public/images/aiCompanion/bg.png
Normal file
After Width: | Height: | Size: 176 KiB |
BIN
public/images/aiCompanion/fold.png
Normal file
After Width: | Height: | Size: 447 B |
BIN
public/images/aiCompanion/homework.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
public/images/aiCompanion/ii.jpg
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/images/aiCompanion/lock.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
public/images/aiCompanion/reply.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
public/images/aiCompanion/talk.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
public/images/aiCompanion/做作业@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
public/images/aiCompanion/切换@2x.png
Normal file
After Width: | Height: | Size: 442 B |
BIN
public/images/aiCompanion/删除@2x.png
Normal file
After Width: | Height: | Size: 724 B |
BIN
public/images/aiCompanion/升序/降序@2x.png
Normal file
After Width: | Height: | Size: 516 B |
BIN
public/images/aiCompanion/发送@2x.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
public/images/aiCompanion/图片添加@2x.png
Normal file
After Width: | Height: | Size: 858 B |
BIN
public/images/aiCompanion/弹幕(开).png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
public/images/aiCompanion/智能问答@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
public/images/aiCompanion/知识图谱@2x.png
Normal file
After Width: | Height: | Size: 252 KiB |
BIN
public/images/aiCompanion/笔记-灰@2x.png
Normal file
After Width: | Height: | Size: 547 B |
BIN
public/images/aiCompanion/笔记-蓝@2x.png
Normal file
After Width: | Height: | Size: 594 B |
BIN
public/images/aiCompanion/背景色@2x.png
Normal file
After Width: | Height: | Size: 3.3 MiB |
BIN
public/images/aiCompanion/表情@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
public/images/aiCompanion/记录@2x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
public/images/aiCompanion/记笔记@2x.png
Normal file
After Width: | Height: | Size: 680 B |
BIN
public/images/aiCompanion/随便聊聊@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 359 B After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 4.2 KiB |
BIN
public/images/auth/revise.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/images/course/courseAi.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
public/images/course/courseAii.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
public/images/courses/@.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
public/images/courses/Image.png
Normal file
After Width: | Height: | Size: 858 B |
BIN
public/images/courses/comments-note-active.png
Normal file
After Width: | Height: | Size: 594 B |
BIN
public/images/courses/comments-note.png
Normal file
After Width: | Height: | Size: 547 B |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 273 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 236 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 260 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 394 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 359 KiB |
BIN
public/images/courses/expression.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
public/images/examination/score-bg.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
public/images/examination/topic1.png
Normal file
After Width: | Height: | Size: 21 KiB |