Compare commits
182 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 |
@ -4,3 +4,4 @@ type: "manual"
|
||||
|
||||
1、在接下来的每一个步骤当中,请帮我实现对页面的响应式设计
|
||||
2、必须严格执行我给你的指令,一步一步执行,不得有缩减
|
||||
3、我们用的是naive UI组件 ,TS,vue3
|
||||
|
2
.env
@ -1,5 +1,5 @@
|
||||
# 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
|
||||
VITE_ENABLE_MOCK=false
|
||||
|
@ -1,7 +1,7 @@
|
||||
# 开发环境配置
|
||||
|
||||
# API配置
|
||||
VITE_API_BASE_URL=http://110.42.96.65:55510/api
|
||||
VITE_API_BASE_URL=http://103.40.14.23:25526/jeecgboot
|
||||
|
||||
# Mock配置
|
||||
# 设置为 true 使用Mock数据,false 使用真实API
|
||||
|
@ -1,7 +1,7 @@
|
||||
# 生产环境配置
|
||||
|
||||
# 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
|
||||
VITE_ENABLE_MOCK=false
|
||||
|
@ -6,6 +6,6 @@ services:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "55514:80"
|
||||
- "25527:80"
|
||||
container_name: vue3-nginx
|
||||
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`
|
@ -2,13 +2,15 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<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">
|
||||
<title>在线学习平台</title>
|
||||
<meta name="description" content="专业的在线学习平台,提供优质的编程和技术课程">
|
||||
<meta name="keywords" content="在线学习,编程课程,技术培训,Vue.js,React,Node.js">
|
||||
<!-- 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 -->
|
||||
<script src="/ckplayer/js/ckplayer.js"></script>
|
||||
</head>
|
||||
|
1094
package-lock.json
generated
14
package.json
@ -13,16 +13,28 @@
|
||||
"test:ui": "vitest --ui"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@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",
|
||||
"ckplayer": "^3.1.2",
|
||||
"dplayer": "^1.27.1",
|
||||
"echarts": "5.6.0",
|
||||
"naive-ui": "^2.42.0",
|
||||
"naive-ui-editor": "^1.0.6",
|
||||
"pinia": "^3.0.3",
|
||||
"quill": "^2.0.3",
|
||||
"vue": "^3.5.17",
|
||||
"vue-echarts": "7.0.3",
|
||||
"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": {
|
||||
"@types/dplayer": "^1.25.5",
|
||||
"@types/node": "^24.0.15",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
|
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.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 |
BIN
public/images/examination/topic2.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
public/images/icon/customer.jpg
Normal file
After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 812 B |
BIN
public/images/icon/finish.png
Normal file
After Width: | Height: | Size: 2.2 KiB |