hookehuyr

feat(相册): 实现相册功能接口对接及组件优化

- 新增相册相关API接口文件photo.js
- 修改UploadMedia页面实现相册保存功能
- 优化FamilyAlbum组件对接相册列表接口并添加加载状态
- 完善AlbumList页面实现相册删除功能
- 扩展useMediaPreview功能以支持相册数据类型
1 +/*
2 + * @Date: 2025-09-11 12:20:17
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-09-11 15:16:03
5 + * @FilePath: /lls_program/src/api/photo.js
6 + * @Description: 文件描述
7 + */
8 +import { fn, fetch } from './fn';
9 +
10 +const Api = {
11 + PHOTO_LIST: '/srv/?a=media&t=list', // 获取相册列表
12 + SAVE_PHOTO: '/srv/?a=media&t=add', // 保存相册
13 + DELETE_PHOTO: '/srv/?a=media&t=del', // 删除相册
14 +}
15 +
16 +/**
17 + * @description 获取相册列表
18 + * @param {Object} params - 请求参数
19 + * @param {number} params.page - 页码,从0开始
20 + * @param {number} params.limit - 每页数量,默认10
21 + * @returns {Object} response - 响应对象
22 + * @returns {number} response.code - 响应状态码
23 + * @returns {string} response.msg - 响应消息
24 + * @returns {Object} response.data[] - 响应数据
25 + * @returns {number} response.data.id - 图片ID
26 + * @returns {string} response.data.media_type - 媒体文件类型 必需 IMAGE=图片, VIDEO=视频
27 + * @returns {string} response.data.media_url - 媒体文件URL
28 + * @returns {Boolean} response.data.is_my - 是否是我的相册 1=是, 0=否
29 + * @returns {string} response.data.thumbnail - 缩略图URL
30 + */
31 +export const getPhotoListAPI = (params = {}) => fn(fetch.post(Api.PHOTO_LIST, params));
32 +
33 +/**
34 + * @description 保存相册
35 + * @param {Object} params - 请求参数
36 + * @param {string} params.media_type - 媒体文件类型 必需 IMAGE=图片, VIDEO=视频
37 + * @param {string} params.media_url - 媒体文件URL 必需
38 + * @param {string} params.source_type - 上传来源 必需 CHECK_IN=打卡, COMPANION=陪伴
39 + * @param {string} params.source_id - 上传来源ID 必需
40 + * @param {string} params.qiniu_audit - 七牛云审核状态 必需
41 + * @param {Object} data - 请求数据
42 + * @returns {Promise} 返回保存结果
43 + */
44 +export const savePhotoAPI = (data) => fn(fetch.post(Api.SAVE_PHOTO, data));
45 +
46 +/**
47 + * @description 删除相册
48 + * @param {Array} ids - 要删除的图片/视频ID数组
49 + * @returns {Promise} 返回删除结果
50 + */
51 +export const deletePhotoAPI = (data) => fn(fetch.post(Api.DELETE_PHOTO, data));
...@@ -5,31 +5,54 @@ ...@@ -5,31 +5,54 @@
5 <view class="flex justify-between items-center mb-2"> 5 <view class="flex justify-between items-center mb-2">
6 <h2 class="font-medium text-lg">多彩瞬间</h2> 6 <h2 class="font-medium text-lg">多彩瞬间</h2>
7 <view class="text-blue-500 flex items-center text-xs" @click="openAlbumList"> 7 <view class="text-blue-500 flex items-center text-xs" @click="openAlbumList">
8 - 查看更多 8 + 进入相册
9 </view> 9 </view>
10 </view> 10 </view>
11 <p class="text-sm text-gray-500 mb-3">记录每一个家庭活动瞬间</p> 11 <p class="text-sm text-gray-500 mb-3">记录每一个家庭活动瞬间</p>
12 - <view class="grid grid-cols-2 gap-3"> 12 + <!-- 加载状态 -->
13 + <view v-if="loading" class="grid grid-cols-2 gap-3">
14 + <view
15 + v-for="n in 4"
16 + :key="n"
17 + class="rounded-lg h-32 bg-gray-200 animate-pulse"
18 + ></view>
19 + </view>
20 +
21 + <!-- 相册内容 -->
22 + <view v-else-if="albumData.length > 0" class="grid grid-cols-2 gap-3">
13 <view 23 <view
14 v-for="(item, index) in albumData" 24 v-for="(item, index) in albumData"
15 - :key="index" 25 + :key="item.id || index"
16 class="rounded-lg overflow-hidden h-32 relative cursor-pointer" 26 class="rounded-lg overflow-hidden h-32 relative cursor-pointer"
17 @click="handleMediaClick(item, albumData)" 27 @click="handleMediaClick(item, albumData)"
18 > 28 >
19 <image 29 <image
20 - :src="item.type === 'video' ? item.thumbnail : item.url" 30 + :src="item.media_type === 'VIDEO' ? item.thumbnail : item.media_url"
21 alt="家庭活动照片" 31 alt="家庭活动照片"
22 class="w-full h-full object-cover rounded-lg" 32 class="w-full h-full object-cover rounded-lg"
23 /> 33 />
24 <!-- 视频标识 --> 34 <!-- 视频标识 -->
25 <view 35 <view
26 - v-if="item.type === 'video'" 36 + v-if="item.media_type === 'VIDEO'"
27 class="absolute top-2 left-2 px-2 py-1 bg-black bg-opacity-70 rounded text-white text-xs" 37 class="absolute top-2 left-2 px-2 py-1 bg-black bg-opacity-70 rounded text-white text-xs"
28 > 38 >
29 视频 39 视频
30 </view> 40 </view>
41 + <!-- 我的标识 -->
42 + <!-- <view
43 + v-if="item.is_my"
44 + class="absolute top-2 right-2 px-2 py-1 bg-blue-500 bg-opacity-80 rounded text-white text-xs"
45 + >
46 + 我的
47 + </view> -->
31 </view> 48 </view>
32 </view> 49 </view>
50 +
51 + <!-- 空状态 -->
52 + <view v-else class="text-center py-8 text-gray-400">
53 + <view class="text-sm">暂无相册内容</view>
54 + <!-- <view class="text-xs mt-1">快去上传第一张照片吧~</view> -->
55 + </view>
33 </view> 56 </view>
34 57
35 <!-- 图片预览 --> 58 <!-- 图片预览 -->
...@@ -81,10 +104,11 @@ ...@@ -81,10 +104,11 @@
81 </template> 104 </template>
82 105
83 <script setup> 106 <script setup>
84 -import { ref } from 'vue'; 107 +import { ref, onMounted } from 'vue';
85 -import Taro from '@tarojs/taro'; 108 +import Taro, { useDidShow } from '@tarojs/taro';
86 import { Close } from '@nutui/icons-vue-taro'; 109 import { Close } from '@nutui/icons-vue-taro';
87 import { useMediaPreview } from '@/composables/useMediaPreview'; 110 import { useMediaPreview } from '@/composables/useMediaPreview';
111 +import { getPhotoListAPI } from '@/api/photo';
88 112
89 // 使用媒体预览 composable 113 // 使用媒体预览 composable
90 const { 114 const {
...@@ -104,17 +128,29 @@ const { ...@@ -104,17 +128,29 @@ const {
104 } = useMediaPreview(); 128 } = useMediaPreview();
105 129
106 // 家庭相册数据 130 // 家庭相册数据
107 -const albumData = ref([ 131 +const albumData = ref([]);
108 - { 132 +const loading = ref(false);
109 - type: 'image', 133 +
110 - url: 'https://cdn.ipadbiz.cn/hager/0513-1_FsxMk28AGz6N_D1zZFFOl_EaRdss.png', 134 +/**
111 - }, 135 + * 获取家庭相册数据
112 - { 136 + */
113 - type: 'video', 137 +const fetchAlbumData = async () => {
114 - url: 'https://vjs.zencdn.net/v/oceans.mp4', 138 + try {
115 - thumbnail: 'https://cdn.ipadbiz.cn/hager/0513-1_FsxMk28AGz6N_D1zZFFOl_EaRdss.png', 139 + loading.value = true;
140 + const response = await getPhotoListAPI({
141 + page: 0,
142 + limit: 4 // 首页只显示4张
143 + });
144 +
145 + if (response.code) {
146 + albumData.value = response.data || [];
147 + }
148 + } catch (error) {
149 + console.error('获取相册数据失败:', error);
150 + } finally {
151 + loading.value = false;
116 } 152 }
117 -]); 153 +};
118 154
119 /** 155 /**
120 * 打开相册列表页面 156 * 打开相册列表页面
...@@ -122,6 +158,15 @@ const albumData = ref([ ...@@ -122,6 +158,15 @@ const albumData = ref([
122 const openAlbumList = () => { 158 const openAlbumList = () => {
123 Taro.navigateTo({ url: '/pages/AlbumList/index' }); 159 Taro.navigateTo({ url: '/pages/AlbumList/index' });
124 }; 160 };
161 +
162 +// 组件挂载时获取数据
163 +onMounted(() => {
164 + fetchAlbumData();
165 +});
166 +
167 +useDidShow(() => {
168 + fetchAlbumData();
169 +});
125 </script> 170 </script>
126 171
127 <style lang="less" scoped> 172 <style lang="less" scoped>
......
...@@ -19,21 +19,35 @@ export function useMediaPreview() { ...@@ -19,21 +19,35 @@ export function useMediaPreview() {
19 /** 19 /**
20 * 处理媒体项目点击事件 20 * 处理媒体项目点击事件
21 * @param {Object} item - 媒体项目 21 * @param {Object} item - 媒体项目
22 + * @param {number} item.id - 媒体ID
23 + * @param {string} item.media_type - 媒体类型 IMAGE=图片, VIDEO=视频
24 + * @param {string} item.media_url - 媒体文件URL
25 + * @param {string} item.thumbnail - 缩略图URL
26 + * @param {boolean} item.is_my - 是否是我的相册
22 * @param {Array} mediaList - 完整的媒体列表 27 * @param {Array} mediaList - 完整的媒体列表
23 */ 28 */
24 const handleMediaClick = (item, mediaList = []) => { 29 const handleMediaClick = (item, mediaList = []) => {
25 - if (item.type === 'image') { 30 + if (item.media_type === 'IMAGE') {
26 // 图片预览 31 // 图片预览
27 - const imageItems = mediaList.filter(media => media.type === 'image'); 32 + const imageItems = mediaList.filter(media => media.media_type === 'IMAGE');
28 - previewImages.value = imageItems.map(img => ({ src: img.url })); 33 + previewImages.value = imageItems.map(img => ({
34 + src: img.media_url,
35 + id: img.id,
36 + thumbnail: img.thumbnail,
37 + is_my: img.is_my
38 + }));
29 39
30 // 计算当前图片在图片列表中的索引 40 // 计算当前图片在图片列表中的索引
31 - const imageIndex = imageItems.findIndex(img => img.url === item.url); 41 + const imageIndex = imageItems.findIndex(img => img.media_url === item.media_url);
32 previewIndex.value = imageIndex >= 0 ? imageIndex : 0; 42 previewIndex.value = imageIndex >= 0 ? imageIndex : 0;
33 previewVisible.value = true; 43 previewVisible.value = true;
34 - } else if (item.type === 'video') { 44 + } else if (item.media_type === 'VIDEO') {
35 // 视频播放 45 // 视频播放
36 - currentVideo.value = item; 46 + currentVideo.value = {
47 + ...item,
48 + url: item.media_url, // 兼容原有的url字段
49 + type: 'video' // 兼容原有的type字段
50 + };
37 videoId.value = Date.now(); // 生成新的视频ID 51 videoId.value = Date.now(); // 生成新的视频ID
38 videoVisible.value = true; 52 videoVisible.value = true;
39 } 53 }
...@@ -51,21 +65,42 @@ export function useMediaPreview() { ...@@ -51,21 +65,42 @@ export function useMediaPreview() {
51 65
52 /** 66 /**
53 * 预览多张图片 67 * 预览多张图片
54 - * @param {Array} images - 图片URL数组 68 + * @param {Array} images - 图片数组,可以是URL字符串数组或媒体对象数组
55 * @param {number} index - 初始显示的图片索引 69 * @param {number} index - 初始显示的图片索引
56 */ 70 */
57 const previewMultipleImages = (images, index = 0) => { 71 const previewMultipleImages = (images, index = 0) => {
58 - previewImages.value = images.map(url => ({ src: url })); 72 + previewImages.value = images.map(item => {
73 + if (typeof item === 'string') {
74 + // 兼容URL字符串数组
75 + return { src: item };
76 + } else {
77 + // 处理媒体对象数组
78 + return {
79 + src: item.media_url || item.url,
80 + id: item.id,
81 + thumbnail: item.thumbnail,
82 + is_my: item.is_my
83 + };
84 + }
85 + });
59 previewIndex.value = index; 86 previewIndex.value = index;
60 previewVisible.value = true; 87 previewVisible.value = true;
61 }; 88 };
62 89
63 /** 90 /**
64 * 播放视频 91 * 播放视频
65 - * @param {Object} video - 视频对象 {url, thumbnail, duration} 92 + * @param {Object} video - 视频对象
93 + * @param {string} video.media_url - 视频URL
94 + * @param {string} video.thumbnail - 缩略图URL
95 + * @param {number} video.id - 视频ID
96 + * @param {boolean} video.is_my - 是否是我的视频
66 */ 97 */
67 const playVideo = (video) => { 98 const playVideo = (video) => {
68 - currentVideo.value = video; 99 + currentVideo.value = {
100 + ...video,
101 + url: video.media_url || video.url, // 兼容新旧字段
102 + type: 'video'
103 + };
69 videoId.value = Date.now(); 104 videoId.value = Date.now();
70 videoVisible.value = true; 105 videoVisible.value = true;
71 }; 106 };
...@@ -132,6 +167,59 @@ export function useMediaPreview() { ...@@ -132,6 +167,59 @@ export function useMediaPreview() {
132 return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; 167 return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
133 }; 168 };
134 169
170 + /**
171 + * 判断媒体类型是否为图片
172 + * @param {Object} media - 媒体对象
173 + * @returns {boolean} 是否为图片
174 + */
175 + const isImage = (media) => {
176 + return media.media_type === 'IMAGE' || media.type === 'image';
177 + };
178 +
179 + /**
180 + * 判断媒体类型是否为视频
181 + * @param {Object} media - 媒体对象
182 + * @returns {boolean} 是否为视频
183 + */
184 + const isVideo = (media) => {
185 + return media.media_type === 'VIDEO' || media.type === 'video';
186 + };
187 +
188 + /**
189 + * 获取媒体URL
190 + * @param {Object} media - 媒体对象
191 + * @returns {string} 媒体URL
192 + */
193 + const getMediaUrl = (media) => {
194 + return media.media_url || media.url || '';
195 + };
196 +
197 + /**
198 + * 获取缩略图URL
199 + * @param {Object} media - 媒体对象
200 + * @returns {string} 缩略图URL
201 + */
202 + const getThumbnailUrl = (media) => {
203 + return media.thumbnail || media.media_url || media.url || '';
204 + };
205 +
206 + /**
207 + * 转换接口数据为预览格式
208 + * @param {Array} mediaList - 接口返回的媒体列表
209 + * @returns {Array} 转换后的媒体列表
210 + */
211 + const transformMediaList = (mediaList = []) => {
212 + return mediaList.map(item => ({
213 + id: item.id,
214 + type: item.media_type === 'IMAGE' ? 'image' : 'video',
215 + media_type: item.media_type,
216 + url: item.media_url,
217 + media_url: item.media_url,
218 + thumbnail: item.thumbnail,
219 + is_my: item.is_my
220 + }));
221 + };
222 +
135 return { 223 return {
136 // 状态 224 // 状态
137 previewVisible, 225 previewVisible,
...@@ -152,6 +240,13 @@ export function useMediaPreview() { ...@@ -152,6 +240,13 @@ export function useMediaPreview() {
152 handleVideoPause, 240 handleVideoPause,
153 handleFullscreenChange, 241 handleFullscreenChange,
154 handleVideoError, 242 handleVideoError,
155 - formatDuration 243 + formatDuration,
244 +
245 + // 辅助方法
246 + isImage,
247 + isVideo,
248 + getMediaUrl,
249 + getThumbnailUrl,
250 + transformMediaList
156 }; 251 };
157 } 252 }
......
...@@ -32,6 +32,23 @@ ...@@ -32,6 +32,23 @@
32 视频 32 视频
33 </view> 33 </view>
34 34
35 + <!-- 我的标识 -->
36 + <view
37 + v-if="item.is_my"
38 + class="absolute top-2 right-2 px-2 py-1 bg-blue-500 bg-opacity-80 rounded text-white text-xs"
39 + >
40 + 我的
41 + </view>
42 +
43 + <!-- 删除按钮 -->
44 + <view
45 + v-if="item.is_my"
46 + @click.stop="handleDeleteClick(item)"
47 + class="absolute bottom-2 right-2 px-2 py-1 bg-red-500 bg-opacity-80 rounded text-white text-xs"
48 + >
49 + 删除
50 + </view>
51 +
35 <!-- 视频时长 --> 52 <!-- 视频时长 -->
36 <!-- <view 53 <!-- <view
37 v-if="item.type === 'video'" 54 v-if="item.type === 'video'"
...@@ -94,14 +111,17 @@ ...@@ -94,14 +111,17 @@
94 @fullscreenchange="handleFullscreenChange" 111 @fullscreenchange="handleFullscreenChange"
95 /> 112 />
96 </view> 113 </view>
114 +
115 +
97 </view> 116 </view>
98 </template> 117 </template>
99 118
100 <script setup> 119 <script setup>
101 import { ref, onMounted } from 'vue'; 120 import { ref, onMounted } from 'vue';
102 -import Taro from '@tarojs/taro'; 121 +import Taro, { useDidShow } from '@tarojs/taro';
103 -import { Left, Service, Photograph, Close } from '@nutui/icons-vue-taro'; 122 +import { Photograph, Close } from '@nutui/icons-vue-taro';
104 import { useMediaPreview } from '@/composables/useMediaPreview'; 123 import { useMediaPreview } from '@/composables/useMediaPreview';
124 +import { getPhotoListAPI, deletePhotoAPI } from '@/api/photo';
105 125
106 // 响应式数据 126 // 响应式数据
107 const albumList = ref([]); 127 const albumList = ref([]);
...@@ -123,69 +143,50 @@ const { ...@@ -123,69 +143,50 @@ const {
123 handleVideoError, 143 handleVideoError,
124 } = useMediaPreview(); 144 } = useMediaPreview();
125 145
146 +// 删除相关状态
147 +const currentDeleteItem = ref(null);
148 +
126 /** 149 /**
127 * 页面加载时设置标题和初始化数据 150 * 页面加载时设置标题和初始化数据
128 */ 151 */
129 onMounted(() => { 152 onMounted(() => {
130 Taro.setNavigationBarTitle({ title: '家庭相册' }); 153 Taro.setNavigationBarTitle({ title: '家庭相册' });
131 - initMockData(); 154 + fetchAlbumList();
155 +});
156 +
157 +useDidShow(() => {
158 + fetchAlbumList();
132 }); 159 });
133 160
134 /** 161 /**
135 - * 初始化模拟数据 162 + * 获取相册列表数据
136 */ 163 */
137 -const initMockData = () => { 164 +const fetchAlbumList = async () => {
138 - albumList.value = [ 165 + try {
139 - { 166 + Taro.showLoading({ title: '加载中...' });
140 - type: 'image', 167 + const response = await getPhotoListAPI({ page: 0, limit: 50 });
141 - url: 'https://img.yzcdn.cn/vant/cat.jpeg', 168 +
142 - createTime: '2024-01-15 10:30' 169 + if (response.code) {
143 - }, 170 + // 转换接口数据格式为组件需要的格式
144 - { 171 + albumList.value = response.data.map(item => ({
145 - type: 'video', 172 + id: item.id,
146 - url: 'https://vjs.zencdn.net/v/oceans.mp4', 173 + type: item.media_type === 'IMAGE' ? 'image' : 'video',
147 - thumbnail: 'https://img.yzcdn.cn/vant/apple-1.jpg', 174 + url: item.media_url,
148 - // duration: 125, // 秒 175 + thumbnail: item.thumbnail || item.media_url, // 使用thumbnail字段,如果没有则使用media_url
149 - createTime: '2024-01-14 16:20' 176 + is_my: item.is_my,
150 - }, 177 + media_type: item.media_type,
151 - { 178 + media_url: item.media_url
152 - type: 'image', 179 + }));
153 - url: 'https://img.yzcdn.cn/vant/apple-2.jpg', 180 + }
154 - createTime: '2024-01-13 14:15' 181 + } catch (error) {
155 - }, 182 + console.error('获取相册列表失败:', error);
156 - { 183 + Taro.showToast({
157 - type: 'image', 184 + title: '获取相册列表失败',
158 - url: 'https://img.yzcdn.cn/vant/apple-3.jpg', 185 + icon: 'none'
159 - createTime: '2024-01-12 09:45' 186 + });
160 - }, 187 + } finally {
161 - { 188 + Taro.hideLoading();
162 - type: 'video', 189 + }
163 - url: 'https://vjs.zencdn.net/v/oceans.mp4',
164 - thumbnail: 'https://img.yzcdn.cn/vant/apple-4.jpg',
165 - createTime: '2024-01-11 18:30'
166 - },
167 - {
168 - type: 'image',
169 - url: 'https://img.yzcdn.cn/vant/tree.jpg',
170 - createTime: '2024-01-10 12:00'
171 - },
172 - {
173 - type: 'video',
174 - url: 'https://vjs.zencdn.net/v/oceans.mp4',
175 - thumbnail: 'https://img.yzcdn.cn/vant/leaf.jpg',
176 - createTime: '2024-01-09 15:20'
177 - },
178 - {
179 - type: 'image',
180 - url: 'https://img.yzcdn.cn/vant/sand.jpg',
181 - createTime: '2024-01-08 11:10'
182 - },
183 - {
184 - type: 'image',
185 - url: 'https://img.yzcdn.cn/vant/sand.jpg',
186 - createTime: '2024-01-08 11:10'
187 - },
188 - ];
189 }; 190 };
190 191
191 /** 192 /**
...@@ -196,6 +197,69 @@ const initMockData = () => { ...@@ -196,6 +197,69 @@ const initMockData = () => {
196 const handleItemClick = (item, index) => { 197 const handleItemClick = (item, index) => {
197 handleMediaClick(item, albumList.value); 198 handleMediaClick(item, albumList.value);
198 }; 199 };
200 +
201 +/**
202 + * 处理删除按钮点击事件
203 + * @param {Object} item - 要删除的相册项目
204 + */
205 +const handleDeleteClick = (item) => {
206 + currentDeleteItem.value = item;
207 +
208 + Taro.showModal({
209 + title: '删除确认',
210 + content: '确定要删除这张照片吗?删除后无法恢复。',
211 + success: (res) => {
212 + if (res.confirm) {
213 + confirmDelete();
214 + } else if (res.cancel) {
215 + cancelDelete();
216 + }
217 + }
218 + });
219 +};
220 +
221 +/**
222 + * 确认删除相册项目
223 + */
224 +const confirmDelete = async () => {
225 + if (!currentDeleteItem.value) return;
226 +
227 + try {
228 + Taro.showLoading({ title: '删除中...' });
229 + const response = await deletePhotoAPI({ ids: [currentDeleteItem.value.id] });
230 +
231 + if (response.code) {
232 + // 从列表中移除已删除的项目
233 + const index = albumList.value.findIndex(item => item.id === currentDeleteItem.value.id);
234 + if (index > -1) {
235 + albumList.value.splice(index, 1);
236 + }
237 +
238 + Taro.showToast({
239 + title: '删除成功',
240 + icon: 'success'
241 + });
242 + } else {
243 + throw new Error(response.msg || '删除失败');
244 + }
245 + } catch (error) {
246 + console.error('删除相册项目失败:', error);
247 + Taro.showToast({
248 + title: '删除失败',
249 + icon: 'none'
250 + });
251 + } finally {
252 + Taro.hideLoading();
253 + currentDeleteItem.value = null;
254 + }
255 +};
256 +
257 +/**
258 + * 取消删除
259 + */
260 +const cancelDelete = () => {
261 + currentDeleteItem.value = null;
262 +};
199 </script> 263 </script>
200 264
201 <style scoped> 265 <style scoped>
......
...@@ -144,8 +144,9 @@ ...@@ -144,8 +144,9 @@
144 <script setup> 144 <script setup>
145 import { ref, onMounted } from 'vue'; 145 import { ref, onMounted } from 'vue';
146 import Taro from '@tarojs/taro'; 146 import Taro from '@tarojs/taro';
147 -import { Left, Photograph, Close } from '@nutui/icons-vue-taro'; 147 +import { Photograph, Close } from '@nutui/icons-vue-taro';
148 -import BASE_URL, { THEME_COLORS } from '@/utils/config'; 148 +import BASE_URL from '@/utils/config';
149 +import { savePhotoAPI } from '@/api/photo';
149 150
150 // 151 //
151 const playIcon = 'https://cdn.ipadbiz.cn/lls_prog/icon/play.svg'; 152 const playIcon = 'https://cdn.ipadbiz.cn/lls_prog/icon/play.svg';
...@@ -433,20 +434,18 @@ const saveMedia = async () => { ...@@ -433,20 +434,18 @@ const saveMedia = async () => {
433 }); 434 });
434 435
435 try { 436 try {
436 - // TODO: 这里预留给后续的接口调用 437 + // 调用后端接口保存媒体信息
437 - // 调用后端接口保存媒体信息,传递 uploadedFile.value.serverUrl 438 + const saveData = {
438 - // const result = await saveMediaToBackend({ 439 + media_type: uploadedFile.value.type === 'image' ? 'IMAGE' : 'VIDEO',
439 - // type: uploadedFile.value.type, 440 + media_url: uploadedFile.value.serverUrl,
440 - // url: uploadedFile.value.serverUrl, 441 + source_type: pageParams.value.from === 'checkin' ? 'CHECK_IN' : 'COMPANION',
441 - // size: uploadedFile.value.size, 442 + source_id: pageParams.value.id || '0',
442 - // name: uploadedFile.value.name, 443 + qiniu_audit: uploadedFile.value.qiniu_audit || ''
443 - // duration: uploadedFile.value.duration, // 仅视频有此字段 444 + };
444 - // qiniu_audit: uploadedFile.value.qiniu_audit, 445 +
445 - // }); 446 + const result = await savePhotoAPI(saveData);
446 -
447 - // 模拟接口调用
448 - await new Promise(resolve => setTimeout(resolve, 1000));
449 447
448 + if (result.code) {
450 Taro.hideLoading(); 449 Taro.hideLoading();
451 Taro.showToast({ 450 Taro.showToast({
452 title: '保存成功,获得积分奖励!', 451 title: '保存成功,获得积分奖励!',
...@@ -466,25 +465,17 @@ const saveMedia = async () => { ...@@ -466,25 +465,17 @@ const saveMedia = async () => {
466 Taro.navigateBack(); 465 Taro.navigateBack();
467 } 466 }
468 }, 2000); 467 }, 2000);
468 + } else {
469 + throw new Error(result.msg || '保存失败');
470 + }
469 } catch (error) { 471 } catch (error) {
470 console.error('保存失败:', error); 472 console.error('保存失败:', error);
471 Taro.hideLoading(); 473 Taro.hideLoading();
472 Taro.showToast({ 474 Taro.showToast({
473 - title: '保存失败,请重试', 475 + title: error.message || '保存失败,请重试',
474 icon: 'error', 476 icon: 'error',
475 duration: 2000 477 duration: 2000
476 }); 478 });
477 } 479 }
478 }; 480 };
479 -
480 -/**
481 - * 返回上一页
482 - */
483 -const goBack = () => {
484 - Taro.navigateBack();
485 -};
486 </script> 481 </script>
487 -
488 -<style scoped>
489 -/* 自定义样式 */
490 -</style>
......