feat(打卡): 拆分音视频上传组件并实现打卡功能
将原有的file.vue拆分为独立的video.vue和audio.vue组件 添加打卡相关API接口并实现提交功能 更新打卡页面路由和类型选择逻辑 实现打卡动态列表的数据获取和展示
Showing
6 changed files
with
500 additions
and
58 deletions
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-06-06 09:26:16 | 2 | * @Date: 2025-06-06 09:26:16 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-06-06 15:05:19 | 4 | + * @LastEditTime: 2025-06-06 16:19:13 |
| 5 | * @FilePath: /mlaj/src/api/checkin.js | 5 | * @FilePath: /mlaj/src/api/checkin.js |
| 6 | * @Description: 签到模块相关接口 | 6 | * @Description: 签到模块相关接口 |
| 7 | */ | 7 | */ |
| ... | @@ -11,6 +11,8 @@ const Api = { | ... | @@ -11,6 +11,8 @@ const Api = { |
| 11 | GET_TASK_LIST: '/srv/?a=task&t=my_list', | 11 | GET_TASK_LIST: '/srv/?a=task&t=my_list', |
| 12 | GET_TASK_DETAIL: '/srv/?a=task&t=detail', | 12 | GET_TASK_DETAIL: '/srv/?a=task&t=detail', |
| 13 | TASK_CHECKIN: '/srv/?a=checkin&t=checkin', | 13 | TASK_CHECKIN: '/srv/?a=checkin&t=checkin', |
| 14 | + TASK_UPLOAD_ADD: '/srv/?a=checkin&t=upload_add', | ||
| 15 | + TASK_UPLOAD_LIST: '/srv/?a=checkin&t=upload_list', | ||
| 14 | } | 16 | } |
| 15 | 17 | ||
| 16 | /** | 18 | /** |
| ... | @@ -35,3 +37,25 @@ export const getTaskDetailAPI = (params) => fn(fetch.get(Api.GET_TASK_DETAIL, p | ... | @@ -35,3 +37,25 @@ export const getTaskDetailAPI = (params) => fn(fetch.get(Api.GET_TASK_DETAIL, p |
| 35 | * @returns | 37 | * @returns |
| 36 | */ | 38 | */ |
| 37 | export const checkinTaskAPI = (params) => fn(fetch.post(Api.TASK_CHECKIN, params)) | 39 | export const checkinTaskAPI = (params) => fn(fetch.post(Api.TASK_CHECKIN, params)) |
| 40 | + | ||
| 41 | +/** | ||
| 42 | + * @description: 新增上传打卡 | ||
| 43 | + * @param task_id 上传作业ID | ||
| 44 | + * @param note 打卡文字 | ||
| 45 | + * @param meta_id[] 附件ID列表 | ||
| 46 | + * @param file_type 上传附件的类型 image=上传图片,video=视频,audio=音频 | ||
| 47 | + * @returns | ||
| 48 | + */ | ||
| 49 | +export const addUploadTaskAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_ADD, params)) | ||
| 50 | + | ||
| 51 | +/** | ||
| 52 | + * @description: 获取打卡动态列表 | ||
| 53 | + * @param task_id 上传作业ID | ||
| 54 | + * @param date 日期 | ||
| 55 | + * @param keyword 搜索 | ||
| 56 | + * @param order_by_time asc=正序,desc=倒序。默认为倒序 | ||
| 57 | + * @param limit | ||
| 58 | + * @param offset | ||
| 59 | + * @returns | ||
| 60 | + */ | ||
| 61 | +export const getUploadTaskListAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_LIST, params)) | ... | ... |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-03-21 13:28:30 | 2 | * @Date: 2025-03-21 13:28:30 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-06-06 14:35:56 | 4 | + * @LastEditTime: 2025-06-06 15:45:36 |
| 5 | * @FilePath: /mlaj/src/router/checkin.js | 5 | * @FilePath: /mlaj/src/router/checkin.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -61,11 +61,20 @@ export default [ | ... | @@ -61,11 +61,20 @@ export default [ |
| 61 | } | 61 | } |
| 62 | }, | 62 | }, |
| 63 | { | 63 | { |
| 64 | - path: '/checkin/file', | 64 | + path: '/checkin/video', |
| 65 | - name: 'FileCheckIn', | 65 | + name: 'VideoCheckIn', |
| 66 | - component: () => import('@/views/checkin/upload/file.vue'), | 66 | + component: () => import('@root/src/views/checkin/upload/video.vue'), |
| 67 | meta: { | 67 | meta: { |
| 68 | - title: '打卡视频/音频', | 68 | + title: '打卡视频', |
| 69 | + requiresAuth: true | ||
| 70 | + } | ||
| 71 | + }, | ||
| 72 | + { | ||
| 73 | + path: '/checkin/audio', | ||
| 74 | + name: 'AudioCheckIn', | ||
| 75 | + component: () => import('@root/src/views/checkin/upload/audio.vue'), | ||
| 76 | + meta: { | ||
| 77 | + title: '打卡音频', | ||
| 69 | requiresAuth: true | 78 | requiresAuth: true |
| 70 | } | 79 | } |
| 71 | }, | 80 | }, | ... | ... |
| 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-06 14:33:04 | 4 | + * @LastEditTime: 2025-06-06 17:07:25 |
| 5 | * @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue | 5 | * @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | --> | 7 | --> |
| ... | @@ -40,25 +40,26 @@ | ... | @@ -40,25 +40,26 @@ |
| 40 | <van-progress :percentage="progress2" color="#4caf50" :show-pivot="false" /> | 40 | <van-progress :percentage="progress2" color="#4caf50" :show-pivot="false" /> |
| 41 | </div> --> | 41 | </div> --> |
| 42 | <div style="padding: 0.75rem 1rem;"> | 42 | <div style="padding: 0.75rem 1rem;"> |
| 43 | - <van-image round width="2.8rem" height="2.8rem" src="https://cdn.ipadbiz.cn/space/816560/大国少年_FhnF8lsFMPnTDNTBlM6hYa-UFBlW.jpg" | 43 | + <van-image round width="2.8rem" height="2.8rem" :src="item ? item : 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'" fit="contain" |
| 44 | v-for="(item, index) in teamAvatars" :key="index" | 44 | v-for="(item, index) in teamAvatars" :key="index" |
| 45 | - :style="{ marginLeft: index > 0 ? '-0.5rem' : '', border: '2px solid #FFF' }" /> | 45 | + :style="{ marginLeft: index > 0 ? '-0.5rem' : '', border: '2px solid #eff6ff', background: '#fff' }" /> |
| 46 | </div> | 46 | </div> |
| 47 | </div> | 47 | </div> |
| 48 | </div> | 48 | </div> |
| 49 | 49 | ||
| 50 | - <div v-if="!taskDetail.is_gray" class="text-wrapper"> | 50 | + <!-- <div v-if="!taskDetail.is_gray" class="text-wrapper"> --> |
| 51 | + <div v-if="taskDetail.is_gray" class="text-wrapper"> | ||
| 51 | <div class="text-header">打卡类型</div> | 52 | <div class="text-header">打卡类型</div> |
| 52 | <div class="upload-wrapper"> | 53 | <div class="upload-wrapper"> |
| 53 | <div @click="goToCheckinImagePage" class="upload-boxer"> | 54 | <div @click="goToCheckinImagePage" class="upload-boxer"> |
| 54 | <div><van-icon name="photo" size="2.5rem" /></div> | 55 | <div><van-icon name="photo" size="2.5rem" /></div> |
| 55 | <div style="font-size: 0.85rem;">图文打卡</div> | 56 | <div style="font-size: 0.85rem;">图文打卡</div> |
| 56 | </div> | 57 | </div> |
| 57 | - <div @click="goToCheckinFilePage('video')" class="upload-boxer"> | 58 | + <div @click="goToCheckinVideoPage()" class="upload-boxer"> |
| 58 | <div><van-icon name="video" size="2.5rem" /></div> | 59 | <div><van-icon name="video" size="2.5rem" /></div> |
| 59 | <div style="font-size: 0.85rem;">视频打卡</div> | 60 | <div style="font-size: 0.85rem;">视频打卡</div> |
| 60 | </div> | 61 | </div> |
| 61 | - <div @click="goToCheckinFilePage('audio')" class="upload-boxer"> | 62 | + <div @click="goToCheckinAudioPage()" class="upload-boxer"> |
| 62 | <div><van-icon name="music" size="2.5rem" /></div> | 63 | <div><van-icon name="music" size="2.5rem" /></div> |
| 63 | <div style="font-size: 0.85rem;">音频打卡</div> | 64 | <div style="font-size: 0.85rem;">音频打卡</div> |
| 64 | </div> | 65 | </div> |
| ... | @@ -67,11 +68,11 @@ | ... | @@ -67,11 +68,11 @@ |
| 67 | 68 | ||
| 68 | <div class="text-wrapper"> | 69 | <div class="text-wrapper"> |
| 69 | <div class="text-header">打卡动态</div> | 70 | <div class="text-header">打卡动态</div> |
| 70 | - <div class="post-card" v-for="post in mockPosts" :key="post.id"> | 71 | + <div class="post-card" v-for="post in checkinDataList" :key="post.id"> |
| 71 | <div class="post-header"> | 72 | <div class="post-header"> |
| 72 | <van-row> | 73 | <van-row> |
| 73 | <van-col span="4"> | 74 | <van-col span="4"> |
| 74 | - <van-image round width="2.5rem" height="2.5rem" :src="post.user.avatar" /> | 75 | + <van-image round width="2.5rem" height="2.5rem" :src="post.user.avatar" fit="cover" /> |
| 75 | </van-col> | 76 | </van-col> |
| 76 | <van-col span="17"> | 77 | <van-col span="17"> |
| 77 | <div class="user-info"> | 78 | <div class="user-info"> |
| ... | @@ -98,13 +99,13 @@ | ... | @@ -98,13 +99,13 @@ |
| 98 | <div v-for="(v, idx) in post.videoList" :key="idx"> | 99 | <div v-for="(v, idx) in post.videoList" :key="idx"> |
| 99 | <!-- 视频封面和播放按钮 --> | 100 | <!-- 视频封面和播放按钮 --> |
| 100 | <div v-if="v.video && !v.isPlaying" class="relative w-full rounded-lg overflow-hidden" style="aspect-ratio: 16/9; margin-bottom: 1rem;"> | 101 | <div v-if="v.video && !v.isPlaying" class="relative w-full rounded-lg overflow-hidden" style="aspect-ratio: 16/9; margin-bottom: 1rem;"> |
| 101 | - <img :src="v.videoCover || 'https://cdn.ipadbiz.cn/space/816560/大国少年_FhnF8lsFMPnTDNTBlM6hYa-UFBlW.jpg'" | 102 | + <img :src="v.videoCover || 'https://cdn.ipadbiz.cn/mlaj/images/cover_video_1.png'" |
| 102 | :alt="v.content" class="w-full h-full object-cover" /> | 103 | :alt="v.content" class="w-full h-full object-cover" /> |
| 103 | <div class="absolute inset-0 flex items-center justify-center cursor-pointer bg-black/20" | 104 | <div class="absolute inset-0 flex items-center justify-center cursor-pointer bg-black/20" |
| 104 | @click="startPlay(v)"> | 105 | @click="startPlay(v)"> |
| 105 | <div | 106 | <div |
| 106 | class="w-16 h-16 rounded-full bg-black/50 flex items-center justify-center hover:bg-black/70 transition-colors"> | 107 | class="w-16 h-16 rounded-full bg-black/50 flex items-center justify-center hover:bg-black/70 transition-colors"> |
| 107 | - <van-icon name="play-circle-o" class="text-white" size="30" /> | 108 | + <van-icon name="play-circle-o" class="text-white" size="40" /> |
| 108 | </div> | 109 | </div> |
| 109 | </div> | 110 | </div> |
| 110 | </div> | 111 | </div> |
| ... | @@ -163,7 +164,7 @@ import AudioPlayer from "@/components/ui/AudioPlayer.vue"; | ... | @@ -163,7 +164,7 @@ import AudioPlayer from "@/components/ui/AudioPlayer.vue"; |
| 163 | import { useTitle } from '@vueuse/core'; | 164 | import { useTitle } from '@vueuse/core'; |
| 164 | import dayjs from 'dayjs'; | 165 | import dayjs from 'dayjs'; |
| 165 | 166 | ||
| 166 | -import { getTaskDetailAPI } from "@/api/checkin"; | 167 | +import { getTaskDetailAPI, getUploadTaskListAPI } from "@/api/checkin"; |
| 167 | 168 | ||
| 168 | const route = useRoute() | 169 | const route = useRoute() |
| 169 | const router = useRouter() | 170 | const router = useRouter() |
| ... | @@ -200,10 +201,10 @@ onBeforeUnmount(() => { | ... | @@ -200,10 +201,10 @@ onBeforeUnmount(() => { |
| 200 | * @param {Object} post - 要播放视频的帖子对象 | 201 | * @param {Object} post - 要播放视频的帖子对象 |
| 201 | */ | 202 | */ |
| 202 | const startPlay = (post) => { | 203 | const startPlay = (post) => { |
| 203 | - // 确保mockPosts.value是一个数组 | 204 | + // 确保checkinDataList.value是一个数组 |
| 204 | - if (mockPosts.value) { | 205 | + if (checkinDataList.value) { |
| 205 | // 先暂停所有其他视频 | 206 | // 先暂停所有其他视频 |
| 206 | - mockPosts.value.forEach(p => { | 207 | + checkinDataList.value.forEach(p => { |
| 207 | p.videoList.forEach(v => { | 208 | p.videoList.forEach(v => { |
| 208 | if (v.id !== post.id) { | 209 | if (v.id !== post.id) { |
| 209 | v.isPlaying = false; | 210 | v.isPlaying = false; |
| ... | @@ -251,7 +252,7 @@ const stopOtherVideos = (currentPlayer, currentPost) => { | ... | @@ -251,7 +252,7 @@ const stopOtherVideos = (currentPlayer, currentPost) => { |
| 251 | } | 252 | } |
| 252 | 253 | ||
| 253 | // 更新其他帖子的播放状态 | 254 | // 更新其他帖子的播放状态 |
| 254 | - mockPosts.value.forEach(p => { | 255 | + checkinDataList.value.forEach(p => { |
| 255 | p.videoList.forEach(v => { | 256 | p.videoList.forEach(v => { |
| 256 | if (v.id !== currentPost.id) { | 257 | if (v.id !== currentPost.id) { |
| 257 | v.isPlaying = false; | 258 | v.isPlaying = false; |
| ... | @@ -281,7 +282,7 @@ const stopOtherAudio = (currentPlayer, currentPost) => { | ... | @@ -281,7 +282,7 @@ const stopOtherAudio = (currentPlayer, currentPost) => { |
| 281 | }); | 282 | }); |
| 282 | } | 283 | } |
| 283 | // 更新其他帖子的播放状态 | 284 | // 更新其他帖子的播放状态 |
| 284 | - mockPosts.value.forEach(post => { | 285 | + checkinDataList.value.forEach(post => { |
| 285 | if (post.id!== currentPost.id) { | 286 | if (post.id!== currentPost.id) { |
| 286 | post.isPlaying = false; | 287 | post.isPlaying = false; |
| 287 | } | 288 | } |
| ... | @@ -300,7 +301,7 @@ const stopAllAudio = () => { | ... | @@ -300,7 +301,7 @@ const stopAllAudio = () => { |
| 300 | } | 301 | } |
| 301 | }); | 302 | }); |
| 302 | // 更新所有帖子的播放状态 | 303 | // 更新所有帖子的播放状态 |
| 303 | - mockPosts.value.forEach(post => { | 304 | + checkinDataList.value.forEach(post => { |
| 304 | if (post.audio.length) { | 305 | if (post.audio.length) { |
| 305 | post.isPlaying = false; | 306 | post.isPlaying = false; |
| 306 | } | 307 | } |
| ... | @@ -315,7 +316,7 @@ const stopAllVideos = () => { | ... | @@ -315,7 +316,7 @@ const stopAllVideos = () => { |
| 315 | if (!videoPlayers.value) return; | 316 | if (!videoPlayers.value) return; |
| 316 | 317 | ||
| 317 | // 更新所有帖子的播放状态 | 318 | // 更新所有帖子的播放状态 |
| 318 | - mockPosts.value.forEach(p => { | 319 | + checkinDataList.value.forEach(p => { |
| 319 | p.videoList.forEach(v => { | 320 | p.videoList.forEach(v => { |
| 320 | v.isPlaying = false; | 321 | v.isPlaying = false; |
| 321 | }); | 322 | }); |
| ... | @@ -338,9 +339,7 @@ const mockPosts = ref([ | ... | @@ -338,9 +339,7 @@ const mockPosts = ref([ |
| 338 | 'https://cdn.ipadbiz.cn/space/816560/大国少年_FhnF8lsFMPnTDNTBlM6hYa-UFBlW.jpg', | 339 | 'https://cdn.ipadbiz.cn/space/816560/大国少年_FhnF8lsFMPnTDNTBlM6hYa-UFBlW.jpg', |
| 339 | 'https://cdn.ipadbiz.cn/space/816560/大国少年_FhnF8lsFMPnTDNTBlM6hYa-UFBlW.jpg', | 340 | 'https://cdn.ipadbiz.cn/space/816560/大国少年_FhnF8lsFMPnTDNTBlM6hYa-UFBlW.jpg', |
| 340 | ], | 341 | ], |
| 341 | - video: '', | ||
| 342 | videoList: [], | 342 | videoList: [], |
| 343 | - videoCover: '', | ||
| 344 | isPlaying: false, | 343 | isPlaying: false, |
| 345 | audio: [], | 344 | audio: [], |
| 346 | likes: 12, | 345 | likes: 12, |
| ... | @@ -367,7 +366,6 @@ const mockPosts = ref([ | ... | @@ -367,7 +366,6 @@ const mockPosts = ref([ |
| 367 | videoCover: '', | 366 | videoCover: '', |
| 368 | isPlaying: false, | 367 | isPlaying: false, |
| 369 | }], | 368 | }], |
| 370 | - videoCover: '', | ||
| 371 | isPlaying: false, | 369 | isPlaying: false, |
| 372 | audio: [], | 370 | audio: [], |
| 373 | likes: 12, | 371 | likes: 12, |
| ... | @@ -382,9 +380,7 @@ const mockPosts = ref([ | ... | @@ -382,9 +380,7 @@ const mockPosts = ref([ |
| 382 | }, | 380 | }, |
| 383 | content: '今天完成了React基础课程的学习,收获满满!', | 381 | content: '今天完成了React基础课程的学习,收获满满!', |
| 384 | images: [], | 382 | images: [], |
| 385 | - video: '', | ||
| 386 | videoList: [], | 383 | videoList: [], |
| 387 | - videoCover: '', | ||
| 388 | isPlaying: false, | 384 | isPlaying: false, |
| 389 | audio: [ | 385 | audio: [ |
| 390 | { | 386 | { |
| ... | @@ -412,7 +408,6 @@ const mockPosts = ref([ | ... | @@ -412,7 +408,6 @@ const mockPosts = ref([ |
| 412 | }, | 408 | }, |
| 413 | content: '今天完成了React基础课程的学习,收获满满!', | 409 | content: '今天完成了React基础课程的学习,收获满满!', |
| 414 | images: [], | 410 | images: [], |
| 415 | - video: 'https://cdn.ipadbiz.cn/space/lk3DmvLO02dUC2zPiFwiClDe3nKL.mp4', | ||
| 416 | videoList: [{ | 411 | videoList: [{ |
| 417 | id: 3, | 412 | id: 3, |
| 418 | video: 'https://cdn.ipadbiz.cn/space/lk3DmvLO02dUC2zPiFwiClDe3nKL.mp4', | 413 | video: 'https://cdn.ipadbiz.cn/space/lk3DmvLO02dUC2zPiFwiClDe3nKL.mp4', |
| ... | @@ -424,7 +419,6 @@ const mockPosts = ref([ | ... | @@ -424,7 +419,6 @@ const mockPosts = ref([ |
| 424 | videoCover: '', | 419 | videoCover: '', |
| 425 | isPlaying: false, | 420 | isPlaying: false, |
| 426 | }], | 421 | }], |
| 427 | - videoCover: '', | ||
| 428 | isPlaying: false, | 422 | isPlaying: false, |
| 429 | audio: [], | 423 | audio: [], |
| 430 | likes: 12, | 424 | likes: 12, |
| ... | @@ -439,9 +433,7 @@ const mockPosts = ref([ | ... | @@ -439,9 +433,7 @@ const mockPosts = ref([ |
| 439 | }, | 433 | }, |
| 440 | content: '今天完成了React基础课程的学习,收获满满!', | 434 | content: '今天完成了React基础课程的学习,收获满满!', |
| 441 | images: [], | 435 | images: [], |
| 442 | - video: '', | ||
| 443 | videoList: [], | 436 | videoList: [], |
| 444 | - videoCover: '', | ||
| 445 | isPlaying: false, | 437 | isPlaying: false, |
| 446 | audio: [ | 438 | audio: [ |
| 447 | { | 439 | { |
| ... | @@ -461,7 +453,7 @@ const themeVars = { | ... | @@ -461,7 +453,7 @@ const themeVars = { |
| 461 | } | 453 | } |
| 462 | 454 | ||
| 463 | const progress1 = ref(0); | 455 | const progress1 = ref(0); |
| 464 | -const progress2 = ref(76); | 456 | +// const progress2 = ref(76); |
| 465 | 457 | ||
| 466 | const teamAvatars = ref([]) | 458 | const teamAvatars = ref([]) |
| 467 | 459 | ||
| ... | @@ -485,7 +477,7 @@ const formatter = (day) => { | ... | @@ -485,7 +477,7 @@ const formatter = (day) => { |
| 485 | const month = day.date.getMonth() + 1; | 477 | const month = day.date.getMonth() + 1; |
| 486 | const date = day.date.getDate(); | 478 | const date = day.date.getDate(); |
| 487 | 479 | ||
| 488 | - let checkin_days = [1, 2]; | 480 | + let checkin_days = myCheckinDates.value; |
| 489 | 481 | ||
| 490 | if (month === 6) { | 482 | if (month === 6) { |
| 491 | if (checkin_days.includes(date)) { | 483 | if (checkin_days.includes(date)) { |
| ... | @@ -511,10 +503,32 @@ const onClickSubtitle = (evt) => { | ... | @@ -511,10 +503,32 @@ const onClickSubtitle = (evt) => { |
| 511 | } | 503 | } |
| 512 | 504 | ||
| 513 | const goToCheckinImagePage = () => { | 505 | const goToCheckinImagePage = () => { |
| 514 | - router.push('/checkin/image'); | 506 | + router.push({ |
| 507 | + path: '/checkin/image', | ||
| 508 | + query: { | ||
| 509 | + id: route.query.id, | ||
| 510 | + type: 'image' | ||
| 511 | + } | ||
| 512 | + }) | ||
| 515 | } | 513 | } |
| 516 | -const goToCheckinFilePage = (type) => { | 514 | +const goToCheckinVideoPage = (type) => { |
| 517 | - router.push('/checkin/file?type=' + type); | 515 | + router.push({ |
| 516 | + path: '/checkin/video', | ||
| 517 | + query: { | ||
| 518 | + id: route.query.id, | ||
| 519 | + type: 'video', | ||
| 520 | + } | ||
| 521 | + }) | ||
| 522 | +} | ||
| 523 | + | ||
| 524 | +const goToCheckinAudioPage = (type) => { | ||
| 525 | + router.push({ | ||
| 526 | + path: '/checkin/audio', | ||
| 527 | + query: { | ||
| 528 | + id: route.query.id, | ||
| 529 | + type: 'audio', | ||
| 530 | + } | ||
| 531 | + }) | ||
| 518 | } | 532 | } |
| 519 | 533 | ||
| 520 | const handLike = (post) => { | 534 | const handLike = (post) => { |
| ... | @@ -522,15 +536,22 @@ const handLike = (post) => { | ... | @@ -522,15 +536,22 @@ const handLike = (post) => { |
| 522 | // TODO: 调用接口 | 536 | // TODO: 调用接口 |
| 523 | } | 537 | } |
| 524 | 538 | ||
| 525 | -const editCheckin = () => { | 539 | +const editCheckin = (type) => { |
| 526 | - let type = 'image'; | ||
| 527 | if (type === 'image') { | 540 | if (type === 'image') { |
| 528 | router.push({ | 541 | router.push({ |
| 529 | path: '/checkin/image', | 542 | path: '/checkin/image', |
| 543 | + query: { | ||
| 544 | + id: route.query.id, | ||
| 545 | + type, | ||
| 546 | + } | ||
| 530 | }) | 547 | }) |
| 531 | } else { | 548 | } else { |
| 532 | router.push({ | 549 | router.push({ |
| 533 | path: '/checkin/file', | 550 | path: '/checkin/file', |
| 551 | + query: { | ||
| 552 | + id: route.query.id, | ||
| 553 | + type, | ||
| 554 | + } | ||
| 534 | }) | 555 | }) |
| 535 | } | 556 | } |
| 536 | } | 557 | } |
| ... | @@ -554,14 +575,73 @@ const delCheckin = () => { | ... | @@ -554,14 +575,73 @@ const delCheckin = () => { |
| 554 | } | 575 | } |
| 555 | 576 | ||
| 556 | const taskDetail = ref({}); | 577 | const taskDetail = ref({}); |
| 578 | +const myCheckinDates = ref([]); | ||
| 579 | +const checkinDataList = ref([]); | ||
| 557 | 580 | ||
| 558 | onMounted(async () => { | 581 | onMounted(async () => { |
| 559 | - const { code, data } = await getTaskDetailAPI({ id: route.query.id, month: dayjs().format('YYYY-MM') }); | 582 | + const { code, data } = await getTaskDetailAPI({ i: route.query.id, month: dayjs().format('YYYY-MM') }); |
| 560 | if (code) { | 583 | if (code) { |
| 561 | console.warn(data); | 584 | console.warn(data); |
| 562 | taskDetail.value = data; | 585 | taskDetail.value = data; |
| 563 | progress1.value = (data.checkin_number/data.target_number)*100 ; | 586 | progress1.value = (data.checkin_number/data.target_number)*100 ; |
| 564 | teamAvatars.value = data.checkin_avatars; | 587 | teamAvatars.value = data.checkin_avatars; |
| 588 | + // 获取当前用户的打卡日期 | ||
| 589 | + myCheckinDates.value = data.my_checkin_dates; | ||
| 590 | + // 把['2025-06-06'] 转化为 [6] 只取日期去掉0 | ||
| 591 | + myCheckinDates.value = myCheckinDates.value.map(date => { | ||
| 592 | + return dayjs(date).date(); | ||
| 593 | + }) | ||
| 594 | + } | ||
| 595 | + // 获取打卡动态列表 | ||
| 596 | + const task_list = await getUploadTaskListAPI({ task_id: route.query.id, date: dayjs().format('YYYY-MM-DD'), limit: 999 }); | ||
| 597 | + if (task_list.code) { | ||
| 598 | + console.warn(task_list.data?.checkin_list); | ||
| 599 | + // 整理数据结构 | ||
| 600 | + task_list.data?.checkin_list.forEach((item, index) => { | ||
| 601 | + let images = []; | ||
| 602 | + let audio = []; | ||
| 603 | + let videoList = []; | ||
| 604 | + if (item.file_type === 'image') { | ||
| 605 | + images = item.files.map(file => { | ||
| 606 | + return file.value; | ||
| 607 | + }); | ||
| 608 | + } else if (item.file_type === 'video') { | ||
| 609 | + videoList = item.files.map(file => { | ||
| 610 | + return { | ||
| 611 | + id: file.meta_id, | ||
| 612 | + video: file.value, | ||
| 613 | + videoCover: file.cover, | ||
| 614 | + isPlaying: false, | ||
| 615 | + } | ||
| 616 | + }) | ||
| 617 | + } else if (item.file_type === 'audio') { | ||
| 618 | + audio = item.files.map(file => { | ||
| 619 | + return { | ||
| 620 | + title: file.title ? file.title : '打卡音频', | ||
| 621 | + artist: file.artist ? file.artist : '', | ||
| 622 | + url: file.value, | ||
| 623 | + cover: file.cover ? file.cover : '', | ||
| 624 | + } | ||
| 625 | + }) | ||
| 626 | + } | ||
| 627 | + checkinDataList.value.push({ | ||
| 628 | + id: item.id, | ||
| 629 | + task_id: item.task_id, | ||
| 630 | + user: { | ||
| 631 | + name: item.username, | ||
| 632 | + avatar: item.avatar, | ||
| 633 | + time: item.created_time_desc, | ||
| 634 | + }, | ||
| 635 | + content: item.note, | ||
| 636 | + images, | ||
| 637 | + videoList, | ||
| 638 | + audio, | ||
| 639 | + isPlaying: false, | ||
| 640 | + likes: item.like_count, | ||
| 641 | + is_liked: item.is_like, | ||
| 642 | + is_my: item.is_my, | ||
| 643 | + }) | ||
| 644 | + }) | ||
| 565 | } | 645 | } |
| 566 | }) | 646 | }) |
| 567 | </script> | 647 | </script> | ... | ... |
src/views/checkin/upload/audio.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2025-06-03 09:41:41 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-06-06 15:51:31 | ||
| 5 | + * @FilePath: /mlaj/src/views/checkin/upload/audio.vue | ||
| 6 | + * @Description: 音视频文件上传组件 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <div class="checkin-upload-file p-4"> | ||
| 10 | + <!-- 文件上传区域 --> | ||
| 11 | + <div class="mb-4"> | ||
| 12 | + <van-uploader | ||
| 13 | + v-model="fileList" | ||
| 14 | + :max-count="max_count" | ||
| 15 | + :max-size="20 * 1024 * 1024" | ||
| 16 | + :before-read="beforeRead" | ||
| 17 | + :after-read="afterRead" | ||
| 18 | + @delete="onDelete" | ||
| 19 | + multiple | ||
| 20 | + accept="audio/*" | ||
| 21 | + result-type="file" | ||
| 22 | + upload-icon="plus" | ||
| 23 | + > | ||
| 24 | + <template #upload-text> | ||
| 25 | + <!-- :accept="route.query.type === 'video' ? 'video/*' : 'audio/*'" --> | ||
| 26 | + <div class="text-center"> | ||
| 27 | + <van-icon name="plus" size="24" /> | ||
| 28 | + <div class="mt-1 text-sm text-gray-600">上传文件</div> | ||
| 29 | + </div> | ||
| 30 | + </template> | ||
| 31 | + </van-uploader> | ||
| 32 | + <div class="mt-2 text-xs text-gray-500">最多上传{{ max_count }}个文件,每个不超过20M</div> | ||
| 33 | + <div class="mt-2 text-xs text-gray-500">上传类型: 音频文件</div> | ||
| 34 | + </div> | ||
| 35 | + | ||
| 36 | + <!-- 文字留言区域 --> | ||
| 37 | + <div class="mb-4 border"> | ||
| 38 | + <van-field | ||
| 39 | + v-model="message" | ||
| 40 | + rows="4" | ||
| 41 | + autosize | ||
| 42 | + type="textarea" | ||
| 43 | + placeholder="请输入打卡留言" | ||
| 44 | + /> | ||
| 45 | + </div> | ||
| 46 | + | ||
| 47 | + <!-- 提交按钮 --> | ||
| 48 | + <div class="fixed bottom-0 left-0 right-0 p-4 bg-white"> | ||
| 49 | + <van-button | ||
| 50 | + type="primary" | ||
| 51 | + block | ||
| 52 | + :loading="uploading" | ||
| 53 | + :disabled="!canSubmit" | ||
| 54 | + @click="onSubmit" | ||
| 55 | + > | ||
| 56 | + 提交 | ||
| 57 | + </van-button> | ||
| 58 | + </div> | ||
| 59 | + | ||
| 60 | + <!-- 上传加载遮罩 --> | ||
| 61 | + <van-overlay :show="loading"> | ||
| 62 | + <div class="wrapper" @click.stop> | ||
| 63 | + <van-loading vertical color="#FFFFFF">上传中...</van-loading> | ||
| 64 | + </div> | ||
| 65 | + </van-overlay> | ||
| 66 | + </div> | ||
| 67 | +</template> | ||
| 68 | + | ||
| 69 | +<script setup> | ||
| 70 | +import { ref, computed } from 'vue' | ||
| 71 | +import { useRoute, useRouter } from 'vue-router' | ||
| 72 | +import { showToast, showLoadingToast } from 'vant' | ||
| 73 | +import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common' | ||
| 74 | +import { addUploadTaskAPI } from "@/api/checkin"; | ||
| 75 | +import BMF from 'browser-md5-file' | ||
| 76 | +import _ from 'lodash' | ||
| 77 | +import { useTitle } from '@vueuse/core'; | ||
| 78 | +import { useAuth } from '@/contexts/auth' | ||
| 79 | + | ||
| 80 | +const route = useRoute() | ||
| 81 | +const router = useRouter() | ||
| 82 | +const { currentUser } = useAuth() | ||
| 83 | +useTitle(route.meta.title); | ||
| 84 | + | ||
| 85 | +const max_count = ref(5); | ||
| 86 | + | ||
| 87 | +// 文件列表 | ||
| 88 | +const fileList = ref([]) | ||
| 89 | +// 留言内容 | ||
| 90 | +const message = ref('') | ||
| 91 | +// 上传状态 | ||
| 92 | +const uploading = ref(false) | ||
| 93 | +// 上传loading | ||
| 94 | +const loading = ref(false) | ||
| 95 | + | ||
| 96 | +// 是否可以提交 | ||
| 97 | +const canSubmit = computed(() => { | ||
| 98 | + return fileList.value.length > 0 && message.value.trim() !== '' | ||
| 99 | +}) | ||
| 100 | + | ||
| 101 | +// 文件校验 | ||
| 102 | +const beforeRead = (file) => { | ||
| 103 | + let flag = true | ||
| 104 | + | ||
| 105 | + if (Array.isArray(file)) { | ||
| 106 | + // 多个文件 | ||
| 107 | + const invalidTypes = file.filter(item => { | ||
| 108 | + const fileType = item.type.toLowerCase(); | ||
| 109 | + return !fileType.startsWith('audio/'); | ||
| 110 | + }) | ||
| 111 | + if (invalidTypes.length) { | ||
| 112 | + flag = false | ||
| 113 | + showToast('请上传音频文件') | ||
| 114 | + } | ||
| 115 | + if (fileList.value.length + file.length > max_count.value) { | ||
| 116 | + flag = false | ||
| 117 | + showToast(`最大上传数量为${max_count.value}个`) | ||
| 118 | + } | ||
| 119 | + } else { | ||
| 120 | + const fileType = file.type.toLowerCase(); | ||
| 121 | + if (!fileType.startsWith('audio/')) { | ||
| 122 | + showToast('请上传音频文件') | ||
| 123 | + flag = false | ||
| 124 | + } | ||
| 125 | + if (fileList.value.length + 1 > max_count.value) { | ||
| 126 | + flag = false | ||
| 127 | + showToast(`最大上传数量为${max_count.value}个`) | ||
| 128 | + } | ||
| 129 | + if ((file.size / 1024 / 1024).toFixed(2) > 20) { | ||
| 130 | + flag = false | ||
| 131 | + showToast('最大文件体积为20MB') | ||
| 132 | + } | ||
| 133 | + } | ||
| 134 | + return flag | ||
| 135 | +} | ||
| 136 | + | ||
| 137 | +// 获取文件MD5 | ||
| 138 | +const getFileMD5 = (file) => { | ||
| 139 | + return new Promise((resolve, reject) => { | ||
| 140 | + const bmf = new BMF() | ||
| 141 | + bmf.md5(file, (err, md5) => { | ||
| 142 | + if (err) { | ||
| 143 | + reject(err) | ||
| 144 | + return | ||
| 145 | + } | ||
| 146 | + resolve(md5) | ||
| 147 | + }) | ||
| 148 | + }) | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +// 上传到七牛云 | ||
| 152 | +const uploadToQiniu = async (file, token, fileName) => { | ||
| 153 | + const formData = new FormData() | ||
| 154 | + formData.append('file', file) | ||
| 155 | + formData.append('token', token) | ||
| 156 | + formData.append('key', fileName) | ||
| 157 | + | ||
| 158 | + const config = { | ||
| 159 | + headers: { 'Content-Type': 'multipart/form-data' } | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + // 根据协议选择上传地址 | ||
| 163 | + const qiniuUploadUrl = window.location.protocol === 'https:' | ||
| 164 | + ? 'https://up.qbox.me' | ||
| 165 | + : 'http://upload.qiniu.com' | ||
| 166 | + | ||
| 167 | + return await qiniuUploadAPI(qiniuUploadUrl, formData, config) | ||
| 168 | +} | ||
| 169 | + | ||
| 170 | +// 处理单个文件上传 | ||
| 171 | +const handleUpload = async (file) => { | ||
| 172 | + loading.value = true | ||
| 173 | + try { | ||
| 174 | + // 获取MD5值 | ||
| 175 | + const md5 = await getFileMD5(file.file) | ||
| 176 | + | ||
| 177 | + // 获取七牛token | ||
| 178 | + const tokenResult = await qiniuTokenAPI({ | ||
| 179 | + name: file.file.name, | ||
| 180 | + hash: md5 | ||
| 181 | + }) | ||
| 182 | + | ||
| 183 | + // 文件已存在,直接返回 | ||
| 184 | + if (tokenResult.data) { | ||
| 185 | + return tokenResult.data | ||
| 186 | + } | ||
| 187 | + | ||
| 188 | + // 新文件上传 | ||
| 189 | + if (tokenResult.token) { | ||
| 190 | + const suffix = /.[^.]+$/.exec(file.file.name) || '' | ||
| 191 | + const fileName = `mlaj/upload/checkin/${currentUser.value.mobile}/file/${md5}${suffix}` | ||
| 192 | + | ||
| 193 | + const { filekey } = await uploadToQiniu( | ||
| 194 | + file.file, | ||
| 195 | + tokenResult.token, | ||
| 196 | + fileName | ||
| 197 | + ) | ||
| 198 | + | ||
| 199 | + if (filekey) { | ||
| 200 | + // 保存文件信息 | ||
| 201 | + const { data } = await saveFileAPI({ | ||
| 202 | + name: file.file.name, | ||
| 203 | + filekey, | ||
| 204 | + hash: md5 | ||
| 205 | + }) | ||
| 206 | + return data | ||
| 207 | + } | ||
| 208 | + } | ||
| 209 | + return null | ||
| 210 | + } catch (error) { | ||
| 211 | + console.error('Upload error:', error) | ||
| 212 | + return null | ||
| 213 | + } finally { | ||
| 214 | + loading.value = false | ||
| 215 | + } | ||
| 216 | +} | ||
| 217 | + | ||
| 218 | +// 文件读取后的处理 | ||
| 219 | +const afterRead = async (file) => { | ||
| 220 | + if (Array.isArray(file)) { | ||
| 221 | + // 多文件上传 | ||
| 222 | + for (const item of file) { | ||
| 223 | + item.status = 'uploading' | ||
| 224 | + item.message = '上传中...' | ||
| 225 | + const result = await handleUpload(item) | ||
| 226 | + if (result) { | ||
| 227 | + item.status = 'done' | ||
| 228 | + item.message = '上传成功' | ||
| 229 | + item.url = result.url | ||
| 230 | + item.meta_id = result.meta_id | ||
| 231 | + } else { | ||
| 232 | + item.status = 'failed' | ||
| 233 | + item.message = '上传失败' | ||
| 234 | + showToast('上传失败,请重试') | ||
| 235 | + } | ||
| 236 | + } | ||
| 237 | + } else { | ||
| 238 | + // 单文件上传 | ||
| 239 | + file.status = 'uploading' | ||
| 240 | + file.message = '上传中...' | ||
| 241 | + const result = await handleUpload(file) | ||
| 242 | + if (result) { | ||
| 243 | + file.status = 'done' | ||
| 244 | + file.message = '上传成功' | ||
| 245 | + file.url = result.url | ||
| 246 | + file.meta_id = result.meta_id | ||
| 247 | + } else { | ||
| 248 | + file.status = 'failed' | ||
| 249 | + file.message = '上传失败' | ||
| 250 | + showToast('上传失败,请重试') | ||
| 251 | + } | ||
| 252 | + } | ||
| 253 | +} | ||
| 254 | + | ||
| 255 | +// 删除文件 | ||
| 256 | +const onDelete = (file) => { | ||
| 257 | + const index = fileList.value.indexOf(file) | ||
| 258 | + if (index !== -1) { | ||
| 259 | + fileList.value.splice(index, 1) | ||
| 260 | + } | ||
| 261 | +} | ||
| 262 | + | ||
| 263 | +// 提交表单 | ||
| 264 | +const onSubmit = async () => { | ||
| 265 | + if (uploading.value) return | ||
| 266 | + | ||
| 267 | + // 检查是否所有文件都上传完成 | ||
| 268 | + const hasUploadingFiles = fileList.value.some(file => file.status === 'uploading') | ||
| 269 | + if (hasUploadingFiles) { | ||
| 270 | + showToast('请等待所有文件上传完成') | ||
| 271 | + return | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + uploading.value = true | ||
| 275 | + const toast = showLoadingToast({ | ||
| 276 | + message: '提交中...', | ||
| 277 | + forbidClick: true, | ||
| 278 | + }) | ||
| 279 | + | ||
| 280 | + try { | ||
| 281 | + // 调用提交打卡接口 | ||
| 282 | + const { code, data } = await addUploadTaskAPI({ | ||
| 283 | + task_id: route.query.id, | ||
| 284 | + note: message.value, | ||
| 285 | + meta_id: fileList.value.map(item => item.meta_id), | ||
| 286 | + file_type: route.query.type, | ||
| 287 | + }); | ||
| 288 | + if (code) { | ||
| 289 | + showToast('提交成功') | ||
| 290 | + router.back() | ||
| 291 | + } | ||
| 292 | + } catch (error) { | ||
| 293 | + showToast('提交失败,请重试') | ||
| 294 | + } finally { | ||
| 295 | + toast.close() | ||
| 296 | + uploading.value = false | ||
| 297 | + } | ||
| 298 | +} | ||
| 299 | +</script> | ||
| 300 | + | ||
| 301 | +<style lang="less" scoped> | ||
| 302 | +.checkin-upload-file { | ||
| 303 | + min-height: 100vh; | ||
| 304 | + padding-bottom: 80px; | ||
| 305 | +} | ||
| 306 | + | ||
| 307 | +.wrapper { | ||
| 308 | + display: flex; | ||
| 309 | + align-items: center; | ||
| 310 | + justify-content: center; | ||
| 311 | + height: 100%; | ||
| 312 | +} | ||
| 313 | +</style> |
| ... | @@ -56,6 +56,7 @@ import { ref, computed } from 'vue' | ... | @@ -56,6 +56,7 @@ import { ref, computed } from 'vue' |
| 56 | import { useRoute, useRouter } from 'vue-router' | 56 | import { useRoute, useRouter } from 'vue-router' |
| 57 | import { showToast, showLoadingToast } from 'vant' | 57 | import { showToast, showLoadingToast } from 'vant' |
| 58 | import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common' | 58 | import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common' |
| 59 | +import { addUploadTaskAPI } from "@/api/checkin"; | ||
| 59 | import BMF from 'browser-md5-file' | 60 | import BMF from 'browser-md5-file' |
| 60 | import _ from 'lodash' | 61 | import _ from 'lodash' |
| 61 | import { useTitle } from '@vueuse/core'; | 62 | import { useTitle } from '@vueuse/core'; |
| ... | @@ -274,11 +275,17 @@ const onSubmit = async () => { | ... | @@ -274,11 +275,17 @@ const onSubmit = async () => { |
| 274 | }) | 275 | }) |
| 275 | 276 | ||
| 276 | try { | 277 | try { |
| 277 | - // TODO: 调用提交打卡接口 | 278 | + // 调用提交打卡接口 |
| 278 | - await new Promise(resolve => setTimeout(resolve, 1000)) | 279 | + const { code, data } = await addUploadTaskAPI({ |
| 279 | - console.warn('提交打卡接口', fileList.value, message.value); | 280 | + task_id: route.query.id, |
| 280 | - showToast('提交成功') | 281 | + note: message.value, |
| 281 | - router.back() | 282 | + meta_id: fileList.value.map(item => item.meta_id), |
| 283 | + file_type: route.query.type, | ||
| 284 | + }); | ||
| 285 | + if (code) { | ||
| 286 | + showToast('提交成功') | ||
| 287 | + router.back() | ||
| 288 | + } | ||
| 282 | } catch (error) { | 289 | } catch (error) { |
| 283 | showToast('提交失败,请重试') | 290 | showToast('提交失败,请重试') |
| 284 | } finally { | 291 | } finally { | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-06-03 09:41:41 | 2 | * @Date: 2025-06-03 09:41:41 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-06-06 14:38:20 | 4 | + * @LastEditTime: 2025-06-06 15:49:37 |
| 5 | - * @FilePath: /mlaj/src/views/checkin/upload/file.vue | 5 | + * @FilePath: /mlaj/src/views/checkin/upload/video.vue |
| 6 | * @Description: 音视频文件上传组件 | 6 | * @Description: 音视频文件上传组件 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| ... | @@ -17,11 +17,12 @@ | ... | @@ -17,11 +17,12 @@ |
| 17 | :after-read="afterRead" | 17 | :after-read="afterRead" |
| 18 | @delete="onDelete" | 18 | @delete="onDelete" |
| 19 | multiple | 19 | multiple |
| 20 | - :accept="route.query.type === 'video' ? 'video/*' : 'audio/*'" | 20 | + accept="video/*" |
| 21 | result-type="file" | 21 | result-type="file" |
| 22 | upload-icon="plus" | 22 | upload-icon="plus" |
| 23 | > | 23 | > |
| 24 | <template #upload-text> | 24 | <template #upload-text> |
| 25 | + <!-- :accept="route.query.type === 'video' ? 'video/*' : 'audio/*'" --> | ||
| 25 | <div class="text-center"> | 26 | <div class="text-center"> |
| 26 | <van-icon name="plus" size="24" /> | 27 | <van-icon name="plus" size="24" /> |
| 27 | <div class="mt-1 text-sm text-gray-600">上传文件</div> | 28 | <div class="mt-1 text-sm text-gray-600">上传文件</div> |
| ... | @@ -29,7 +30,7 @@ | ... | @@ -29,7 +30,7 @@ |
| 29 | </template> | 30 | </template> |
| 30 | </van-uploader> | 31 | </van-uploader> |
| 31 | <div class="mt-2 text-xs text-gray-500">最多上传{{ max_count }}个文件,每个不超过20M</div> | 32 | <div class="mt-2 text-xs text-gray-500">最多上传{{ max_count }}个文件,每个不超过20M</div> |
| 32 | - <div class="mt-2 text-xs text-gray-500">上传类型: {{ route.query.type === 'video' ? "视频文件" : '音频文件' }}</div> | 33 | + <div class="mt-2 text-xs text-gray-500">上传类型: 视频文件</div> |
| 33 | </div> | 34 | </div> |
| 34 | 35 | ||
| 35 | <!-- 文字留言区域 --> | 36 | <!-- 文字留言区域 --> |
| ... | @@ -70,6 +71,7 @@ import { ref, computed } from 'vue' | ... | @@ -70,6 +71,7 @@ import { ref, computed } from 'vue' |
| 70 | import { useRoute, useRouter } from 'vue-router' | 71 | import { useRoute, useRouter } from 'vue-router' |
| 71 | import { showToast, showLoadingToast } from 'vant' | 72 | import { showToast, showLoadingToast } from 'vant' |
| 72 | import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common' | 73 | import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common' |
| 74 | +import { addUploadTaskAPI } from "@/api/checkin"; | ||
| 73 | import BMF from 'browser-md5-file' | 75 | import BMF from 'browser-md5-file' |
| 74 | import _ from 'lodash' | 76 | import _ from 'lodash' |
| 75 | import { useTitle } from '@vueuse/core'; | 77 | import { useTitle } from '@vueuse/core'; |
| ... | @@ -104,11 +106,11 @@ const beforeRead = (file) => { | ... | @@ -104,11 +106,11 @@ const beforeRead = (file) => { |
| 104 | // 多个文件 | 106 | // 多个文件 |
| 105 | const invalidTypes = file.filter(item => { | 107 | const invalidTypes = file.filter(item => { |
| 106 | const fileType = item.type.toLowerCase(); | 108 | const fileType = item.type.toLowerCase(); |
| 107 | - return !fileType.startsWith('audio/') && !fileType.startsWith('video/'); | 109 | + return !fileType.startsWith('video/'); |
| 108 | }) | 110 | }) |
| 109 | if (invalidTypes.length) { | 111 | if (invalidTypes.length) { |
| 110 | flag = false | 112 | flag = false |
| 111 | - showToast('请上传音频或视频文件') | 113 | + showToast('请上传视频文件') |
| 112 | } | 114 | } |
| 113 | if (fileList.value.length + file.length > max_count.value) { | 115 | if (fileList.value.length + file.length > max_count.value) { |
| 114 | flag = false | 116 | flag = false |
| ... | @@ -116,8 +118,8 @@ const beforeRead = (file) => { | ... | @@ -116,8 +118,8 @@ const beforeRead = (file) => { |
| 116 | } | 118 | } |
| 117 | } else { | 119 | } else { |
| 118 | const fileType = file.type.toLowerCase(); | 120 | const fileType = file.type.toLowerCase(); |
| 119 | - if (!fileType.startsWith('audio/') && !fileType.startsWith('video/')) { | 121 | + if (!fileType.startsWith('video/')) { |
| 120 | - showToast('请上传音频或视频文件') | 122 | + showToast('请上传视频文件') |
| 121 | flag = false | 123 | flag = false |
| 122 | } | 124 | } |
| 123 | if (fileList.value.length + 1 > max_count.value) { | 125 | if (fileList.value.length + 1 > max_count.value) { |
| ... | @@ -276,10 +278,17 @@ const onSubmit = async () => { | ... | @@ -276,10 +278,17 @@ const onSubmit = async () => { |
| 276 | }) | 278 | }) |
| 277 | 279 | ||
| 278 | try { | 280 | try { |
| 279 | - // TODO: 调用提交打卡接口 | 281 | + // 调用提交打卡接口 |
| 280 | - await new Promise(resolve => setTimeout(resolve, 1000)) | 282 | + const { code, data } = await addUploadTaskAPI({ |
| 281 | - showToast('提交成功') | 283 | + task_id: route.query.id, |
| 282 | - router.back() | 284 | + note: message.value, |
| 285 | + meta_id: fileList.value.map(item => item.meta_id), | ||
| 286 | + file_type: route.query.type, | ||
| 287 | + }); | ||
| 288 | + if (code) { | ||
| 289 | + showToast('提交成功') | ||
| 290 | + router.back() | ||
| 291 | + } | ||
| 283 | } catch (error) { | 292 | } catch (error) { |
| 284 | showToast('提交失败,请重试') | 293 | showToast('提交失败,请重试') |
| 285 | } finally { | 294 | } finally { | ... | ... |
-
Please register or login to post a comment