feat: 教师端数据看板与学校端课程统计功能
教师端数据看板: - 新增 TeacherDashboardResponse/TeacherLessonVO/TeacherLessonTrendVO - 新增 TeacherWeeklyStatsResponse 周统计响应 - 新增 TeacherActivityLevel 枚举和 TeacherActivityRankResponse 活跃度排行 - 实现教师端课程统计、任务完成详情、任务反馈接口 学校端课程统计: - 新增 CourseUsageVO/CourseUsageStatsVO/CoursePackageVO - 新增 SchoolCourseResponse 和学校端课程使用查询接口 - 实现学校端统计数据和课程趋势接口 用户资料功能: - 新增 UpdateProfileRequest/UpdateProfileResponse - 实现用户资料更新接口 前后端对齐: - 更新 OpenAPI 规范和前端 API 类型生成 - 优化 DashboardView 组件和 API 调用 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c93d325cee
commit
6f64723428
@ -497,6 +497,63 @@ definePage({
|
|||||||
- **命名**: `YYYY-MM-DD.md`
|
- **命名**: `YYYY-MM-DD.md`
|
||||||
- **创建时机**: 每天开始开发时检查并创建
|
- **创建时机**: 每天开始开发时检查并创建
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件写入最佳实践(JetBrains 环境)
|
||||||
|
|
||||||
|
> **重要**: 在 JetBrains IDE 插件中使用 Claude Code 时,按以下优先级选择写入方式:
|
||||||
|
|
||||||
|
### 写入方式优先级
|
||||||
|
|
||||||
|
| 优先级 | 方式 | 适用场景 | 示例 |
|
||||||
|
|--------|------|----------|------|
|
||||||
|
| **1** | Write 工具 | 简单文件、单文件写入 | `Write(file_path="...", content="...")` |
|
||||||
|
| **2** | Bash heredoc | 复杂文件、批量写入、Write 失败时 | `cat > /path/to/file << 'EOF'` |
|
||||||
|
| **3** | Python 写入 | 需要复杂逻辑处理时 | `python3 -c "..."` |
|
||||||
|
| **❌ 避免** | sed/awk | Windows Git Bash 中兼容性差 | — |
|
||||||
|
|
||||||
|
### Write 工具使用规范
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ✅ 正确:使用 Unix 风格路径
|
||||||
|
Write(file_path="/f/LesingleProject/.../file.txt", content="...")
|
||||||
|
|
||||||
|
# ❌ 错误:不要使用 Windows 路径
|
||||||
|
Write(file_path="F:\\LesingleProject\\...\\file.txt", content="...")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bash heredoc 方式(推荐备选)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 多行文件写入
|
||||||
|
cat > /f/LesingleProject/.../file.txt << 'EOF'
|
||||||
|
第一行内容
|
||||||
|
第二行内容
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 单行追加
|
||||||
|
echo "追加内容" >> /f/LesingleProject/.../file.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 写入前检查清单
|
||||||
|
|
||||||
|
- [ ] 确保目录存在:`mkdir -p /f/.../父目录`
|
||||||
|
- [ ] 使用 Unix 路径格式:`/f/...` 不是 `F:\...`
|
||||||
|
- [ ] 检查文件是否被占用(IDE 索引、格式化中)
|
||||||
|
|
||||||
|
### 写入失败诊断
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 检查目录是否存在
|
||||||
|
ls -la /f/路径/到的/父目录/
|
||||||
|
|
||||||
|
# 检查文件属性
|
||||||
|
ls -la /f/路径/到/文件.txt
|
||||||
|
|
||||||
|
# 测试写入权限
|
||||||
|
echo 'test' > /f/路径/到/文件.txt && echo '成功' || echo '失败'
|
||||||
|
```
|
||||||
|
|
||||||
### 测试记录
|
### 测试记录
|
||||||
- **位置**: `/docs/test-logs/{端}/`
|
- **位置**: `/docs/test-logs/{端}/`
|
||||||
- **命名**: `YYYY-MM-DD.md`
|
- **命名**: `YYYY-MM-DD.md`
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(mvn compile:*)",
|
"Bash(mvn compile:*)",
|
||||||
"Bash(sed:*)"
|
"Bash(sed:*)",
|
||||||
|
"Bash(grep:*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -349,7 +349,7 @@
|
|||||||
│ │
|
│ │
|
||||||
│ 📊 平台整体数据 │
|
│ 📊 平台整体数据 │
|
||||||
│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
|
│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
|
||||||
│ │ 租户总数 │ 活跃租户 │ 月授课次数 │ 覆盖学生数 │ │
|
│ │ 租户总数 │ 活跃租户 │ 月授课次数 │ 学生总数数 │ │
|
||||||
│ │ 1,254所 │ 986所 │ 45,678次 │ 234,567人 │ │
|
│ │ 1,254所 │ 986所 │ 45,678次 │ 234,567人 │ │
|
||||||
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
|
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
|
||||||
│ │
|
│ │
|
||||||
|
|||||||
@ -54,7 +54,7 @@
|
|||||||
- ✅ 账号不存在登录失败
|
- ✅ 账号不存在登录失败
|
||||||
|
|
||||||
#### 02-dashboard.spec.ts - 数据看板测试 (7 个用例)
|
#### 02-dashboard.spec.ts - 数据看板测试 (7 个用例)
|
||||||
- ✅ 验证统计卡片显示(租户数、课程包数、月授课次数、覆盖学生)
|
- ✅ 验证统计卡片显示(租户数、课程包数、月授课次数、学生总数)
|
||||||
- ✅ 验证趋势图加载
|
- ✅ 验证趋势图加载
|
||||||
- ✅ 验证活跃租户 TOP5 列表
|
- ✅ 验证活跃租户 TOP5 列表
|
||||||
- ✅ 验证热门课程包 TOP5 列表
|
- ✅ 验证热门课程包 TOP5 列表
|
||||||
|
|||||||
176
docs/dev-logs/2026-03-20-profile-feature.md
Normal file
176
docs/dev-logs/2026-03-20-profile-feature.md
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# 个人中心功能实现记录
|
||||||
|
|
||||||
|
## 日期:2026-03-20
|
||||||
|
|
||||||
|
## 实现内容
|
||||||
|
|
||||||
|
### 需求目标
|
||||||
|
在个人中心页面新增:
|
||||||
|
1. **密码修改功能**:输入旧密码和新密码进行修改
|
||||||
|
2. **信息编辑功能**:可修改姓名、手机号、邮箱
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 后端实现
|
||||||
|
|
||||||
|
### 1. 新建 DTO 文件
|
||||||
|
|
||||||
|
#### `UpdateProfileRequest.java`
|
||||||
|
- 路径:`reading-platform-java/src/main/java/com/reading/platform/dto/request/UpdateProfileRequest.java`
|
||||||
|
- 字段:name(姓名)、phone(手机号)、email(邮箱)
|
||||||
|
- 校验注解:
|
||||||
|
- name: `@Pattern(regexp = "^[\u4e00-\u9fa5a-zA-Z\s]{2,20}$")`
|
||||||
|
- phone: `@Pattern(regexp = "^1[3-9]\d{9}$")`
|
||||||
|
- email: `@Email`
|
||||||
|
|
||||||
|
#### `UpdateProfileResponse.java`
|
||||||
|
- 路径:`reading-platform-java/src/main/java/com/reading/platform/dto/response/UpdateProfileResponse.java`
|
||||||
|
- 字段:userInfo(用户信息)、token(新 token)
|
||||||
|
|
||||||
|
### 2. 修改 Service 接口
|
||||||
|
|
||||||
|
#### `AuthService.java`
|
||||||
|
新增方法:
|
||||||
|
```java
|
||||||
|
UpdateProfileResponse updateProfile(UpdateProfileRequest request);
|
||||||
|
void changePassword(String oldPassword, String newPassword, String currentToken);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 修改 Service 实现
|
||||||
|
|
||||||
|
#### `AuthServiceImpl.java`
|
||||||
|
- 新增 `updateProfile()` 方法:
|
||||||
|
- 根据当前用户角色更新对应表(AdminUser/Tenant/Teacher/Parent)
|
||||||
|
- 学校管理员(Tenant)更新 `contactName/contactPhone/contactEmail` 字段
|
||||||
|
- 其他角色更新 `name/phone/email` 字段
|
||||||
|
- 生成新 token 并更新 Redis
|
||||||
|
- 返回更新后的用户信息和新 token
|
||||||
|
|
||||||
|
- 新增 `changePassword(String oldPassword, String newPassword, String currentToken)` 方法:
|
||||||
|
- 调用原有 `changePassword()` 方法修改密码
|
||||||
|
- 将当前 token 加入黑名单(使旧 token 失效)
|
||||||
|
|
||||||
|
- 新增 `convertToUserInfoResponse(Object userInfo, String role)` 私有方法:
|
||||||
|
- 将 Entity 对象转换为 UserInfoResponse
|
||||||
|
|
||||||
|
### 4. 修改 Controller
|
||||||
|
|
||||||
|
#### `AuthController.java`
|
||||||
|
- 新增接口 `PUT /api/v1/auth/profile`:修改个人信息
|
||||||
|
- 修改接口 `POST /api/v1/auth/change-password`:增加 HttpServletRequest 参数获取 token
|
||||||
|
- 新增 `resolveToken(HttpServletRequest request)` 辅助方法:从 Authorization header 获取 token
|
||||||
|
|
||||||
|
### 5. 扩展 JwtTokenProvider
|
||||||
|
|
||||||
|
#### `JwtTokenProvider.java`
|
||||||
|
新增方法:
|
||||||
|
```java
|
||||||
|
public long getRemainingExpiration(String token)
|
||||||
|
```
|
||||||
|
- 用于获取 token 剩余过期时间(秒)
|
||||||
|
- 用于将 token 加入黑名单时设置过期时间
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 前端实现
|
||||||
|
|
||||||
|
### 1. 扩展 API 文件
|
||||||
|
|
||||||
|
#### `src/api/auth.ts`
|
||||||
|
新增类型和方法:
|
||||||
|
```typescript
|
||||||
|
export interface UpdateProfileDto {
|
||||||
|
name?: string;
|
||||||
|
phone?: string;
|
||||||
|
email?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateProfileResponse {
|
||||||
|
userInfo: UserProfile;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateProfile(data: UpdateProfileDto): Promise<UpdateProfileResponse>
|
||||||
|
export function changePassword(oldPassword: string, newPassword: string): Promise<void>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 修改个人中心页面
|
||||||
|
|
||||||
|
#### `src/views/profile/ProfileView.vue`
|
||||||
|
- 新增「编辑资料」按钮:点击后弹出编辑弹窗
|
||||||
|
- 新增「修改密码」按钮:点击后弹出密码修改弹窗
|
||||||
|
- 编辑表单:
|
||||||
|
- 姓名输入框(必填,2-20 位中文或英文)
|
||||||
|
- 手机号输入框(选填,11 位数字正则校验)
|
||||||
|
- 邮箱输入框(选填,email 格式校验)
|
||||||
|
- 密码修改表单:
|
||||||
|
- 旧密码输入框(必填)
|
||||||
|
- 新密码输入框(必填,最少 6 位)
|
||||||
|
- 确认密码输入框(必填,与 new password 一致)
|
||||||
|
- 修改信息成功后:刷新本地用户信息
|
||||||
|
- 修改密码成功后:清除 token 和用户信息,跳转到登录页
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 后端 API 列表
|
||||||
|
|
||||||
|
| 接口 | 方法 | 路径 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| 获取个人信息 | GET | `/api/v1/auth/profile` | 已有 |
|
||||||
|
| 修改个人信息 | PUT | `/api/v1/auth/profile` | 新增,返回新 token |
|
||||||
|
| 修改密码 | POST | `/api/v1/auth/change-password` | 已有,修改后 token 失效 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 验证方案
|
||||||
|
|
||||||
|
### 后端验证
|
||||||
|
1. 启动后端服务(端口 8480)
|
||||||
|
2. 使用 Swagger 测试接口:
|
||||||
|
- `GET /api/v1/auth/profile` - 获取个人信息
|
||||||
|
- `PUT /api/v1/auth/profile` - 修改个人信息
|
||||||
|
- `POST /api/v1/auth/change-password?oldPassword=xxx&newPassword=yyy` - 修改密码
|
||||||
|
|
||||||
|
### 前端验证
|
||||||
|
1. 启动前端服务(端口 5173)
|
||||||
|
2. 登录任意角色账户
|
||||||
|
3. 访问个人中心页面
|
||||||
|
4. 测试编辑信息功能
|
||||||
|
5. 测试修改密码功能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **字段映射**:
|
||||||
|
- 前端统一使用 `name/phone/email`
|
||||||
|
- 后端 Service 层根据角色映射到对应字段
|
||||||
|
- 学校管理员(Tenant)映射到 `contactName/contactPhone/contactEmail`
|
||||||
|
|
||||||
|
2. **Token 处理**:
|
||||||
|
- 修改个人信息:返回新 token,前端替换 localStorage 中的旧 token
|
||||||
|
- 修改密码:将当前 token 加入黑名单,前端清除 token 并跳转到登录页
|
||||||
|
|
||||||
|
3. **表单校验**:
|
||||||
|
- 前端:手机号正则 `/^1[3-9]\d{9}$/`,邮箱使用 Ant Design 内置校验
|
||||||
|
- 后端:使用 `@Valid` + `@Pattern`/`@Email` 注解校验
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 待验证事项
|
||||||
|
|
||||||
|
- [ ] 超管角色修改信息功能
|
||||||
|
- [ ] 学校管理员修改信息功能
|
||||||
|
- [ ] 教师角色修改信息功能
|
||||||
|
- [ ] 家长角色修改信息功能
|
||||||
|
- [ ] 所有角色修改密码功能
|
||||||
|
- [ ] 修改信息后 token 是否正确刷新
|
||||||
|
- [ ] 修改密码后 token 是否失效并需重新登录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 下一步计划
|
||||||
|
|
||||||
|
1. 启动服务进行功能验证
|
||||||
|
2. 修复可能发现的问题
|
||||||
|
3. 更新测试日志
|
||||||
204
docs/dev-logs/2026-03-20-teacher-course-usage-stats.md
Normal file
204
docs/dev-logs/2026-03-20-teacher-course-usage-stats.md
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
# 开发日志 - 2026-03-20
|
||||||
|
|
||||||
|
## 教师端课程使用统计功能实现
|
||||||
|
|
||||||
|
### 实现内容
|
||||||
|
|
||||||
|
实现了教师端课程使用统计功能(增强版),支持按时间周期筛选和更多维度的统计。
|
||||||
|
|
||||||
|
### 后端改动
|
||||||
|
|
||||||
|
#### 1. 新增 DTO 类
|
||||||
|
|
||||||
|
**CourseUsageQuery.java** - 请求参数 DTO
|
||||||
|
- `periodType`: 统计周期类型(TODAY/WEEK/MONTH/CUSTOM)
|
||||||
|
- `startDate`: 自定义周期开始日期
|
||||||
|
- `endDate`: 自定义周期结束日期
|
||||||
|
|
||||||
|
**CourseUsageStatsVO.java** - 响应对象 DTO(增强版)
|
||||||
|
- `coursePackageId`: 课程包 ID
|
||||||
|
- `coursePackageName`: 课程包名称
|
||||||
|
- `usageCount`: 使用次数(基于实际授课记录统计)
|
||||||
|
- `studentCount`: 参与学生数(去重)
|
||||||
|
- `avgDuration`: 平均时长(分钟)
|
||||||
|
- `lastUsedAt`: 最后使用时间
|
||||||
|
|
||||||
|
#### 2. LessonMapper.java
|
||||||
|
|
||||||
|
新增 `getCourseUsageStats()` 方法:
|
||||||
|
- 使用自定义 SQL 统计课程包使用情况
|
||||||
|
- 基于 `lesson` 表实际授课记录
|
||||||
|
- 支持时间范围筛选
|
||||||
|
- 支持教师 ID 筛选
|
||||||
|
- 只统计 `COMPLETED` 状态的课程
|
||||||
|
- 返回 TOP 10 课程包
|
||||||
|
|
||||||
|
#### 3. TeacherStatsService.java
|
||||||
|
|
||||||
|
新增接口方法:
|
||||||
|
```java
|
||||||
|
List<CourseUsageStatsVO> getCourseUsageStats(Long tenantId, Long teacherId, CourseUsageQuery query);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. TeacherStatsServiceImpl.java
|
||||||
|
|
||||||
|
实现 `getCourseUsageStats()` 方法:
|
||||||
|
- 调用 `calculateTimeRange()` 计算时间范围
|
||||||
|
- 支持四种周期类型:TODAY、WEEK、MONTH、CUSTOM
|
||||||
|
- 调用 Mapper 获取统计数据
|
||||||
|
|
||||||
|
实现 `calculateTimeRange()` 辅助方法:
|
||||||
|
- TODAY: 当天 00:00:00 至今
|
||||||
|
- WEEK: 本周一至今
|
||||||
|
- MONTH: 本月 1 号至今
|
||||||
|
- CUSTOM: 自定义日期范围
|
||||||
|
|
||||||
|
#### 5. TeacherStatsController.java
|
||||||
|
|
||||||
|
新增接口:
|
||||||
|
- `GET /api/v1/teacher/course-usage-stats` - 增强版课程使用统计
|
||||||
|
- 支持参数:`periodType`、`startDate`、`endDate`
|
||||||
|
- 保留旧接口 `/api/v1/teacher/course-usage`(标记为 @Deprecated)
|
||||||
|
|
||||||
|
### 前端改动
|
||||||
|
|
||||||
|
#### 1. src/api/teacher.ts
|
||||||
|
|
||||||
|
新增类型定义:
|
||||||
|
- `CourseUsageQueryParams` - 查询参数类型
|
||||||
|
- `CourseUsageStatsItem` - 响应数据类型
|
||||||
|
|
||||||
|
新增 API 方法:
|
||||||
|
- `getTeacherCourseUsageStats()` - 获取增强版课程使用统计
|
||||||
|
|
||||||
|
保留旧 API(向后兼容):
|
||||||
|
- `getTeacherCourseUsage()`
|
||||||
|
|
||||||
|
#### 2. src/views/teacher/DashboardView.vue
|
||||||
|
|
||||||
|
**新增响应式数据**:
|
||||||
|
- `usagePeriodType`: 当前选择的周期类型
|
||||||
|
- `courseUsageStatsData`: 增强版课程使用统计数据
|
||||||
|
- `periodOptions`: 周期选项(今日/本周/本月)
|
||||||
|
|
||||||
|
**UI 改动**:
|
||||||
|
- 在课程使用卡片添加周期选择器(a-segmented)
|
||||||
|
- 用户可通过点击切换统计周期
|
||||||
|
|
||||||
|
**图表增强**:
|
||||||
|
- `initUsageChart()` 支持新旧两种数据类型
|
||||||
|
- tooltip 显示更详细信息:
|
||||||
|
- 使用次数
|
||||||
|
- 参与学生数
|
||||||
|
- 平均时长
|
||||||
|
- 最后使用时间
|
||||||
|
|
||||||
|
**数据加载**:
|
||||||
|
- `loadUsageData()` 调用新 API `getTeacherCourseUsageStats()`
|
||||||
|
- 周期切换时自动重新加载数据
|
||||||
|
|
||||||
|
### 统计逻辑
|
||||||
|
|
||||||
|
1. **使用次数**:统计周期内 COMPLETED 状态的 lesson 数量
|
||||||
|
2. **参与学生数**:去重统计 student_record 中的 student_id
|
||||||
|
3. **平均时长**:计算 lesson 的 start_datetime 到 end_datetime 的分钟差平均值
|
||||||
|
4. **最后使用时间**:最近一次授课完成时间
|
||||||
|
|
||||||
|
### 数据流向
|
||||||
|
|
||||||
|
```
|
||||||
|
用户选择周期 → 前端调用 API → Controller 接收参数
|
||||||
|
→ Service 计算时间范围 → Mapper 执行 SQL 统计
|
||||||
|
→ 返回 CourseUsageStatsVO 列表 → 前端图表展示
|
||||||
|
```
|
||||||
|
|
||||||
|
### SQL 统计逻辑
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
cp.id AS coursePackageId,
|
||||||
|
cp.name AS coursePackageName,
|
||||||
|
COUNT(l.id) AS usageCount,
|
||||||
|
COUNT(DISTINCT sr.student_id) AS studentCount,
|
||||||
|
AVG(TIMESTAMPDIFF(MINUTE, l.start_datetime, l.end_datetime)) AS avgDuration,
|
||||||
|
MAX(l.end_datetime) AS lastUsedAt
|
||||||
|
FROM lesson l
|
||||||
|
INNER JOIN course_package cp ON l.course_id = cp.id
|
||||||
|
LEFT JOIN student_record sr ON l.id = sr.lesson_id
|
||||||
|
WHERE l.tenant_id = #{tenantId}
|
||||||
|
AND l.end_datetime >= #{startTime}
|
||||||
|
AND l.end_datetime <= #{endTime}
|
||||||
|
AND l.status = 'COMPLETED'
|
||||||
|
AND l.teacher_id = #{teacherId}
|
||||||
|
GROUP BY cp.id, cp.name
|
||||||
|
ORDER BY usageCount DESC
|
||||||
|
LIMIT 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### 测试验证
|
||||||
|
|
||||||
|
- [x] 后端编译通过 ✅
|
||||||
|
- [x] 启动后端服务测试 API ✅
|
||||||
|
- [x] API 返回数据正确 ✅
|
||||||
|
- [ ] 前端展示周期选择器
|
||||||
|
- [ ] 切换周期数据正确刷新
|
||||||
|
- [ ] tooltip 显示完整信息
|
||||||
|
|
||||||
|
### API 测试响应示例
|
||||||
|
|
||||||
|
**请求**: `GET /api/v1/teacher/course-usage-stats?periodType=MONTH`
|
||||||
|
|
||||||
|
**响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "操作成功",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"coursePackageId": "17",
|
||||||
|
"coursePackageName": "课程简介课程简介课程简介课程简介课程简介课程简介课程简介",
|
||||||
|
"usageCount": 15,
|
||||||
|
"studentCount": 5,
|
||||||
|
"avgDuration": 0,
|
||||||
|
"lastUsedAt": "2026-03-16T00:00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coursePackageId": "16",
|
||||||
|
"coursePackageName": "T 色他",
|
||||||
|
"usageCount": 1,
|
||||||
|
"studentCount": 0,
|
||||||
|
"avgDuration": 0,
|
||||||
|
"lastUsedAt": "2026-03-16T00:00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 技术难点解决
|
||||||
|
|
||||||
|
**问题**: MyBatis `@SelectProvider` 不支持 XML 格式的 `<if>` 动态标签
|
||||||
|
|
||||||
|
**解决方案**: 使用 SQL 条件 `AND (#{teacherId} IS NULL OR l.teacher_id = #{teacherId})` 替代动态 SQL,实现可选参数过滤。
|
||||||
|
|
||||||
|
### 文件清单
|
||||||
|
|
||||||
|
**后端新增**:
|
||||||
|
- `dto/request/CourseUsageQuery.java`
|
||||||
|
- `dto/response/CourseUsageStatsVO.java`
|
||||||
|
|
||||||
|
**后端修改**:
|
||||||
|
- `mapper/LessonMapper.java`
|
||||||
|
- `service/TeacherStatsService.java`
|
||||||
|
- `service/impl/TeacherStatsServiceImpl.java`
|
||||||
|
- `controller/teacher/TeacherStatsController.java`
|
||||||
|
|
||||||
|
**前端修改**:
|
||||||
|
- `src/api/teacher.ts`
|
||||||
|
- `src/views/teacher/DashboardView.vue`
|
||||||
|
|
||||||
|
### 后续优化建议
|
||||||
|
|
||||||
|
1. **前端展示优化**:可以考虑将饼图改为条形图,更适合展示 TOP 排行
|
||||||
|
2. **导出功能**:支持导出统计数据为 Excel
|
||||||
|
3. **更多维度**:增加课程类型分布、班级使用对比等
|
||||||
|
4. **缓存优化**:统计数据可以缓存在 Redis 中,提高查询性能
|
||||||
169
docs/dev-logs/2026-03-21.md
Normal file
169
docs/dev-logs/2026-03-21.md
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
# 开发日志 2026-03-21
|
||||||
|
|
||||||
|
## 学校端 - 课程使用统计功能实现
|
||||||
|
|
||||||
|
### 需求背景
|
||||||
|
|
||||||
|
学校端 Dashboard 页面已有"课程使用统计"卡片组件,但后端返回空数据。需要实现该功能,让学校管理员能够查看本校各课程包的使用情况。
|
||||||
|
|
||||||
|
### 实现内容
|
||||||
|
|
||||||
|
#### 1. 后端修改
|
||||||
|
|
||||||
|
**Controller 层** (`SchoolStatsController.java`)
|
||||||
|
|
||||||
|
- 添加日期范围参数支持,允许前端传入 `startDate` 和 `endDate`
|
||||||
|
- 不传参数时默认统计本月数据
|
||||||
|
|
||||||
|
```java
|
||||||
|
@GetMapping("/courses")
|
||||||
|
@Operation(summary = "获取课程使用统计")
|
||||||
|
public Result<List<Map<String, Object>>> getCourseUsageStats(
|
||||||
|
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||||||
|
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
return Result.success(schoolStatsService.getCourseUsageStats(tenantId, startDate, endDate));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Service 接口** (`SchoolStatsService.java`)
|
||||||
|
|
||||||
|
- 更新方法签名,添加日期参数
|
||||||
|
- 添加 `LocalDate` 导入
|
||||||
|
|
||||||
|
```java
|
||||||
|
List<Map<String, Object>> getCourseUsageStats(Long tenantId, LocalDate startDate, LocalDate endDate);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Service 实现** (`SchoolStatsServiceImpl.java`)
|
||||||
|
|
||||||
|
- 使用 `LessonMapper.getCourseUsageStats()` 查询实际数据
|
||||||
|
- 支持日期范围筛选,默认统计本月
|
||||||
|
- 将 `CourseUsageStatsVO` 转换为 `Map` 格式返回
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> getCourseUsageStats(Long tenantId, LocalDate startDate, LocalDate endDate) {
|
||||||
|
// 计算时间范围
|
||||||
|
LocalDateTime startTime;
|
||||||
|
LocalDateTime endTime;
|
||||||
|
|
||||||
|
if (startDate != null) {
|
||||||
|
startTime = startDate.atStartOfDay();
|
||||||
|
} else {
|
||||||
|
startTime = LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endDate != null) {
|
||||||
|
endTime = endDate.atTime(23, 59, 59);
|
||||||
|
} else {
|
||||||
|
endTime = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用 Mapper 查询
|
||||||
|
List<CourseUsageStatsVO> stats = lessonMapper.getCourseUsageStats(
|
||||||
|
tenantId, null, startTime, endTime
|
||||||
|
);
|
||||||
|
|
||||||
|
// 转换为 Map 格式返回
|
||||||
|
return stats.stream().map(vo -> {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("courseId", vo.getCoursePackageId());
|
||||||
|
map.put("courseName", vo.getCoursePackageName());
|
||||||
|
map.put("usageCount", vo.getUsageCount());
|
||||||
|
map.put("studentCount", vo.getStudentCount() != null ? vo.getStudentCount() : 0);
|
||||||
|
map.put("avgDuration", vo.getAvgDuration() != null ? vo.getAvgDuration() : 0);
|
||||||
|
map.put("lastUsedAt", vo.getLastUsedAt());
|
||||||
|
return map;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 前端修改
|
||||||
|
|
||||||
|
**API 层** (`src/api/school.ts`)
|
||||||
|
|
||||||
|
- 更新 `getCourseUsageStats` 支持日期参数
|
||||||
|
- 增强返回类型定义
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const getCourseUsageStats = (startDate?: string, endDate?: string) => {
|
||||||
|
const params: Record<string, string> = {};
|
||||||
|
if (startDate) params.startDate = startDate;
|
||||||
|
if (endDate) params.endDate = endDate;
|
||||||
|
return http.get<Array<{
|
||||||
|
courseId: number;
|
||||||
|
courseName: string;
|
||||||
|
usageCount: number;
|
||||||
|
studentCount?: number;
|
||||||
|
avgDuration?: number;
|
||||||
|
lastUsedAt?: string;
|
||||||
|
}>>('/v1/school/stats/courses', { params });
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**组件层** (`src/views/school/DashboardView.vue`)
|
||||||
|
|
||||||
|
- `loadCourseStats` 函数传递日期范围参数
|
||||||
|
- `onMounted` 中设置默认日期范围为当月
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const loadCourseStats = async () => {
|
||||||
|
courseStatsLoading.value = true;
|
||||||
|
try {
|
||||||
|
const startDate = dateRange.value?.[0]?.format('YYYY-MM-DD');
|
||||||
|
const endDate = dateRange.value?.[1]?.format('YYYY-MM-DD');
|
||||||
|
const data = await getCourseUsageStats(startDate, endDate);
|
||||||
|
courseStats.value = data.slice(0, 10);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load course stats:', error);
|
||||||
|
} finally {
|
||||||
|
courseStatsLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// ... 其他初始化
|
||||||
|
// 设置默认日期范围为当月
|
||||||
|
const now = dayjs();
|
||||||
|
const monthStart = now.startOf('month');
|
||||||
|
const monthEnd = now.endOf('month');
|
||||||
|
dateRange.value = [monthStart, monthEnd];
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据来源
|
||||||
|
|
||||||
|
课程使用统计基于 `lesson` 表的实际授课记录统计:
|
||||||
|
|
||||||
|
- **usageCount**: 统计周期内 COMPLETED 状态的 lesson 数量
|
||||||
|
- **studentCount**: 参与学生数(去重统计 student_record 中的 student_id)
|
||||||
|
- **avgDuration**: 平均时长(lesson 的 start_datetime 到 end_datetime 的分钟差平均值)
|
||||||
|
- **lastUsedAt**: 最后使用时间(最近一次授课完成时间)
|
||||||
|
|
||||||
|
### 文件变更列表
|
||||||
|
|
||||||
|
| 文件 | 变更说明 |
|
||||||
|
|------|---------|
|
||||||
|
| `reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolStatsController.java` | 添加日期范围参数 |
|
||||||
|
| `reading-platform-java/src/main/java/com/reading/platform/service/SchoolStatsService.java` | 更新方法签名 |
|
||||||
|
| `reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolStatsServiceImpl.java` | 实现实际查询逻辑 |
|
||||||
|
| `reading-platform-frontend/src/api/school.ts` | 更新 API 函数和类型 |
|
||||||
|
| `reading-platform-frontend/src/views/school/DashboardView.vue` | 传递日期参数,设置默认日期范围 |
|
||||||
|
|
||||||
|
### 测试验证
|
||||||
|
|
||||||
|
- [ ] 后端编译通过
|
||||||
|
- [ ] 启动后端服务(端口 8480)
|
||||||
|
- [ ] 启动前端服务(端口 5173)
|
||||||
|
- [ ] 登录学校管理员账号
|
||||||
|
- [ ] 访问 Dashboard 页面,查看"课程使用统计"卡片
|
||||||
|
- [ ] 验证日期范围筛选功能
|
||||||
|
- [ ] 验证数据显示正确性
|
||||||
|
|
||||||
|
### 后续优化建议
|
||||||
|
|
||||||
|
1. **增加更多统计维度**:按班级、按教师统计
|
||||||
|
2. **可视化增强**:趋势图、热力图
|
||||||
|
3. **导出功能**:Excel 导出课程使用明细
|
||||||
|
4. **性能优化**:大数据量时考虑缓存
|
||||||
@ -114,7 +114,7 @@
|
|||||||
| 租户总数 | 显示真实数据 | 显示 2 | ✅ |
|
| 租户总数 | 显示真实数据 | 显示 2 | ✅ |
|
||||||
| 课程包总数 | 显示真实数据 | 显示 5 | ✅ |
|
| 课程包总数 | 显示真实数据 | 显示 5 | ✅ |
|
||||||
| 月授课次数 | 显示真实数据 | 显示 22 | ✅ |
|
| 月授课次数 | 显示真实数据 | 显示 22 | ✅ |
|
||||||
| 覆盖学生 | 显示真实数据 | 显示 5 | ✅ |
|
| 学生总数 | 显示真实数据 | 显示 5 | ✅ |
|
||||||
|
|
||||||
### 3.2 使用趋势图
|
### 3.2 使用趋势图
|
||||||
| 测试项 | 预期结果 | 实际结果 | 状态 |
|
| 测试项 | 预期结果 | 实际结果 | 状态 |
|
||||||
|
|||||||
@ -51,7 +51,7 @@ reading-platform-frontend/tests/e2e/admin/
|
|||||||
|
|
||||||
| 测试项 | 状态 | 说明 |
|
| 测试项 | 状态 | 说明 |
|
||||||
|--------|------|------|
|
|--------|------|------|
|
||||||
| 验证统计卡片显示 | ✅ | 租户数、课程包数、月授课次数、覆盖学生 |
|
| 验证统计卡片显示 | ✅ | 租户数、课程包数、月授课次数、学生总数 |
|
||||||
| 验证趋势图加载 | ✅ | 验证图表容器显示 |
|
| 验证趋势图加载 | ✅ | 验证图表容器显示 |
|
||||||
| 验证活跃租户 TOP5 列表 | ✅ | 验证列表数据 |
|
| 验证活跃租户 TOP5 列表 | ✅ | 验证列表数据 |
|
||||||
| 验证热门课程包 TOP5 列表 | ✅ | 验证列表数据 |
|
| 验证热门课程包 TOP5 列表 | ✅ | 验证列表数据 |
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -107,6 +107,7 @@ export interface AdminStatsResponse {
|
|||||||
totalStudents: number;
|
totalStudents: number;
|
||||||
totalTeachers: number;
|
totalTeachers: number;
|
||||||
totalLessons: number;
|
totalLessons: number;
|
||||||
|
monthlyLessons: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 前端使用的统计数据结构
|
// 前端使用的统计数据结构
|
||||||
@ -124,9 +125,8 @@ export interface AdminStats {
|
|||||||
// 后端返回的趋势数据结构(分离数组格式)
|
// 后端返回的趋势数据结构(分离数组格式)
|
||||||
export interface StatsTrendResponse {
|
export interface StatsTrendResponse {
|
||||||
dates: string[];
|
dates: string[];
|
||||||
newStudents: number[];
|
lessonCounts: number[];
|
||||||
newTeachers: number[];
|
studentCounts: number[];
|
||||||
newCourses: number[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 前端使用的趋势数据结构
|
// 前端使用的趋势数据结构
|
||||||
@ -141,17 +141,16 @@ export interface TrendData {
|
|||||||
export interface ActiveTenantResponse {
|
export interface ActiveTenantResponse {
|
||||||
tenantId: number;
|
tenantId: number;
|
||||||
tenantName: string;
|
tenantName: string;
|
||||||
activeUsers: number;
|
activeTeacherCount: number;
|
||||||
courseCount: number;
|
completedLessonCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 前端使用的活跃租户结构
|
// 前端使用的活跃租户结构
|
||||||
export interface ActiveTenant {
|
export interface ActiveTenant {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
lessonCount: number;
|
activeTeacherCount: number;
|
||||||
teacherCount: number | string;
|
completedLessonCount: number;
|
||||||
studentCount: number | string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 后端返回的热门课程结构
|
// 后端返回的热门课程结构
|
||||||
@ -273,7 +272,7 @@ const mapStatsData = (data: AdminStatsResponse): AdminStats => ({
|
|||||||
teacherCount: data.totalTeachers || 0,
|
teacherCount: data.totalTeachers || 0,
|
||||||
lessonCount: data.totalLessons || 0,
|
lessonCount: data.totalLessons || 0,
|
||||||
publishedCourseCount: 0, // 后端暂无此数据
|
publishedCourseCount: 0, // 后端暂无此数据
|
||||||
monthlyLessons: 0, // 后端暂无此数据
|
monthlyLessons: data.monthlyLessons || 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 趋势数据映射:分离数组 -> 对象数组
|
// 趋势数据映射:分离数组 -> 对象数组
|
||||||
@ -282,8 +281,8 @@ const mapTrendData = (data: StatsTrendResponse): TrendData[] => {
|
|||||||
return data.dates.map((date, index) => ({
|
return data.dates.map((date, index) => ({
|
||||||
month: date,
|
month: date,
|
||||||
tenantCount: 0, // 后端无此数据
|
tenantCount: 0, // 后端无此数据
|
||||||
lessonCount: data.newCourses?.[index] || 0,
|
lessonCount: data.lessonCounts?.[index] || 0,
|
||||||
studentCount: data.newStudents?.[index] || 0,
|
studentCount: data.studentCounts?.[index] || 0,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -293,9 +292,8 @@ const mapActiveTenants = (data: ActiveTenantResponse[]): ActiveTenant[] => {
|
|||||||
return data.map(item => ({
|
return data.map(item => ({
|
||||||
id: item.tenantId,
|
id: item.tenantId,
|
||||||
name: item.tenantName,
|
name: item.tenantName,
|
||||||
teacherCount: '-', // 后端无单独字段
|
activeTeacherCount: item.activeTeacherCount ?? 0,
|
||||||
studentCount: '-', // 后端无单独字段
|
completedLessonCount: item.completedLessonCount ?? 0,
|
||||||
lessonCount: item.courseCount, // 使用 courseCount 替代
|
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -55,3 +55,26 @@ export function refreshToken(): Promise<{ token: string }> {
|
|||||||
export function getProfile(): Promise<UserProfile> {
|
export function getProfile(): Promise<UserProfile> {
|
||||||
return http.get('/v1/auth/profile');
|
return http.get('/v1/auth/profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改个人信息
|
||||||
|
export interface UpdateProfileDto {
|
||||||
|
name?: string;
|
||||||
|
phone?: string;
|
||||||
|
email?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateProfileResponse {
|
||||||
|
userInfo: UserProfile;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateProfile(data: UpdateProfileDto): Promise<UpdateProfileResponse> {
|
||||||
|
return http.put('/v1/auth/profile', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改密码(修改成功后 token 失效,需重新登录)
|
||||||
|
export function changePassword(oldPassword: string, newPassword: string): Promise<void> {
|
||||||
|
return http.post('/v1/auth/change-password', null, {
|
||||||
|
params: { oldPassword, newPassword },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import type {
|
|||||||
GenerateReadOnlyTokenParams,
|
GenerateReadOnlyTokenParams,
|
||||||
GetActiveTeachersParams,
|
GetActiveTeachersParams,
|
||||||
GetActiveTenantsParams,
|
GetActiveTenantsParams,
|
||||||
|
GetAllCoursesParams,
|
||||||
GetAllStudentsParams,
|
GetAllStudentsParams,
|
||||||
GetCalendarViewDataParams,
|
GetCalendarViewDataParams,
|
||||||
GetClassPageParams,
|
GetClassPageParams,
|
||||||
@ -57,8 +58,11 @@ import type {
|
|||||||
GetSchedules1Params,
|
GetSchedules1Params,
|
||||||
GetSchedulesParams,
|
GetSchedulesParams,
|
||||||
GetSchoolCoursesParams,
|
GetSchoolCoursesParams,
|
||||||
|
GetStatisticsParams,
|
||||||
GetStudentPageParams,
|
GetStudentPageParams,
|
||||||
GetTaskPage1Params,
|
GetTaskCompletions1Params,
|
||||||
|
GetTaskCompletionsParams,
|
||||||
|
GetTaskListParams,
|
||||||
GetTaskPageParams,
|
GetTaskPageParams,
|
||||||
GetTasksByStudentParams,
|
GetTasksByStudentParams,
|
||||||
GetTeacherPageParams,
|
GetTeacherPageParams,
|
||||||
@ -82,7 +86,6 @@ import type {
|
|||||||
RefreshTokenRequest,
|
RefreshTokenRequest,
|
||||||
RenewRequest,
|
RenewRequest,
|
||||||
ResetPassword1Params,
|
ResetPassword1Params,
|
||||||
ResetPasswordParams,
|
|
||||||
ResourceItemCreateRequest,
|
ResourceItemCreateRequest,
|
||||||
ResourceItemUpdateRequest,
|
ResourceItemUpdateRequest,
|
||||||
ResourceLibraryCreateRequest,
|
ResourceLibraryCreateRequest,
|
||||||
@ -96,6 +99,8 @@ import type {
|
|||||||
StudentRecordRequest,
|
StudentRecordRequest,
|
||||||
StudentUpdateRequest,
|
StudentUpdateRequest,
|
||||||
TaskCreateRequest,
|
TaskCreateRequest,
|
||||||
|
TaskFeedbackRequest,
|
||||||
|
TaskSubmitRequest,
|
||||||
TaskTemplateCreateRequest,
|
TaskTemplateCreateRequest,
|
||||||
TaskUpdateRequest,
|
TaskUpdateRequest,
|
||||||
TeacherCreateRequest,
|
TeacherCreateRequest,
|
||||||
@ -106,6 +111,7 @@ import type {
|
|||||||
UpdateBasicSettings1Body,
|
UpdateBasicSettings1Body,
|
||||||
UpdateClassTeacherBody,
|
UpdateClassTeacherBody,
|
||||||
UpdateNotificationSettings1Body,
|
UpdateNotificationSettings1Body,
|
||||||
|
UpdateProfileRequest,
|
||||||
UpdateSecuritySettings1Body,
|
UpdateSecuritySettings1Body,
|
||||||
UpdateSettings1Body,
|
UpdateSettings1Body,
|
||||||
UpdateStorageSettingsBody,
|
UpdateStorageSettingsBody,
|
||||||
@ -162,6 +168,38 @@ const deleteTask = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 修改评价
|
||||||
|
*/
|
||||||
|
const updateFeedback = (
|
||||||
|
completionId: number,
|
||||||
|
taskFeedbackRequest: TaskFeedbackRequest,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/teacher/tasks/completions/${completionId}/feedback`, method: 'PUT',
|
||||||
|
headers: {'Content-Type': 'application/json', },
|
||||||
|
data: taskFeedbackRequest,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 提交评价
|
||||||
|
*/
|
||||||
|
const submitFeedback = (
|
||||||
|
completionId: number,
|
||||||
|
taskFeedbackRequest: TaskFeedbackRequest,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/teacher/tasks/completions/${completionId}/feedback`, method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json', },
|
||||||
|
data: taskFeedbackRequest,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary 获取模板详情
|
* @summary 获取模板详情
|
||||||
*/
|
*/
|
||||||
@ -388,48 +426,6 @@ const deleteTeacher = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get task by ID
|
|
||||||
*/
|
|
||||||
const getTask1 = (
|
|
||||||
id: number,
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/school/tasks/${id}`, method: 'GET',
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Update task
|
|
||||||
*/
|
|
||||||
const updateTask1 = (
|
|
||||||
id: number,
|
|
||||||
taskUpdateRequest: TaskUpdateRequest,
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/school/tasks/${id}`, method: 'PUT',
|
|
||||||
headers: {'Content-Type': 'application/json', },
|
|
||||||
data: taskUpdateRequest,
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Delete task
|
|
||||||
*/
|
|
||||||
const deleteTask1 = (
|
|
||||||
id: number,
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/school/tasks/${id}`, method: 'DELETE',
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary 获取模板详情
|
* @summary 获取模板详情
|
||||||
*/
|
*/
|
||||||
@ -825,6 +821,38 @@ const removeClassTeacher = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 修改任务提交
|
||||||
|
*/
|
||||||
|
const updateSubmission = (
|
||||||
|
taskId: number,
|
||||||
|
taskSubmitRequest: TaskSubmitRequest,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/parent/tasks/${taskId}/submit`, method: 'PUT',
|
||||||
|
headers: {'Content-Type': 'application/json', },
|
||||||
|
data: taskSubmitRequest,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 提交任务完成
|
||||||
|
*/
|
||||||
|
const submitTask = (
|
||||||
|
taskId: number,
|
||||||
|
taskSubmitRequest: TaskSubmitRequest,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/parent/tasks/${taskId}/submit`, method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json', },
|
||||||
|
data: taskSubmitRequest,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get growth record by ID
|
* @summary Get growth record by ID
|
||||||
*/
|
*/
|
||||||
@ -867,6 +895,34 @@ const deleteGrowthRecord2 = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取当前用户信息
|
||||||
|
*/
|
||||||
|
const getCurrentUser = (
|
||||||
|
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/auth/profile`, method: 'GET',
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 修改个人信息
|
||||||
|
*/
|
||||||
|
const updateProfile = (
|
||||||
|
updateProfileRequest: UpdateProfileRequest,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/auth/profile`, method: 'PUT',
|
||||||
|
headers: {'Content-Type': 'application/json', },
|
||||||
|
data: updateProfileRequest,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary 查询主题详情
|
* @summary 查询主题详情
|
||||||
*/
|
*/
|
||||||
@ -1650,7 +1706,7 @@ const getLessonFeedback = (
|
|||||||
/**
|
/**
|
||||||
* @summary 提交课时反馈
|
* @summary 提交课时反馈
|
||||||
*/
|
*/
|
||||||
const submitFeedback = (
|
const submitFeedback1 = (
|
||||||
id: number,
|
id: number,
|
||||||
lessonFeedbackRequest: LessonFeedbackRequest,
|
lessonFeedbackRequest: LessonFeedbackRequest,
|
||||||
) => {
|
) => {
|
||||||
@ -1778,40 +1834,9 @@ const createTeacher = (
|
|||||||
*/
|
*/
|
||||||
const resetPassword = (
|
const resetPassword = (
|
||||||
id: number,
|
id: number,
|
||||||
params: ResetPasswordParams,
|
|
||||||
) => {
|
) => {
|
||||||
return customMutator<Blob>(
|
return customMutator<Blob>(
|
||||||
{url: `/v1/school/teachers/${id}/reset-password`, method: 'POST',
|
{url: `/v1/school/teachers/${id}/reset-password`, method: 'POST',
|
||||||
params,
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get task page
|
|
||||||
*/
|
|
||||||
const getTaskPage1 = (
|
|
||||||
params?: GetTaskPage1Params,
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/school/tasks`, method: 'GET',
|
|
||||||
params,
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Create task
|
|
||||||
*/
|
|
||||||
const createTask1 = (
|
|
||||||
taskCreateRequest: TaskCreateRequest,
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/school/tasks`, method: 'POST',
|
|
||||||
headers: {'Content-Type': 'application/json', },
|
|
||||||
data: taskCreateRequest,
|
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -2157,7 +2182,7 @@ const assignStudents = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Complete task
|
* @summary 完成任务(旧接口,兼容使用)
|
||||||
*/
|
*/
|
||||||
const completeTask = (
|
const completeTask = (
|
||||||
id: number,
|
id: number,
|
||||||
@ -2723,6 +2748,34 @@ const getTodayLessons = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取任务完成情况列表
|
||||||
|
*/
|
||||||
|
const getTaskCompletions = (
|
||||||
|
taskId: number,
|
||||||
|
params?: GetTaskCompletionsParams,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/teacher/tasks/${taskId}/completions`, method: 'GET',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取提交详情
|
||||||
|
*/
|
||||||
|
const getCompletionDetail = (
|
||||||
|
completionId: number,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/teacher/tasks/completions/${completionId}`, method: 'GET',
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary 获取默认模板
|
* @summary 获取默认模板
|
||||||
*/
|
*/
|
||||||
@ -2941,10 +2994,11 @@ const getCourse = (
|
|||||||
* @summary 获取所有课程
|
* @summary 获取所有课程
|
||||||
*/
|
*/
|
||||||
const getAllCourses = (
|
const getAllCourses = (
|
||||||
|
params?: GetAllCoursesParams,
|
||||||
) => {
|
) => {
|
||||||
return customMutator<Blob>(
|
return customMutator<Blob>(
|
||||||
{url: `/v1/teacher/courses/all`, method: 'GET',
|
{url: `/v1/teacher/courses/all`, method: 'GET',
|
||||||
|
params,
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -3191,6 +3245,75 @@ const getCourseReports = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取任务列表(支持多维度筛选)
|
||||||
|
*/
|
||||||
|
const getTaskList = (
|
||||||
|
params?: GetTaskListParams,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/school/reading-tasks`, method: 'GET',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取任务详情
|
||||||
|
*/
|
||||||
|
const getTaskDetail = (
|
||||||
|
taskId: number,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/school/reading-tasks/${taskId}`, method: 'GET',
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取任务完成情况列表
|
||||||
|
*/
|
||||||
|
const getTaskCompletions1 = (
|
||||||
|
taskId: number,
|
||||||
|
params?: GetTaskCompletions1Params,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/school/reading-tasks/${taskId}/completions`, method: 'GET',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取统计数据
|
||||||
|
*/
|
||||||
|
const getStatistics = (
|
||||||
|
params?: GetStatisticsParams,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/school/reading-tasks/statistics`, method: 'GET',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取学生提交详情
|
||||||
|
*/
|
||||||
|
const getCompletionDetail1 = (
|
||||||
|
completionId: number,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/school/reading-tasks/completions/${completionId}`, method: 'GET',
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get children of parent
|
* @summary Get children of parent
|
||||||
*/
|
*/
|
||||||
@ -3418,7 +3541,7 @@ const getSchoolCourse = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get my tasks
|
* @summary 获取我的任务列表
|
||||||
*/
|
*/
|
||||||
const getMyTasks = (
|
const getMyTasks = (
|
||||||
params?: GetMyTasksParams,
|
params?: GetMyTasksParams,
|
||||||
@ -3432,9 +3555,9 @@ const getMyTasks = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get task by ID
|
* @summary 获取任务详情
|
||||||
*/
|
*/
|
||||||
const getTask2 = (
|
const getTask1 = (
|
||||||
id: number,
|
id: number,
|
||||||
) => {
|
) => {
|
||||||
return customMutator<Blob>(
|
return customMutator<Blob>(
|
||||||
@ -3445,7 +3568,7 @@ const getTask2 = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get tasks by student ID
|
* @summary 获取孩子的任务列表
|
||||||
*/
|
*/
|
||||||
const getTasksByStudent = (
|
const getTasksByStudent = (
|
||||||
studentId: number,
|
studentId: number,
|
||||||
@ -3459,6 +3582,32 @@ const getTasksByStudent = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取提交详情
|
||||||
|
*/
|
||||||
|
const getCompletionDetail2 = (
|
||||||
|
completionId: number,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/parent/tasks/completions/${completionId}`, method: 'GET',
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取教师评价
|
||||||
|
*/
|
||||||
|
const getFeedback = (
|
||||||
|
completionId: number,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/parent/tasks/completions/${completionId}/feedback`, method: 'GET',
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get my notifications
|
* @summary Get my notifications
|
||||||
*/
|
*/
|
||||||
@ -3612,19 +3761,6 @@ const getOssToken = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 获取当前用户信息
|
|
||||||
*/
|
|
||||||
const getCurrentUser = (
|
|
||||||
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/auth/profile`, method: 'GET',
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary 获取租户统计信息
|
* @summary 获取租户统计信息
|
||||||
*/
|
*/
|
||||||
@ -3800,10 +3936,12 @@ const deleteFile = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {getTask,updateTask,deleteTask,getTemplate,updateTemplate,deleteTemplate,getSchedule,updateSchedule,cancelSchedule,getLesson,updateLesson,getLessonProgress,saveLessonProgress,getGrowthRecord,updateGrowthRecord,deleteGrowthRecord,getTeacher,updateTeacher,deleteTeacher,getTask1,updateTask1,deleteTask1,getTemplate1,updateTemplate1,deleteTemplate1,getStudent,updateStudent,deleteStudent,getSettings,updateSettings,getSecuritySettings,updateSecuritySettings,getNotificationSettings,updateNotificationSettings,getBasicSettings,updateBasicSettings,getSchedule1,updateSchedule1,cancelSchedule1,getParent,updateParent,deleteParent,getGrowthRecord1,updateGrowthRecord1,deleteGrowthRecord1,getClass,updateClass,deleteClass,updateClassTeacher,removeClassTeacher,getGrowthRecord2,updateGrowthRecord2,deleteGrowthRecord2,findOne,update,_delete,reorder,getTenant,updateTenant,deleteTenant,updateTenantStatus,updateTenantQuota,getAllSettings,updateSettings1,getStorageSettings,updateStorageSettings,getSecuritySettings1,updateSecuritySettings1,getNotificationSettings1,updateNotificationSettings1,getBasicSettings1,updateBasicSettings1,findLibrary,updateLibrary,deleteLibrary,findItem,updateItem,deleteItem,getCourse1,updateCourse,deleteCourse,reorderSteps,findOne1,update1,delete1,updateStep,removeStep,reorder1,findOne2,update2,delete2,setPackages,getTaskPage,createTask,getTemplates,createTemplate,createFromTemplate,getSchedules,createSchedule,markAsRead,markAllAsRead,getMyLessons,createLesson,saveStudentRecord,batchSaveStudentRecords,startLesson,getLessonFeedback,submitFeedback,completeLesson,cancelLesson,createLessonFromSchedule,startLessonFromSchedule,getGrowthRecordPage,createGrowthRecord,getTeacherPage,createTeacher,resetPassword,getTaskPage1,createTask1,getTemplates1,createTemplate1,getStudentPage,createStudent,getSchedules1,createSchedule1,checkConflict,batchCreateSchedules,createSchedulesByClasses,getParentPage,createParent,bindStudent,unbindStudent,resetPassword1,renewCollection,getGrowthRecordPage1,createGrowthRecord1,getClassPage,createClass,getClassTeachers1,assignTeachers,getClassStudents1,assignStudents,completeTask,markAsRead1,markAllAsRead1,createGrowthRecord2,refreshToken,uploadFile,refreshToken1,logout,login,changePassword,findAll,create,getTenantPage,createTenant,resetTenantPassword,findAllLibraries,createLibrary,findAllItems,createItem,batchDeleteItems,getCoursePage1,createCourse,submitCourse,rejectCourse,publishCourse,archiveCourse,findAll1,create1,findSteps,createStep,page,create2,withdraw,submit,republish,reject,publish,archive,getWeeklyStats,getTodayLessons,getDefaultTemplate,getAllStudents,getTodaySchedules,getTimetable,getRecommendedCourses,getMyNotifications,getNotification,getUnreadCount,getStudentRecords,getTodayLessons1,getLessonTrend,getFeedbacks,getFeedbackStats,getDashboard,getCoursePage,getCourse,getAllCourses,getCourseUsage,getClasses,getClassTeachers,getClassStudents,getDefaultTemplate1,getSchoolStats,getActiveTeachers,getLessonTrend1,getCourseUsageStats,getCourseDistribution,getRecentActivities,getTimetable1,getCoursePackageLessonTypes,getCalendarViewData,getTeacherReports,getStudentReports,getOverview,getCourseReports,getParentChildren,findTenantCollections,getPackagesByCollection,getPackageCourses,getPackageInfo,getPackageUsage,getLogList,getLogDetail,getLogStats,getFeedbacks1,getFeedbackStats1,exportTeacherStats,exportStudentStats,exportLessons,exportGrowthRecords,getSchoolCourses,getSchoolCourse,getMyTasks,getTask2,getTasksByStudent,getMyNotifications1,getNotification1,getUnreadCount1,getGrowthRecordsByStudent,getRecentGrowthRecords,getMyChildren,getChild,getChildGrowth,generateEditToken,generateReadOnlyToken,getOssToken,getCurrentUser,getTenantStats,getAllActiveTenants,getStats,getTrendData,getActiveTenants,getPopularCourses,getRecentActivities1,getTenantDefaults,getStats1,getAllPublishedCourses,findByType,getAllPublishedCollections,deleteFile}};
|
return {getTask,updateTask,deleteTask,updateFeedback,submitFeedback,getTemplate,updateTemplate,deleteTemplate,getSchedule,updateSchedule,cancelSchedule,getLesson,updateLesson,getLessonProgress,saveLessonProgress,getGrowthRecord,updateGrowthRecord,deleteGrowthRecord,getTeacher,updateTeacher,deleteTeacher,getTemplate1,updateTemplate1,deleteTemplate1,getStudent,updateStudent,deleteStudent,getSettings,updateSettings,getSecuritySettings,updateSecuritySettings,getNotificationSettings,updateNotificationSettings,getBasicSettings,updateBasicSettings,getSchedule1,updateSchedule1,cancelSchedule1,getParent,updateParent,deleteParent,getGrowthRecord1,updateGrowthRecord1,deleteGrowthRecord1,getClass,updateClass,deleteClass,updateClassTeacher,removeClassTeacher,updateSubmission,submitTask,getGrowthRecord2,updateGrowthRecord2,deleteGrowthRecord2,getCurrentUser,updateProfile,findOne,update,_delete,reorder,getTenant,updateTenant,deleteTenant,updateTenantStatus,updateTenantQuota,getAllSettings,updateSettings1,getStorageSettings,updateStorageSettings,getSecuritySettings1,updateSecuritySettings1,getNotificationSettings1,updateNotificationSettings1,getBasicSettings1,updateBasicSettings1,findLibrary,updateLibrary,deleteLibrary,findItem,updateItem,deleteItem,getCourse1,updateCourse,deleteCourse,reorderSteps,findOne1,update1,delete1,updateStep,removeStep,reorder1,findOne2,update2,delete2,setPackages,getTaskPage,createTask,getTemplates,createTemplate,createFromTemplate,getSchedules,createSchedule,markAsRead,markAllAsRead,getMyLessons,createLesson,saveStudentRecord,batchSaveStudentRecords,startLesson,getLessonFeedback,submitFeedback1,completeLesson,cancelLesson,createLessonFromSchedule,startLessonFromSchedule,getGrowthRecordPage,createGrowthRecord,getTeacherPage,createTeacher,resetPassword,getTemplates1,createTemplate1,getStudentPage,createStudent,getSchedules1,createSchedule1,checkConflict,batchCreateSchedules,createSchedulesByClasses,getParentPage,createParent,bindStudent,unbindStudent,resetPassword1,renewCollection,getGrowthRecordPage1,createGrowthRecord1,getClassPage,createClass,getClassTeachers1,assignTeachers,getClassStudents1,assignStudents,completeTask,markAsRead1,markAllAsRead1,createGrowthRecord2,refreshToken,uploadFile,refreshToken1,logout,login,changePassword,findAll,create,getTenantPage,createTenant,resetTenantPassword,findAllLibraries,createLibrary,findAllItems,createItem,batchDeleteItems,getCoursePage1,createCourse,submitCourse,rejectCourse,publishCourse,archiveCourse,findAll1,create1,findSteps,createStep,page,create2,withdraw,submit,republish,reject,publish,archive,getWeeklyStats,getTodayLessons,getTaskCompletions,getCompletionDetail,getDefaultTemplate,getAllStudents,getTodaySchedules,getTimetable,getRecommendedCourses,getMyNotifications,getNotification,getUnreadCount,getStudentRecords,getTodayLessons1,getLessonTrend,getFeedbacks,getFeedbackStats,getDashboard,getCoursePage,getCourse,getAllCourses,getCourseUsage,getClasses,getClassTeachers,getClassStudents,getDefaultTemplate1,getSchoolStats,getActiveTeachers,getLessonTrend1,getCourseUsageStats,getCourseDistribution,getRecentActivities,getTimetable1,getCoursePackageLessonTypes,getCalendarViewData,getTeacherReports,getStudentReports,getOverview,getCourseReports,getTaskList,getTaskDetail,getTaskCompletions1,getStatistics,getCompletionDetail1,getParentChildren,findTenantCollections,getPackagesByCollection,getPackageCourses,getPackageInfo,getPackageUsage,getLogList,getLogDetail,getLogStats,getFeedbacks1,getFeedbackStats1,exportTeacherStats,exportStudentStats,exportLessons,exportGrowthRecords,getSchoolCourses,getSchoolCourse,getMyTasks,getTask1,getTasksByStudent,getCompletionDetail2,getFeedback,getMyNotifications1,getNotification1,getUnreadCount1,getGrowthRecordsByStudent,getRecentGrowthRecords,getMyChildren,getChild,getChildGrowth,generateEditToken,generateReadOnlyToken,getOssToken,getTenantStats,getAllActiveTenants,getStats,getTrendData,getActiveTenants,getPopularCourses,getRecentActivities1,getTenantDefaults,getStats1,getAllPublishedCourses,findByType,getAllPublishedCollections,deleteFile}};
|
||||||
export type GetTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTask']>>>
|
export type GetTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTask']>>>
|
||||||
export type UpdateTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTask']>>>
|
export type UpdateTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTask']>>>
|
||||||
export type DeleteTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTask']>>>
|
export type DeleteTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTask']>>>
|
||||||
|
export type UpdateFeedbackResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateFeedback']>>>
|
||||||
|
export type SubmitFeedbackResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['submitFeedback']>>>
|
||||||
export type GetTemplateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTemplate']>>>
|
export type GetTemplateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTemplate']>>>
|
||||||
export type UpdateTemplateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTemplate']>>>
|
export type UpdateTemplateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTemplate']>>>
|
||||||
export type DeleteTemplateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTemplate']>>>
|
export type DeleteTemplateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTemplate']>>>
|
||||||
@ -3820,9 +3958,6 @@ export type DeleteGrowthRecordResult = NonNullable<Awaited<ReturnType<ReturnType
|
|||||||
export type GetTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTeacher']>>>
|
export type GetTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTeacher']>>>
|
||||||
export type UpdateTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTeacher']>>>
|
export type UpdateTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTeacher']>>>
|
||||||
export type DeleteTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTeacher']>>>
|
export type DeleteTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTeacher']>>>
|
||||||
export type GetTask1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTask1']>>>
|
|
||||||
export type UpdateTask1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTask1']>>>
|
|
||||||
export type DeleteTask1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTask1']>>>
|
|
||||||
export type GetTemplate1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTemplate1']>>>
|
export type GetTemplate1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTemplate1']>>>
|
||||||
export type UpdateTemplate1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTemplate1']>>>
|
export type UpdateTemplate1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTemplate1']>>>
|
||||||
export type DeleteTemplate1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTemplate1']>>>
|
export type DeleteTemplate1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTemplate1']>>>
|
||||||
@ -3851,9 +3986,13 @@ export type UpdateClassResult = NonNullable<Awaited<ReturnType<ReturnType<typeof
|
|||||||
export type DeleteClassResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteClass']>>>
|
export type DeleteClassResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteClass']>>>
|
||||||
export type UpdateClassTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateClassTeacher']>>>
|
export type UpdateClassTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateClassTeacher']>>>
|
||||||
export type RemoveClassTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['removeClassTeacher']>>>
|
export type RemoveClassTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['removeClassTeacher']>>>
|
||||||
|
export type UpdateSubmissionResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateSubmission']>>>
|
||||||
|
export type SubmitTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['submitTask']>>>
|
||||||
export type GetGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getGrowthRecord2']>>>
|
export type GetGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getGrowthRecord2']>>>
|
||||||
export type UpdateGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateGrowthRecord2']>>>
|
export type UpdateGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateGrowthRecord2']>>>
|
||||||
export type DeleteGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteGrowthRecord2']>>>
|
export type DeleteGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteGrowthRecord2']>>>
|
||||||
|
export type GetCurrentUserResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getCurrentUser']>>>
|
||||||
|
export type UpdateProfileResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateProfile']>>>
|
||||||
export type FindOneResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['findOne']>>>
|
export type FindOneResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['findOne']>>>
|
||||||
export type UpdateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['update']>>>
|
export type UpdateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['update']>>>
|
||||||
export type _DeleteResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['_delete']>>>
|
export type _DeleteResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['_delete']>>>
|
||||||
@ -3908,7 +4047,7 @@ export type SaveStudentRecordResult = NonNullable<Awaited<ReturnType<ReturnType<
|
|||||||
export type BatchSaveStudentRecordsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['batchSaveStudentRecords']>>>
|
export type BatchSaveStudentRecordsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['batchSaveStudentRecords']>>>
|
||||||
export type StartLessonResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['startLesson']>>>
|
export type StartLessonResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['startLesson']>>>
|
||||||
export type GetLessonFeedbackResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getLessonFeedback']>>>
|
export type GetLessonFeedbackResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getLessonFeedback']>>>
|
||||||
export type SubmitFeedbackResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['submitFeedback']>>>
|
export type SubmitFeedback1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['submitFeedback1']>>>
|
||||||
export type CompleteLessonResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['completeLesson']>>>
|
export type CompleteLessonResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['completeLesson']>>>
|
||||||
export type CancelLessonResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['cancelLesson']>>>
|
export type CancelLessonResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['cancelLesson']>>>
|
||||||
export type CreateLessonFromScheduleResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['createLessonFromSchedule']>>>
|
export type CreateLessonFromScheduleResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['createLessonFromSchedule']>>>
|
||||||
@ -3918,8 +4057,6 @@ export type CreateGrowthRecordResult = NonNullable<Awaited<ReturnType<ReturnType
|
|||||||
export type GetTeacherPageResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTeacherPage']>>>
|
export type GetTeacherPageResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTeacherPage']>>>
|
||||||
export type CreateTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['createTeacher']>>>
|
export type CreateTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['createTeacher']>>>
|
||||||
export type ResetPasswordResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['resetPassword']>>>
|
export type ResetPasswordResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['resetPassword']>>>
|
||||||
export type GetTaskPage1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTaskPage1']>>>
|
|
||||||
export type CreateTask1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['createTask1']>>>
|
|
||||||
export type GetTemplates1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTemplates1']>>>
|
export type GetTemplates1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTemplates1']>>>
|
||||||
export type CreateTemplate1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['createTemplate1']>>>
|
export type CreateTemplate1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['createTemplate1']>>>
|
||||||
export type GetStudentPageResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getStudentPage']>>>
|
export type GetStudentPageResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getStudentPage']>>>
|
||||||
@ -3983,6 +4120,8 @@ export type PublishResult = NonNullable<Awaited<ReturnType<ReturnType<typeof get
|
|||||||
export type ArchiveResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['archive']>>>
|
export type ArchiveResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['archive']>>>
|
||||||
export type GetWeeklyStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getWeeklyStats']>>>
|
export type GetWeeklyStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getWeeklyStats']>>>
|
||||||
export type GetTodayLessonsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTodayLessons']>>>
|
export type GetTodayLessonsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTodayLessons']>>>
|
||||||
|
export type GetTaskCompletionsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTaskCompletions']>>>
|
||||||
|
export type GetCompletionDetailResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getCompletionDetail']>>>
|
||||||
export type GetDefaultTemplateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getDefaultTemplate']>>>
|
export type GetDefaultTemplateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getDefaultTemplate']>>>
|
||||||
export type GetAllStudentsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getAllStudents']>>>
|
export type GetAllStudentsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getAllStudents']>>>
|
||||||
export type GetTodaySchedulesResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTodaySchedules']>>>
|
export type GetTodaySchedulesResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTodaySchedules']>>>
|
||||||
@ -4018,6 +4157,11 @@ export type GetTeacherReportsResult = NonNullable<Awaited<ReturnType<ReturnType<
|
|||||||
export type GetStudentReportsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getStudentReports']>>>
|
export type GetStudentReportsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getStudentReports']>>>
|
||||||
export type GetOverviewResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getOverview']>>>
|
export type GetOverviewResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getOverview']>>>
|
||||||
export type GetCourseReportsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getCourseReports']>>>
|
export type GetCourseReportsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getCourseReports']>>>
|
||||||
|
export type GetTaskListResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTaskList']>>>
|
||||||
|
export type GetTaskDetailResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTaskDetail']>>>
|
||||||
|
export type GetTaskCompletions1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTaskCompletions1']>>>
|
||||||
|
export type GetStatisticsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getStatistics']>>>
|
||||||
|
export type GetCompletionDetail1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getCompletionDetail1']>>>
|
||||||
export type GetParentChildrenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getParentChildren']>>>
|
export type GetParentChildrenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getParentChildren']>>>
|
||||||
export type FindTenantCollectionsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['findTenantCollections']>>>
|
export type FindTenantCollectionsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['findTenantCollections']>>>
|
||||||
export type GetPackagesByCollectionResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getPackagesByCollection']>>>
|
export type GetPackagesByCollectionResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getPackagesByCollection']>>>
|
||||||
@ -4036,8 +4180,10 @@ export type ExportGrowthRecordsResult = NonNullable<Awaited<ReturnType<ReturnTyp
|
|||||||
export type GetSchoolCoursesResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getSchoolCourses']>>>
|
export type GetSchoolCoursesResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getSchoolCourses']>>>
|
||||||
export type GetSchoolCourseResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getSchoolCourse']>>>
|
export type GetSchoolCourseResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getSchoolCourse']>>>
|
||||||
export type GetMyTasksResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getMyTasks']>>>
|
export type GetMyTasksResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getMyTasks']>>>
|
||||||
export type GetTask2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTask2']>>>
|
export type GetTask1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTask1']>>>
|
||||||
export type GetTasksByStudentResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTasksByStudent']>>>
|
export type GetTasksByStudentResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTasksByStudent']>>>
|
||||||
|
export type GetCompletionDetail2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getCompletionDetail2']>>>
|
||||||
|
export type GetFeedbackResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getFeedback']>>>
|
||||||
export type GetMyNotifications1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getMyNotifications1']>>>
|
export type GetMyNotifications1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getMyNotifications1']>>>
|
||||||
export type GetNotification1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getNotification1']>>>
|
export type GetNotification1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getNotification1']>>>
|
||||||
export type GetUnreadCount1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getUnreadCount1']>>>
|
export type GetUnreadCount1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getUnreadCount1']>>>
|
||||||
@ -4049,7 +4195,6 @@ export type GetChildGrowthResult = NonNullable<Awaited<ReturnType<ReturnType<typ
|
|||||||
export type GenerateEditTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['generateEditToken']>>>
|
export type GenerateEditTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['generateEditToken']>>>
|
||||||
export type GenerateReadOnlyTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['generateReadOnlyToken']>>>
|
export type GenerateReadOnlyTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['generateReadOnlyToken']>>>
|
||||||
export type GetOssTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getOssToken']>>>
|
export type GetOssTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getOssToken']>>>
|
||||||
export type GetCurrentUserResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getCurrentUser']>>>
|
|
||||||
export type GetTenantStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTenantStats']>>>
|
export type GetTenantStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTenantStats']>>>
|
||||||
export type GetAllActiveTenantsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getAllActiveTenants']>>>
|
export type GetAllActiveTenantsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getAllActiveTenants']>>>
|
||||||
export type GetStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getStats']>>>
|
export type GetStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getStats']>>>
|
||||||
|
|||||||
@ -14,8 +14,8 @@ export interface ActiveTenantItemResponse {
|
|||||||
tenantId?: number;
|
tenantId?: number;
|
||||||
/** 租户名称 */
|
/** 租户名称 */
|
||||||
tenantName?: string;
|
tenantName?: string;
|
||||||
/** 活跃用户数 */
|
/** 活跃教师数(近 30 天有完成课程的老师数) */
|
||||||
activeUsers?: number;
|
activeTeacherCount?: number;
|
||||||
/** 课程使用数 */
|
/** 完成课次数(近 30 天 COMPLETED 状态的 lesson 总数) */
|
||||||
courseCount?: number;
|
completedLessonCount?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 班级信息
|
||||||
|
*/
|
||||||
|
export interface ClassInfo {
|
||||||
|
/** 班级ID */
|
||||||
|
id?: number;
|
||||||
|
/** 班级名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 年级 */
|
||||||
|
grade?: string;
|
||||||
|
}
|
||||||
@ -16,4 +16,6 @@ export interface CourseCollectionPageQueryRequest {
|
|||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
|
/** 年级(支持多个,逗号分隔) */
|
||||||
|
gradeLevels?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课程包视图对象
|
||||||
|
*/
|
||||||
|
export interface CoursePackageVO {
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** 名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 描述 */
|
||||||
|
description?: string;
|
||||||
|
/** 适用年级 */
|
||||||
|
gradeLevel?: string;
|
||||||
|
/** 课程数量 */
|
||||||
|
courseCount?: number;
|
||||||
|
/** 状态 */
|
||||||
|
status?: string;
|
||||||
|
/** 使用次数 */
|
||||||
|
usageCount?: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt?: string;
|
||||||
|
}
|
||||||
@ -20,6 +20,8 @@ export interface CoursePageQueryRequest {
|
|||||||
category?: string;
|
category?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
|
/** 年级(支持多个,逗号分隔) */
|
||||||
|
gradeTags?: string;
|
||||||
/** 是否仅查询待审核 */
|
/** 是否仅查询待审核 */
|
||||||
reviewOnly?: boolean;
|
reviewOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
* OpenAPI spec version: 1.0.0
|
* OpenAPI spec version: 1.0.0
|
||||||
*/
|
*/
|
||||||
import type { CourseLessonResponse } from './courseLessonResponse';
|
import type { CourseLessonResponse } from './courseLessonResponse';
|
||||||
|
import type { LessonTagResponse } from './lessonTagResponse';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 课程响应
|
* 课程响应
|
||||||
@ -89,10 +90,10 @@ export interface CourseResponse {
|
|||||||
activitiesData?: string;
|
activitiesData?: string;
|
||||||
/** 评估数据 */
|
/** 评估数据 */
|
||||||
assessmentData?: string;
|
assessmentData?: string;
|
||||||
/** 年级标签 */
|
/** 年级标签(规范为数组,与套餐管理适用年级对齐) */
|
||||||
gradeTags?: string;
|
gradeTags?: string[];
|
||||||
/** 领域标签 */
|
/** 领域标签(规范为数组) */
|
||||||
domainTags?: string;
|
domainTags?: string[];
|
||||||
/** 是否有集体课 */
|
/** 是否有集体课 */
|
||||||
hasCollectiveLesson?: number;
|
hasCollectiveLesson?: number;
|
||||||
/** 版本号 */
|
/** 版本号 */
|
||||||
@ -129,4 +130,6 @@ export interface CourseResponse {
|
|||||||
updatedAt?: string;
|
updatedAt?: string;
|
||||||
/** 关联的课程环节 */
|
/** 关联的课程环节 */
|
||||||
courseLessons?: CourseLessonResponse[];
|
courseLessons?: CourseLessonResponse[];
|
||||||
|
/** 课程环节标签(列表展示用,仅 name 和 lessonType) */
|
||||||
|
lessonTags?: LessonTagResponse[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课程使用统计视图对象
|
||||||
|
*/
|
||||||
|
export interface CourseUsageVO {
|
||||||
|
/** 课程包名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 使用次数 */
|
||||||
|
value?: number;
|
||||||
|
}
|
||||||
@ -16,6 +16,8 @@ export interface DayScheduleItem {
|
|||||||
className?: string;
|
className?: string;
|
||||||
/** 课程包名称 */
|
/** 课程包名称 */
|
||||||
coursePackageName?: string;
|
coursePackageName?: string;
|
||||||
|
/** 课程类型代码 (如 DOMAIN_HEALTH) */
|
||||||
|
lessonType?: string;
|
||||||
/** 课程类型名称 */
|
/** 课程类型名称 */
|
||||||
lessonTypeName?: string;
|
lessonTypeName?: string;
|
||||||
/** 教师名称 */
|
/** 教师名称 */
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type GetAllCoursesParams = {
|
||||||
|
keyword?: string;
|
||||||
|
grade?: string;
|
||||||
|
domain?: string;
|
||||||
|
};
|
||||||
@ -10,5 +10,7 @@ export type GetCoursePageParams = {
|
|||||||
pageNum?: number;
|
pageNum?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
keyword?: string;
|
keyword?: string;
|
||||||
category?: string;
|
grade?: string;
|
||||||
|
domain?: string;
|
||||||
|
lessonType?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,5 +7,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export type GetLessonTrend1Params = {
|
export type GetLessonTrend1Params = {
|
||||||
months?: number;
|
days?: number;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type GetStatisticsParams = {
|
||||||
|
dateType?: string;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
|
};
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type GetTaskCompletions1Params = {
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
status?: string;
|
||||||
|
};
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type GetTaskCompletionsParams = {
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
status?: string;
|
||||||
|
};
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type GetTaskListParams = {
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
keyword?: string;
|
||||||
|
type?: string;
|
||||||
|
status?: string;
|
||||||
|
classIds?: number[];
|
||||||
|
teacherIds?: number[];
|
||||||
|
dateType?: string;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
|
completionRate?: string;
|
||||||
|
sortBy?: string;
|
||||||
|
sortOrder?: string;
|
||||||
|
};
|
||||||
@ -10,4 +10,6 @@ export type GetTemplates1Params = {
|
|||||||
pageNum?: number;
|
pageNum?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
taskType?: string;
|
||||||
|
keyword?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,4 +10,5 @@ export type GetTemplatesParams = {
|
|||||||
pageNum?: number;
|
pageNum?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
keyword?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,7 +12,6 @@ export * from './addClassTeacherDto';
|
|||||||
export * from './addPackageToCollectionParams';
|
export * from './addPackageToCollectionParams';
|
||||||
export * from './adminStatsControllerGetActiveTenantsParams';
|
export * from './adminStatsControllerGetActiveTenantsParams';
|
||||||
export * from './adminStatsControllerGetPopularCoursesParams';
|
export * from './adminStatsControllerGetPopularCoursesParams';
|
||||||
export * from './adminStatsControllerGetRecentActivitiesParams';
|
|
||||||
export * from './approveCourseDto';
|
export * from './approveCourseDto';
|
||||||
export * from './approveCourseDtoChecklist';
|
export * from './approveCourseDtoChecklist';
|
||||||
export * from './basicSettingsResponse';
|
export * from './basicSettingsResponse';
|
||||||
@ -26,6 +25,7 @@ export * from './calendarViewResponseSchedules';
|
|||||||
export * from './changePasswordParams';
|
export * from './changePasswordParams';
|
||||||
export * from './checkConflictParams';
|
export * from './checkConflictParams';
|
||||||
export * from './classCreateRequest';
|
export * from './classCreateRequest';
|
||||||
|
export * from './classInfo';
|
||||||
export * from './classResponse';
|
export * from './classResponse';
|
||||||
export * from './classTeacherResponse';
|
export * from './classTeacherResponse';
|
||||||
export * from './classUpdateRequest';
|
export * from './classUpdateRequest';
|
||||||
@ -48,11 +48,13 @@ export * from './coursePackageControllerFindAllParams';
|
|||||||
export * from './coursePackageCourseItem';
|
export * from './coursePackageCourseItem';
|
||||||
export * from './coursePackageItem';
|
export * from './coursePackageItem';
|
||||||
export * from './coursePackageResponse';
|
export * from './coursePackageResponse';
|
||||||
|
export * from './coursePackageVO';
|
||||||
export * from './coursePageQueryRequest';
|
export * from './coursePageQueryRequest';
|
||||||
export * from './courseRejectRequest';
|
export * from './courseRejectRequest';
|
||||||
export * from './courseReportResponse';
|
export * from './courseReportResponse';
|
||||||
export * from './courseResponse';
|
export * from './courseResponse';
|
||||||
export * from './courseUpdateRequest';
|
export * from './courseUpdateRequest';
|
||||||
|
export * from './courseUsageVO';
|
||||||
export * from './createClassDto';
|
export * from './createClassDto';
|
||||||
export * from './createCollectionRequest';
|
export * from './createCollectionRequest';
|
||||||
export * from './createFromSourceDto';
|
export * from './createFromSourceDto';
|
||||||
@ -95,6 +97,7 @@ export * from './getActiveTeachersParams';
|
|||||||
export * from './getActiveTenants200';
|
export * from './getActiveTenants200';
|
||||||
export * from './getActiveTenants200DataItem';
|
export * from './getActiveTenants200DataItem';
|
||||||
export * from './getActiveTenantsParams';
|
export * from './getActiveTenantsParams';
|
||||||
|
export * from './getAllCoursesParams';
|
||||||
export * from './getAllStudentsParams';
|
export * from './getAllStudentsParams';
|
||||||
export * from './getCalendarViewDataParams';
|
export * from './getCalendarViewDataParams';
|
||||||
export * from './getClassPageParams';
|
export * from './getClassPageParams';
|
||||||
@ -119,15 +122,15 @@ export * from './getParentPageParams';
|
|||||||
export * from './getPopularCourses200';
|
export * from './getPopularCourses200';
|
||||||
export * from './getPopularCourses200DataItem';
|
export * from './getPopularCourses200DataItem';
|
||||||
export * from './getPopularCoursesParams';
|
export * from './getPopularCoursesParams';
|
||||||
export * from './getRecentActivities1200';
|
|
||||||
export * from './getRecentActivities1200DataItem';
|
|
||||||
export * from './getRecentActivities1Params';
|
|
||||||
export * from './getRecentActivitiesParams';
|
|
||||||
export * from './getRecentGrowthRecordsParams';
|
export * from './getRecentGrowthRecordsParams';
|
||||||
export * from './getSchedules1Params';
|
export * from './getSchedules1Params';
|
||||||
export * from './getSchedulesParams';
|
export * from './getSchedulesParams';
|
||||||
export * from './getSchoolCoursesParams';
|
export * from './getSchoolCoursesParams';
|
||||||
|
export * from './getStatisticsParams';
|
||||||
export * from './getStudentPageParams';
|
export * from './getStudentPageParams';
|
||||||
|
export * from './getTaskCompletions1Params';
|
||||||
|
export * from './getTaskCompletionsParams';
|
||||||
|
export * from './getTaskListParams';
|
||||||
export * from './getTaskPage1Params';
|
export * from './getTaskPage1Params';
|
||||||
export * from './getTaskPageParams';
|
export * from './getTaskPageParams';
|
||||||
export * from './getTasksByStudentParams';
|
export * from './getTasksByStudentParams';
|
||||||
@ -164,6 +167,7 @@ export * from './lessonResponse';
|
|||||||
export * from './lessonStep';
|
export * from './lessonStep';
|
||||||
export * from './lessonStepCreateRequest';
|
export * from './lessonStepCreateRequest';
|
||||||
export * from './lessonStepResponse';
|
export * from './lessonStepResponse';
|
||||||
|
export * from './lessonTagResponse';
|
||||||
export * from './lessonTypeInfo';
|
export * from './lessonTypeInfo';
|
||||||
export * from './lessonUpdateRequest';
|
export * from './lessonUpdateRequest';
|
||||||
export * from './libraryCreateRequest';
|
export * from './libraryCreateRequest';
|
||||||
@ -211,9 +215,11 @@ export * from './pageResultResourceItemResponse';
|
|||||||
export * from './pageResultResourceLibrary';
|
export * from './pageResultResourceLibrary';
|
||||||
export * from './pageResultResourceLibraryResponse';
|
export * from './pageResultResourceLibraryResponse';
|
||||||
export * from './pageResultSchedulePlanResponse';
|
export * from './pageResultSchedulePlanResponse';
|
||||||
|
export * from './pageResultSchoolCourseResponse';
|
||||||
export * from './pageResultStudent';
|
export * from './pageResultStudent';
|
||||||
export * from './pageResultStudentResponse';
|
export * from './pageResultStudentResponse';
|
||||||
export * from './pageResultTask';
|
export * from './pageResultTask';
|
||||||
|
export * from './pageResultTaskCompletionDetailResponse';
|
||||||
export * from './pageResultTaskResponse';
|
export * from './pageResultTaskResponse';
|
||||||
export * from './pageResultTaskTemplateResponse';
|
export * from './pageResultTaskTemplateResponse';
|
||||||
export * from './pageResultTeacher';
|
export * from './pageResultTeacher';
|
||||||
@ -227,8 +233,6 @@ export * from './parentStudentResponse';
|
|||||||
export * from './parentUpdateRequest';
|
export * from './parentUpdateRequest';
|
||||||
export * from './popularCourseItemResponse';
|
export * from './popularCourseItemResponse';
|
||||||
export * from './popularCoursesQueryRequest';
|
export * from './popularCoursesQueryRequest';
|
||||||
export * from './recentActivitiesQueryRequest';
|
|
||||||
export * from './recentActivityItemResponse';
|
|
||||||
export * from './refreshTokenRequest';
|
export * from './refreshTokenRequest';
|
||||||
export * from './rejectCourseDto';
|
export * from './rejectCourseDto';
|
||||||
export * from './rejectCourseDtoChecklist';
|
export * from './rejectCourseDtoChecklist';
|
||||||
@ -278,8 +282,10 @@ export * from './resultListCourseLesson';
|
|||||||
export * from './resultListCourseLessonResponse';
|
export * from './resultListCourseLessonResponse';
|
||||||
export * from './resultListCoursePackage';
|
export * from './resultListCoursePackage';
|
||||||
export * from './resultListCoursePackageResponse';
|
export * from './resultListCoursePackageResponse';
|
||||||
|
export * from './resultListCoursePackageVO';
|
||||||
export * from './resultListCourseReportResponse';
|
export * from './resultListCourseReportResponse';
|
||||||
export * from './resultListCourseResponse';
|
export * from './resultListCourseResponse';
|
||||||
|
export * from './resultListCourseUsageVO';
|
||||||
export * from './resultListGrowthRecord';
|
export * from './resultListGrowthRecord';
|
||||||
export * from './resultListGrowthRecordResponse';
|
export * from './resultListGrowthRecordResponse';
|
||||||
export * from './resultListLesson';
|
export * from './resultListLesson';
|
||||||
@ -291,12 +297,13 @@ export * from './resultListMapStringObject';
|
|||||||
export * from './resultListMapStringObjectDataItem';
|
export * from './resultListMapStringObjectDataItem';
|
||||||
export * from './resultListParentStudentResponse';
|
export * from './resultListParentStudentResponse';
|
||||||
export * from './resultListPopularCourseItemResponse';
|
export * from './resultListPopularCourseItemResponse';
|
||||||
export * from './resultListRecentActivityItemResponse';
|
|
||||||
export * from './resultListSchedulePlanResponse';
|
export * from './resultListSchedulePlanResponse';
|
||||||
export * from './resultListStudent';
|
export * from './resultListStudent';
|
||||||
export * from './resultListStudentRecordResponse';
|
export * from './resultListStudentRecordResponse';
|
||||||
export * from './resultListStudentReportResponse';
|
export * from './resultListStudentReportResponse';
|
||||||
export * from './resultListStudentResponse';
|
export * from './resultListStudentResponse';
|
||||||
|
export * from './resultListTeacherLessonTrendVO';
|
||||||
|
export * from './resultListTeacherLessonVO';
|
||||||
export * from './resultListTeacherReportResponse';
|
export * from './resultListTeacherReportResponse';
|
||||||
export * from './resultListTeacherResponse';
|
export * from './resultListTeacherResponse';
|
||||||
export * from './resultListTenantPackage';
|
export * from './resultListTenantPackage';
|
||||||
@ -308,6 +315,8 @@ export * from './resultLoginResponse';
|
|||||||
export * from './resultLong';
|
export * from './resultLong';
|
||||||
export * from './resultMapStringObject';
|
export * from './resultMapStringObject';
|
||||||
export * from './resultMapStringObjectData';
|
export * from './resultMapStringObjectData';
|
||||||
|
export * from './resultMapStringString';
|
||||||
|
export * from './resultMapStringStringData';
|
||||||
export * from './resultNotification';
|
export * from './resultNotification';
|
||||||
export * from './resultNotificationResponse';
|
export * from './resultNotificationResponse';
|
||||||
export * from './resultNotificationSettingsResponse';
|
export * from './resultNotificationSettingsResponse';
|
||||||
@ -341,9 +350,11 @@ export * from './resultPageResultResourceItemResponse';
|
|||||||
export * from './resultPageResultResourceLibrary';
|
export * from './resultPageResultResourceLibrary';
|
||||||
export * from './resultPageResultResourceLibraryResponse';
|
export * from './resultPageResultResourceLibraryResponse';
|
||||||
export * from './resultPageResultSchedulePlanResponse';
|
export * from './resultPageResultSchedulePlanResponse';
|
||||||
|
export * from './resultPageResultSchoolCourseResponse';
|
||||||
export * from './resultPageResultStudent';
|
export * from './resultPageResultStudent';
|
||||||
export * from './resultPageResultStudentResponse';
|
export * from './resultPageResultStudentResponse';
|
||||||
export * from './resultPageResultTask';
|
export * from './resultPageResultTask';
|
||||||
|
export * from './resultPageResultTaskCompletionDetailResponse';
|
||||||
export * from './resultPageResultTaskResponse';
|
export * from './resultPageResultTaskResponse';
|
||||||
export * from './resultPageResultTaskTemplateResponse';
|
export * from './resultPageResultTaskTemplateResponse';
|
||||||
export * from './resultPageResultTeacher';
|
export * from './resultPageResultTeacher';
|
||||||
@ -358,6 +369,7 @@ export * from './resultResourceItemResponse';
|
|||||||
export * from './resultResourceLibrary';
|
export * from './resultResourceLibrary';
|
||||||
export * from './resultResourceLibraryResponse';
|
export * from './resultResourceLibraryResponse';
|
||||||
export * from './resultSchedulePlanResponse';
|
export * from './resultSchedulePlanResponse';
|
||||||
|
export * from './resultSchoolCourseResponse';
|
||||||
export * from './resultSchoolSettingsResponse';
|
export * from './resultSchoolSettingsResponse';
|
||||||
export * from './resultSecuritySettingsResponse';
|
export * from './resultSecuritySettingsResponse';
|
||||||
export * from './resultStatsResponse';
|
export * from './resultStatsResponse';
|
||||||
@ -367,16 +379,21 @@ export * from './resultStudent';
|
|||||||
export * from './resultStudentRecordResponse';
|
export * from './resultStudentRecordResponse';
|
||||||
export * from './resultStudentResponse';
|
export * from './resultStudentResponse';
|
||||||
export * from './resultTask';
|
export * from './resultTask';
|
||||||
|
export * from './resultTaskCompletionDetailResponse';
|
||||||
|
export * from './resultTaskFeedbackResponse';
|
||||||
export * from './resultTaskResponse';
|
export * from './resultTaskResponse';
|
||||||
export * from './resultTaskTemplateResponse';
|
export * from './resultTaskTemplateResponse';
|
||||||
export * from './resultTeacher';
|
export * from './resultTeacher';
|
||||||
|
export * from './resultTeacherDashboardResponse';
|
||||||
export * from './resultTeacherResponse';
|
export * from './resultTeacherResponse';
|
||||||
|
export * from './resultTeacherWeeklyStatsResponse';
|
||||||
export * from './resultTenant';
|
export * from './resultTenant';
|
||||||
export * from './resultTenantResponse';
|
export * from './resultTenantResponse';
|
||||||
export * from './resultTheme';
|
export * from './resultTheme';
|
||||||
export * from './resultThemeResponse';
|
export * from './resultThemeResponse';
|
||||||
export * from './resultTimetableResponse';
|
export * from './resultTimetableResponse';
|
||||||
export * from './resultTokenResponse';
|
export * from './resultTokenResponse';
|
||||||
|
export * from './resultUpdateProfileResponse';
|
||||||
export * from './resultUserInfoResponse';
|
export * from './resultUserInfoResponse';
|
||||||
export * from './resultVoid';
|
export * from './resultVoid';
|
||||||
export * from './resultVoidData';
|
export * from './resultVoidData';
|
||||||
@ -387,6 +404,7 @@ export * from './schedulePlanCreateRequest';
|
|||||||
export * from './schedulePlanResponse';
|
export * from './schedulePlanResponse';
|
||||||
export * from './schedulePlanUpdateRequest';
|
export * from './schedulePlanUpdateRequest';
|
||||||
export * from './schoolControllerImportStudentsParams';
|
export * from './schoolControllerImportStudentsParams';
|
||||||
|
export * from './schoolCourseResponse';
|
||||||
export * from './schoolFeedbackControllerFindAllParams';
|
export * from './schoolFeedbackControllerFindAllParams';
|
||||||
export * from './schoolSettingsResponse';
|
export * from './schoolSettingsResponse';
|
||||||
export * from './schoolSettingsUpdateRequest';
|
export * from './schoolSettingsUpdateRequest';
|
||||||
@ -395,12 +413,12 @@ export * from './securitySettingsResponse';
|
|||||||
export * from './securitySettingsUpdateRequest';
|
export * from './securitySettingsUpdateRequest';
|
||||||
export * from './statsControllerGetActiveTeachersParams';
|
export * from './statsControllerGetActiveTeachersParams';
|
||||||
export * from './statsControllerGetLessonTrendParams';
|
export * from './statsControllerGetLessonTrendParams';
|
||||||
export * from './statsControllerGetRecentActivitiesParams';
|
|
||||||
export * from './statsResponse';
|
export * from './statsResponse';
|
||||||
export * from './statsTrendResponse';
|
export * from './statsTrendResponse';
|
||||||
export * from './stepCreateRequest';
|
export * from './stepCreateRequest';
|
||||||
export * from './student';
|
export * from './student';
|
||||||
export * from './studentCreateRequest';
|
export * from './studentCreateRequest';
|
||||||
|
export * from './studentInfo';
|
||||||
export * from './studentRecordDto';
|
export * from './studentRecordDto';
|
||||||
export * from './studentRecordRequest';
|
export * from './studentRecordRequest';
|
||||||
export * from './studentRecordResponse';
|
export * from './studentRecordResponse';
|
||||||
@ -409,8 +427,12 @@ export * from './studentResponse';
|
|||||||
export * from './studentUpdateRequest';
|
export * from './studentUpdateRequest';
|
||||||
export * from './submitCourseDto';
|
export * from './submitCourseDto';
|
||||||
export * from './task';
|
export * from './task';
|
||||||
|
export * from './taskCompletionDetailResponse';
|
||||||
export * from './taskCreateRequest';
|
export * from './taskCreateRequest';
|
||||||
|
export * from './taskFeedbackRequest';
|
||||||
|
export * from './taskFeedbackResponse';
|
||||||
export * from './taskResponse';
|
export * from './taskResponse';
|
||||||
|
export * from './taskSubmitRequest';
|
||||||
export * from './taskTemplateCreateRequest';
|
export * from './taskTemplateCreateRequest';
|
||||||
export * from './taskTemplateResponse';
|
export * from './taskTemplateResponse';
|
||||||
export * from './taskUpdateRequest';
|
export * from './taskUpdateRequest';
|
||||||
@ -422,11 +444,17 @@ export * from './teacherCourseControllerGetLessonTrendParams';
|
|||||||
export * from './teacherCourseControllerGetTeacherSchedulesParams';
|
export * from './teacherCourseControllerGetTeacherSchedulesParams';
|
||||||
export * from './teacherCourseControllerGetTeacherTimetableParams';
|
export * from './teacherCourseControllerGetTeacherTimetableParams';
|
||||||
export * from './teacherCreateRequest';
|
export * from './teacherCreateRequest';
|
||||||
|
export * from './teacherDashboardResponse';
|
||||||
export * from './teacherFeedbackControllerFindAllParams';
|
export * from './teacherFeedbackControllerFindAllParams';
|
||||||
|
export * from './teacherLessonTrendVO';
|
||||||
|
export * from './teacherLessonVO';
|
||||||
export * from './teacherReportResponse';
|
export * from './teacherReportResponse';
|
||||||
export * from './teacherResponse';
|
export * from './teacherResponse';
|
||||||
|
export * from './teacherResponseClassNames';
|
||||||
|
export * from './teacherStats';
|
||||||
export * from './teacherTaskControllerGetMonthlyStatsParams';
|
export * from './teacherTaskControllerGetMonthlyStatsParams';
|
||||||
export * from './teacherUpdateRequest';
|
export * from './teacherUpdateRequest';
|
||||||
|
export * from './teacherWeeklyStatsResponse';
|
||||||
export * from './tenant';
|
export * from './tenant';
|
||||||
export * from './tenantControllerFindAllPackageType';
|
export * from './tenantControllerFindAllPackageType';
|
||||||
export * from './tenantControllerFindAllParams';
|
export * from './tenantControllerFindAllParams';
|
||||||
@ -452,6 +480,8 @@ export * from './updateLessonDto';
|
|||||||
export * from './updateLibraryDto';
|
export * from './updateLibraryDto';
|
||||||
export * from './updateNotificationSettings1Body';
|
export * from './updateNotificationSettings1Body';
|
||||||
export * from './updateNotificationSettingsBody';
|
export * from './updateNotificationSettingsBody';
|
||||||
|
export * from './updateProfileRequest';
|
||||||
|
export * from './updateProfileResponse';
|
||||||
export * from './updateResourceItemDto';
|
export * from './updateResourceItemDto';
|
||||||
export * from './updateSchedule1Body';
|
export * from './updateSchedule1Body';
|
||||||
export * from './updateScheduleBody';
|
export * from './updateScheduleBody';
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import type { LessonResponse } from './lessonResponse';
|
|||||||
export interface LessonDetailResponse {
|
export interface LessonDetailResponse {
|
||||||
lesson?: LessonResponse;
|
lesson?: LessonResponse;
|
||||||
course?: CourseResponse;
|
course?: CourseResponse;
|
||||||
class?: ClassResponse;
|
|
||||||
/** 排课选择的课程类型(子课程模式时用于直接进入对应子课程) */
|
/** 排课选择的课程类型(子课程模式时用于直接进入对应子课程) */
|
||||||
lessonType?: string;
|
lessonType?: string;
|
||||||
|
class?: ClassResponse;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课程环节标签
|
||||||
|
*/
|
||||||
|
export interface LessonTagResponse {
|
||||||
|
/** 环节名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 环节类型:INTRODUCTION、COLLECTIVE、LANGUAGE、HEALTH、SCIENCE、SOCIAL、ART */
|
||||||
|
lessonType?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { SchoolCourseResponse } from './schoolCourseResponse';
|
||||||
|
|
||||||
|
export interface PageResultSchoolCourseResponse {
|
||||||
|
list?: SchoolCourseResponse[];
|
||||||
|
total?: number;
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
pages?: number;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { TaskCompletionDetailResponse } from './taskCompletionDetailResponse';
|
||||||
|
|
||||||
|
export interface PageResultTaskCompletionDetailResponse {
|
||||||
|
list?: TaskCompletionDetailResponse[];
|
||||||
|
total?: number;
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
pages?: number;
|
||||||
|
}
|
||||||
@ -1,25 +0,0 @@
|
|||||||
/**
|
|
||||||
* Generated by orval v8.5.3 🍺
|
|
||||||
* Do not edit manually.
|
|
||||||
* Reading Platform API
|
|
||||||
* Reading Platform Backend Service API Documentation
|
|
||||||
* OpenAPI spec version: 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 最近活动项响应
|
|
||||||
*/
|
|
||||||
export interface RecentActivityItemResponse {
|
|
||||||
/** 活动 ID */
|
|
||||||
activityId?: number;
|
|
||||||
/** 活动类型 */
|
|
||||||
activityType?: string;
|
|
||||||
/** 活动描述 */
|
|
||||||
description?: string;
|
|
||||||
/** 操作人 ID */
|
|
||||||
operatorId?: number;
|
|
||||||
/** 操作人名称 */
|
|
||||||
operatorName?: string;
|
|
||||||
/** 操作时间 */
|
|
||||||
operationTime?: string;
|
|
||||||
}
|
|
||||||
@ -5,10 +5,10 @@
|
|||||||
* Reading Platform Backend Service API Documentation
|
* Reading Platform Backend Service API Documentation
|
||||||
* OpenAPI spec version: 1.0.0
|
* OpenAPI spec version: 1.0.0
|
||||||
*/
|
*/
|
||||||
import type { RecentActivityItemResponse } from './recentActivityItemResponse';
|
import type { CoursePackageVO } from './coursePackageVO';
|
||||||
|
|
||||||
export interface ResultListRecentActivityItemResponse {
|
export interface ResultListCoursePackageVO {
|
||||||
code?: number;
|
code?: number;
|
||||||
message?: string;
|
message?: string;
|
||||||
data?: RecentActivityItemResponse[];
|
data?: CoursePackageVO[];
|
||||||
}
|
}
|
||||||
@ -5,10 +5,10 @@
|
|||||||
* Reading Platform Backend Service API Documentation
|
* Reading Platform Backend Service API Documentation
|
||||||
* OpenAPI spec version: 1.0.0
|
* OpenAPI spec version: 1.0.0
|
||||||
*/
|
*/
|
||||||
import type { GetRecentActivities1200DataItem } from './getRecentActivities1200DataItem';
|
import type { CourseUsageVO } from './courseUsageVO';
|
||||||
|
|
||||||
export type GetRecentActivities1200 = {
|
export interface ResultListCourseUsageVO {
|
||||||
code?: number;
|
code?: number;
|
||||||
message?: string;
|
message?: string;
|
||||||
data?: GetRecentActivities1200DataItem[];
|
data?: CourseUsageVO[];
|
||||||
};
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { TeacherLessonTrendVO } from './teacherLessonTrendVO';
|
||||||
|
|
||||||
|
export interface ResultListTeacherLessonTrendVO {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: TeacherLessonTrendVO[];
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { TeacherLessonVO } from './teacherLessonVO';
|
||||||
|
|
||||||
|
export interface ResultListTeacherLessonVO {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: TeacherLessonVO[];
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { ResultMapStringStringData } from './resultMapStringStringData';
|
||||||
|
|
||||||
|
export interface ResultMapStringString {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: ResultMapStringStringData;
|
||||||
|
}
|
||||||
@ -6,4 +6,4 @@
|
|||||||
* OpenAPI spec version: 1.0.0
|
* OpenAPI spec version: 1.0.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type GetRecentActivities1200DataItem = { [key: string]: unknown };
|
export type ResultMapStringStringData = {[key: string]: string};
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { PageResultSchoolCourseResponse } from './pageResultSchoolCourseResponse';
|
||||||
|
|
||||||
|
export interface ResultPageResultSchoolCourseResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: PageResultSchoolCourseResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { PageResultTaskCompletionDetailResponse } from './pageResultTaskCompletionDetailResponse';
|
||||||
|
|
||||||
|
export interface ResultPageResultTaskCompletionDetailResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: PageResultTaskCompletionDetailResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { SchoolCourseResponse } from './schoolCourseResponse';
|
||||||
|
|
||||||
|
export interface ResultSchoolCourseResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: SchoolCourseResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { TaskCompletionDetailResponse } from './taskCompletionDetailResponse';
|
||||||
|
|
||||||
|
export interface ResultTaskCompletionDetailResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: TaskCompletionDetailResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { TaskFeedbackResponse } from './taskFeedbackResponse';
|
||||||
|
|
||||||
|
export interface ResultTaskFeedbackResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: TaskFeedbackResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { TeacherDashboardResponse } from './teacherDashboardResponse';
|
||||||
|
|
||||||
|
export interface ResultTeacherDashboardResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: TeacherDashboardResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { TeacherWeeklyStatsResponse } from './teacherWeeklyStatsResponse';
|
||||||
|
|
||||||
|
export interface ResultTeacherWeeklyStatsResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: TeacherWeeklyStatsResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { UpdateProfileResponse } from './updateProfileResponse';
|
||||||
|
|
||||||
|
export interface ResultUpdateProfileResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: UpdateProfileResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { LessonTagResponse } from './lessonTagResponse';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 学校端课程响应
|
||||||
|
*/
|
||||||
|
export interface SchoolCourseResponse {
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** 租户 ID */
|
||||||
|
tenantId?: number;
|
||||||
|
/** 课程名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 课程编码 */
|
||||||
|
code?: string;
|
||||||
|
/** 描述 */
|
||||||
|
description?: string;
|
||||||
|
/** 绘本名称 */
|
||||||
|
pictureBookName?: string;
|
||||||
|
/** 封面图片路径 */
|
||||||
|
coverImagePath?: string;
|
||||||
|
/** 封面 URL */
|
||||||
|
coverUrl?: string;
|
||||||
|
/** 年级标签(规范为数组) */
|
||||||
|
gradeTags?: string[];
|
||||||
|
/** 领域标签(规范为数组) */
|
||||||
|
domainTags?: string[];
|
||||||
|
/** 课程时长(分钟) */
|
||||||
|
duration?: number;
|
||||||
|
/** 使用次数 */
|
||||||
|
usageCount?: number;
|
||||||
|
/** 教师数量 */
|
||||||
|
teacherCount?: number;
|
||||||
|
/** 平均评分 */
|
||||||
|
avgRating?: number;
|
||||||
|
/** 状态 */
|
||||||
|
status?: string;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt?: string;
|
||||||
|
/** 课程环节标签(列表展示用,仅 name 和 lessonType) */
|
||||||
|
lessonTags?: LessonTagResponse[];
|
||||||
|
}
|
||||||
@ -22,4 +22,6 @@ export interface StatsResponse {
|
|||||||
totalCourses?: number;
|
totalCourses?: number;
|
||||||
/** 课时总数 */
|
/** 课时总数 */
|
||||||
totalLessons?: number;
|
totalLessons?: number;
|
||||||
|
/** 月授课次数(本月 COMPLETED 状态的 lesson 总数) */
|
||||||
|
monthlyLessons?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,10 +12,8 @@
|
|||||||
export interface StatsTrendResponse {
|
export interface StatsTrendResponse {
|
||||||
/** 日期列表 */
|
/** 日期列表 */
|
||||||
dates?: string[];
|
dates?: string[];
|
||||||
/** 新增学生数列表 */
|
/** 授课次数列表(近 7 天每天完成的课程数) */
|
||||||
newStudents?: number[];
|
lessonCounts?: number[];
|
||||||
/** 新增教师数列表 */
|
/** 活跃学生数列表(近 7 天每天有上课记录的去重学生数) */
|
||||||
newTeachers?: number[];
|
studentCounts?: number[];
|
||||||
/** 新增课程数列表 */
|
|
||||||
newCourses?: number[];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { ClassInfo } from './classInfo';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 学生信息
|
||||||
|
*/
|
||||||
|
export interface StudentInfo {
|
||||||
|
/** 学生ID */
|
||||||
|
id?: number;
|
||||||
|
/** 学生姓名 */
|
||||||
|
name?: string;
|
||||||
|
/** 学生头像 */
|
||||||
|
avatar?: string;
|
||||||
|
/** 性别:MALE/FEMALE */
|
||||||
|
gender?: string;
|
||||||
|
classInfo?: ClassInfo;
|
||||||
|
}
|
||||||
@ -28,6 +28,8 @@ export interface Task {
|
|||||||
description?: string;
|
description?: string;
|
||||||
/** 任务类型 */
|
/** 任务类型 */
|
||||||
type?: string;
|
type?: string;
|
||||||
|
/** 关联绘本名称 */
|
||||||
|
relatedBookName?: string;
|
||||||
/** 课程 ID */
|
/** 课程 ID */
|
||||||
courseId?: number;
|
courseId?: number;
|
||||||
/** 创建人 ID */
|
/** 创建人 ID */
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { StudentInfo } from './studentInfo';
|
||||||
|
import type { TaskFeedbackResponse } from './taskFeedbackResponse';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务完成详情响应
|
||||||
|
*/
|
||||||
|
export interface TaskCompletionDetailResponse {
|
||||||
|
/** 完成记录ID */
|
||||||
|
id?: number;
|
||||||
|
/** 任务ID */
|
||||||
|
taskId?: number;
|
||||||
|
/** 任务标题 */
|
||||||
|
taskTitle?: string;
|
||||||
|
student?: StudentInfo;
|
||||||
|
/** 状态:PENDING/SUBMITTED/REVIEWED */
|
||||||
|
status?: string;
|
||||||
|
/** 状态文本:待完成/已提交/已评价 */
|
||||||
|
statusText?: string;
|
||||||
|
/** 照片URL数组 */
|
||||||
|
photos?: string[];
|
||||||
|
/** 视频URL */
|
||||||
|
videoUrl?: string;
|
||||||
|
/** 语音URL */
|
||||||
|
audioUrl?: string;
|
||||||
|
/** 完成内容/阅读心得 */
|
||||||
|
content?: string;
|
||||||
|
/** 提交时间 */
|
||||||
|
submittedAt?: string;
|
||||||
|
/** 评价时间 */
|
||||||
|
reviewedAt?: string;
|
||||||
|
feedback?: TaskFeedbackResponse;
|
||||||
|
}
|
||||||
@ -16,6 +16,8 @@ export interface TaskCreateRequest {
|
|||||||
description?: string;
|
description?: string;
|
||||||
/** 任务类型:reading-阅读,homework-作业,activity-活动 */
|
/** 任务类型:reading-阅读,homework-作业,activity-活动 */
|
||||||
type?: string;
|
type?: string;
|
||||||
|
/** 关联绘本名称(手动填写) */
|
||||||
|
relatedBookName?: string;
|
||||||
/** 课程 ID */
|
/** 课程 ID */
|
||||||
courseId?: number;
|
courseId?: number;
|
||||||
/** 开始日期 */
|
/** 开始日期 */
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务评价请求
|
||||||
|
*/
|
||||||
|
export interface TaskFeedbackRequest {
|
||||||
|
/** 评价结果:EXCELLENT-优秀/PASSED-通过/NEEDS_WORK-需改进 */
|
||||||
|
result: string;
|
||||||
|
/**
|
||||||
|
* 评分 1-5(可选)
|
||||||
|
* @minimum 1
|
||||||
|
* @maximum 5
|
||||||
|
*/
|
||||||
|
rating?: number;
|
||||||
|
/**
|
||||||
|
* 评语
|
||||||
|
* @minLength 0
|
||||||
|
* @maxLength 500
|
||||||
|
*/
|
||||||
|
comment?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务评价响应
|
||||||
|
*/
|
||||||
|
export interface TaskFeedbackResponse {
|
||||||
|
/** 评价ID */
|
||||||
|
id?: number;
|
||||||
|
/** 完成记录ID */
|
||||||
|
completionId?: number;
|
||||||
|
/** 评价结果:EXCELLENT/PASSED/NEEDS_WORK */
|
||||||
|
result?: string;
|
||||||
|
/** 评价结果文本:优秀/通过/需改进 */
|
||||||
|
resultText?: string;
|
||||||
|
/** 评分 1-5 */
|
||||||
|
rating?: number;
|
||||||
|
/** 评语 */
|
||||||
|
comment?: string;
|
||||||
|
/** 教师ID */
|
||||||
|
teacherId?: number;
|
||||||
|
/** 教师姓名 */
|
||||||
|
teacherName?: string;
|
||||||
|
/** 教师头像 */
|
||||||
|
teacherAvatar?: string;
|
||||||
|
/** 评价时间 */
|
||||||
|
createdAt?: string;
|
||||||
|
}
|
||||||
@ -20,6 +20,8 @@ export interface TaskResponse {
|
|||||||
description?: string;
|
description?: string;
|
||||||
/** 任务类型 */
|
/** 任务类型 */
|
||||||
type?: string;
|
type?: string;
|
||||||
|
/** 关联绘本名称 */
|
||||||
|
relatedBookName?: string;
|
||||||
/** 课程 ID */
|
/** 课程 ID */
|
||||||
courseId?: number;
|
courseId?: number;
|
||||||
/** 创建人 ID */
|
/** 创建人 ID */
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务提交请求
|
||||||
|
*/
|
||||||
|
export interface TaskSubmitRequest {
|
||||||
|
/** 学生ID */
|
||||||
|
studentId?: number;
|
||||||
|
/**
|
||||||
|
* 照片URL数组(最多9张)
|
||||||
|
* @minItems 0
|
||||||
|
* @maxItems 9
|
||||||
|
*/
|
||||||
|
photos?: string[];
|
||||||
|
/** 视频URL */
|
||||||
|
videoUrl?: string;
|
||||||
|
/** 语音URL */
|
||||||
|
audioUrl?: string;
|
||||||
|
/**
|
||||||
|
* 阅读心得/完成内容
|
||||||
|
* @minLength 0
|
||||||
|
* @maxLength 1000
|
||||||
|
*/
|
||||||
|
content?: string;
|
||||||
|
}
|
||||||
@ -23,9 +23,9 @@ export interface TaskTemplateCreateRequest {
|
|||||||
/** 默认持续时间 (天) */
|
/** 默认持续时间 (天) */
|
||||||
defaultDuration?: number;
|
defaultDuration?: number;
|
||||||
/** 是否默认模板 */
|
/** 是否默认模板 */
|
||||||
isDefault?: number;
|
isDefault?: boolean;
|
||||||
/** 模板内容 */
|
/** 模板内容 */
|
||||||
content?: string;
|
content?: string;
|
||||||
/** 是否公开 */
|
/** 是否公开 */
|
||||||
isPublic?: number;
|
isPublic?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,18 +10,20 @@
|
|||||||
* 教师创建请求
|
* 教师创建请求
|
||||||
*/
|
*/
|
||||||
export interface TeacherCreateRequest {
|
export interface TeacherCreateRequest {
|
||||||
/** 用户名 */
|
/** 用户名/登录账号 */
|
||||||
username: string;
|
username: string;
|
||||||
/** 密码 */
|
/** 密码 */
|
||||||
password: string;
|
password: string;
|
||||||
/** 姓名 */
|
/** 姓名 */
|
||||||
name: string;
|
name: string;
|
||||||
/** 电话 */
|
/** 电话 */
|
||||||
phone?: string;
|
phone: string;
|
||||||
/** 邮箱 */
|
/** 邮箱 */
|
||||||
email?: string;
|
email?: string;
|
||||||
/** 性别 */
|
/** 性别 */
|
||||||
gender?: string;
|
gender?: string;
|
||||||
/** 简介 */
|
/** 简介 */
|
||||||
bio?: string;
|
bio?: string;
|
||||||
|
/** 负责班级ID列表 */
|
||||||
|
classIds?: number[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { CoursePackageVO } from './coursePackageVO';
|
||||||
|
import type { TeacherDashboardResponseRecentActivitiesItem } from './teacherDashboardResponseRecentActivitiesItem';
|
||||||
|
import type { TeacherLessonVO } from './teacherLessonVO';
|
||||||
|
import type { TeacherStats } from './teacherStats';
|
||||||
|
import type { TeacherWeeklyStatsResponse } from './teacherWeeklyStatsResponse';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教师仪表盘响应
|
||||||
|
*/
|
||||||
|
export interface TeacherDashboardResponse {
|
||||||
|
stats?: TeacherStats;
|
||||||
|
/** 今日课程 */
|
||||||
|
todayLessons?: TeacherLessonVO[];
|
||||||
|
/** 推荐课程 */
|
||||||
|
recommendedCourses?: CoursePackageVO[];
|
||||||
|
weeklyStats?: TeacherWeeklyStatsResponse;
|
||||||
|
/** 近期活动 */
|
||||||
|
recentActivities?: TeacherDashboardResponseRecentActivitiesItem[];
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教师课程趋势视图对象
|
||||||
|
*/
|
||||||
|
export interface TeacherLessonTrendVO {
|
||||||
|
/** 月份(yyyy-MM 格式) */
|
||||||
|
month?: string;
|
||||||
|
/** 课时数量 */
|
||||||
|
lessonCount?: number;
|
||||||
|
/** 平均评分 */
|
||||||
|
avgRating?: number;
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { LocalTime } from './localTime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教师课程视图对象
|
||||||
|
*/
|
||||||
|
export interface TeacherLessonVO {
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** 租户 ID */
|
||||||
|
tenantId?: number;
|
||||||
|
/** 课程 ID */
|
||||||
|
courseId?: number;
|
||||||
|
/** 班级 ID */
|
||||||
|
classId?: number;
|
||||||
|
/** 课程名称 */
|
||||||
|
courseName?: string;
|
||||||
|
/** 班级名称 */
|
||||||
|
className?: string;
|
||||||
|
/** 教师 ID */
|
||||||
|
teacherId?: number;
|
||||||
|
/** 标题 */
|
||||||
|
title?: string;
|
||||||
|
/** 课时日期 */
|
||||||
|
lessonDate?: string;
|
||||||
|
startTime?: LocalTime;
|
||||||
|
endTime?: LocalTime;
|
||||||
|
/** 地点 */
|
||||||
|
location?: string;
|
||||||
|
/** 状态 */
|
||||||
|
status?: string;
|
||||||
|
/** 备注 */
|
||||||
|
notes?: string;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt?: string;
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@
|
|||||||
* Reading Platform Backend Service API Documentation
|
* Reading Platform Backend Service API Documentation
|
||||||
* OpenAPI spec version: 1.0.0
|
* OpenAPI spec version: 1.0.0
|
||||||
*/
|
*/
|
||||||
|
import type { TeacherResponseClassNames } from './teacherResponseClassNames';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 教师响应
|
* 教师响应
|
||||||
@ -14,8 +15,6 @@ export interface TeacherResponse {
|
|||||||
id?: number;
|
id?: number;
|
||||||
/** 租户 ID */
|
/** 租户 ID */
|
||||||
tenantId?: number;
|
tenantId?: number;
|
||||||
/** 用户名 */
|
|
||||||
username?: string;
|
|
||||||
/** 姓名 */
|
/** 姓名 */
|
||||||
name?: string;
|
name?: string;
|
||||||
/** 电话 */
|
/** 电话 */
|
||||||
@ -30,10 +29,18 @@ export interface TeacherResponse {
|
|||||||
bio?: string;
|
bio?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
|
/** 负责班级ID列表 */
|
||||||
|
classIds?: number[];
|
||||||
|
/** 负责班级名称 */
|
||||||
|
classNames?: TeacherResponseClassNames;
|
||||||
|
/** 授课次数 */
|
||||||
|
lessonCount?: number;
|
||||||
/** 最后登录时间 */
|
/** 最后登录时间 */
|
||||||
lastLoginAt?: string;
|
lastLoginAt?: string;
|
||||||
/** 创建时间 */
|
/** 创建时间 */
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
/** 更新时间 */
|
/** 更新时间 */
|
||||||
updatedAt?: string;
|
updatedAt?: string;
|
||||||
|
/** 登录账号 */
|
||||||
|
loginAccount?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 负责班级名称
|
||||||
|
*/
|
||||||
|
export type TeacherResponseClassNames = { [key: string]: unknown };
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础统计
|
||||||
|
*/
|
||||||
|
export interface TeacherStats {
|
||||||
|
/** 班级数量 */
|
||||||
|
classCount?: number;
|
||||||
|
/** 学生数量 */
|
||||||
|
studentCount?: number;
|
||||||
|
/** 课程包数量 */
|
||||||
|
courseCount?: number;
|
||||||
|
/** 课时数量 */
|
||||||
|
lessonCount?: number;
|
||||||
|
}
|
||||||
@ -24,4 +24,6 @@ export interface TeacherUpdateRequest {
|
|||||||
bio?: string;
|
bio?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
|
/** 负责班级ID列表 */
|
||||||
|
classIds?: number[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教师本周统计响应
|
||||||
|
*/
|
||||||
|
export interface TeacherWeeklyStatsResponse {
|
||||||
|
/** 本周课时数量 */
|
||||||
|
lessonCount?: number;
|
||||||
|
/** 学生参与率 */
|
||||||
|
studentParticipation?: number;
|
||||||
|
/** 平均评分 */
|
||||||
|
avgRating?: number;
|
||||||
|
/** 总时长(分钟) */
|
||||||
|
totalDuration?: number;
|
||||||
|
}
|
||||||
@ -26,8 +26,6 @@ export interface TenantCreateRequest {
|
|||||||
address?: string;
|
address?: string;
|
||||||
/** Logo URL */
|
/** Logo URL */
|
||||||
logoUrl?: string;
|
logoUrl?: string;
|
||||||
/** 套餐类型 */
|
|
||||||
packageType?: string;
|
|
||||||
/** 教师配额 */
|
/** 教师配额 */
|
||||||
teacherQuota?: number;
|
teacherQuota?: number;
|
||||||
/** 学生配额 */
|
/** 学生配额 */
|
||||||
@ -36,6 +34,6 @@ export interface TenantCreateRequest {
|
|||||||
startDate?: string;
|
startDate?: string;
|
||||||
/** 结束日期 */
|
/** 结束日期 */
|
||||||
expireDate?: string;
|
expireDate?: string;
|
||||||
/** 课程套餐 ID(可选) */
|
/** 课程套餐 ID 列表(可选,支持多选) */
|
||||||
collectionId?: number;
|
collectionIds?: number[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,8 +36,8 @@ export interface TenantResponse {
|
|||||||
maxStudents?: number;
|
maxStudents?: number;
|
||||||
/** 最大教师数 */
|
/** 最大教师数 */
|
||||||
maxTeachers?: number;
|
maxTeachers?: number;
|
||||||
/** 套餐类型 */
|
/** 套餐名称列表 */
|
||||||
packageType?: string;
|
packageNames?: string[];
|
||||||
/** 教师配额 */
|
/** 教师配额 */
|
||||||
teacherQuota?: number;
|
teacherQuota?: number;
|
||||||
/** 学生配额 */
|
/** 学生配额 */
|
||||||
|
|||||||
@ -24,10 +24,8 @@ export interface TenantUpdateRequest {
|
|||||||
logoUrl?: string;
|
logoUrl?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 套餐类型 */
|
|
||||||
packageType?: string;
|
|
||||||
/** 课程套餐ID(用于三层架构) */
|
/** 课程套餐ID(用于三层架构) */
|
||||||
collectionId?: number;
|
collectionIds?: number[];
|
||||||
/** 教师配额 */
|
/** 教师配额 */
|
||||||
teacherQuota?: number;
|
teacherQuota?: number;
|
||||||
/** 学生配额 */
|
/** 学生配额 */
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改个人信息请求
|
||||||
|
*/
|
||||||
|
export interface UpdateProfileRequest {
|
||||||
|
/**
|
||||||
|
* 姓名
|
||||||
|
* @pattern ^[一-龥a-zA-Z\s]{2,20}$
|
||||||
|
*/
|
||||||
|
name?: string;
|
||||||
|
/**
|
||||||
|
* 手机号
|
||||||
|
* @pattern ^1[3-9]\d{9}$
|
||||||
|
*/
|
||||||
|
phone?: string;
|
||||||
|
/** 邮箱 */
|
||||||
|
email?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { UserInfoResponse } from './userInfoResponse';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改个人信息响应
|
||||||
|
*/
|
||||||
|
export interface UpdateProfileResponse {
|
||||||
|
userInfo?: UserInfoResponse;
|
||||||
|
/** 新的 Token(用于刷新) */
|
||||||
|
token?: string;
|
||||||
|
}
|
||||||
@ -247,13 +247,23 @@ export const getSchoolStats = () =>
|
|||||||
http.get<SchoolStats>('/v1/school/stats');
|
http.get<SchoolStats>('/v1/school/stats');
|
||||||
|
|
||||||
export const getActiveTeachers = (limit?: number) =>
|
export const getActiveTeachers = (limit?: number) =>
|
||||||
http.get<Array<{ id: number; name: string; lessonCount: number }>>('/v1/school/stats/teachers', { params: { limit } });
|
http.get<Array<{
|
||||||
|
teacherId: number | string;
|
||||||
|
teacherName: string;
|
||||||
|
classNames: string;
|
||||||
|
lessonCount: number;
|
||||||
|
courseCount: number;
|
||||||
|
lastActiveAt?: string;
|
||||||
|
activityLevelCode: string;
|
||||||
|
activityLevelDesc: string;
|
||||||
|
}>>('/v1/school/stats/teachers', { params: { limit } });
|
||||||
|
|
||||||
export const getCourseUsageStats = () =>
|
export const getCourseUsageStats = (startDate?: string, endDate?: string) => {
|
||||||
http.get<Array<{ courseId: number; courseName: string; usageCount: number }>>('/v1/school/stats/courses');
|
const params: Record<string, string> = {};
|
||||||
|
if (startDate) params.startDate = startDate;
|
||||||
export const getRecentActivities = (limit?: number) =>
|
if (endDate) params.endDate = endDate;
|
||||||
http.get<Array<{ id: number; type: string; title: string; time: string }>>('/v1/school/stats/activities', { params: { limit } });
|
return http.get<Array<{ courseId: number; courseName: string; usageCount: number; studentCount?: number; avgDuration?: number; lastUsedAt?: string }>>('/v1/school/stats/courses', { params });
|
||||||
|
};
|
||||||
|
|
||||||
// ==================== 套餐信息(旧 API,保留兼容) ====================
|
// ==================== 套餐信息(旧 API,保留兼容) ====================
|
||||||
|
|
||||||
@ -632,7 +642,7 @@ export const getCalendarViewData = (params?: {
|
|||||||
// ==================== 趋势与分布统计 ====================
|
// ==================== 趋势与分布统计 ====================
|
||||||
|
|
||||||
export interface LessonTrendItem {
|
export interface LessonTrendItem {
|
||||||
month: string;
|
date: string; // 日期(MM-dd 格式)
|
||||||
lessonCount: number;
|
lessonCount: number;
|
||||||
studentCount: number;
|
studentCount: number;
|
||||||
}
|
}
|
||||||
@ -642,8 +652,11 @@ export interface CourseDistributionItem {
|
|||||||
value: number;
|
value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getLessonTrend = (months?: number) =>
|
// 后端趋势数据响应(对象数组格式)
|
||||||
http.get<LessonTrendItem[]>('/v1/school/stats/lesson-trend', { params: { months } });
|
export type LessonTrendResponse = LessonTrendItem[];
|
||||||
|
|
||||||
|
export const getLessonTrend = (days?: number) =>
|
||||||
|
http.get<LessonTrendResponse>('/v1/school/stats/lesson-trend', { params: { days } });
|
||||||
|
|
||||||
export const getCourseDistribution = () =>
|
export const getCourseDistribution = () =>
|
||||||
http.get<CourseDistributionItem[]>('/v1/school/stats/course-distribution');
|
http.get<CourseDistributionItem[]>('/v1/school/stats/course-distribution');
|
||||||
|
|||||||
@ -312,59 +312,69 @@ export function batchSaveStudentRecords(
|
|||||||
|
|
||||||
// ==================== 教师首页 API ====================
|
// ==================== 教师首页 API ====================
|
||||||
|
|
||||||
|
export interface TeacherDashboardStats {
|
||||||
|
classCount: number;
|
||||||
|
studentCount: number;
|
||||||
|
lessonCount: number;
|
||||||
|
courseCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TeacherLessonItem {
|
||||||
|
id: number;
|
||||||
|
tenantId: number;
|
||||||
|
courseId: number;
|
||||||
|
classId: number;
|
||||||
|
courseName: string;
|
||||||
|
className: string;
|
||||||
|
teacherId: number;
|
||||||
|
title: string;
|
||||||
|
lessonDate: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
location: string;
|
||||||
|
status: string;
|
||||||
|
notes?: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TeacherCoursePackageItem {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
gradeLevel?: string;
|
||||||
|
courseCount?: number;
|
||||||
|
status: string;
|
||||||
|
usageCount?: number;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TeacherWeeklyStatsData {
|
||||||
|
lessonCount: number;
|
||||||
|
studentParticipation: number;
|
||||||
|
avgRating: number;
|
||||||
|
totalDuration: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DashboardData {
|
export interface DashboardData {
|
||||||
stats: {
|
stats: TeacherDashboardStats;
|
||||||
classCount: number;
|
todayLessons: TeacherLessonItem[];
|
||||||
studentCount: number;
|
recommendedCourses: TeacherCoursePackageItem[];
|
||||||
lessonCount: number;
|
weeklyStats: TeacherWeeklyStatsData;
|
||||||
courseCount: number;
|
|
||||||
};
|
|
||||||
todayLessons: Array<{
|
|
||||||
id: number;
|
|
||||||
courseId: number;
|
|
||||||
courseName: string;
|
|
||||||
pictureBookName?: string;
|
|
||||||
classId: number;
|
|
||||||
className: string;
|
|
||||||
plannedDatetime: string;
|
|
||||||
status: string;
|
|
||||||
duration: number;
|
|
||||||
}>;
|
|
||||||
recommendedCourses: Array<{
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
pictureBookName?: string;
|
|
||||||
coverImagePath?: string;
|
|
||||||
duration: number;
|
|
||||||
usageCount: number;
|
|
||||||
avgRating: number;
|
|
||||||
gradeTags: string[];
|
|
||||||
}>;
|
|
||||||
weeklyStats: {
|
|
||||||
lessonCount: number;
|
|
||||||
studentParticipation: number;
|
|
||||||
avgRating: number;
|
|
||||||
totalDuration: number;
|
|
||||||
};
|
|
||||||
recentActivities: Array<{
|
|
||||||
id: number;
|
|
||||||
type: string;
|
|
||||||
description: string;
|
|
||||||
time: string;
|
|
||||||
}>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTeacherDashboard = () =>
|
export const getTeacherDashboard = () =>
|
||||||
http.get('/v1/teacher/dashboard') as any;
|
http.get<DashboardData>('/v1/teacher/dashboard');
|
||||||
|
|
||||||
export const getTodayLessons = () =>
|
export const getTodayLessons = () =>
|
||||||
http.get('/v1/teacher/today-lessons') as any;
|
http.get<TeacherLessonItem[]>('/v1/teacher/today-lessons');
|
||||||
|
|
||||||
export const getRecommendedCourses = () =>
|
export const getRecommendedCourses = () =>
|
||||||
http.get('/v1/teacher/recommended-courses') as any;
|
http.get<TeacherCoursePackageItem[]>('/v1/teacher/recommended-courses');
|
||||||
|
|
||||||
export const getWeeklyStats = () =>
|
export const getWeeklyStats = () =>
|
||||||
http.get('/v1/teacher/weekly-stats') as any;
|
http.get<TeacherWeeklyStatsData>('/v1/teacher/weekly-stats');
|
||||||
|
|
||||||
// ==================== 教师统计趋势 ====================
|
// ==================== 教师统计趋势 ====================
|
||||||
|
|
||||||
@ -379,12 +389,33 @@ export interface TeacherCourseUsageItem {
|
|||||||
value: number;
|
value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 增强版课程使用统计类型
|
||||||
|
export interface CourseUsageQueryParams {
|
||||||
|
periodType?: 'TODAY' | 'WEEK' | 'MONTH' | 'CUSTOM';
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CourseUsageStatsItem {
|
||||||
|
coursePackageId: number;
|
||||||
|
coursePackageName: string;
|
||||||
|
usageCount: number;
|
||||||
|
studentCount: number;
|
||||||
|
avgDuration: number;
|
||||||
|
lastUsedAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const getTeacherLessonTrend = (months?: number) => {
|
export const getTeacherLessonTrend = (months?: number) => {
|
||||||
return http.get('/v1/teacher/lesson-trend', { params: { months } }) as any;
|
return http.get<TeacherLessonTrendItem[]>('/v1/teacher/lesson-trend', { params: { months } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 旧版 API(保留向后兼容)
|
||||||
export const getTeacherCourseUsage = () =>
|
export const getTeacherCourseUsage = () =>
|
||||||
http.get('/v1/teacher/course-usage') as any;
|
http.get<TeacherCourseUsageItem[]>('/v1/teacher/course-usage');
|
||||||
|
|
||||||
|
// 增强版课程使用统计 API
|
||||||
|
export const getTeacherCourseUsageStats = (params?: CourseUsageQueryParams) =>
|
||||||
|
http.get<CourseUsageStatsItem[]>('/v1/teacher/course-usage-stats', { params });
|
||||||
|
|
||||||
// ==================== 课程反馈 API ====================
|
// ==================== 课程反馈 API ====================
|
||||||
|
|
||||||
|
|||||||
@ -107,28 +107,6 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<!-- 最近活动 -->
|
|
||||||
<a-row :gutter="24" style="margin-top: 24px">
|
|
||||||
<a-col :span="24">
|
|
||||||
<a-card title="最近活动" :bordered="false" :loading="activitiesLoading" class="modern-card">
|
|
||||||
<div v-if="recentActivities.length > 0" class="activity-timeline">
|
|
||||||
<div v-for="activity in recentActivities" :key="activity.id" class="activity-item">
|
|
||||||
<div class="activity-dot" :class="'type-' + activity.type"></div>
|
|
||||||
<div class="activity-content">
|
|
||||||
<div class="activity-header">
|
|
||||||
<a-tag :color="getActivityTagColor(activity.type)" class="activity-tag">
|
|
||||||
{{ getActivityTypeText(activity.type) }}
|
|
||||||
</a-tag>
|
|
||||||
<span class="activity-title">{{ activity.title }}</span>
|
|
||||||
</div>
|
|
||||||
<span class="activity-time">{{ formatTime(activity.time) }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a-empty v-else description="暂无活动记录" />
|
|
||||||
</a-card>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -167,7 +145,6 @@ let trendChart: echarts.ECharts | null = null;
|
|||||||
const trendLoading = ref(false);
|
const trendLoading = ref(false);
|
||||||
const tenantsLoading = ref(false);
|
const tenantsLoading = ref(false);
|
||||||
const coursesLoading = ref(false);
|
const coursesLoading = ref(false);
|
||||||
const activitiesLoading = ref(false);
|
|
||||||
|
|
||||||
// 统计数据
|
// 统计数据
|
||||||
const statsData = ref<AdminStats>({
|
const statsData = ref<AdminStats>({
|
||||||
@ -187,37 +164,6 @@ const activeTenants = ref<ActiveTenant[]>([]);
|
|||||||
// 热门课程包
|
// 热门课程包
|
||||||
const popularCourses = ref<PopularCourse[]>([]);
|
const popularCourses = ref<PopularCourse[]>([]);
|
||||||
|
|
||||||
// 最近活动
|
|
||||||
const recentActivities = ref<Array<{
|
|
||||||
id: number;
|
|
||||||
type: string;
|
|
||||||
title: string;
|
|
||||||
time: string;
|
|
||||||
}>>([]);
|
|
||||||
|
|
||||||
// Activity colors
|
|
||||||
const getActivityTagColor = (type: string) => {
|
|
||||||
const colors: Record<string, string> = {
|
|
||||||
lesson: 'blue',
|
|
||||||
tenant: 'green',
|
|
||||||
course: 'orange',
|
|
||||||
};
|
|
||||||
return colors[type] || 'default';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getActivityTypeText = (type: string) => {
|
|
||||||
const texts: Record<string, string> = {
|
|
||||||
lesson: '授课',
|
|
||||||
tenant: '租户',
|
|
||||||
course: '课程',
|
|
||||||
};
|
|
||||||
return texts[type] || '其他';
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatTime = (time: string) => {
|
|
||||||
return dayjs(time).format('YYYY-MM-DD HH:mm');
|
|
||||||
};
|
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
const viewTenantDetail = (id: number) => {
|
const viewTenantDetail = (id: number) => {
|
||||||
router.push(`/admin/tenants?id=${id}`);
|
router.push(`/admin/tenants?id=${id}`);
|
||||||
@ -393,24 +339,6 @@ const fetchPopularCourses = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchRecentActivities = async () => {
|
|
||||||
activitiesLoading.value = true;
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/v1/admin/stats/activities?limit=10', {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (response.ok) {
|
|
||||||
recentActivities.value = await response.json();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch recent activities:', error);
|
|
||||||
} finally {
|
|
||||||
activitiesLoading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle window resize
|
// Handle window resize
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
trendChart?.resize();
|
trendChart?.resize();
|
||||||
@ -421,7 +349,6 @@ onMounted(() => {
|
|||||||
fetchTrendData();
|
fetchTrendData();
|
||||||
fetchActiveTenants();
|
fetchActiveTenants();
|
||||||
fetchPopularCourses();
|
fetchPopularCourses();
|
||||||
fetchRecentActivities();
|
|
||||||
|
|
||||||
window.addEventListener('resize', handleResize);
|
window.addEventListener('resize', handleResize);
|
||||||
});
|
});
|
||||||
@ -624,65 +551,6 @@ $bg-light: #F9FAFB;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 活动时间线
|
|
||||||
.activity-timeline {
|
|
||||||
.activity-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 12px 0;
|
|
||||||
border-bottom: 1px solid $border-color;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-dot {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-top: 6px;
|
|
||||||
margin-right: 16px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
&.type-lesson {
|
|
||||||
background: $primary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.type-tenant {
|
|
||||||
background: $success-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.type-course {
|
|
||||||
background: $warning-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-content {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
.activity-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
.activity-tag {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-title {
|
|
||||||
color: $text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-time {
|
|
||||||
font-size: 12px;
|
|
||||||
color: $text-secondary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
|
|||||||
@ -34,27 +34,145 @@
|
|||||||
<a-descriptions-item label="邮箱">
|
<a-descriptions-item label="邮箱">
|
||||||
{{ profile.email || '-' }}
|
{{ profile.email || '-' }}
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<!-- <a-descriptions-item label="所属机构" v-if="profile.tenantId">
|
|
||||||
{{ profile.tenantName || `租户ID: ${profile.tenantId}` }}
|
|
||||||
</a-descriptions-item> -->
|
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="action-section">
|
||||||
|
<a-button type="primary" @click="enterEditMode">
|
||||||
|
<EditOutlined /> 编辑资料
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="showChangePasswordModal" style="margin-left: 12px">
|
||||||
|
<LockOutlined /> 修改密码
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-empty v-else-if="!loading" description="加载失败,请刷新重试" />
|
<a-empty v-else-if="!loading" description="加载失败,请刷新重试" />
|
||||||
</a-spin>
|
</a-spin>
|
||||||
|
|
||||||
|
<!-- 编辑资料弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:open="editModalOpen"
|
||||||
|
title="编辑个人信息"
|
||||||
|
@ok="submitEdit"
|
||||||
|
:confirmLoading="editLoading"
|
||||||
|
width="480px"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
:model="editForm"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:wrapper-col="{ span: 16 }"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
label="姓名"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入姓名' },
|
||||||
|
{ pattern: /^[\u4e00-\u9fa5a-zA-Z\s]{2,20}$/, message: '姓名长度为 2-20 位,只能包含中文或英文' }
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input v-model:value="editForm.name" placeholder="请输入姓名" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="手机号"
|
||||||
|
name="phone"
|
||||||
|
:rules="[
|
||||||
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input v-model:value="editForm.phone" placeholder="请输入手机号" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="邮箱"
|
||||||
|
name="email"
|
||||||
|
:rules="[
|
||||||
|
{ type: 'email', message: '请输入正确的邮箱格式' }
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input v-model:value="editForm.email" placeholder="请输入邮箱" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 修改密码弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:open="passwordModalOpen"
|
||||||
|
title="修改密码"
|
||||||
|
@ok="submitChangePassword"
|
||||||
|
:confirmLoading="passwordLoading"
|
||||||
|
width="480px"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
:model="passwordForm"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:wrapper-col="{ span: 16 }"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
label="旧密码"
|
||||||
|
name="oldPassword"
|
||||||
|
:rules="[{ required: true, message: '请输入旧密码' }]"
|
||||||
|
>
|
||||||
|
<a-input-password v-model:value="passwordForm.oldPassword" placeholder="请输入旧密码" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="新密码"
|
||||||
|
name="newPassword"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入新密码' },
|
||||||
|
{ min: 6, message: '密码长度不能少于 6 位' }
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input-password v-model:value="passwordForm.newPassword" placeholder="请输入新密码" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="确认密码"
|
||||||
|
name="confirmPassword"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请确认新密码' },
|
||||||
|
{ validator: validateConfirmPassword }
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input-password v-model:value="passwordForm.confirmPassword" placeholder="请再次输入新密码" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import { UserOutlined } from '@ant-design/icons-vue';
|
import { UserOutlined, EditOutlined, LockOutlined } from '@ant-design/icons-vue';
|
||||||
import { getProfile, type UserProfile } from '@/api/auth';
|
import { message } from 'ant-design-vue';
|
||||||
|
import { getProfile, type UserProfile, updateProfile, changePassword, type UpdateProfileDto } from '@/api/auth';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const profile = ref<UserProfile | null>(null);
|
const profile = ref<UserProfile | null>(null);
|
||||||
|
|
||||||
|
// 编辑资料相关
|
||||||
|
const editModalOpen = ref(false);
|
||||||
|
const editLoading = ref(false);
|
||||||
|
const editForm = ref<UpdateProfileDto>({
|
||||||
|
name: '',
|
||||||
|
phone: '',
|
||||||
|
email: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 修改密码相关
|
||||||
|
const passwordModalOpen = ref(false);
|
||||||
|
const passwordLoading = ref(false);
|
||||||
|
const passwordForm = ref({
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
});
|
||||||
|
|
||||||
const avatarUrl = computed(() => {
|
const avatarUrl = computed(() => {
|
||||||
const p = profile.value;
|
const p = profile.value;
|
||||||
if (!p) return '';
|
if (!p) return '';
|
||||||
@ -84,6 +202,73 @@ const loadProfile = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 进入编辑模式
|
||||||
|
const enterEditMode = () => {
|
||||||
|
if (profile.value) {
|
||||||
|
editForm.value = {
|
||||||
|
name: profile.value.name || '',
|
||||||
|
phone: profile.value.phone || '',
|
||||||
|
email: profile.value.email || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
editModalOpen.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交编辑
|
||||||
|
const submitEdit = async () => {
|
||||||
|
try {
|
||||||
|
editLoading.value = true;
|
||||||
|
await updateProfile(editForm.value);
|
||||||
|
message.success('修改成功');
|
||||||
|
editModalOpen.value = false;
|
||||||
|
await loadProfile();
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('修改失败', error);
|
||||||
|
message.error(error.message || '修改失败,请重试');
|
||||||
|
} finally {
|
||||||
|
editLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示修改密码弹窗
|
||||||
|
const showChangePasswordModal = () => {
|
||||||
|
passwordForm.value = {
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
};
|
||||||
|
passwordModalOpen.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 验证确认密码
|
||||||
|
const validateConfirmPassword = async (_rule: any, value: string) => {
|
||||||
|
if (value !== passwordForm.value.newPassword) {
|
||||||
|
throw new Error('两次输入的密码不一致');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交修改密码
|
||||||
|
const submitChangePassword = async () => {
|
||||||
|
try {
|
||||||
|
passwordLoading.value = true;
|
||||||
|
await changePassword(passwordForm.value.oldPassword, passwordForm.value.newPassword);
|
||||||
|
message.success('密码修改成功,请重新登录');
|
||||||
|
passwordModalOpen.value = false;
|
||||||
|
|
||||||
|
// 清除本地存储并跳转到登录页
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
localStorage.removeItem('userInfo');
|
||||||
|
setTimeout(() => {
|
||||||
|
router.push('/login');
|
||||||
|
}, 1000);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('修改密码失败', error);
|
||||||
|
message.error(error.message || '修改密码失败,请重试');
|
||||||
|
} finally {
|
||||||
|
passwordLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadProfile();
|
loadProfile();
|
||||||
});
|
});
|
||||||
@ -139,4 +324,11 @@ onMounted(() => {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-section {
|
||||||
|
margin-top: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -53,69 +53,55 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 主要内容区域 -->
|
<!-- 教师活跃度排行(全宽) -->
|
||||||
<div class="content-grid">
|
<div class="teachers-card-full">
|
||||||
<!-- 近期活动 -->
|
<div class="card-header">
|
||||||
<div class="content-card activities-card">
|
<span class="card-icon"><TrophyOutlined /></span>
|
||||||
<div class="card-header">
|
<h3>教师活跃度排行</h3>
|
||||||
<span class="card-icon"><CalendarOutlined /></span>
|
</div>
|
||||||
<h3>近期课程活动</h3>
|
<div class="card-body" :class="{ 'is-loading': loading }">
|
||||||
|
<a-spin v-if="loading" />
|
||||||
|
<div v-else-if="activeTeachers.length === 0" class="empty-state">
|
||||||
|
<span class="empty-icon"><TeamOutlined /></span>
|
||||||
|
<p>暂无数据</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body" :class="{ 'is-loading': loading }">
|
<div v-else class="teacher-list">
|
||||||
<a-spin v-if="loading" />
|
<div
|
||||||
<div v-else-if="recentActivities.length === 0" class="empty-state">
|
v-for="(item, index) in activeTeachers"
|
||||||
<span class="empty-icon"><InboxOutlined /></span>
|
:key="item.teacherId"
|
||||||
<p>暂无近期活动</p>
|
class="teacher-item"
|
||||||
</div>
|
>
|
||||||
<div v-else class="activity-list">
|
<div class="rank-badge" :class="'rank-' + (index + 1)">
|
||||||
<div
|
{{ index + 1 }}
|
||||||
v-for="item in recentActivities"
|
</div>
|
||||||
:key="item.id"
|
<div class="teacher-info">
|
||||||
class="activity-item"
|
<div class="teacher-name-row">
|
||||||
>
|
<span class="teacher-name">{{ item.teacherName }}</span>
|
||||||
<div class="activity-avatar">
|
<a-tag :color="getActivityLevelColor(item.activityLevelCode)" size="small">
|
||||||
<BookOutlined />
|
{{ item.activityLevelDesc }}
|
||||||
|
</a-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="activity-content">
|
<div class="teacher-details">
|
||||||
<div class="activity-title">{{ item.title }}</div>
|
<span class="detail-item">
|
||||||
<div class="activity-time">{{ formatTime(item.time) }}</div>
|
<HomeOutlined />
|
||||||
|
{{ item.classNames || '未分配班级' }}
|
||||||
|
</span>
|
||||||
|
<span class="detail-item">
|
||||||
|
<ReadOutlined />
|
||||||
|
授课 {{ item.lessonCount }} 次
|
||||||
|
</span>
|
||||||
|
<span class="detail-item">
|
||||||
|
<BookOutlined />
|
||||||
|
课程 {{ item.courseCount }} 个
|
||||||
|
</span>
|
||||||
|
<span class="detail-item" v-if="item.lastActiveAt">
|
||||||
|
<ClockCircleOutlined />
|
||||||
|
{{ formatLastActive(item.lastActiveAt) }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="teacher-medal">
|
||||||
</div>
|
<component :is="getMedalIcon(index)" :style="getMedalStyle(index)" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 教师活跃度排行 -->
|
|
||||||
<div class="content-card teachers-card">
|
|
||||||
<div class="card-header">
|
|
||||||
<span class="card-icon"><TrophyOutlined /></span>
|
|
||||||
<h3>教师活跃度排行</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body" :class="{ 'is-loading': loading }">
|
|
||||||
<a-spin v-if="loading" />
|
|
||||||
<div v-else-if="activeTeachers.length === 0" class="empty-state">
|
|
||||||
<span class="empty-icon"><TeamOutlined /></span>
|
|
||||||
<p>暂无数据</p>
|
|
||||||
</div>
|
|
||||||
<div v-else class="teacher-list">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in activeTeachers"
|
|
||||||
:key="item.id"
|
|
||||||
class="teacher-item"
|
|
||||||
>
|
|
||||||
<div class="rank-badge" :class="'rank-' + (index + 1)">
|
|
||||||
{{ index + 1 }}
|
|
||||||
</div>
|
|
||||||
<div class="teacher-info">
|
|
||||||
<div class="teacher-name">{{ item.name }}</div>
|
|
||||||
<div class="teacher-lessons">
|
|
||||||
<span class="lesson-icon"><ReadOutlined /></span>
|
|
||||||
授课 {{ item.lessonCount }} 次
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="teacher-medal">
|
|
||||||
<component :is="getMedalIcon(index)" :style="getMedalStyle(index)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -225,21 +211,19 @@ import {
|
|||||||
TeamOutlined,
|
TeamOutlined,
|
||||||
UserOutlined,
|
UserOutlined,
|
||||||
ReadOutlined,
|
ReadOutlined,
|
||||||
CalendarOutlined,
|
|
||||||
InboxOutlined,
|
|
||||||
TrophyOutlined,
|
TrophyOutlined,
|
||||||
TrophyFilled,
|
TrophyFilled,
|
||||||
StarFilled,
|
StarFilled,
|
||||||
BarChartOutlined,
|
BarChartOutlined,
|
||||||
LineChartOutlined,
|
LineChartOutlined,
|
||||||
DownloadOutlined,
|
DownloadOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import {
|
import {
|
||||||
getSchoolStats,
|
getSchoolStats,
|
||||||
getActiveTeachers,
|
getActiveTeachers,
|
||||||
getRecentActivities,
|
|
||||||
getCourseUsageStats,
|
getCourseUsageStats,
|
||||||
getLessonTrend,
|
getLessonTrend,
|
||||||
getCourseDistribution,
|
getCourseDistribution,
|
||||||
@ -249,7 +233,7 @@ import {
|
|||||||
} from '@/api/school';
|
} from '@/api/school';
|
||||||
import type { SchoolStats, LessonTrendItem, CourseDistributionItem } from '@/api/school';
|
import type { SchoolStats, LessonTrendItem, CourseDistributionItem } from '@/api/school';
|
||||||
import type { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
import { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const courseStatsLoading = ref(false);
|
const courseStatsLoading = ref(false);
|
||||||
@ -269,8 +253,16 @@ const stats = ref<SchoolStats>({
|
|||||||
lessonCount: 0,
|
lessonCount: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const recentActivities = ref<Array<{ id: number; type: string; title: string; time: string }>>([]);
|
const activeTeachers = ref<Array<{
|
||||||
const activeTeachers = ref<Array<{ id: number; name: string; lessonCount: number }>>([]);
|
teacherId: number | string;
|
||||||
|
teacherName: string;
|
||||||
|
classNames: string;
|
||||||
|
lessonCount: number;
|
||||||
|
courseCount: number;
|
||||||
|
lastActiveAt?: string;
|
||||||
|
activityLevelCode: string;
|
||||||
|
activityLevelDesc: string;
|
||||||
|
}>>([]);
|
||||||
|
|
||||||
const courseStats = ref<Array<{ courseId: number; courseName: string; usageCount: number }>>([]);
|
const courseStats = ref<Array<{ courseId: number; courseName: string; usageCount: number }>>([]);
|
||||||
const dateRange = ref<[Dayjs, Dayjs] | undefined>(undefined);
|
const dateRange = ref<[Dayjs, Dayjs] | undefined>(undefined);
|
||||||
@ -331,6 +323,42 @@ const getMedalIcon = (index: number): Component => {
|
|||||||
return icons[index] || StarOutlined;
|
return icons[index] || StarOutlined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据活跃度等级获取标签颜色
|
||||||
|
*/
|
||||||
|
const getActivityLevelColor = (code: string) => {
|
||||||
|
const colors: Record<string, string> = {
|
||||||
|
HIGH: 'red',
|
||||||
|
MEDIUM: 'orange',
|
||||||
|
LOW: 'blue',
|
||||||
|
INACTIVE: 'default',
|
||||||
|
};
|
||||||
|
return colors[code] || 'default';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化最后活跃时间
|
||||||
|
*/
|
||||||
|
const formatLastActive = (time: string) => {
|
||||||
|
if (!time) return '';
|
||||||
|
const date = new Date(time);
|
||||||
|
const now = new Date();
|
||||||
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||||
|
const yesterday = new Date(today.getTime() - 86400000);
|
||||||
|
const dateOnly = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||||
|
|
||||||
|
if (dateOnly.getTime() >= today.getTime()) {
|
||||||
|
return `今天 ${date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}`;
|
||||||
|
} else if (dateOnly.getTime() >= yesterday.getTime()) {
|
||||||
|
return `昨天 ${date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}`;
|
||||||
|
} else if (now.getTime() - date.getTime() < 7 * 86400000) {
|
||||||
|
const days = Math.floor((now.getTime() - date.getTime()) / 86400000);
|
||||||
|
return `${days} 天前`;
|
||||||
|
} else {
|
||||||
|
return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getMedalStyle = (index: number) => {
|
const getMedalStyle = (index: number) => {
|
||||||
const styles = [
|
const styles = [
|
||||||
{ color: '#FFD700', fontSize: '20px' },
|
{ color: '#FFD700', fontSize: '20px' },
|
||||||
@ -394,7 +422,7 @@ const initTrendChart = (data: LessonTrendItem[]) => {
|
|||||||
|
|
||||||
// 确保数据格式正确
|
// 确保数据格式正确
|
||||||
const validData = data.map(d => ({
|
const validData = data.map(d => ({
|
||||||
month: d.month || '',
|
date: d.date || '',
|
||||||
lessonCount: d.lessonCount || 0,
|
lessonCount: d.lessonCount || 0,
|
||||||
studentCount: d.studentCount || 0,
|
studentCount: d.studentCount || 0,
|
||||||
}));
|
}));
|
||||||
@ -426,7 +454,7 @@ const initTrendChart = (data: LessonTrendItem[]) => {
|
|||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: validData.map((d) => d.month),
|
data: validData.map((d) => d.date),
|
||||||
axisLine: {
|
axisLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: '#E5E7EB',
|
color: '#E5E7EB',
|
||||||
@ -515,27 +543,39 @@ const initDistributionChart = (data: CourseDistributionItem[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 确保数据格式正确
|
// 确保数据格式正确
|
||||||
const validData = data.map((item, index) => ({
|
const validData = data.map((item, index) => {
|
||||||
name: item.name || `课程${index + 1}`,
|
// 限制名称长度,超过 10 个字则缩略显示
|
||||||
value: item.value || 0,
|
const displayName = item.name && item.name.length > 10
|
||||||
itemStyle: {
|
? item.name.substring(0, 10) + '...'
|
||||||
color: [
|
: (item.name || `课程${index + 1}`);
|
||||||
'#FF8C42',
|
|
||||||
'#667eea',
|
return {
|
||||||
'#f093fb',
|
name: displayName,
|
||||||
'#4facfe',
|
value: item.value || 0,
|
||||||
'#43e97b',
|
fullName: item.name, // 保存完整名称用于 tooltip 显示
|
||||||
'#fa709a',
|
itemStyle: {
|
||||||
'#fee140',
|
color: [
|
||||||
'#30cfd0',
|
'#FF8C42',
|
||||||
][index % 8],
|
'#667eea',
|
||||||
},
|
'#f093fb',
|
||||||
}));
|
'#4facfe',
|
||||||
|
'#43e97b',
|
||||||
|
'#fa709a',
|
||||||
|
'#fee140',
|
||||||
|
'#30cfd0',
|
||||||
|
][index % 8],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const option: echarts.EChartsOption = {
|
const option: echarts.EChartsOption = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: '{b}: {c}次 ({d}%)',
|
formatter: (params: any) => {
|
||||||
|
// 使用完整名称显示 tooltip
|
||||||
|
const fullName = params.data.fullName || params.name;
|
||||||
|
return `${fullName}: ${params.value}次 (${params.percent}%)`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
orient: 'vertical',
|
orient: 'vertical',
|
||||||
@ -584,15 +624,13 @@ const handleResize = () => {
|
|||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const [statsData, teachersData, activitiesData] = await Promise.all([
|
const [statsData, teachersData] = await Promise.all([
|
||||||
getSchoolStats(),
|
getSchoolStats(),
|
||||||
getActiveTeachers(5),
|
getActiveTeachers(10), // 获取 TOP10
|
||||||
getRecentActivities(10),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
stats.value = statsData;
|
stats.value = statsData;
|
||||||
activeTeachers.value = teachersData;
|
activeTeachers.value = teachersData;
|
||||||
recentActivities.value = activitiesData;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load dashboard data:', error);
|
console.error('Failed to load dashboard data:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@ -603,7 +641,10 @@ const loadData = async () => {
|
|||||||
const loadCourseStats = async () => {
|
const loadCourseStats = async () => {
|
||||||
courseStatsLoading.value = true;
|
courseStatsLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const data = await getCourseUsageStats();
|
const startDate = dateRange.value?.[0]?.format('YYYY-MM-DD');
|
||||||
|
const endDate = dateRange.value?.[1]?.format('YYYY-MM-DD');
|
||||||
|
|
||||||
|
const data = await getCourseUsageStats(startDate, endDate);
|
||||||
courseStats.value = data.slice(0, 10);
|
courseStats.value = data.slice(0, 10);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load course stats:', error);
|
console.error('Failed to load course stats:', error);
|
||||||
@ -616,7 +657,7 @@ const loadCourseStats = async () => {
|
|||||||
const loadTrendData = async () => {
|
const loadTrendData = async () => {
|
||||||
trendLoading.value = true;
|
trendLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const data = await getLessonTrend(6);
|
const data = await getLessonTrend(7);
|
||||||
lessonTrendData.value = data;
|
lessonTrendData.value = data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load trend data:', error);
|
console.error('Failed to load trend data:', error);
|
||||||
@ -685,6 +726,12 @@ onMounted(() => {
|
|||||||
loadTrendData();
|
loadTrendData();
|
||||||
loadDistributionData();
|
loadDistributionData();
|
||||||
|
|
||||||
|
// 设置默认日期范围为当月
|
||||||
|
const now = dayjs();
|
||||||
|
const monthStart = now.startOf('month');
|
||||||
|
const monthEnd = now.endOf('month');
|
||||||
|
dateRange.value = [monthStart, monthEnd];
|
||||||
|
|
||||||
window.addEventListener('resize', handleResize);
|
window.addEventListener('resize', handleResize);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -808,11 +855,20 @@ onUnmounted(() => {
|
|||||||
/* 内容网格 */
|
/* 内容网格 */
|
||||||
.content-grid {
|
.content-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: 1fr;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 教师活跃度排行(全宽) */
|
||||||
|
.teachers-card-full {
|
||||||
|
background: white;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
/* 图表网格 */
|
/* 图表网格 */
|
||||||
.charts-grid {
|
.charts-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
@ -905,50 +961,6 @@ onUnmounted(() => {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 活动列表 */
|
|
||||||
.activity-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
padding: 12px 16px;
|
|
||||||
background: #FAFAFA;
|
|
||||||
border-radius: 12px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-item:hover {
|
|
||||||
background: #FFF8F0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-avatar {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: linear-gradient(135deg, #FF8C42 0%, #FFB347 100%);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-title {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #2D3436;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-time {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #B2BEC3;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 教师列表 */
|
/* 教师列表 */
|
||||||
.teacher-list {
|
.teacher-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -991,26 +1003,37 @@ onUnmounted(() => {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.teacher-name-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.teacher-name {
|
.teacher-name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
color: #2D3436;
|
color: #2D3436;
|
||||||
}
|
}
|
||||||
|
|
||||||
.teacher-lessons {
|
.teacher-details {
|
||||||
font-size: 12px;
|
display: flex;
|
||||||
color: #636E72;
|
align-items: center;
|
||||||
margin-top: 4px;
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #636E72;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lesson-icon {
|
.detail-item .anticon {
|
||||||
font-size: 14px;
|
font-size: 12px;
|
||||||
color: #636E72;
|
color: #999;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.teacher-medal {
|
.teacher-medal {
|
||||||
|
|||||||
@ -331,7 +331,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="detail-stat-item">
|
<div class="detail-stat-item">
|
||||||
<div class="stat-number">{{ selectedCourse.studentCount }}</div>
|
<div class="stat-number">{{ selectedCourse.studentCount }}</div>
|
||||||
<div class="stat-label">覆盖学生</div>
|
<div class="stat-label">学生总数</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-divider />
|
<a-divider />
|
||||||
|
|||||||
@ -91,6 +91,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<span>课程使用</span>
|
<span>课程使用</span>
|
||||||
</div>
|
</div>
|
||||||
|
<a-segmented
|
||||||
|
v-model:value="usagePeriodType"
|
||||||
|
:options="periodOptions"
|
||||||
|
@change="loadUsageData"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body" :class="{ 'is-loading': usageLoading }">
|
<div class="card-body" :class="{ 'is-loading': usageLoading }">
|
||||||
<a-spin v-if="usageLoading" />
|
<a-spin v-if="usageLoading" />
|
||||||
@ -131,8 +137,8 @@
|
|||||||
:class="{ 'finished': lesson.status === 'FINISHED' }"
|
:class="{ 'finished': lesson.status === 'FINISHED' }"
|
||||||
>
|
>
|
||||||
<div class="lesson-time">
|
<div class="lesson-time">
|
||||||
<div class="time-value">{{ formatTime(lesson.plannedDatetime) }}</div>
|
<div class="time-value">{{ formatTime(lesson.startTime || lesson.lessonDate) }}</div>
|
||||||
<div class="time-duration">{{ lesson.duration }}分钟</div>
|
<div class="time-duration">30 分钟</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="lesson-info">
|
<div class="lesson-info">
|
||||||
<div class="lesson-name">{{ lesson.courseName }}</div>
|
<div class="lesson-name">{{ lesson.courseName }}</div>
|
||||||
@ -193,23 +199,23 @@
|
|||||||
>
|
>
|
||||||
<div class="recommend-cover">
|
<div class="recommend-cover">
|
||||||
<img
|
<img
|
||||||
v-if="course.coverImagePath"
|
v-if="(course as any).coverImagePath || course.description"
|
||||||
:src="getImageUrl(course.coverImagePath)"
|
:src="getImageUrl((course as any).coverImagePath || course.description || '')"
|
||||||
class="cover-img"
|
class="cover-img"
|
||||||
/>
|
/>
|
||||||
<div v-else class="cover-placeholder">
|
<div v-else class="cover-placeholder">
|
||||||
<BookFilled />
|
<BookFilled />
|
||||||
</div>
|
</div>
|
||||||
<div class="duration-tag">{{ course.duration || 30 }}分钟</div>
|
<div class="duration-tag">{{ course.courseCount || 30 }}分钟</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="recommend-info">
|
<div class="recommend-info">
|
||||||
<div class="recommend-name">{{ course.name }}</div>
|
<div class="recommend-name">{{ course.name }}</div>
|
||||||
<div class="recommend-meta">
|
<div class="recommend-meta">
|
||||||
<span class="meta-item">
|
<span class="meta-item">
|
||||||
<FireOutlined /> {{ course.usageCount }}次使用
|
<FireOutlined /> {{ course.usageCount || 0 }}次使用
|
||||||
</span>
|
</span>
|
||||||
<span v-if="course.avgRating > 0" class="meta-item">
|
<span v-if="course.gradeLevel" class="meta-item">
|
||||||
<StarFilled class="star-icon" /> {{ course.avgRating.toFixed(1) }}
|
<StarFilled class="star-icon" /> {{ course.gradeLevel }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -219,46 +225,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 近期活动 -->
|
|
||||||
<div class="activity-section">
|
|
||||||
<div class="content-card activity-card">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-title">
|
|
||||||
<div class="title-icon-wrapper list">
|
|
||||||
<UnorderedListOutlined />
|
|
||||||
</div>
|
|
||||||
<span>近期活动</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body" :class="{ 'is-loading': loading }">
|
|
||||||
<a-spin :spinning="loading">
|
|
||||||
<div v-if="recentActivities.length === 0" class="empty-state-horizontal">
|
|
||||||
<div class="empty-icon-wrapper small">
|
|
||||||
<FileTextOutlined />
|
|
||||||
</div>
|
|
||||||
<p class="empty-text">暂无近期活动</p>
|
|
||||||
</div>
|
|
||||||
<div v-else class="activity-timeline">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in recentActivities"
|
|
||||||
:key="item.id"
|
|
||||||
class="activity-item"
|
|
||||||
:class="'type-' + item.type"
|
|
||||||
>
|
|
||||||
<div class="activity-dot">
|
|
||||||
<component :is="getActivityIcon(item.type)" />
|
|
||||||
</div>
|
|
||||||
<div class="activity-content">
|
|
||||||
<div class="activity-text">{{ item.description }}</div>
|
|
||||||
<div class="activity-time">{{ item.time }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a-spin>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -284,11 +250,6 @@ import {
|
|||||||
FireOutlined,
|
FireOutlined,
|
||||||
InboxOutlined,
|
InboxOutlined,
|
||||||
RightOutlined,
|
RightOutlined,
|
||||||
UnorderedListOutlined,
|
|
||||||
FileTextOutlined,
|
|
||||||
MessageOutlined,
|
|
||||||
UserOutlined,
|
|
||||||
FolderOutlined,
|
|
||||||
ClockCircleOutlined,
|
ClockCircleOutlined,
|
||||||
LineChartOutlined,
|
LineChartOutlined,
|
||||||
PieChartOutlined,
|
PieChartOutlined,
|
||||||
@ -296,9 +257,14 @@ import {
|
|||||||
import {
|
import {
|
||||||
getTeacherDashboard,
|
getTeacherDashboard,
|
||||||
getTeacherLessonTrend,
|
getTeacherLessonTrend,
|
||||||
getTeacherCourseUsage,
|
getTeacherCourseUsageStats,
|
||||||
|
type DashboardData,
|
||||||
|
type TeacherLessonItem,
|
||||||
|
type TeacherCoursePackageItem,
|
||||||
|
type TeacherLessonTrendItem,
|
||||||
|
type TeacherCourseUsageItem,
|
||||||
|
type CourseUsageStatsItem,
|
||||||
} from '@/api/teacher';
|
} from '@/api/teacher';
|
||||||
import type { TeacherLessonTrendItem, TeacherCourseUsageItem } from '@/api/teacher';
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -314,15 +280,25 @@ let usageChart: echarts.ECharts | null = null;
|
|||||||
|
|
||||||
// Chart data
|
// Chart data
|
||||||
const lessonTrendData = ref<TeacherLessonTrendItem[]>([]);
|
const lessonTrendData = ref<TeacherLessonTrendItem[]>([]);
|
||||||
const courseUsageData = ref<TeacherCourseUsageItem[]>([]);
|
|
||||||
|
|
||||||
const stats = ref({
|
const stats = ref<DashboardData['stats']>({
|
||||||
classCount: 0,
|
classCount: 0,
|
||||||
studentCount: 0,
|
studentCount: 0,
|
||||||
lessonCount: 0,
|
lessonCount: 0,
|
||||||
courseCount: 0,
|
courseCount: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 课程使用统计周期选择
|
||||||
|
const usagePeriodType = ref<'TODAY' | 'WEEK' | 'MONTH' | 'CUSTOM'>('MONTH');
|
||||||
|
const periodOptions = [
|
||||||
|
{ label: '今日', value: 'TODAY' },
|
||||||
|
{ label: '本周', value: 'WEEK' },
|
||||||
|
{ label: '本月', value: 'MONTH' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 增强版课程使用统计数据
|
||||||
|
const courseUsageStatsData = ref<CourseUsageStatsItem[]>([]);
|
||||||
|
|
||||||
const currentDate = computed(() => {
|
const currentDate = computed(() => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
||||||
@ -332,39 +308,8 @@ const currentDate = computed(() => {
|
|||||||
return `${month}月${date}日 ${day}`;
|
return `${month}月${date}日 ${day}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
interface TodayLesson {
|
const todayLessons = ref<TeacherLessonItem[]>([]);
|
||||||
id: number;
|
const recommendedCourses = ref<TeacherCoursePackageItem[]>([]);
|
||||||
courseId: number;
|
|
||||||
courseName: string;
|
|
||||||
pictureBookName?: string;
|
|
||||||
classId: number;
|
|
||||||
className: string;
|
|
||||||
plannedDatetime: string;
|
|
||||||
status: string;
|
|
||||||
duration: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RecommendedCourse {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
pictureBookName?: string;
|
|
||||||
coverImagePath?: string;
|
|
||||||
duration: number;
|
|
||||||
usageCount: number;
|
|
||||||
avgRating: number;
|
|
||||||
gradeTags: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RecentActivity {
|
|
||||||
id: number;
|
|
||||||
type: string;
|
|
||||||
description: string;
|
|
||||||
time: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const todayLessons = ref<TodayLesson[]>([]);
|
|
||||||
const recommendedCourses = ref<RecommendedCourse[]>([]);
|
|
||||||
const recentActivities = ref<RecentActivity[]>([]);
|
|
||||||
|
|
||||||
const getImageUrl = (path: string) => {
|
const getImageUrl = (path: string) => {
|
||||||
if (!path) return '';
|
if (!path) return '';
|
||||||
@ -379,36 +324,6 @@ const formatTime = (datetime: string) => {
|
|||||||
return `${hours}:${minutes}`;
|
return `${hours}:${minutes}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatActivityTime = (time: string) => {
|
|
||||||
const date = new Date(time);
|
|
||||||
const now = new Date();
|
|
||||||
const diff = now.getTime() - date.getTime();
|
|
||||||
|
|
||||||
const minutes = Math.floor(diff / 60000);
|
|
||||||
const hours = Math.floor(diff / 3600000);
|
|
||||||
const days = Math.floor(diff / 86400000);
|
|
||||||
|
|
||||||
if (minutes < 60) {
|
|
||||||
return `${minutes}分钟前`;
|
|
||||||
} else if (hours < 24) {
|
|
||||||
return `${hours}小时前`;
|
|
||||||
} else if (days < 7) {
|
|
||||||
return `${days}天前`;
|
|
||||||
} else {
|
|
||||||
return date.toLocaleDateString();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getActivityIcon = (type: string) => {
|
|
||||||
const iconMap: Record<string, any> = {
|
|
||||||
lesson: ReadOutlined,
|
|
||||||
feedback: MessageOutlined,
|
|
||||||
student: UserOutlined,
|
|
||||||
course: FolderOutlined,
|
|
||||||
};
|
|
||||||
return iconMap[type] || FileTextOutlined;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 初始化授课趋势图表
|
// 初始化授课趋势图表
|
||||||
const initTrendChart = (data: TeacherLessonTrendItem[]) => {
|
const initTrendChart = (data: TeacherLessonTrendItem[]) => {
|
||||||
if (!trendChartRef.value) return;
|
if (!trendChartRef.value) return;
|
||||||
@ -526,8 +441,8 @@ const initTrendChart = (data: TeacherLessonTrendItem[]) => {
|
|||||||
trendChart.setOption(option);
|
trendChart.setOption(option);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 初始化课程使用饼图
|
// 初始化课程使用饼图(支持旧版数据类型)
|
||||||
const initUsageChart = (data: TeacherCourseUsageItem[]) => {
|
const initUsageChart = (data: TeacherCourseUsageItem[] | CourseUsageStatsItem[]) => {
|
||||||
if (!usageChartRef.value) return;
|
if (!usageChartRef.value) return;
|
||||||
|
|
||||||
if (usageChart) {
|
if (usageChart) {
|
||||||
@ -536,10 +451,45 @@ const initUsageChart = (data: TeacherCourseUsageItem[]) => {
|
|||||||
|
|
||||||
usageChart = echarts.init(usageChartRef.value);
|
usageChart = echarts.init(usageChartRef.value);
|
||||||
|
|
||||||
|
// 转换为统一的格式
|
||||||
|
const chartData = data.map(item => {
|
||||||
|
if ('coursePackageName' in item) {
|
||||||
|
// CourseUsageStatsItem 类型
|
||||||
|
return {
|
||||||
|
name: (item as CourseUsageStatsItem).coursePackageName,
|
||||||
|
value: (item as CourseUsageStatsItem).usageCount,
|
||||||
|
studentCount: (item as CourseUsageStatsItem).studentCount,
|
||||||
|
avgDuration: (item as CourseUsageStatsItem).avgDuration,
|
||||||
|
lastUsedAt: (item as CourseUsageStatsItem).lastUsedAt,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// TeacherCourseUsageItem 类型(旧版)
|
||||||
|
return {
|
||||||
|
name: (item as TeacherCourseUsageItem).name,
|
||||||
|
value: (item as TeacherCourseUsageItem).value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const option: echarts.EChartsOption = {
|
const option: echarts.EChartsOption = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: '{b}: {c}次 ({d}%)',
|
formatter: (params: any) => {
|
||||||
|
const data = params.data;
|
||||||
|
let tooltip = `${data.name}: ${data.value}次 (${params.percent}%)`;
|
||||||
|
if (data.studentCount !== undefined) {
|
||||||
|
tooltip += `\n参与学生:${data.studentCount}人`;
|
||||||
|
}
|
||||||
|
if (data.avgDuration !== undefined) {
|
||||||
|
tooltip += `\n平均时长:${data.avgDuration}分钟`;
|
||||||
|
}
|
||||||
|
if (data.lastUsedAt) {
|
||||||
|
const lastUsed = new Date(data.lastUsedAt);
|
||||||
|
const lastUsedStr = `${lastUsed.getMonth() + 1}月${lastUsed.getDate()}日 ${lastUsed.getHours().toString().padStart(2, '0')}:${lastUsed.getMinutes().toString().padStart(2, '0')}`;
|
||||||
|
tooltip += `\n最后使用:${lastUsedStr}`;
|
||||||
|
}
|
||||||
|
return tooltip;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
orient: 'vertical',
|
orient: 'vertical',
|
||||||
@ -571,7 +521,7 @@ const initUsageChart = (data: TeacherCourseUsageItem[]) => {
|
|||||||
labelLine: {
|
labelLine: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
data: data.map((item, index) => ({
|
data: chartData.map((item, index) => ({
|
||||||
...item,
|
...item,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: [
|
color: [
|
||||||
@ -609,22 +559,20 @@ const loadTrendData = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载课程使用数据
|
// 加载课程使用数据(增强版)
|
||||||
const loadUsageData = async () => {
|
const loadUsageData = async () => {
|
||||||
usageLoading.value = true;
|
usageLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const data = await getTeacherCourseUsage();
|
const data = await getTeacherCourseUsageStats({
|
||||||
courseUsageData.value = data;
|
periodType: usagePeriodType.value
|
||||||
|
});
|
||||||
|
courseUsageStatsData.value = data;
|
||||||
|
initUsageChart(courseUsageStatsData.value);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load usage data:', error);
|
console.error('Failed to load usage data:', error);
|
||||||
} finally {
|
} finally {
|
||||||
usageLoading.value = false;
|
usageLoading.value = false;
|
||||||
}
|
}
|
||||||
// 在 loading 结束后初始化图表
|
|
||||||
await nextTick();
|
|
||||||
if (courseUsageData.value.length > 0) {
|
|
||||||
initUsageChart(courseUsageData.value);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 窗口大小变化时重绘图表
|
// 窗口大小变化时重绘图表
|
||||||
@ -639,36 +587,8 @@ const loadDashboard = async () => {
|
|||||||
const data = await getTeacherDashboard();
|
const data = await getTeacherDashboard();
|
||||||
stats.value = data.stats || { classCount: 0, studentCount: 0, lessonCount: 0, courseCount: 0 };
|
stats.value = data.stats || { classCount: 0, studentCount: 0, lessonCount: 0, courseCount: 0 };
|
||||||
|
|
||||||
// 映射今日课程数据,处理可能缺失的字段
|
todayLessons.value = data.todayLessons || [];
|
||||||
todayLessons.value = (data.todayLessons || []).map((lesson: any) => ({
|
recommendedCourses.value = data.recommendedCourses || [];
|
||||||
id: lesson.id,
|
|
||||||
courseId: lesson.courseId,
|
|
||||||
courseName: lesson.courseName || lesson.course?.name || '未命名课程',
|
|
||||||
pictureBookName: lesson.pictureBookName,
|
|
||||||
classId: lesson.classId,
|
|
||||||
className: lesson.className || lesson.class?.name || '未命名班级',
|
|
||||||
plannedDatetime: lesson.plannedDatetime || lesson.startDatetime || lesson.lessonDate,
|
|
||||||
status: lesson.status,
|
|
||||||
duration: lesson.duration || lesson.actualDuration || 30, // 默认30分钟
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 映射推荐课程数据,处理可能缺失的字段
|
|
||||||
recommendedCourses.value = (data.recommendedCourses || []).map((course: any) => ({
|
|
||||||
id: course.id,
|
|
||||||
name: course.name || '未命名课程',
|
|
||||||
pictureBookName: course.pictureBookName,
|
|
||||||
coverImagePath: course.coverImagePath,
|
|
||||||
duration: course.duration || 30, // 默认30分钟
|
|
||||||
usageCount: course.usageCount || 0,
|
|
||||||
avgRating: course.avgRating || 0, // 默认0分
|
|
||||||
gradeTags: course.gradeTags || [],
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 处理近期活动数据
|
|
||||||
recentActivities.value = (data.recentActivities || []).map((item: RecentActivity) => ({
|
|
||||||
...item,
|
|
||||||
time: formatActivityTime(item.time),
|
|
||||||
}));
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Failed to load dashboard:', error);
|
console.error('Failed to load dashboard:', error);
|
||||||
message.error(error?.response?.data?.message || '加载数据失败');
|
message.error(error?.response?.data?.message || '加载数据失败');
|
||||||
@ -677,11 +597,11 @@ const loadDashboard = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const startLesson = (lesson: TodayLesson) => {
|
const startLesson = (lesson: TeacherLessonItem) => {
|
||||||
router.push(`/teacher/lessons/${lesson.id}`);
|
router.push(`/teacher/lessons/${lesson.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewCourse = (course: RecommendedCourse) => {
|
const viewCourse = (course: TeacherCoursePackageItem) => {
|
||||||
router.push(`/teacher/courses/${course.id}`);
|
router.push(`/teacher/courses/${course.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1244,75 +1164,6 @@ onUnmounted(() => {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 活动区域 */
|
|
||||||
.activity-section {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-card .card-body {
|
|
||||||
min-height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-timeline {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-item {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-dot {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-item.type-lesson .activity-dot {
|
|
||||||
background: linear-gradient(135deg, #FFE4C9 0%, #FFF0E0 100%);
|
|
||||||
color: #FF8C42;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-item.type-feedback .activity-dot {
|
|
||||||
background: linear-gradient(135deg, #D6E4FF 0%, #E8F0FF 100%);
|
|
||||||
color: #1890ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-item.type-student .activity-dot {
|
|
||||||
background: linear-gradient(135deg, #D9F7BE 0%, #E8FFD4 100%);
|
|
||||||
color: #52c41a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-item.type-course .activity-dot {
|
|
||||||
background: linear-gradient(135deg, #FFE7BA 0%, #FFF1D6 100%);
|
|
||||||
color: #FA8C16;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-content {
|
|
||||||
flex: 1;
|
|
||||||
padding-top: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-text {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-time {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #999;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式 */
|
/* 响应式 */
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.stats-grid {
|
.stats-grid {
|
||||||
|
|||||||
@ -27,8 +27,8 @@ test.describe('数据看板', () => {
|
|||||||
const lessonCard = page.getByText(/月授课|授课次数/).first();
|
const lessonCard = page.getByText(/月授课|授课次数/).first();
|
||||||
await expect(lessonCard).toBeVisible();
|
await expect(lessonCard).toBeVisible();
|
||||||
|
|
||||||
// 验证覆盖学生卡片
|
// 验证学生总数卡片
|
||||||
const studentCard = page.getByText(/覆盖学生|学生数/).first();
|
const studentCard = page.getByText(/学生总数|学生数/).first();
|
||||||
await expect(studentCard).toBeVisible();
|
await expect(studentCard).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,74 @@
|
|||||||
|
package com.reading.platform.common.enums;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教师活跃度等级枚举
|
||||||
|
* 根据统计周期内的授课次数划分活跃程度
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum TeacherActivityLevel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高频活跃:统计周期内授课次数 >= 10 次
|
||||||
|
*/
|
||||||
|
HIGH("HIGH", "高频", 10),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 中频活跃:统计周期内授课次数 5-9 次
|
||||||
|
*/
|
||||||
|
MEDIUM("MEDIUM", "中频", 5),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 低频活跃:统计周期内授课次数 1-4 次
|
||||||
|
*/
|
||||||
|
LOW("LOW", "低频", 1),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未活跃:统计周期内授课次数为 0 次
|
||||||
|
*/
|
||||||
|
INACTIVE("INACTIVE", "未活跃", 0);
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
private final String description;
|
||||||
|
private final int minLessonCount;
|
||||||
|
|
||||||
|
TeacherActivityLevel(String code, String description, int minLessonCount) {
|
||||||
|
this.code = code;
|
||||||
|
this.description = description;
|
||||||
|
this.minLessonCount = minLessonCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据授课次数获取活跃度等级
|
||||||
|
*
|
||||||
|
* @param lessonCount 授课次数
|
||||||
|
* @return 活跃度等级
|
||||||
|
*/
|
||||||
|
public static TeacherActivityLevel fromLessonCount(int lessonCount) {
|
||||||
|
if (lessonCount >= HIGH.minLessonCount) {
|
||||||
|
return HIGH;
|
||||||
|
} else if (lessonCount >= MEDIUM.minLessonCount) {
|
||||||
|
return MEDIUM;
|
||||||
|
} else if (lessonCount >= LOW.minLessonCount) {
|
||||||
|
return LOW;
|
||||||
|
} else {
|
||||||
|
return INACTIVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据编码获取活跃度等级
|
||||||
|
*
|
||||||
|
* @param code 编码
|
||||||
|
* @return 活跃度等级
|
||||||
|
*/
|
||||||
|
public static TeacherActivityLevel fromCode(String code) {
|
||||||
|
for (TeacherActivityLevel level : values()) {
|
||||||
|
if (level.getCode().equals(code)) {
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return INACTIVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -119,4 +119,25 @@ public class JwtTokenProvider {
|
|||||||
.getSubject();
|
.getSubject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 token 剩余过期时间(秒)
|
||||||
|
* @param token token 字符串
|
||||||
|
* @return 剩余秒数,如果 token 无效则返回 0
|
||||||
|
*/
|
||||||
|
public long getRemainingExpiration(String token) {
|
||||||
|
try {
|
||||||
|
Date expiryDate = Jwts.parser()
|
||||||
|
.verifyWith(secretKey)
|
||||||
|
.build()
|
||||||
|
.parseSignedClaims(token)
|
||||||
|
.getPayload()
|
||||||
|
.getExpiration();
|
||||||
|
long remaining = (expiryDate.getTime() - System.currentTimeMillis()) / 1000;
|
||||||
|
return remaining > 0 ? remaining : 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("获取 token 剩余过期时间失败:{}", e.getMessage());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,10 @@ import com.reading.platform.common.mapper.TenantMapper;
|
|||||||
import com.reading.platform.common.mapper.TeacherMapper;
|
import com.reading.platform.common.mapper.TeacherMapper;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.dto.request.LoginRequest;
|
import com.reading.platform.dto.request.LoginRequest;
|
||||||
|
import com.reading.platform.dto.request.UpdateProfileRequest;
|
||||||
import com.reading.platform.dto.response.LoginResponse;
|
import com.reading.platform.dto.response.LoginResponse;
|
||||||
import com.reading.platform.dto.response.TokenResponse;
|
import com.reading.platform.dto.response.TokenResponse;
|
||||||
|
import com.reading.platform.dto.response.UpdateProfileResponse;
|
||||||
import com.reading.platform.dto.response.UserInfoResponse;
|
import com.reading.platform.dto.response.UserInfoResponse;
|
||||||
import com.reading.platform.entity.AdminUser;
|
import com.reading.platform.entity.AdminUser;
|
||||||
import com.reading.platform.entity.Parent;
|
import com.reading.platform.entity.Parent;
|
||||||
@ -16,8 +18,10 @@ import com.reading.platform.entity.Teacher;
|
|||||||
import com.reading.platform.service.AuthService;
|
import com.reading.platform.service.AuthService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@Tag(name = "认证管理", description = "Authentication APIs")
|
@Tag(name = "认证管理", description = "Authentication APIs")
|
||||||
@ -63,11 +67,21 @@ public class AuthController {
|
|||||||
@PostMapping("/change-password")
|
@PostMapping("/change-password")
|
||||||
public Result<Void> changePassword(
|
public Result<Void> changePassword(
|
||||||
@RequestParam String oldPassword,
|
@RequestParam String oldPassword,
|
||||||
@RequestParam String newPassword) {
|
@RequestParam String newPassword,
|
||||||
authService.changePassword(oldPassword, newPassword);
|
HttpServletRequest request) {
|
||||||
|
String token = resolveToken(request);
|
||||||
|
authService.changePassword(oldPassword, newPassword, token);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "修改个人信息")
|
||||||
|
@PutMapping("/profile")
|
||||||
|
public Result<UpdateProfileResponse> updateProfile(
|
||||||
|
@Valid @RequestBody UpdateProfileRequest request) {
|
||||||
|
UpdateProfileResponse response = authService.updateProfile(request);
|
||||||
|
return Result.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
private UserInfoResponse convertToUserInfoResponse(Object userInfo) {
|
private UserInfoResponse convertToUserInfoResponse(Object userInfo) {
|
||||||
if (userInfo instanceof Tenant) {
|
if (userInfo instanceof Tenant) {
|
||||||
Tenant tenant = (Tenant) userInfo;
|
Tenant tenant = (Tenant) userInfo;
|
||||||
@ -121,4 +135,15 @@ public class AuthController {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从请求头获取 token
|
||||||
|
*/
|
||||||
|
private String resolveToken(HttpServletRequest request) {
|
||||||
|
String bearerToken = request.getHeader("Authorization");
|
||||||
|
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
|
||||||
|
return bearerToken.substring(7);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,8 @@ import com.reading.platform.common.enums.UserRole;
|
|||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.dto.request.ActiveTenantsQueryRequest;
|
import com.reading.platform.dto.request.ActiveTenantsQueryRequest;
|
||||||
import com.reading.platform.dto.request.PopularCoursesQueryRequest;
|
import com.reading.platform.dto.request.PopularCoursesQueryRequest;
|
||||||
import com.reading.platform.dto.request.RecentActivitiesQueryRequest;
|
|
||||||
import com.reading.platform.dto.response.ActiveTenantItemResponse;
|
import com.reading.platform.dto.response.ActiveTenantItemResponse;
|
||||||
import com.reading.platform.dto.response.PopularCourseItemResponse;
|
import com.reading.platform.dto.response.PopularCourseItemResponse;
|
||||||
import com.reading.platform.dto.response.RecentActivityItemResponse;
|
|
||||||
import com.reading.platform.dto.response.StatsResponse;
|
import com.reading.platform.dto.response.StatsResponse;
|
||||||
import com.reading.platform.dto.response.StatsTrendResponse;
|
import com.reading.platform.dto.response.StatsTrendResponse;
|
||||||
import com.reading.platform.service.StatsService;
|
import com.reading.platform.service.StatsService;
|
||||||
@ -58,10 +56,4 @@ public class AdminStatsController {
|
|||||||
return Result.success(statsService.getPopularCourses(request));
|
return Result.success(statsService.getPopularCourses(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/activities")
|
|
||||||
@Operation(summary = "获取最近活动")
|
|
||||||
public Result<List<RecentActivityItemResponse>> getRecentActivities(@ModelAttribute RecentActivitiesQueryRequest request) {
|
|
||||||
return Result.success(statsService.getRecentActivities(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -9,6 +9,7 @@ import com.reading.platform.dto.response.LessonTagResponse;
|
|||||||
import com.reading.platform.dto.response.SchoolCourseResponse;
|
import com.reading.platform.dto.response.SchoolCourseResponse;
|
||||||
import com.reading.platform.entity.CourseLesson;
|
import com.reading.platform.entity.CourseLesson;
|
||||||
import com.reading.platform.entity.CoursePackage;
|
import com.reading.platform.entity.CoursePackage;
|
||||||
|
import com.reading.platform.mapper.LessonMapper;
|
||||||
import com.reading.platform.service.CourseLessonService;
|
import com.reading.platform.service.CourseLessonService;
|
||||||
import com.reading.platform.service.CoursePackageService;
|
import com.reading.platform.service.CoursePackageService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@ -19,6 +20,7 @@ import org.springframework.util.StringUtils;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,6 +36,7 @@ public class SchoolCourseController {
|
|||||||
|
|
||||||
private final CoursePackageService courseService;
|
private final CoursePackageService courseService;
|
||||||
private final CourseLessonService courseLessonService;
|
private final CourseLessonService courseLessonService;
|
||||||
|
private final LessonMapper lessonMapper;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@Operation(summary = "获取学校课程包列表(分页)")
|
@Operation(summary = "获取学校课程包列表(分页)")
|
||||||
@ -56,6 +59,25 @@ public class SchoolCourseController {
|
|||||||
.map(SchoolCourseResponse::toSchoolCourseResponse)
|
.map(SchoolCourseResponse::toSchoolCourseResponse)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 按租户 ID 统计每个课程的使用次数(已完成状态的 Lesson 数量)
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
List<Long> courseIds = list.stream().map(SchoolCourseResponse::getId).collect(Collectors.toList());
|
||||||
|
List<Map<String, Object>> usageStats = lessonMapper.countUsageByTenantAndCourseIds(tenantId, courseIds);
|
||||||
|
|
||||||
|
// 构建课程 ID -> 使用次数的映射
|
||||||
|
Map<Long, Integer> usageCountMap = usageStats.stream()
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
m -> ((Number) m.get("course_id")).longValue(),
|
||||||
|
m -> ((Number) m.get("usageCount")).intValue(),
|
||||||
|
(v1, v2) -> v1 // 如果有重复,取第一个值
|
||||||
|
));
|
||||||
|
|
||||||
|
// 填充 usageCount,如果没有统计数据显示为 0
|
||||||
|
for (SchoolCourseResponse vo : list) {
|
||||||
|
vo.setUsageCount(usageCountMap.getOrDefault(vo.getId(), 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 填充 lessonTags
|
// 填充 lessonTags
|
||||||
for (SchoolCourseResponse vo : list) {
|
for (SchoolCourseResponse vo : list) {
|
||||||
List<CourseLesson> lessons = courseLessonService.findByCourseId(vo.getId());
|
List<CourseLesson> lessons = courseLessonService.findByCourseId(vo.getId());
|
||||||
|
|||||||
@ -2,12 +2,15 @@ package com.reading.platform.controller.school;
|
|||||||
|
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.common.security.SecurityUtils;
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.dto.response.TeacherActivityRankResponse;
|
||||||
import com.reading.platform.service.SchoolStatsService;
|
import com.reading.platform.service.SchoolStatsService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -31,33 +34,27 @@ public class SchoolStatsController {
|
|||||||
|
|
||||||
@GetMapping("/teachers")
|
@GetMapping("/teachers")
|
||||||
@Operation(summary = "获取活跃教师排行")
|
@Operation(summary = "获取活跃教师排行")
|
||||||
public Result<List<Map<String, Object>>> getActiveTeachers(
|
public Result<List<TeacherActivityRankResponse>> getActiveTeachers(
|
||||||
@RequestParam(defaultValue = "5") int limit) {
|
@RequestParam(defaultValue = "10") int limit) {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
return Result.success(schoolStatsService.getActiveTeachers(tenantId, limit));
|
return Result.success(schoolStatsService.getActiveTeachers(tenantId, limit));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/courses")
|
@GetMapping("/courses")
|
||||||
@Operation(summary = "获取课程使用统计")
|
@Operation(summary = "获取课程使用统计")
|
||||||
public Result<List<Map<String, Object>>> getCourseUsageStats() {
|
public Result<List<Map<String, Object>>> getCourseUsageStats(
|
||||||
|
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||||||
|
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
return Result.success(schoolStatsService.getCourseUsageStats(tenantId));
|
return Result.success(schoolStatsService.getCourseUsageStats(tenantId, startDate, endDate));
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/activities")
|
|
||||||
@Operation(summary = "获取近期活动")
|
|
||||||
public Result<List<Map<String, Object>>> getRecentActivities(
|
|
||||||
@RequestParam(defaultValue = "10") int limit) {
|
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
|
||||||
return Result.success(schoolStatsService.getRecentActivities(tenantId, limit));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/lesson-trend")
|
@GetMapping("/lesson-trend")
|
||||||
@Operation(summary = "获取授课趋势")
|
@Operation(summary = "获取授课趋势")
|
||||||
public Result<List<Map<String, Object>>> getLessonTrend(
|
public Result<List<Map<String, Object>>> getLessonTrend(
|
||||||
@RequestParam(defaultValue = "6") int months) {
|
@RequestParam(defaultValue = "7") int days) {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
return Result.success(schoolStatsService.getLessonTrend(tenantId, months));
|
return Result.success(schoolStatsService.getLessonTrend(tenantId, days));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/course-distribution")
|
@GetMapping("/course-distribution")
|
||||||
|
|||||||
@ -2,16 +2,16 @@ package com.reading.platform.controller.teacher;
|
|||||||
|
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.common.security.SecurityUtils;
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
import com.reading.platform.entity.CoursePackage;
|
import com.reading.platform.dto.request.CourseUsageQuery;
|
||||||
import com.reading.platform.entity.Lesson;
|
import com.reading.platform.dto.response.*;
|
||||||
import com.reading.platform.service.TeacherStatsService;
|
import com.reading.platform.service.TeacherStatsService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统计数据控制器(教师端)
|
* 统计数据控制器(教师端)
|
||||||
@ -26,7 +26,7 @@ public class TeacherStatsController {
|
|||||||
|
|
||||||
@GetMapping("/dashboard")
|
@GetMapping("/dashboard")
|
||||||
@Operation(summary = "获取教师端首页统计数据")
|
@Operation(summary = "获取教师端首页统计数据")
|
||||||
public Result<Map<String, Object>> getDashboard() {
|
public Result<TeacherDashboardResponse> getDashboard() {
|
||||||
Long teacherId = SecurityUtils.getCurrentUserId();
|
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
return Result.success(teacherStatsService.getDashboard(teacherId, tenantId));
|
return Result.success(teacherStatsService.getDashboard(teacherId, tenantId));
|
||||||
@ -34,36 +34,56 @@ public class TeacherStatsController {
|
|||||||
|
|
||||||
@GetMapping("/today-lessons")
|
@GetMapping("/today-lessons")
|
||||||
@Operation(summary = "获取今日课程")
|
@Operation(summary = "获取今日课程")
|
||||||
public Result<List<Lesson>> getTodayLessons() {
|
public Result<List<TeacherLessonVO>> getTodayLessons() {
|
||||||
Long teacherId = SecurityUtils.getCurrentUserId();
|
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||||
return Result.success(teacherStatsService.getTodayLessons(teacherId));
|
return Result.success(teacherStatsService.getTodayLessons(teacherId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/recommended-courses")
|
@GetMapping("/recommended-courses")
|
||||||
@Operation(summary = "获取推荐课程")
|
@Operation(summary = "获取推荐课程")
|
||||||
public Result<List<CoursePackage>> getRecommendedCourses() {
|
public Result<List<CoursePackageVO>> getRecommendedCourses() {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
return Result.success(teacherStatsService.getRecommendedCourses(tenantId));
|
return Result.success(teacherStatsService.getRecommendedCourses(tenantId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/weekly-stats")
|
@GetMapping("/weekly-stats")
|
||||||
@Operation(summary = "获取本周统计")
|
@Operation(summary = "获取本周统计")
|
||||||
public Result<Map<String, Object>> getWeeklyStats() {
|
public Result<TeacherWeeklyStatsResponse> getWeeklyStats() {
|
||||||
Long teacherId = SecurityUtils.getCurrentUserId();
|
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||||
return Result.success(teacherStatsService.getWeeklyStats(teacherId));
|
return Result.success(teacherStatsService.getWeeklyStats(teacherId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/lesson-trend")
|
@GetMapping("/lesson-trend")
|
||||||
@Operation(summary = "获取授课趋势")
|
@Operation(summary = "获取授课趋势")
|
||||||
public Result<List<Map<String, Object>>> getLessonTrend(
|
public Result<List<TeacherLessonTrendVO>> getLessonTrend(
|
||||||
@RequestParam(defaultValue = "6") int months) {
|
@RequestParam(defaultValue = "6") int months) {
|
||||||
Long teacherId = SecurityUtils.getCurrentUserId();
|
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||||
return Result.success(teacherStatsService.getLessonTrend(teacherId, months));
|
return Result.success(teacherStatsService.getLessonTrend(teacherId, months));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/course-usage-stats")
|
||||||
|
@Operation(summary = "获取课程使用统计(增强版)")
|
||||||
|
public Result<List<CourseUsageStatsVO>> getCourseUsageStats(
|
||||||
|
@RequestParam(required = false) String periodType,
|
||||||
|
@RequestParam(required = false) LocalDate startDate,
|
||||||
|
@RequestParam(required = false) LocalDate endDate) {
|
||||||
|
|
||||||
|
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
|
||||||
|
CourseUsageQuery query = CourseUsageQuery.builder()
|
||||||
|
.periodType(periodType)
|
||||||
|
.startDate(startDate)
|
||||||
|
.endDate(endDate)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return Result.success(teacherStatsService.getCourseUsageStats(tenantId, teacherId, query));
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/course-usage")
|
@GetMapping("/course-usage")
|
||||||
@Operation(summary = "获取课程使用统计")
|
@Operation(summary = "获取课程使用统计(旧版)")
|
||||||
public Result<List<Map<String, Object>>> getCourseUsage() {
|
@Deprecated
|
||||||
|
public Result<List<CourseUsageVO>> getCourseUsage() {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
return Result.success(teacherStatsService.getCourseUsage(tenantId));
|
return Result.success(teacherStatsService.getCourseUsage(tenantId));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课程使用统计查询参数
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "课程使用统计查询参数")
|
||||||
|
public class CourseUsageQuery {
|
||||||
|
|
||||||
|
@Schema(description = "统计周期类型:TODAY-今日,WEEK-本周,MONTH-本月,CUSTOM-自定义")
|
||||||
|
private String periodType;
|
||||||
|
|
||||||
|
@Schema(description = "自定义周期开始日期")
|
||||||
|
private LocalDate startDate;
|
||||||
|
|
||||||
|
@Schema(description = "自定义周期结束日期")
|
||||||
|
private LocalDate endDate;
|
||||||
|
}
|
||||||
@ -1,15 +0,0 @@
|
|||||||
package com.reading.platform.dto.request;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 最近活动查询请求
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Schema(description = "最近活动查询请求")
|
|
||||||
public class RecentActivitiesQueryRequest {
|
|
||||||
|
|
||||||
@Schema(description = "返回数量限制", example = "10")
|
|
||||||
private Integer limit = 10;
|
|
||||||
}
|
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.Email;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改个人信息请求 DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "修改个人信息请求")
|
||||||
|
public class UpdateProfileRequest {
|
||||||
|
|
||||||
|
@Schema(description = "姓名", example = "张三")
|
||||||
|
@Pattern(regexp = "^[\u4e00-\u9fa5a-zA-Z\\s]{2,20}$", message = "姓名长度为 2-20 位,只能包含中文或英文")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "手机号", example = "13800138000")
|
||||||
|
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@Schema(description = "邮箱", example = "zhangsan@example.com")
|
||||||
|
@Email(message = "邮箱格式不正确")
|
||||||
|
private String email;
|
||||||
|
}
|
||||||
@ -22,9 +22,9 @@ public class ActiveTenantItemResponse {
|
|||||||
@Schema(description = "租户名称")
|
@Schema(description = "租户名称")
|
||||||
private String tenantName;
|
private String tenantName;
|
||||||
|
|
||||||
@Schema(description = "活跃用户数")
|
@Schema(description = "活跃教师数(近 30 天有完成课程的老师数)")
|
||||||
private Integer activeUsers;
|
private Integer activeTeacherCount;
|
||||||
|
|
||||||
@Schema(description = "课程使用数")
|
@Schema(description = "完成课次数(近 30 天 COMPLETED 状态的 lesson 总数)")
|
||||||
private Integer courseCount;
|
private Integer completedLessonCount;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,48 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课程包视图对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "课程包视图对象")
|
||||||
|
public class CoursePackageVO {
|
||||||
|
|
||||||
|
@Schema(description = "ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "适用年级")
|
||||||
|
private String gradeLevel;
|
||||||
|
|
||||||
|
@Schema(description = "课程数量")
|
||||||
|
private Integer courseCount;
|
||||||
|
|
||||||
|
@Schema(description = "状态")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "使用次数")
|
||||||
|
private Integer usageCount;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课程使用统计响应对象(增强版)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "课程使用统计响应对象")
|
||||||
|
public class CourseUsageStatsVO {
|
||||||
|
|
||||||
|
@Schema(description = "课程包 ID")
|
||||||
|
private Long coursePackageId;
|
||||||
|
|
||||||
|
@Schema(description = "课程包名称")
|
||||||
|
private String coursePackageName;
|
||||||
|
|
||||||
|
@Schema(description = "使用次数")
|
||||||
|
private Integer usageCount;
|
||||||
|
|
||||||
|
@Schema(description = "参与学生数(去重)")
|
||||||
|
private Integer studentCount;
|
||||||
|
|
||||||
|
@Schema(description = "平均时长(分钟)")
|
||||||
|
private Integer avgDuration;
|
||||||
|
|
||||||
|
@Schema(description = "最后使用时间")
|
||||||
|
private LocalDateTime lastUsedAt;
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课程使用统计视图对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "课程使用统计视图对象")
|
||||||
|
public class CourseUsageVO {
|
||||||
|
|
||||||
|
@Schema(description = "课程包名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "使用次数")
|
||||||
|
private Integer value;
|
||||||
|
}
|
||||||
@ -1,38 +0,0 @@
|
|||||||
package com.reading.platform.dto.response;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 最近活动项响应
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Builder
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Schema(description = "最近活动项响应")
|
|
||||||
public class RecentActivityItemResponse {
|
|
||||||
|
|
||||||
@Schema(description = "活动 ID")
|
|
||||||
private Long activityId;
|
|
||||||
|
|
||||||
@Schema(description = "活动类型")
|
|
||||||
private String activityType;
|
|
||||||
|
|
||||||
@Schema(description = "活动描述")
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
@Schema(description = "操作人 ID")
|
|
||||||
private Long operatorId;
|
|
||||||
|
|
||||||
@Schema(description = "操作人名称")
|
|
||||||
private String operatorName;
|
|
||||||
|
|
||||||
@Schema(description = "操作时间")
|
|
||||||
private LocalDateTime operationTime;
|
|
||||||
}
|
|
||||||
@ -33,4 +33,7 @@ public class StatsResponse {
|
|||||||
|
|
||||||
@Schema(description = "课时总数")
|
@Schema(description = "课时总数")
|
||||||
private Long totalLessons;
|
private Long totalLessons;
|
||||||
|
|
||||||
|
@Schema(description = "月授课次数(本月 COMPLETED 状态的 lesson 总数)")
|
||||||
|
private Long monthlyLessons;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user