diff --git a/src/api/base/server.ts b/src/api/base/server.ts
index 244a9cf..6687e6c 100644
--- a/src/api/base/server.ts
+++ b/src/api/base/server.ts
@@ -94,24 +94,11 @@ export const jsdXkListApi = async (params: any) => {
return await get("/mobile/js/xk/list", params);
};
-/**
- * 获取当前学期教师上课课程列表
- */
-export const getCurrentSemesterTeacherCoursesApi = async (jsId?: string) => {
- const params = jsId ? { jsId } : {};
- return await get("/api/xkkc/getCurrentSemesterTeacherCourses", params);
-};
-
// 选课列表
export const jsdXkkcSaveApi = async (params: any) => {
return await post("/api/xkkc/save", params);
};
-// 选课学生列表
-export const jsdXkXsListApi = async (params: any) => {
- return await get("/mobile/js/xkxs/list", params);
-};
-
// 获取班级学生考试成绩(按科目)
export const jsdBjKscjKmApi = async (params: any) => {
return await get("/mobile/js/kscj/bjKm", params);
diff --git a/src/api/base/xkApi.ts b/src/api/base/xkApi.ts
new file mode 100644
index 0000000..d7e3b0d
--- /dev/null
+++ b/src/api/base/xkApi.ts
@@ -0,0 +1,48 @@
+import { get, post } from "@/utils/request";
+
+/**
+ * 根据教师查询选课列表
+ */
+export const xkListByJsIdApi = async (params: any) => {
+ return await get("/api/xk/findXkListByJsId", params);
+};
+
+/**
+ * 根据教师查询选课课程列表
+ */
+export const xkkcListByJsIdApi = async (params: any) => {
+ return await get("/api/xkkc/getXkkcListByJsId", params);
+};
+
+/**
+ * 查询学生点名集合 - 匹配后端XkkcApiController.findXkkcList
+ */
+export const findXkkcListApi = async (params: any) => {
+ return await get("/api/xkkc/findXkkcList", params);
+};
+
+/**
+ * 获取选课点名分页
+ */
+export const getXkDmPageApi = async (params: any) => {
+ return await get('/api/xkDm/findPage', params)
+}
+
+/**
+ * 获取选课点名学生分页
+ */
+export const getXkDmXsPageApi = async (params: any) => {
+ return await get('/api/xkDmXs/findPage', params)
+}
+
+// 选课学生列表
+export const getWaitDmXsListApi = async (params: any) => {
+ return await get("/api/xkDmXs/getWaitDmXsList", params);
+};
+
+/**
+ * 提交选课点名
+ */
+export const submitXkDmApi = async (data: any) => {
+ return await post('/api/xkDm/save', data)
+}
diff --git a/src/pages.json b/src/pages.json
index 027cc14..591fa5d 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -486,19 +486,6 @@
"enablePullDownRefresh": false
}
},
- {
- "path": "pages/base/groupTeaching/xkList",
- "style": {
- "navigationBarTitleText": "选课列表",
- "enablePullDownRefresh": false
- }
- },
- {
- "path": "pages/base/groupTeaching/xkkcDetail",
- "style": {
- "navigationBarTitleText": "选课课程详情"
- }
- },
{
"path": "pages/view/routine/RengJiaoRengZhi/index",
"style": {
@@ -514,29 +501,43 @@
}
},
{
- "path": "pages/base/groupTeaching/dmXkList",
+ "path": "pages/view/routine/xk/xkList",
"style": {
- "navigationBarTitleText": "选课列表"
+ "navigationBarTitleText": "选课列表",
+ "enablePullDownRefresh": false
}
},
{
- "path": "pages/base/groupTeaching/dmXkkcDetail",
+ "path": "pages/view/routine/xk/xkkcDetail",
+ "style": {
+ "navigationBarTitleText": "选课课程详情"
+ }
+ },
+ {
+ "path": "pages/view/routine/xk/dmIndex",
+ "style": {
+ "navigationBarTitleText": "点名选课列表"
+ }
+ },
+ {
+ "path": "pages/view/routine/xk/dm",
"style": {
"navigationBarTitleText": "学生点名",
"enablePullDownRefresh": false
}
},
{
- "path": "pages/base/groupTeaching/dmXkkcRecord",
+ "path": "pages/view/routine/xk/dmRecord",
"style": {
"navigationBarTitleText": "点名记录",
"enablePullDownRefresh": false
}
},
{
- "path": "pages/base/groupTeaching/photoXkkcDetail",
+ "path": "pages/view/routine/xk/dmXsRecord",
"style": {
- "navigationBarTitleText": "课堂随拍"
+ "navigationBarTitleText": "点名学生记录",
+ "enablePullDownRefresh": false
}
},
{
diff --git a/src/pages/base/groupTeaching/photoXkkcDetail.vue b/src/pages/base/groupTeaching/photoXkkcDetail.vue
deleted file mode 100644
index 8b5d78f..0000000
--- a/src/pages/base/groupTeaching/photoXkkcDetail.vue
+++ /dev/null
@@ -1,419 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- {{ xkkc.kcmc }}
-
- 上课周期:
- {{ xkkc.skzqmc }}
-
-
- 上课时间:
- {{ formatClassTime(xkkc.skkstime, xkkc.skjstime) }}
-
-
- 上课地点:
- {{ xkkc.kcdd }}
-
-
-
-
-
-
-
-
-
- 现场拍照
-
-
-
-
-
-
-
-
-
-
-
-
-
- 点击拍照
-
-
-
-
-
-
- 现场视频
-
-
-
-
-
-
-
-
-
-
-
-
-
- 点击录制
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/base/groupTeaching_bak/studentRollCall.vue b/src/pages/base/groupTeaching_bak/studentRollCall.vue
deleted file mode 100644
index 1bdcc91..0000000
--- a/src/pages/base/groupTeaching_bak/studentRollCall.vue
+++ /dev/null
@@ -1,410 +0,0 @@
-
-
-
-
-
-
-
-
- 机器人创客
- 2024-12-25 (周三)
-
-
-
-
-
- 18
- 应到
-
-
- 18
- 实到
-
-
- 0
- 请假
-
-
- 0
- 缺勤
-
-
- 18
- 总人数
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{
- student.name
- }}
-
-
- {{ student.status }}
-
-
-
- {{ student.className }}
-
- 联系家长
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/base/groupTeaching_bak/zhujiao.vue b/src/pages/base/groupTeaching_bak/zhujiao.vue
deleted file mode 100644
index 17fed47..0000000
--- a/src/pages/base/groupTeaching_bak/zhujiao.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
-
- {{ "机器人创客" }}
-
- 开课老师:
- {{ "叶老师" }}
-
-
- 上课地点:
- {{ "第一教学楼302" }}
-
-
- 金额:
- ¥{{ "142" }}
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/base/groupTeaching_bak/zhujiaoDetails.vue b/src/pages/base/groupTeaching_bak/zhujiaoDetails.vue
deleted file mode 100644
index fe1b322..0000000
--- a/src/pages/base/groupTeaching_bak/zhujiaoDetails.vue
+++ /dev/null
@@ -1,165 +0,0 @@
-
-
-
-
-
-
- {{ "机器人创客" }}
-
- 开课老师:
- {{ "叶老师" }}
-
-
- 上课地点:
- {{ "第一教学楼302" }}
-
-
- 金额:
- ¥{{ "142" }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 教学计划暂无数据
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/base/service/index.vue b/src/pages/base/service/index.vue
index 96bae89..daa9d0f 100644
--- a/src/pages/base/service/index.vue
+++ b/src/pages/base/service/index.vue
@@ -237,7 +237,7 @@ const sections = reactive([
text: "课程填报",
show: true,
permissionKey: "routine-kcjs", // 课程介绍权限编码
- path: "/pages/base/groupTeaching/xkList",
+ path: "/pages/view/routine/xk/xkList",
},
{
id: "r6",
@@ -295,7 +295,7 @@ const sections = reactive([
text: "选课点名",
show: true,
permissionKey: "routine-kcdm", // 选课点名权限编码
- path: "/pages/base/groupTeaching/dmXkList",
+ path: "/pages/view/routine/xk/dmIndex",
},
{
id: "r11",
diff --git a/src/pages/components/dmPs/INTEGRATION.md b/src/pages/components/dmPs/INTEGRATION.md
new file mode 100644
index 0000000..22de0aa
--- /dev/null
+++ b/src/pages/components/dmPs/INTEGRATION.md
@@ -0,0 +1,225 @@
+# DmPs 组件集成文档
+
+## 概述
+
+`DmPs` 组件已成功集成到选课点名和就餐点名两个页面中,提供统一的拍照和视频录制功能。
+
+## 集成页面
+
+### 1. 选课点名页面
+**文件路径**: `zhxy-jsd/src/pages/view/routine/xk/dm.vue`
+
+**功能特点**:
+- 课程现场拍照和视频录制
+- 支持最多9张照片和3个视频
+- 视频最大时长60秒
+- 提交时自动上传媒体文件到服务器
+
+**关键代码**:
+```vue
+
+```
+
+### 2. 就餐点名页面
+**文件路径**: `zhxy-jsd/src/pages/view/routine/jc/components/dm.vue`
+
+**功能特点**:
+- 就餐现场拍照和视频录制
+- 支持最多9张照片和3个视频
+- 视频最大时长60秒
+- 提交时自动上传媒体文件到服务器
+
+**关键代码**:
+```vue
+
+```
+
+## 数据结构
+
+### 媒体数据对象
+```typescript
+interface MediaData {
+ photoList: Array<{
+ url: string; // 照片URL(本地临时路径)
+ path: string; // 照片路径(本地临时路径)
+ }>;
+ videoList: Array<{
+ url: string; // 视频URL(本地临时路径)
+ path: string; // 视频路径(本地临时路径)
+ }>;
+}
+```
+
+### 上传结果对象
+```typescript
+interface UploadResult {
+ photoUrls: string; // 照片服务器地址,逗号分隔
+ videoUrls: string; // 视频服务器地址,逗号分隔
+}
+```
+
+## 事件处理
+
+### 事件处理函数
+两个页面都实现了以下事件处理函数:
+
+```typescript
+// 处理照片变化
+const handlePhotoChange = (photoList: Array<{url: string, path: string}>) => {
+ console.log('照片列表变化:', photoList);
+};
+
+// 处理视频变化
+const handleVideoChange = (videoList: Array<{url: string, path: string}>) => {
+ console.log('视频列表变化:', videoList);
+};
+
+// 处理照片添加
+const handlePhotoAdd = (photo: {url: string, path: string}) => {
+ console.log('添加照片:', photo);
+};
+
+// 处理视频添加
+const handleVideoAdd = (video: {url: string, path: string}) => {
+ console.log('添加视频:', video);
+};
+
+// 处理上传完成事件
+const handleUploadComplete = (result: {photoUrls: string, videoUrls: string}) => {
+ console.log('媒体文件上传完成:', result);
+ console.log('照片地址:', result.photoUrls);
+ console.log('视频地址:', result.videoUrls);
+};
+```
+
+## 提交流程
+
+### 选课点名提交流程
+1. 用户点击提交按钮
+2. 验证数据完整性
+3. 如果有媒体文件,调用 `dmPsRef.value.uploadMedia()`
+4. 等待上传完成,获取服务器地址
+5. 将地址转换为逗号分隔字符串
+6. 设置到 `dmData.zp`(照片)和 `dmData.sp`(视频)
+7. 提交到后端 API
+
+### 就餐点名提交流程
+1. 用户点击提交按钮
+2. 验证班级和学生数据
+3. 如果有媒体文件,调用 `dmPsRef.value.uploadMedia()`
+4. 等待上传完成,获取服务器地址
+5. 将地址转换为逗号分隔字符串
+6. 设置到 `dmData.zp`(照片)和 `dmData.sp`(视频)
+7. 提交到后端 API
+
+## 错误处理
+
+### 上传失败处理
+```typescript
+try {
+ if (dmPsRef.value) {
+ const uploadResult = await dmPsRef.value.uploadMedia();
+ photoUrls = uploadResult.photoUrls;
+ videoUrls = uploadResult.videoUrls;
+ }
+} catch (uploadError) {
+ console.error('媒体文件上传失败:', uploadError);
+ uni.showToast({
+ title: '媒体文件上传失败,请重试',
+ icon: 'none'
+ });
+ // 停止提交流程
+ return;
+}
+```
+
+### TypeScript 类型安全
+- 使用 `ref(null)` 定义组件引用
+- 添加空值检查 `if (dmPsRef.value)`
+- 完整的类型定义和错误处理
+
+## 样式适配
+
+### 选课点名样式
+- 使用 `BasicLayout` 布局
+- 卡片式设计
+- 响应式网格布局
+
+### 就餐点名样式
+- 使用 `section` 分区块设计
+- 卡片式学生列表
+- 固定底部提交按钮
+
+## 数据存储
+
+### 后端字段映射
+- `zp`: 照片服务器地址(逗号分隔字符串)
+- `sp`: 视频服务器地址(逗号分隔字符串)
+
+### 数据重置
+提交成功后,两个页面都会重置媒体数据:
+```typescript
+mediaData.value = {
+ photoList: [],
+ videoList: []
+};
+```
+
+## 使用建议
+
+1. **权限管理**: 确保应用有相机和麦克风权限
+2. **网络状态**: 上传前检查网络连接状态
+3. **文件大小**: 注意控制照片和视频的文件大小
+4. **用户体验**: 上传过程中显示加载状态
+5. **错误恢复**: 上传失败时提供重试机制
+
+## 扩展功能
+
+### 可能的扩展
+1. **批量上传**: 支持同时上传多个文件
+2. **进度显示**: 显示上传进度百分比
+3. **压缩功能**: 自动压缩大文件
+4. **预览功能**: 上传前预览媒体文件
+5. **编辑功能**: 简单的图片编辑功能
+
+## 维护说明
+
+### 组件更新
+当 `DmPs` 组件更新时,需要同步更新两个页面的:
+1. 组件引用
+2. 事件处理函数
+3. 数据结构定义
+4. 错误处理逻辑
+
+### 测试要点
+1. 拍照功能测试
+2. 视频录制测试
+3. 文件上传测试
+4. 错误处理测试
+5. 数据提交测试
diff --git a/src/pages/components/dmPs/README.md b/src/pages/components/dmPs/README.md
new file mode 100644
index 0000000..afe98af
--- /dev/null
+++ b/src/pages/components/dmPs/README.md
@@ -0,0 +1,264 @@
+# DmPsComponent 拍照视频组件
+
+这是一个通用的拍照和视频录制组件,可以在就餐点名和选课点名等场景中复用。
+
+## 功能特性
+
+- 📸 **拍照功能**:支持相机拍照,可设置最大照片数量
+- 🎥 **视频录制**:支持视频录制,可设置最大视频数量和时长
+- 🖼️ **照片预览**:点击照片可全屏预览
+- 🗑️ **删除功能**:支持删除已拍摄的照片和视频
+- 📤 **文件上传**:支持将本地文件上传到服务器
+- 📱 **响应式设计**:适配不同屏幕尺寸
+- 🎨 **可定制样式**:支持自定义标题、按钮文字等
+
+## 使用方法
+
+### 基础用法
+
+```vue
+
+
+
+
+
+```
+
+### 完整用法(包含上传功能)
+
+```vue
+
+
+
+
+
+```
+
+## Props 属性
+
+| 属性名 | 类型 | 默认值 | 说明 |
+|--------|------|--------|------|
+| modelValue | Object | - | 双向绑定的媒体数据对象 |
+| photoTitle | String | '现场拍照' | 拍照区域标题 |
+| photoUploadText | String | '点击拍照' | 拍照按钮文字 |
+| maxPhotoCount | Number | 9 | 最大照片数量 |
+| videoTitle | String | '现场视频' | 视频区域标题 |
+| videoUploadText | String | '点击录制' | 录制按钮文字 |
+| maxVideoCount | Number | 3 | 最大视频数量 |
+| maxVideoDuration | Number | 60 | 最大视频时长(秒) |
+
+## Events 事件
+
+| 事件名 | 参数 | 说明 |
+|--------|------|------|
+| update:modelValue | value | 媒体数据变化时触发 |
+| photoChange | photoList | 照片列表变化时触发 |
+| videoChange | videoList | 视频列表变化时触发 |
+| photoAdd | photo | 添加照片时触发 |
+| photoDelete | index | 删除照片时触发 |
+| videoAdd | video | 添加视频时触发 |
+| videoDelete | index | 删除视频时触发 |
+| uploadComplete | result | 文件上传完成时触发 |
+
+## Methods 方法
+
+通过 ref 可以调用以下方法:
+
+| 方法名 | 参数 | 返回值 | 说明 |
+|--------|------|--------|------|
+| takePhoto | - | - | 触发拍照 |
+| recordVideo | - | - | 触发录制视频 |
+| deletePhoto | index | - | 删除指定索引的照片 |
+| deleteVideo | index | - | 删除指定索引的视频 |
+| uploadMedia | - | Promise<{photoUrls: string, videoUrls: string}> | 上传所有媒体文件到服务器 |
+| clearAll | - | - | 清空所有媒体数据 |
+
+## 数据结构
+
+### modelValue 对象结构
+
+```typescript
+interface MediaData {
+ photoList: Array<{
+ url: string; // 照片URL(本地临时路径)
+ path: string; // 照片路径(本地临时路径)
+ }>;
+ videoList: Array<{
+ url: string; // 视频URL(本地临时路径)
+ path: string; // 视频路径(本地临时路径)
+ }>;
+}
+```
+
+### uploadMedia 返回值结构
+
+```typescript
+interface UploadResult {
+ photoUrls: string; // 照片服务器地址,逗号分隔
+ videoUrls: string; // 视频服务器地址,逗号分隔
+}
+```
+
+## 文件上传流程
+
+1. **拍照/录制**:用户拍照或录制视频,文件存储在本地临时路径
+2. **预览/删除**:用户可以预览或删除已拍摄的媒体文件
+3. **提交时上传**:调用 `uploadMedia()` 方法,将所有文件上传到服务器
+4. **获取服务器地址**:上传成功后返回服务器文件路径
+5. **存储到数据库**:将服务器路径(逗号分隔)存储到数据库对应字段
+
+## 使用场景
+
+### 1. 选课点名拍照
+
+```vue
+
+```
+
+### 2. 就餐点名拍照
+
+```vue
+
+```
+
+### 3. 活动记录拍照
+
+```vue
+
+```
+
+## 注意事项
+
+1. **权限要求**:使用相机和麦克风需要相应的系统权限
+2. **文件大小**:注意控制照片和视频的文件大小,避免影响性能
+3. **存储空间**:大量媒体文件可能占用较多存储空间
+4. **网络上传**:提交时需要将本地文件上传到服务器
+5. **上传失败处理**:上传失败时会抛出异常,需要妥善处理
+6. **服务器地址格式**:上传成功后返回的是服务器相对路径,需要配合 `imagUrl()` 函数使用
+
+## 样式定制
+
+组件使用 SCSS 编写,可以通过以下方式定制样式:
+
+```scss
+// 自定义组件样式
+.dm-ps-component {
+ .section-card {
+ background: #f8f9fa;
+ border-radius: 12px;
+ }
+
+ .section-title {
+ color: #007aff;
+ font-weight: bold;
+ }
+
+ .photo-upload, .video-upload {
+ border-color: #007aff;
+ background: rgba(0, 122, 255, 0.1);
+ }
+}
+```
diff --git a/src/pages/components/dmPs/index.vue b/src/pages/components/dmPs/index.vue
new file mode 100644
index 0000000..1aeb0a6
--- /dev/null
+++ b/src/pages/components/dmPs/index.vue
@@ -0,0 +1,395 @@
+
+
+
+
+ {{ photoTitle || '现场拍照' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ photoUploadText || '点击拍照' }}
+
+
+
+
+
+
+ {{ videoTitle || '现场视频' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ videoUploadText || '点击录制' }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/system/login/login.vue b/src/pages/system/login/login.vue
index 2b7191e..b967659 100644
--- a/src/pages/system/login/login.vue
+++ b/src/pages/system/login/login.vue
@@ -156,7 +156,7 @@ const handleGetCode = async () => {
function toHome(data: any) {
if (data.type == 1) {
uni.reLaunch({
- url: "/pages/base/groupTeaching/zhujiao",
+ url: "/pages/view/routine/xk/zhujiao",
});
} else {
uni.switchTab({
diff --git a/src/pages/view/routine/jc/components/dm.vue b/src/pages/view/routine/jc/components/dm.vue
index 856f56f..56f1f61 100644
--- a/src/pages/view/routine/jc/components/dm.vue
+++ b/src/pages/view/routine/jc/components/dm.vue
@@ -193,6 +193,24 @@
+
+
+
+
+
(null) // 当前教师
const jsZtXzK = ref(false) // 教师状态选择可见
const jsMqXz = ref([0]) // 教师状态默认选择
+// 媒体数据
+const mediaData = ref({
+ photoList: [],
+ videoList: []
+});
+const dmPsRef = ref(null);
+
// 计算属性
const kTj = computed(() => {
return curBj.value && yjfXs.value.length > 0 // 改为检查已缴费学生数量
@@ -312,6 +338,33 @@ const changeNjBj = async (nj: any, bj: any) => {
await jzXsLb()
};
+// 处理照片变化
+const handlePhotoChange = (photoList: Array<{url: string, path: string}>) => {
+ console.log('照片列表变化:', photoList);
+};
+
+// 处理视频变化
+const handleVideoChange = (videoList: Array<{url: string, path: string}>) => {
+ console.log('视频列表变化:', videoList);
+};
+
+// 处理照片添加
+const handlePhotoAdd = (photo: {url: string, path: string}) => {
+ console.log('添加照片:', photo);
+};
+
+// 处理视频添加
+const handleVideoAdd = (video: {url: string, path: string}) => {
+ console.log('添加视频:', video);
+};
+
+// 处理上传完成事件
+const handleUploadComplete = (result: {photoUrls: string, videoUrls: string}) => {
+ console.log('媒体文件上传完成:', result);
+ console.log('照片地址:', result.photoUrls);
+ console.log('视频地址:', result.videoUrls);
+};
+
const jsXz = (teachers: any[]) => {
xzJs.value = teachers.map(teacher => ({
...teacher,
@@ -553,6 +606,28 @@ const tjDm = async () => {
jzZt.value = true
try {
+ // 先上传媒体文件
+ let photoUrls = '';
+ let videoUrls = '';
+
+ if (mediaData.value.photoList.length > 0 || mediaData.value.videoList.length > 0) {
+ try {
+ if (dmPsRef.value) {
+ const uploadResult = await dmPsRef.value.uploadMedia();
+ photoUrls = uploadResult.photoUrls;
+ videoUrls = uploadResult.videoUrls;
+ }
+ } catch (uploadError) {
+ console.error('媒体文件上传失败:', uploadError);
+ uni.showToast({
+ title: '媒体文件上传失败,请重试',
+ icon: 'none'
+ });
+ jzZt.value = false;
+ return;
+ }
+ }
+
// 准备点名数据
const dmData: any = {
bjId: curBj.value.key,
@@ -561,6 +636,9 @@ const tjDm = async () => {
njmc: curNj.value.title,
dmJsId: getJs.id || '', // 点名教师ID
dmTime: new Date(),
+ // 媒体文件地址
+ zp: photoUrls, // 照片字段,逗号分隔的字符串
+ sp: videoUrls, // 视频字段,逗号分隔的字符串
xsList: yjfXs.value.map(student => ({
xsId: student.id,
xsXm: student.xm, // 传入学生姓名
@@ -592,6 +670,10 @@ const tjDm = async () => {
curBj.value = null
xsLb.value = []
xzJs.value = []
+ mediaData.value = {
+ photoList: [],
+ videoList: []
+ };
// 返回上一页
uni.navigateBack()
diff --git a/src/pages/base/groupTeaching/dmXkkcDetail.vue b/src/pages/view/routine/xk/dm.vue
similarity index 61%
rename from src/pages/base/groupTeaching/dmXkkcDetail.vue
rename to src/pages/view/routine/xk/dm.vue
index ee7ebf1..789fd96 100644
--- a/src/pages/base/groupTeaching/dmXkkcDetail.vue
+++ b/src/pages/view/routine/xk/dm.vue
@@ -13,19 +13,19 @@
- {{ numInfo.zg }}
+ {{ dmInfo.zrs }}
总人数
- {{ numInfo.sd }}
+ {{ dmInfo.sdRs }}
实到
- {{ numInfo.qj }}
+ {{ dmInfo.qjRs }}
请假
- {{ numInfo.qq }}
+ {{ dmInfo.qqRs }}
缺勤
@@ -43,19 +43,19 @@
- {{ xs.xsxm }}
+ {{ xs.xsXm || xs.xsxm }}
- {{ xs.xszt }}
+ {{ getStatusText(xs.xsZt || xs.xszt) }}
@@ -75,6 +75,16 @@
+
+
import {onMounted, ref, computed} from "vue";
import BasicLayout from "@/components/BasicLayout/Layout.vue";
+import DmPsComponent from "@/pages/components/dmPs/index.vue";
import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
-import { jsdXkXsListApi, jsdXkdmListApi } from "@/api/base/server";
+import { getWaitDmXsListApi, submitXkDmApi, findXkkcListApi } from "@/api/base/xkApi";
import { useDicStore } from "@/store/modules/dic";
import { BASE_IMAGE_URL } from "@/config";
const { findByPid } = useDicStore();
@@ -125,6 +136,12 @@ const { getData, setData } = useDataStore();
const js = computed(() => getJs)
const xkkc = computed(() => getData)
+const mediaData = ref({
+ photoList: [],
+ videoList: []
+});
+const dmPsRef = ref(null);
+
const now = dayjs();
let wDay = now.day();
if (wDay === 0) {
@@ -135,18 +152,15 @@ const todayInfo = ref({
date: now.format("YYYY-MM-DD"),
weekName: wdNameList[wDay - 1]
})
-// 人数信息
-const numInfo = ref({
- yd: 18,
- sd: 18,
- qj: 0,
- qq: 0,
- zg: 18
-});
-// 学生列表
-const xsList = ref([
-]);
+const dmInfo = ref({
+ zrs: 0,
+ sdRs: 0,
+ qjRs: 0,
+ qqRs: 0
+});
+// 学生列表 - 匹配后端XkDmXs实体字段
+const xsList = ref([]);
// 状态选择相关
const statusPickerVisible = ref(false);
@@ -167,10 +181,13 @@ const getImageUrl = (imagePath: string) => {
// 获取状态对应的样式类
const getStatusClass = (status: string) => {
switch (status) {
+ case "A":
case "正常":
return "status-normal";
+ case "B":
case "请假":
return "status-leave";
+ case "C":
case "缺勤":
return "status-absent";
default:
@@ -178,6 +195,20 @@ const getStatusClass = (status: string) => {
}
};
+// 获取状态文本
+const getStatusText = (status: string) => {
+ switch (status) {
+ case "A":
+ return "正常";
+ case "B":
+ return "请假";
+ case "C":
+ return "缺勤";
+ default:
+ return status || "正常";
+ }
+};
+
// 获取学生状态选项
const loadStatusOptions = async () => {
try {
@@ -193,60 +224,93 @@ const loadStatusOptions = async () => {
}
} catch (error) {
console.error("获取状态选项失败", error);
- // 使用默认状态
+ // 使用默认状态 - 匹配后端字段
statusOptions.value = [
- {text: "正常", value: "正常"},
- {text: "请假", value: "请假"},
- {text: "缺勤", value: "缺勤"}
+ {text: "正常", value: "A"},
+ {text: "请假", value: "B"},
+ {text: "缺勤", value: "C"}
];
}
};
+// 加载学生列表 - 匹配后端数据结构
const loadXsList = async () => {
- const res = await jsdXkXsListApi({
- xkkcId: xkkc.value.id
- });
- if (res && res.resultCode === 1) {
- xsList.value = res.result || [];
- rebuildNumInfo();
+ // 如果新接口失败,尝试使用旧接口
+ try {
+ const res = await getWaitDmXsListApi({
+ xkkcId: xkkc.value.id
+ });
+ if (res && res.resultCode === 1) {
+ dmInfo.value = res.result || {};
+ xsList.value = (dmInfo.value.xsList || []).map((dmXs: any) => ({
+ id: dmXs.id,
+ xsId: dmXs.xsId || dmXs.id,
+ xsXm: dmXs.xsXm || dmXs.xsxm || dmXs.xm,
+ xsZt: dmXs.xsZt || dmXs.xszt || "A",
+ qdId: dmXs.qdId,
+ tx: dmXs.tx || dmXs.xstx || dmXs.avatar,
+ bjmc: dmXs.bjmc,
+ njmc: dmXs.njmc,
+ jzxm: dmXs.jzxm,
+ jzdh: dmXs.jzdh,
+ xsxm: dmXs.xsxm || dmXs.xm,
+ xstx: dmXs.xstx || dmXs.avatar,
+ xszt: dmXs.xszt || "正常"
+ }));
+ rebuildNumInfo();
+ }
+ } catch (fallbackError) {
+ console.error("备用接口也失败:", fallbackError);
+ uni.showToast({
+ title: "加载学生列表失败",
+ icon: "none"
+ });
}
};
+// 重新计算人数统计 - 匹配后端字段
const rebuildNumInfo = () => {
- let sd = 0;
- let qj = 0;
- let qq = 0;
- // 循环统计状态对应的人数
- for (let i = 0; i < xsList.value.length; i++) {
- const xs = xsList.value[i];
- switch (xs.xszt) {
- case "正常":
- sd++;
- break;
- case "请假":
- qj++;
- break;
- case "缺勤":
- qq++;
- break;
- default:
- break;
- }
- }
- numInfo.value = {
- zg: xsList.value.length,
- yd: xsList.value.length - qj,
- sd: sd,
- qj: qj,
- qq: qq
- };
-}
+ let sd = 0;
+ let qj = 0;
+ let qq = 0;
+
+ // 循环统计状态对应的人数
+ for (let i = 0; i < xsList.value.length; i++) {
+ const xs = xsList.value[i];
+ const status = xs.xsZt || xs.xszt;
+
+ switch (status) {
+ case "A":
+ case "正常":
+ sd++;
+ break;
+ case "B":
+ case "请假":
+ qj++;
+ break;
+ case "C":
+ case "缺勤":
+ qq++;
+ break;
+ default:
+ sd++; // 默认算作正常
+ break;
+ }
+ }
+ dmInfo.value.sdRs = sd; // 实到人数
+ dmInfo.value.qjRs = qj; // 请假人数
+ dmInfo.value.qqRs = qq; // 缺勤人数
+};
// 打开状态选择器
const openStatusPicker = (xs: any) => {
curXs.value = xs;
+ const currentStatus = xs.xsZt || xs.xszt;
+
+ // 找到当前状态在选项中的索引
for (let i = 0; i < statusOptions.value.length; i++) {
- if (statusOptions.value[i].text === xs.xszt) {
+ if (statusOptions.value[i].value === currentStatus ||
+ statusOptions.value[i].text === currentStatus) {
defSel.value = [i];
break;
}
@@ -262,42 +326,32 @@ const confirmStatus = (e: any) => {
);
if (selectedStatus) {
- // 更新当前学生状态
- curXs.value.xszt = selectedStatus.text;
+ // 更新当前学生状态 - 使用后端字段
+ curXs.value.xsZt = selectedStatus.value; // 使用代码值
+ curXs.value.xszt = selectedStatus.text; // 保留文本值以兼容
}
- rebuildNumInfo();
+ rebuildNumInfo();
}
statusPickerVisible.value = false;
};
-// 导航相关方法
-const navigateBack = () => {
- uni.navigateBack();
-};
-
-const toRollCallRecord = () => {
- uni.navigateTo({
- url: "/pages/base/groupTeaching/rollCallRecord",
- });
-};
-
// 联系家长
-const contactParent = (student: any) => {
+const contactParent = (dmXs: any) => {
// 构建完整的学生信息,确保包含所有必要字段
const completeStudent = {
- ...student,
+ ...dmXs,
// 确保字段名的一致性
- id: student.xsId || student.id,
- xsxm: student.xsxm || student.xm,
- xstx: student.xstx || student.avatar,
- xb: student.xb || student.gender,
- sfzh: student.sfzh,
- cstime: student.cstime,
- njmc: student.njmcName || student.njmc,
- bjmc: student.bjmc,
+ id: dmXs.xsId || dmXs.id,
+ xsxm: dmXs.xsXm || dmXs.xsxm || dmXs.xm,
+ xstx: dmXs.tx || dmXs.xstx || dmXs.avatar,
+ xb: dmXs.xb || dmXs.gender,
+ sfzh: dmXs.sfzh,
+ cstime: dmXs.cstime,
+ njmc: dmXs.njmcName || dmXs.njmc,
+ bjmc: dmXs.bjmc,
// 如果后端返回的是njId和bjId,也保留
- njId: student.njId,
- bjId: student.bjId
+ njId: dmXs.njId,
+ bjId: dmXs.bjId
};
// 设置完整的学生信息到store中,供详情页面使用
@@ -309,19 +363,97 @@ const contactParent = (student: any) => {
});
};
-// 提交数据
+// 数据验证
+const validateData = () => {
+ if (!xkkc.value || !xkkc.value.id) {
+ uni.showToast({
+ title: "课程信息不完整",
+ icon: "none"
+ });
+ return false;
+ }
+
+ if (!js.value || !js.value.id) {
+ uni.showToast({
+ title: "教师信息不完整",
+ icon: "none"
+ });
+ return false;
+ }
+
+ if (xsList.value.length === 0) {
+ uni.showToast({
+ title: "没有学生数据",
+ icon: "none"
+ });
+ return false;
+ }
+
+ return true;
+};
+
+// 提交数据 - 匹配后端XkDm和XkDmXs实体
const submit = async () => {
if (isSubmitting.value) {
return;
}
+
+ // 数据验证
+ if (!validateData()) {
+ return;
+ }
+
isSubmitting.value = true;
+
try {
- const res = await jsdXkdmListApi({
- jsId: js.value.id,
- xkkcId: xkkc.value.id,
- dmtime: now,
- xkdmList: xsList.value
- });
+ // 先上传媒体文件
+ let photoUrls = '';
+ let videoUrls = '';
+
+ if (mediaData.value.photoList.length > 0 || mediaData.value.videoList.length > 0) {
+ try {
+ if (dmPsRef.value) {
+ const uploadResult = await dmPsRef.value.uploadMedia();
+ photoUrls = uploadResult.photoUrls;
+ videoUrls = uploadResult.videoUrls;
+ }
+ } catch (uploadError) {
+ console.error('媒体文件上传失败:', uploadError);
+ uni.showToast({
+ title: '媒体文件上传失败,请重试',
+ icon: 'none'
+ });
+ isSubmitting.value = false;
+ return;
+ }
+ }
+
+ // 构建点名数据 - 匹配XkDm实体
+ const dmData = {
+ ...dmInfo.value,
+ // 媒体文件地址
+ zp: photoUrls, // 照片字段,逗号分隔的字符串
+ sp: videoUrls, // 视频字段,逗号分隔的字符串
+ // 学生列表 - 匹配XkDmXs实体
+ xkDmXsList: xsList.value.map((xs: any) => ({
+ xsId: xs.xsId || xs.id, // 学生ID
+ xsZt: xs.xsZt || xs.xszt, // 学生状态
+ qdId: xs.qdId, // 关联选课清单ID
+ xsXm: xs.xsXm || xs.xsxm, // 学生姓名
+ tx: xs.tx || xs.xstx, // 头像
+ status: "A" // 状态
+ }))
+ };
+ dmData.jsId = js.value.id; // 教师ID
+ dmData.xkId = xkkc.value.xkId; // 选课ID
+ dmData.xkkcId = xkkc.value.id; // 选课课程ID
+ dmData.xkMc = xkkc.value.xkMc || xkkc.value.xkmc; // 选课名称
+ dmData.xkkcMc = xkkc.value.kcmc; // 选课课程名称
+ dmData.status = "A"; // 状态
+
+ console.log("提交的点名数据:", dmData);
+
+ const res:any = await submitXkDmApi(dmData);
if (res && res.resultCode === 1) {
uni.showToast({
@@ -338,7 +470,7 @@ const submit = async () => {
}, 1500);
} else {
uni.showToast({
- title: res?.resultMessage || "提交失败",
+ title: res.resultMessage || "提交失败",
icon: "none",
});
}
@@ -355,8 +487,10 @@ const submit = async () => {
// 页面加载时获取状态选项
onMounted(async () => {
+ console.log("页面加载,课程信息:", xkkc.value);
await loadXsList();
await loadStatusOptions();
+ console.log("学生列表加载完成,共", xsList.value.length, "人");
});
diff --git a/src/pages/base/groupTeaching/dmXkList.vue b/src/pages/view/routine/xk/dmIndex.vue
similarity index 89%
rename from src/pages/base/groupTeaching/dmXkList.vue
rename to src/pages/view/routine/xk/dmIndex.vue
index faa7fee..6b8c084 100644
--- a/src/pages/base/groupTeaching/dmXkList.vue
+++ b/src/pages/view/routine/xk/dmIndex.vue
@@ -40,7 +40,6 @@
点名
点名记录
-
@@ -65,7 +64,7 @@ import {
} from "vue";
import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
-import { getCurrentSemesterTeacherCoursesApi } from "@/api/base/server";
+import { xkkcListByJsIdApi } from "@/api/base/xkApi";
import { dmBeforeMinuteApi } from "@/api/system/config/index";
import dayjs from "dayjs";
@@ -100,15 +99,15 @@ onMounted(async () => {
// 加载课程列表
const loadCourseList = async () => {
try {
- const res = await getCurrentSemesterTeacherCoursesApi(getJs.id);
- if (res.resultCode == 1) {
- if (res.result && res.result.length) {
- xkkcList.value = res.result;
- // 处理课程周期显示
- processCoursePeriods();
- } else {
- xkkcList.value = [];
- }
+ const res:any = await xkkcListByJsIdApi({ jsId: getJs.id });
+ if (res.resultCode == 1) {
+ if (res.result && res.result.length) {
+ xkkcList.value = res.result;
+ // 处理课程周期显示
+ processCoursePeriods();
+ } else {
+ xkkcList.value = [];
+ }
} else {
xkkcList.value = [];
uni.showToast({
@@ -244,7 +243,7 @@ const goDm = (xkkc: any) => {
if (dmFlag) {
setData(xkkc);
uni.navigateTo({
- url: `/pages/base/groupTeaching/dmXkkcDetail`,
+ url: `/pages/view/routine/xk/dm`,
});
} else {
if (msg === "") {
@@ -299,7 +298,7 @@ const goRecord = (xkkc: any) => {
if (recordFlag) {
setData(xkkc);
uni.navigateTo({
- url: `/pages/base/groupTeaching/dmXkkcRecord`,
+ url: `/pages/view/routine/xk/dmXkkcRecord`,
});
} else {
if (msg === "") {
@@ -313,61 +312,6 @@ const goRecord = (xkkc: any) => {
}
};
-// 跳转到课堂随拍
-const goPhoto = (xkkc: any) => {
- const now = dayjs();
- let wDay = now.day();
- if (wDay === 0) {
- wDay = 7;
- }
- let mDay = now.date();
- const strDate = now.format('YYYY-MM-DD') + ' ';
- let photoFlag = false;
- let msg = "";
- // 判断周期
- switch (xkkc.skzqlx) {
- case '每天':
- photoFlag = true;
- break;
- case '每周':
- const daysOfWeek = xkkc.skzq.split(',').map(Number);
- photoFlag = daysOfWeek.includes(wDay);
- // 从wdNameList读取daysOfWeek对应的周几
- xkkc.skzqmc = daysOfWeek.map((day: number) => wdNameList[day - 1]).join(',');
- break;
- case '每月':
- const daysOfMonth = xkkc.skzq.split(',').map(Number);
- photoFlag = daysOfMonth.includes(mDay);
- // 从根据编号加
- xkkc.skzqmc = daysOfMonth.map((day: number) => day + "号").join(',');
- break;
- }
- // 判断日期是否合格
- if (photoFlag) {
- // xkkc.skkstime开始时间向前dmBeforeMinute分钟
- const startTime = dayjs(strDate + xkkc.skkstime).subtract(dmBeforeMinute.value, 'minute').format('YYYY-MM-DD HH:mm:ss');
- const endTime = dayjs(strDate + xkkc.skjstime, 'YYYY-MM-DD HH:mm:ss');
- photoFlag = now.isBefore(endTime) && now.isAfter(startTime)
- } else {
- msg = "上课时间未到,无法随拍";
- }
- if (photoFlag) {
- setData(xkkc);
- uni.navigateTo({
- url: `/pages/base/groupTeaching/photoXkkcDetail`,
- });
- } else {
- if (msg === "") {
- msg = "上课时间未到,无法随拍";
- }
- uni.showToast({
- title: msg,
- icon: 'none',
- duration: 2000
- });
- }
-};
-
// 页面卸载前清除定时器
onBeforeUnmount(() => {
});
diff --git a/src/pages/view/routine/xk/dmList.vue b/src/pages/view/routine/xk/dmList.vue
new file mode 100644
index 0000000..ae91cb7
--- /dev/null
+++ b/src/pages/view/routine/xk/dmList.vue
@@ -0,0 +1,384 @@
+
+
+
+
+
+
+
+
+ {{ xkkc.xkkcMc || xkkc.kcmc }}
+ {{ todayInfo.date }} ({{ todayInfo.weekName }})
+
+
+
+
+
+ 点名记录
+
+
+
+
+
+
+ {{ record.zrs || 0 }}
+ 总人数
+
+
+ {{ record.sdRs || 0 }}
+ 实到
+
+
+ {{ record.qjRs || 0 }}
+ 请假
+
+
+ {{ record.qqRs || 0 }}
+ 缺勤
+
+
+
+
+
+
+
+
+
+
+ 暂无点名记录
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/base/groupTeaching/dmXkkcRecord.vue b/src/pages/view/routine/xk/dmXsList.vue
similarity index 50%
rename from src/pages/base/groupTeaching/dmXkkcRecord.vue
rename to src/pages/view/routine/xk/dmXsList.vue
index 25b710d..c9bba10 100644
--- a/src/pages/base/groupTeaching/dmXkkcRecord.vue
+++ b/src/pages/view/routine/xk/dmXsList.vue
@@ -1,73 +1,93 @@
-
-
-
-
-
+
+
+
-
-
-
-
-
-
- {{ totalStudents }}
- 总人数
-
-
- {{ presentStudents }}
- 实到
-
-
- {{ leaveStudents }}
- 请假
-
-
- {{ absentStudents }}
- 缺勤
+
+
+ {{ formatDateTime(dmRecord.dmTime) }}
-
-
-
-
+
+
+
+
+
+ {{ (student.xsXm || student.xsxm)?.charAt(0) || '学' }}
+
+
+ {{ student.xsXm || student.xsxm }}
+ {{ student.njmc }} {{ student.bjmc }}
+
+
+
+ {{ getStatusText(student.xsZt || student.xszt) }}
+
+
+
+
+
+
+
+
+ 暂无学生数据
+
+
@@ -81,7 +101,7 @@ import { ref, computed, onMounted } from "vue";
import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
import BasicLayout from "@/components/BasicLayout/Layout.vue";
-import { jsdXkXsListApi } from "@/api/base/server";
+import { getXkDmXsPageApi } from "@/api/base/xkApi";
import { BASE_IMAGE_URL } from "@/config";
import dayjs from "dayjs";
@@ -90,33 +110,35 @@ const { getData } = useDataStore();
const js = computed(() => getJs);
const xkkc = computed(() => getData);
-
-// 今日信息
-const now = dayjs();
-let wDay = now.day();
-if (wDay === 0) {
- wDay = 7;
-}
-const wdNameList = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
-const todayInfo = ref({
- date: now.format("YYYY-MM-DD"),
- weekName: wdNameList[wDay - 1],
-});
+const dmRecord = computed(() => getData?.dmRecord || {});
// 学生列表数据
const studentList = ref([]);
-const currentStatType = ref('');
-const currentStatTitle = ref('');
-const currentStudentList = ref([]);
+const currentFilter = ref('all');
-// 统计数量
-const totalStudents = computed(() => studentList.value.length);
-const presentStudents = computed(() => studentList.value.filter(s => s.xszt === '正常').length);
-const leaveStudents = computed(() => studentList.value.filter(s => s.xszt === '请假').length);
-const absentStudents = computed(() => studentList.value.filter(s => s.xszt === '缺勤').length);
+// 筛选选项
+const filterTabs = ref([
+ { label: '全部', value: 'all' },
+ { label: '正常', value: 'A' },
+ { label: '请假', value: 'B' },
+ { label: '缺勤', value: 'C' }
+]);
-// 弹窗引用
-const studentPopup = ref(null);
+// 筛选后的学生列表
+const filteredStudents = computed(() => {
+ if (currentFilter.value === 'all') {
+ return studentList.value;
+ }
+ return studentList.value.filter(student =>
+ (student.xsZt || student.xszt) === currentFilter.value
+ );
+});
+
+// 格式化日期时间
+const formatDateTime = (dateTime: string | Date) => {
+ if (!dateTime) return '';
+ return dayjs(dateTime).format('MM-DD HH:mm');
+};
// 获取图片完整URL
const getImageUrl = (path: string) => {
@@ -128,8 +150,11 @@ const getImageUrl = (path: string) => {
// 获取状态样式类
const getStatusClass = (status: string) => {
switch (status) {
+ case 'A':
case '正常': return 'status-normal';
+ case 'B':
case '请假': return 'status-leave';
+ case 'C':
case '缺勤': return 'status-absent';
default: return 'status-normal';
}
@@ -138,42 +163,16 @@ const getStatusClass = (status: string) => {
// 获取状态文本
const getStatusText = (status: string) => {
switch (status) {
- case '正常': return '正常';
- case '请假': return '请假';
- case '缺勤': return '缺勤';
- default: return '正常';
+ case 'A': return '正常';
+ case 'B': return '请假';
+ case 'C': return '缺勤';
+ default: return status || '正常';
}
};
-// 显示学生列表
-const showStudentList = (type: string) => {
- currentStatType.value = type;
-
- switch (type) {
- case 'total':
- currentStatTitle.value = '总人数学生列表';
- currentStudentList.value = studentList.value;
- break;
- case 'present':
- currentStatTitle.value = '实到学生列表';
- currentStudentList.value = studentList.value.filter(s => s.xszt === '正常');
- break;
- case 'leave':
- currentStatTitle.value = '请假学生列表';
- currentStudentList.value = studentList.value.filter(s => s.xszt === '请假');
- break;
- case 'absent':
- currentStatTitle.value = '缺勤学生列表';
- currentStudentList.value = studentList.value.filter(s => s.xszt === '缺勤');
- break;
- }
-
- studentPopup.value.open();
-};
-
-// 关闭学生列表
-const closeStudentList = () => {
- studentPopup.value.close();
+// 设置筛选条件
+const setFilter = (filter: string) => {
+ currentFilter.value = filter;
};
// 返回上一页
@@ -186,17 +185,20 @@ const loadStudentList = async () => {
try {
uni.showLoading({ title: '加载中...' });
- const res = await jsdXkXsListApi({
- xkkcId: xkkc.value.id,
- date: todayInfo.value.date
+ const res = await getXkDmXsPageApi({
+ dmId: dmRecord.value.id,
+ pageNum: 1,
+ pageSize: 100,
+ sidx: 'xsXm',
+ sord: 'asc'
});
if (res && res.resultCode === 1) {
- studentList.value = res.result || [];
+ studentList.value = res.result?.rows || [];
} else {
studentList.value = [];
uni.showToast({
- title: res?.message || '获取学生列表失败',
+ title: (res as any)?.resultMessage || '获取学生列表失败',
icon: 'none'
});
}
@@ -218,7 +220,7 @@ onMounted(() => {