feat(study): 添加学习记录功能并更新播放器组件
实现学习记录功能,包括添加记录API接口和前端交互逻辑 为VideoPlayer和AudioPlayer组件添加获取ID的方法 更新LearningRecordsPage使用真实API获取数据 在StudyDetailPage中实现播放时持续记录功能
Showing
6 changed files
with
137 additions
and
55 deletions
src/api/record.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2025-06-11 13:24:46 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-06-11 13:49:42 | ||
| 5 | + * @FilePath: /mlaj/src/api/record.js | ||
| 6 | + * @Description: 学习记录相关接口 | ||
| 7 | + */ | ||
| 8 | +import { fn, fetch } from './fn' | ||
| 9 | + | ||
| 10 | +const Api = { | ||
| 11 | + STUDY_RECORD_LIST: '/srv/?a=study_record&t=list', | ||
| 12 | + STUDY_RECORD_ADD: '/srv/?a=study_record&t=add', | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * @description: 获取学习记录列表 | ||
| 17 | + * @param: page 页码 | ||
| 18 | + * @param: limit 每页数量 | ||
| 19 | + * @param: keyword 搜索 | ||
| 20 | + * @return: data: { id: 课程id, title: 课程名称, subtitle: 课程副标题, cover: 封面图, study_duration: 学习时长, recent_study_time: 最近学习时间, study_progress: 学习进度(小数) } | ||
| 21 | + */ | ||
| 22 | +export const getStudyRecordListAPI = (params) => fn(fetch.get(Api.STUDY_RECORD_LIST, params)) | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * @description: 添加记录 | ||
| 26 | + * @param: schedule_id 课程章节ID | ||
| 27 | + * @param: meta_id 课程章节的视频、音频的ID | ||
| 28 | + * @param: media_duration 视频、音频的时长 | ||
| 29 | + * @param: playback_position 视频、音频当前播放位置 | ||
| 30 | + * @param: playback_id 某一轮播放的ID,需要区分不同轮次播放的开始和结束,最终用来统计播放时长 | ||
| 31 | + * @return: data: { } | ||
| 32 | + */ | ||
| 33 | +export const addStudyRecordAPI = (params) => fn(fetch.post(Api.STUDY_RECORD_ADD, params)) |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-04-07 12:35:35 | 2 | * @Date: 2025-04-07 12:35:35 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-05-30 18:03:29 | 4 | + * @LastEditTime: 2025-06-11 11:58:22 |
| 5 | * @FilePath: /mlaj/src/components/ui/AudioPlayer.vue | 5 | * @FilePath: /mlaj/src/components/ui/AudioPlayer.vue |
| 6 | * @Description: 音频播放器组件,支持播放控制、进度条调节、音量控制、播放列表等功能 | 6 | * @Description: 音频播放器组件,支持播放控制、进度条调节、音量控制、播放列表等功能 |
| 7 | --> | 7 | --> |
| ... | @@ -480,6 +480,10 @@ defineExpose({ | ... | @@ -480,6 +480,10 @@ defineExpose({ |
| 480 | }, | 480 | }, |
| 481 | isPlaying: () => isPlaying.value, | 481 | isPlaying: () => isPlaying.value, |
| 482 | id: props.id, | 482 | id: props.id, |
| 483 | + getPlayer: () => audio.value, | ||
| 484 | + getId() { | ||
| 485 | + return currentSong.value.meta_id || 'meta_id' | ||
| 486 | + } | ||
| 483 | }) | 487 | }) |
| 484 | </script> | 488 | </script> |
| 485 | 489 | ... | ... |
| ... | @@ -30,6 +30,10 @@ const props = defineProps({ | ... | @@ -30,6 +30,10 @@ const props = defineProps({ |
| 30 | type: String, | 30 | type: String, |
| 31 | required: true, | 31 | required: true, |
| 32 | }, | 32 | }, |
| 33 | + videoId: { | ||
| 34 | + type: String, | ||
| 35 | + required: true, | ||
| 36 | + }, | ||
| 33 | autoplay: { | 37 | autoplay: { |
| 34 | type: Boolean, | 38 | type: Boolean, |
| 35 | required: false, | 39 | required: false, |
| ... | @@ -140,7 +144,10 @@ defineExpose({ | ... | @@ -140,7 +144,10 @@ defineExpose({ |
| 140 | }, | 144 | }, |
| 141 | getPlayer() { | 145 | getPlayer() { |
| 142 | return player.value; | 146 | return player.value; |
| 143 | - } | 147 | + }, |
| 148 | + getId() { | ||
| 149 | + return props.videoId || "meta_id"; | ||
| 150 | + }, | ||
| 144 | }); | 151 | }); |
| 145 | </script> | 152 | </script> |
| 146 | 153 | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-05-29 15:34:17 | 2 | * @Date: 2025-05-29 15:34:17 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-06-10 16:13:24 | 4 | + * @LastEditTime: 2025-06-11 13:38:38 |
| 5 | * @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue | 5 | * @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | --> | 7 | --> | ... | ... |
| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | <van-list | 4 | <van-list |
| 5 | v-model:loading="loading" | 5 | v-model:loading="loading" |
| 6 | :finished="finished" | 6 | :finished="finished" |
| 7 | - finished-text="没有更多了" | 7 | + :finished-text="finishText" |
| 8 | @load="onLoad" | 8 | @load="onLoad" |
| 9 | class="px-4 py-3 space-y-4" | 9 | class="px-4 py-3 space-y-4" |
| 10 | > | 10 | > |
| ... | @@ -18,8 +18,8 @@ | ... | @@ -18,8 +18,8 @@ |
| 18 | class="w-20 h-20 rounded-lg overflow-hidden flex-shrink-0 mr-3" | 18 | class="w-20 h-20 rounded-lg overflow-hidden flex-shrink-0 mr-3" |
| 19 | > | 19 | > |
| 20 | <van-image | 20 | <van-image |
| 21 | - :src="record.course.coverImage" | 21 | + :src="record.cover" |
| 22 | - :alt="record.course.title" | 22 | + :alt="record.title" |
| 23 | class="w-full h-full" | 23 | class="w-full h-full" |
| 24 | fit="cover" | 24 | fit="cover" |
| 25 | error-icon="photo-fail" | 25 | error-icon="photo-fail" |
| ... | @@ -30,7 +30,7 @@ | ... | @@ -30,7 +30,7 @@ |
| 30 | </div> | 30 | </div> |
| 31 | <div class="flex-1"> | 31 | <div class="flex-1"> |
| 32 | <h3 class="text-base font-medium mb-2 line-clamp-1"> | 32 | <h3 class="text-base font-medium mb-2 line-clamp-1"> |
| 33 | - {{ record.course.title }} | 33 | + {{ record.title }} |
| 34 | </h3> | 34 | </h3> |
| 35 | <div class="flex items-center text-sm text-gray-500 mb-2"> | 35 | <div class="flex items-center text-sm text-gray-500 mb-2"> |
| 36 | <svg | 36 | <svg |
| ... | @@ -47,7 +47,7 @@ | ... | @@ -47,7 +47,7 @@ |
| 47 | d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" | 47 | d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" |
| 48 | /> | 48 | /> |
| 49 | </svg> | 49 | </svg> |
| 50 | - <span>学习时长:{{ formatDuration(record.duration) }}</span> | 50 | + <span>学习时长:{{ formatDuration(record.study_duration) }}</span> |
| 51 | </div> | 51 | </div> |
| 52 | <div class="flex items-center text-sm text-gray-500 mb-3"> | 52 | <div class="flex items-center text-sm text-gray-500 mb-3"> |
| 53 | <svg | 53 | <svg |
| ... | @@ -64,12 +64,12 @@ | ... | @@ -64,12 +64,12 @@ |
| 64 | d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" | 64 | d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" |
| 65 | /> | 65 | /> |
| 66 | </svg> | 66 | </svg> |
| 67 | - <span>最近学习:{{ formatDate(record.lastStudyTime) }}</span> | 67 | + <span>最近学习:{{ formatDate(record.recent_study_time) }}</span> |
| 68 | </div> | 68 | </div> |
| 69 | <div class="flex items-center"> | 69 | <div class="flex items-center"> |
| 70 | <div class="flex-1"> | 70 | <div class="flex-1"> |
| 71 | <van-progress | 71 | <van-progress |
| 72 | - :percentage="record.progress" | 72 | + :percentage="record.study_progress" |
| 73 | :stroke-width="4" | 73 | :stroke-width="4" |
| 74 | color="#10B981" | 74 | color="#10B981" |
| 75 | /> | 75 | /> |
| ... | @@ -112,6 +112,9 @@ import { useTitle } from '@vueuse/core'; | ... | @@ -112,6 +112,9 @@ import { useTitle } from '@vueuse/core'; |
| 112 | import FrostedGlass from '@/components/ui/FrostedGlass.vue'; | 112 | import FrostedGlass from '@/components/ui/FrostedGlass.vue'; |
| 113 | import { formatDate, formatDuration } from '@/utils/tools'; | 113 | import { formatDate, formatDuration } from '@/utils/tools'; |
| 114 | 114 | ||
| 115 | +// 导入接口 | ||
| 116 | +import { getStudyRecordListAPI } from "@/api/record"; | ||
| 117 | + | ||
| 115 | const $route = useRoute(); | 118 | const $route = useRoute(); |
| 116 | useTitle($route.meta.title); | 119 | useTitle($route.meta.title); |
| 117 | 120 | ||
| ... | @@ -119,50 +122,28 @@ const records = ref([]); | ... | @@ -119,50 +122,28 @@ const records = ref([]); |
| 119 | const loading = ref(false); | 122 | const loading = ref(false); |
| 120 | const finished = ref(false); | 123 | const finished = ref(false); |
| 121 | const page = ref(1); | 124 | const page = ref(1); |
| 122 | -const pageSize = 10; | 125 | +const limit = ref(10); |
| 123 | - | 126 | +const finishText = ref('没有更多了'); |
| 124 | -// 模拟学习记录数据 | ||
| 125 | -const mockRecords = [ | ||
| 126 | - { | ||
| 127 | - id: 1, | ||
| 128 | - course: { | ||
| 129 | - title: '亲子教育必修课:如何培养孩子的学习兴趣', | ||
| 130 | - coverImage: 'https://cdn.ipadbiz.cn/mlaj/images/jbwr0qZvpD4.jpg' | ||
| 131 | - }, | ||
| 132 | - duration: 3600, // 秒 | ||
| 133 | - lastStudyTime: '2024-01-15T10:30:00', | ||
| 134 | - progress: 75 | ||
| 135 | - }, | ||
| 136 | - { | ||
| 137 | - id: 2, | ||
| 138 | - course: { | ||
| 139 | - title: '儿童心理发展指南:0-6岁关键期教育方法', | ||
| 140 | - coverImage: 'https://cdn.ipadbiz.cn/mlaj/images/27kCu7bXGEI.jpg' | ||
| 141 | - }, | ||
| 142 | - duration: 7200, | ||
| 143 | - lastStudyTime: '2024-01-14T15:20:00', | ||
| 144 | - progress: 45 | ||
| 145 | - } | ||
| 146 | -]; | ||
| 147 | 127 | ||
| 148 | // 加载数据 | 128 | // 加载数据 |
| 149 | -const onLoad = () => { | 129 | +const onLoad = async () => { |
| 150 | loading.value = true; | 130 | loading.value = true; |
| 151 | // 模拟异步加载 | 131 | // 模拟异步加载 |
| 152 | - setTimeout(() => { | 132 | + const nextPage = page.value; |
| 153 | - const start = (page.value - 1) * pageSize; | 133 | + const { code, data } = await getStudyRecordListAPI({ |
| 154 | - const end = start + pageSize; | 134 | + limit: limit.value, |
| 155 | - const newRecords = mockRecords.slice(start, end); | 135 | + page: nextPage, |
| 156 | - | 136 | + }); |
| 157 | - records.value.push(...newRecords); | 137 | + if (code) { |
| 138 | + records.value.push(...data); | ||
| 139 | + finished.value = data.length < limit.value; | ||
| 140 | + page.value = nextPage + 1; | ||
| 158 | loading.value = false; | 141 | loading.value = false; |
| 159 | - | 142 | + } else { |
| 160 | - if (newRecords.length < pageSize) { | 143 | + finished.value = true; |
| 161 | - finished.value = true; | 144 | + finishText.value = ''; |
| 162 | - } else { | 145 | + loading.value = false; |
| 163 | - page.value += 1; | 146 | + } |
| 164 | - } | ||
| 165 | - }, 1000); | ||
| 166 | }; | 147 | }; |
| 167 | 148 | ||
| 168 | // 处理图片加载错误 | 149 | // 处理图片加载错误 | ... | ... |
| ... | @@ -24,6 +24,7 @@ | ... | @@ -24,6 +24,7 @@ |
| 24 | <!-- 视频播放器 --> | 24 | <!-- 视频播放器 --> |
| 25 | <VideoPlayer v-show="isPlaying" ref="videoPlayerRef" | 25 | <VideoPlayer v-show="isPlaying" ref="videoPlayerRef" |
| 26 | :video-url="courseFile?.list?.length ? courseFile['list'][0]['url'] : ''" :autoplay="false" | 26 | :video-url="courseFile?.list?.length ? courseFile['list'][0]['url'] : ''" :autoplay="false" |
| 27 | + :video-id="courseFile?.list?.length ? courseFile['list'][0]['meta_id'] : ''" | ||
| 27 | @onPlay="handleVideoPlay" @onPause="handleVideoPause" /> | 28 | @onPlay="handleVideoPlay" @onPause="handleVideoPause" /> |
| 28 | </div> | 29 | </div> |
| 29 | <div v-if="course.course_type === 'audio'" class="w-full relative" | 30 | <div v-if="course.course_type === 'audio'" class="w-full relative" |
| ... | @@ -256,6 +257,7 @@ import PdfPreview from '@/components/ui/PdfPreview.vue'; | ... | @@ -256,6 +257,7 @@ import PdfPreview from '@/components/ui/PdfPreview.vue'; |
| 256 | 257 | ||
| 257 | // 导入接口 | 258 | // 导入接口 |
| 258 | import { getScheduleCourseAPI, getGroupCommentListAPI, addGroupCommentAPI, addGroupCommentLikeAPI, delGroupCommentLikeAPI, getCourseDetailAPI } from '@/api/course'; | 259 | import { getScheduleCourseAPI, getGroupCommentListAPI, addGroupCommentAPI, addGroupCommentLikeAPI, delGroupCommentLikeAPI, getCourseDetailAPI } from '@/api/course'; |
| 260 | +import { addStudyRecordAPI } from "@/api/record"; | ||
| 259 | 261 | ||
| 260 | const route = useRoute(); | 262 | const route = useRoute(); |
| 261 | const router = useRouter(); | 263 | const router = useRouter(); |
| ... | @@ -402,10 +404,16 @@ const pdfShow = ref(false); | ... | @@ -402,10 +404,16 @@ const pdfShow = ref(false); |
| 402 | const pdfTitle = ref(''); | 404 | const pdfTitle = ref(''); |
| 403 | const pdfUrl = ref(''); | 405 | const pdfUrl = ref(''); |
| 404 | 406 | ||
| 405 | -const showPdf = ({ title, url }) => { | 407 | +const showPdf = ({ title, url, meta_id }) => { |
| 406 | pdfTitle.value = title; | 408 | pdfTitle.value = title; |
| 407 | pdfUrl.value = url; | 409 | pdfUrl.value = url; |
| 408 | pdfShow.value = true; | 410 | pdfShow.value = true; |
| 411 | + // 新增记录 | ||
| 412 | + let paramsObj = { | ||
| 413 | + schedule_id: courseId.value, | ||
| 414 | + meta_id | ||
| 415 | + } | ||
| 416 | + addRecord(paramsObj); | ||
| 409 | }; | 417 | }; |
| 410 | 418 | ||
| 411 | const courseId = computed(() => { | 419 | const courseId = computed(() => { |
| ... | @@ -709,12 +717,16 @@ const onAudioPause = (audio) => { | ... | @@ -709,12 +717,16 @@ const onAudioPause = (audio) => { |
| 709 | const videoDuration = ref(0); | 717 | const videoDuration = ref(0); |
| 710 | const currentPosition = ref(0); | 718 | const currentPosition = ref(0); |
| 711 | 719 | ||
| 720 | +// 记录音频时长和当前播放位置的变量 | ||
| 721 | +const audioDuration = ref(0); | ||
| 722 | +const audioPosition = ref(0); | ||
| 723 | + | ||
| 712 | /** | 724 | /** |
| 713 | * 开始操作 | 725 | * 开始操作 |
| 714 | * @param action | 726 | * @param action |
| 715 | * @param item | 727 | * @param item |
| 716 | */ | 728 | */ |
| 717 | -const startAction = (action, item) => { | 729 | +const startAction = (item) => { |
| 718 | // 先清除可能存在的定时器 | 730 | // 先清除可能存在的定时器 |
| 719 | if (window.actionTimer) { | 731 | if (window.actionTimer) { |
| 720 | clearInterval(window.actionTimer); | 732 | clearInterval(window.actionTimer); |
| ... | @@ -725,6 +737,11 @@ const startAction = (action, item) => { | ... | @@ -725,6 +737,11 @@ const startAction = (action, item) => { |
| 725 | videoDuration.value = videoPlayerRef.value.getPlayer().duration(); | 737 | videoDuration.value = videoPlayerRef.value.getPlayer().duration(); |
| 726 | } | 738 | } |
| 727 | 739 | ||
| 740 | + // 获取音频总时长(如果是音频播放) | ||
| 741 | + if (audioPlayerRef.value && audioPlayerRef.value.getPlayer()) { | ||
| 742 | + audioDuration.value = audioPlayerRef.value.getPlayer().duration; | ||
| 743 | + } | ||
| 744 | + | ||
| 728 | // 生成唯一标识符 | 745 | // 生成唯一标识符 |
| 729 | let uuid = uuidv4(); | 746 | let uuid = uuidv4(); |
| 730 | console.warn('开始操作', uuid); | 747 | console.warn('开始操作', uuid); |
| ... | @@ -733,12 +750,37 @@ const startAction = (action, item) => { | ... | @@ -733,12 +750,37 @@ const startAction = (action, item) => { |
| 733 | window.actionTimer = setInterval(() => { | 750 | window.actionTimer = setInterval(() => { |
| 734 | console.warn('持续操作中', uuid); | 751 | console.warn('持续操作中', uuid); |
| 735 | 752 | ||
| 753 | + let paramsObj = {} | ||
| 754 | + | ||
| 736 | // 更新当前播放位置(如果是视频播放) | 755 | // 更新当前播放位置(如果是视频播放) |
| 737 | if (videoPlayerRef.value && videoPlayerRef.value.getPlayer()) { | 756 | if (videoPlayerRef.value && videoPlayerRef.value.getPlayer()) { |
| 738 | currentPosition.value = videoPlayerRef.value.getPlayer().currentTime(); | 757 | currentPosition.value = videoPlayerRef.value.getPlayer().currentTime(); |
| 739 | - console.log('视频总时长:', videoDuration.value, '当前位置:', currentPosition.value); | 758 | + console.log('视频总时长:', videoDuration.value, '当前位置:', currentPosition.value, 'id:', videoPlayerRef.value.getId()); |
| 759 | + paramsObj = { | ||
| 760 | + schedule_id: courseId.value, | ||
| 761 | + meta_id: videoPlayerRef.value.getId(), | ||
| 762 | + media_duration: videoDuration.value, | ||
| 763 | + playback_position: currentPosition.value, | ||
| 764 | + playback_id: uuid, | ||
| 765 | + } | ||
| 766 | + } | ||
| 767 | + | ||
| 768 | + // 更新当前播放位置(如果是音频播放) | ||
| 769 | + if (audioPlayerRef.value && audioPlayerRef.value.getPlayer()) { | ||
| 770 | + audioPosition.value = audioPlayerRef.value.getPlayer().currentTime; | ||
| 771 | + console.log('音频总时长:', audioDuration.value, '当前位置:', audioPosition.value, 'id:', audioPlayerRef.value.getId()); | ||
| 772 | + paramsObj = { | ||
| 773 | + schedule_id: courseId.value, | ||
| 774 | + meta_id: audioPlayerRef.value.getId(), | ||
| 775 | + media_duration: audioDuration.value, | ||
| 776 | + playback_position: audioPosition.value, | ||
| 777 | + playback_id: uuid, | ||
| 778 | + } | ||
| 740 | } | 779 | } |
| 741 | 780 | ||
| 781 | + // 新增记录 | ||
| 782 | + addRecord(paramsObj); | ||
| 783 | + | ||
| 742 | // 这里可以添加需要持续执行的具体操作 | 784 | // 这里可以添加需要持续执行的具体操作 |
| 743 | }, 1000); // 每秒执行一次,可以根据需求调整时间间隔 | 785 | }, 1000); // 每秒执行一次,可以根据需求调整时间间隔 |
| 744 | } | 786 | } |
| ... | @@ -748,11 +790,18 @@ const startAction = (action, item) => { | ... | @@ -748,11 +790,18 @@ const startAction = (action, item) => { |
| 748 | * @param action | 790 | * @param action |
| 749 | * @param item | 791 | * @param item |
| 750 | */ | 792 | */ |
| 751 | -const endAction = (action, item) => { | 793 | +const endAction = (item) => { |
| 752 | // 在结束前记录最后的播放位置 | 794 | // 在结束前记录最后的播放位置 |
| 753 | if (videoPlayerRef.value && videoPlayerRef.value.player) { | 795 | if (videoPlayerRef.value && videoPlayerRef.value.player) { |
| 754 | - window.currentPosition = videoPlayerRef.value.player.currentTime(); | 796 | + currentPosition.value = videoPlayerRef.value.player.currentTime(); |
| 755 | - console.log('结束时 - 视频总时长:', window.videoDuration, '最终位置:', window.currentPosition); | 797 | + console.log('结束时 - 视频总时长:', videoDuration.value, '最终位置:', currentPosition.value); |
| 798 | + } | ||
| 799 | + | ||
| 800 | + // 在结束前记录最后的音频播放位置 | ||
| 801 | + if (course.value?.course_type === 'audio' && document.querySelector('audio')) { | ||
| 802 | + const audioElement = document.querySelector('audio'); | ||
| 803 | + audioPosition.value = audioElement.currentTime || 0; | ||
| 804 | + console.log('结束时 - 音频总时长:', audioDuration.value, '最终位置:', audioPosition.value); | ||
| 756 | } | 805 | } |
| 757 | 806 | ||
| 758 | // 清除定时器,停止执行startAction | 807 | // 清除定时器,停止执行startAction |
| ... | @@ -762,6 +811,14 @@ const endAction = (action, item) => { | ... | @@ -762,6 +811,14 @@ const endAction = (action, item) => { |
| 762 | console.warn('操作已停止'); | 811 | console.warn('操作已停止'); |
| 763 | } | 812 | } |
| 764 | } | 813 | } |
| 814 | + | ||
| 815 | +/** | ||
| 816 | + * 新增记录 | ||
| 817 | + * @param paramsObj | ||
| 818 | + */ | ||
| 819 | +const addRecord = async (paramsObj) => { | ||
| 820 | + await addStudyRecordAPI(paramsObj); | ||
| 821 | +} | ||
| 765 | </script> | 822 | </script> |
| 766 | 823 | ||
| 767 | <style lang="less" scoped> | 824 | <style lang="less" scoped> | ... | ... |
-
Please register or login to post a comment