hookehuyr

feat(相册): 添加家庭相册列表页面及跳转功能

- 在app.config.js中添加AlbumList页面路由配置
- 修改Dashboard页面跳转逻辑,从活动页面改为相册列表
- 新增AlbumList页面,包含图片和视频展示、预览功能
...@@ -26,6 +26,7 @@ export default { ...@@ -26,6 +26,7 @@ export default {
26 'pages/CouponDetail/index', 26 'pages/CouponDetail/index',
27 'pages/EditProfile/index', 27 'pages/EditProfile/index',
28 'pages/EditFamily/index', 28 'pages/EditFamily/index',
29 + 'pages/AlbumList/index',
29 ], 30 ],
30 window: { 31 window: {
31 backgroundTextStyle: 'light', 32 backgroundTextStyle: 'light',
......
1 +<template>
2 + <view class="min-h-screen bg-gray-50">
3 + <!-- Header -->
4 + <!-- <view class="bg-white px-4 py-3 flex items-center justify-between border-b border-gray-100">
5 + <view class="flex items-center" @click="goBack">
6 + <Left size="20" class="text-gray-600" />
7 + <text class="ml-2 text-lg font-medium">家庭相册</text>
8 + </view>
9 + </view> -->
10 +
11 + <!-- Album Grid -->
12 + <view class="p-4">
13 + <view class="grid grid-cols-2 gap-3">
14 + <view
15 + v-for="(item, index) in albumList"
16 + :key="index"
17 + class="relative aspect-square rounded-lg overflow-hidden bg-white shadow-sm"
18 + @click="handleItemClick(item, index)"
19 + >
20 + <!-- 图片封面 -->
21 + <image
22 + :src="item.type === 'video' ? item.thumbnail : item.url"
23 + class="w-full h-full object-cover"
24 + mode="aspectFill"
25 + />
26 +
27 + <!-- 视频播放标识 -->
28 + <view
29 + v-if="item.type === 'video'"
30 + class="absolute top-2 left-2 px-2 py-1 bg-black bg-opacity-70 rounded text-white text-xs"
31 + >
32 + 视频
33 + </view>
34 +
35 + <!-- 视频时长 -->
36 + <view
37 + v-if="item.type === 'video'"
38 + class="absolute bottom-2 right-2 bg-black bg-opacity-60 text-white text-xs px-2 py-1 rounded"
39 + >
40 + {{ formatDuration(item.duration) }}
41 + </view>
42 + </view>
43 + </view>
44 +
45 + <!-- 空状态 -->
46 + <view v-if="albumList.length === 0" class="flex flex-col items-center justify-center py-20">
47 + <Photograph size="48" class="text-gray-300 mb-4" />
48 + <text class="text-gray-400">暂无相册内容</text>
49 + </view>
50 + </view>
51 +
52 + <!-- 图片预览 -->
53 + <nut-image-preview
54 + v-model:show="previewVisible"
55 + :images="previewImages"
56 + :init-no="previewIndex"
57 + @close="closePreview"
58 + />
59 +
60 + <!-- 视频播放器 -->
61 + <view
62 + v-if="videoVisible"
63 + class="fixed inset-0 bg-black"
64 + style="z-index: 9999;"
65 + @click="closeVideo"
66 + >
67 + <!-- 关闭按钮 -->
68 + <view
69 + @click.stop="closeVideo"
70 + class="absolute top-4 right-4 w-10 h-10 bg-black bg-opacity-50 rounded-full flex items-center justify-center"
71 + style="z-index: 10000;"
72 + >
73 + <Close size="24" class="text-white" />
74 + </view>
75 +
76 + <!-- 视频播放器 -->
77 + <video
78 + v-if="currentVideo"
79 + :id="'album-video-' + videoId"
80 + :src="currentVideo.url"
81 + :poster="currentVideo.thumbnail"
82 + :controls="true"
83 + :autoplay="false"
84 + :show-center-play-btn="true"
85 + :show-play-btn="true"
86 + :object-fit="'contain'"
87 + :show-fullscreen-btn="true"
88 + style="width: 100vw; height: 100vh; position: absolute; top: 0; left: 0;"
89 + @click.stop
90 + @play="handleVideoPlay"
91 + @pause="handleVideoPause"
92 + @error="handleVideoError"
93 + @fullscreenchange="handleFullscreenChange"
94 + />
95 + </view>
96 + </view>
97 +</template>
98 +
99 +<script setup>
100 +import { ref, onMounted } from 'vue';
101 +import Taro from '@tarojs/taro';
102 +import { Left, Service, Photograph, Close } from '@nutui/icons-vue-taro';
103 +
104 +// 响应式数据
105 +const albumList = ref([]);
106 +const previewVisible = ref(false);
107 +const previewImages = ref([]);
108 +const previewIndex = ref(0);
109 +const videoVisible = ref(false);
110 +const currentVideo = ref(null);
111 +const videoId = ref(Date.now());
112 +
113 +/**
114 + * 页面加载时设置标题和初始化数据
115 + */
116 +onMounted(() => {
117 + Taro.setNavigationBarTitle({ title: '家庭相册' });
118 + initMockData();
119 +});
120 +
121 +/**
122 + * 初始化模拟数据
123 + */
124 +const initMockData = () => {
125 + albumList.value = [
126 + {
127 + type: 'image',
128 + url: 'https://img.yzcdn.cn/vant/cat.jpeg',
129 + createTime: '2024-01-15 10:30'
130 + },
131 + {
132 + type: 'video',
133 + url: 'https://vjs.zencdn.net/v/oceans.mp4',
134 + thumbnail: 'https://img.yzcdn.cn/vant/apple-1.jpg',
135 + duration: 125, // 秒
136 + createTime: '2024-01-14 16:20'
137 + },
138 + {
139 + type: 'image',
140 + url: 'https://img.yzcdn.cn/vant/apple-2.jpg',
141 + createTime: '2024-01-13 14:15'
142 + },
143 + {
144 + type: 'image',
145 + url: 'https://img.yzcdn.cn/vant/apple-3.jpg',
146 + createTime: '2024-01-12 09:45'
147 + },
148 + {
149 + type: 'video',
150 + url: 'https://vjs.zencdn.net/v/oceans.mp4',
151 + thumbnail: 'https://img.yzcdn.cn/vant/apple-4.jpg',
152 + duration: 30,
153 + createTime: '2024-01-11 18:30'
154 + },
155 + {
156 + type: 'image',
157 + url: 'https://img.yzcdn.cn/vant/tree.jpg',
158 + createTime: '2024-01-10 12:00'
159 + },
160 + {
161 + type: 'video',
162 + url: 'https://vjs.zencdn.net/v/oceans.mp4',
163 + thumbnail: 'https://img.yzcdn.cn/vant/leaf.jpg',
164 + duration: 60,
165 + createTime: '2024-01-09 15:20'
166 + },
167 + {
168 + type: 'image',
169 + url: 'https://img.yzcdn.cn/vant/sand.jpg',
170 + createTime: '2024-01-08 11:10'
171 + }
172 + ];
173 +};
174 +
175 +/**
176 + * 处理项目点击事件
177 + * @param {Object} item - 相册项目
178 + * @param {number} index - 项目索引
179 + */
180 +const handleItemClick = (item, index) => {
181 + if (item.type === 'image') {
182 + // 图片预览
183 + const imageItems = albumList.value.filter(item => item.type === 'image');
184 + previewImages.value = imageItems.map(img => ({ src: img.url }));
185 +
186 + // 计算当前图片在图片列表中的索引
187 + const imageIndex = imageItems.findIndex(img => img.url === item.url);
188 + previewIndex.value = imageIndex;
189 + previewVisible.value = true;
190 + } else if (item.type === 'video') {
191 + // 视频播放
192 + currentVideo.value = item;
193 + videoId.value = Date.now(); // 生成新的视频ID
194 + videoVisible.value = true;
195 + }
196 +};
197 +
198 +/**
199 + * 格式化视频时长
200 + * @param {number} seconds - 秒数
201 + * @returns {string} 格式化后的时长
202 + */
203 +const formatDuration = (seconds) => {
204 + const minutes = Math.floor(seconds / 60);
205 + const remainingSeconds = seconds % 60;
206 + return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
207 +};
208 +
209 +/**
210 + * 关闭图片预览
211 + */
212 +const closePreview = () => {
213 + previewVisible.value = false;
214 +};
215 +
216 +/**
217 + * 关闭视频播放
218 + */
219 +const closeVideo = () => {
220 + videoVisible.value = false;
221 + currentVideo.value = null;
222 +};
223 +
224 +/**
225 + * 处理视频播放
226 + */
227 +const handleVideoPlay = () => {
228 + console.log('视频开始播放');
229 +};
230 +
231 +/**
232 + * 处理视频暂停
233 + */
234 +const handleVideoPause = () => {
235 + console.log('视频暂停播放');
236 +};
237 +
238 +/**
239 + * 处理全屏状态变化
240 + * @param {Event} event - 全屏事件
241 + */
242 +const handleFullscreenChange = (event) => {
243 + console.log('全屏状态变化:', event.detail);
244 +};
245 +
246 +/**
247 + * 处理视频播放错误
248 + * @param {Event} error - 错误事件
249 + */
250 +const handleVideoError = (error) => {
251 + console.error('视频播放错误:', error);
252 + Taro.showToast({
253 + title: '视频播放失败',
254 + icon: 'error',
255 + duration: 2000
256 + });
257 + // 关闭视频弹框
258 + closeVideo();
259 +};
260 +
261 +/**
262 + * 返回上一页
263 + */
264 +const goBack = () => {
265 + Taro.navigateBack();
266 +};
267 +</script>
268 +
269 +<style scoped>
270 +.grid {
271 + display: grid;
272 +}
273 +
274 +.grid-cols-2 {
275 + grid-template-columns: repeat(2, minmax(0, 1fr));
276 +}
277 +
278 +.gap-3 {
279 + gap: 0.75rem;
280 +}
281 +
282 +.aspect-square {
283 + aspect-ratio: 1 / 1;
284 +}
285 +</style>
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
65 <view class="p-5 mt-6 mb-6 bg-white rounded-xl shadow-md mx-4"> 65 <view class="p-5 mt-6 mb-6 bg-white rounded-xl shadow-md mx-4">
66 <view class="flex justify-between items-center mb-2"> 66 <view class="flex justify-between items-center mb-2">
67 <h2 class="font-medium text-lg">家庭相册</h2> 67 <h2 class="font-medium text-lg">家庭相册</h2>
68 - <view class="text-blue-500 flex items-center" @click="goToActivities"> 68 + <view class="text-blue-500 flex items-center" @click="openAlbumList">
69 打开相册 <Right size="16" /> 69 打开相册 <Right size="16" />
70 </view> 70 </view>
71 </view> 71 </view>
...@@ -146,7 +146,7 @@ const goToProfile = () => { ...@@ -146,7 +146,7 @@ const goToProfile = () => {
146 Taro.navigateTo({ url: '/pages/EditFamily/index' }); 146 Taro.navigateTo({ url: '/pages/EditFamily/index' });
147 }; 147 };
148 148
149 -const goToActivities = () => { 149 +const openAlbumList = () => {
150 - 150 + Taro.navigateTo({ url: '/pages/AlbumList/index' });
151 }; 151 };
152 </script> 152 </script>
......