hookehuyr

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

- 在app.config.js中添加AlbumList页面路由配置
- 修改Dashboard页面跳转逻辑,从活动页面改为相册列表
- 新增AlbumList页面,包含图片和视频展示、预览功能
......@@ -26,6 +26,7 @@ export default {
'pages/CouponDetail/index',
'pages/EditProfile/index',
'pages/EditFamily/index',
'pages/AlbumList/index',
],
window: {
backgroundTextStyle: 'light',
......
<template>
<view class="min-h-screen bg-gray-50">
<!-- Header -->
<!-- <view class="bg-white px-4 py-3 flex items-center justify-between border-b border-gray-100">
<view class="flex items-center" @click="goBack">
<Left size="20" class="text-gray-600" />
<text class="ml-2 text-lg font-medium">家庭相册</text>
</view>
</view> -->
<!-- Album Grid -->
<view class="p-4">
<view class="grid grid-cols-2 gap-3">
<view
v-for="(item, index) in albumList"
:key="index"
class="relative aspect-square rounded-lg overflow-hidden bg-white shadow-sm"
@click="handleItemClick(item, index)"
>
<!-- 图片封面 -->
<image
:src="item.type === 'video' ? item.thumbnail : item.url"
class="w-full h-full object-cover"
mode="aspectFill"
/>
<!-- 视频播放标识 -->
<view
v-if="item.type === 'video'"
class="absolute top-2 left-2 px-2 py-1 bg-black bg-opacity-70 rounded text-white text-xs"
>
视频
</view>
<!-- 视频时长 -->
<view
v-if="item.type === 'video'"
class="absolute bottom-2 right-2 bg-black bg-opacity-60 text-white text-xs px-2 py-1 rounded"
>
{{ formatDuration(item.duration) }}
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="albumList.length === 0" class="flex flex-col items-center justify-center py-20">
<Photograph size="48" class="text-gray-300 mb-4" />
<text class="text-gray-400">暂无相册内容</text>
</view>
</view>
<!-- 图片预览 -->
<nut-image-preview
v-model:show="previewVisible"
:images="previewImages"
:init-no="previewIndex"
@close="closePreview"
/>
<!-- 视频播放器 -->
<view
v-if="videoVisible"
class="fixed inset-0 bg-black"
style="z-index: 9999;"
@click="closeVideo"
>
<!-- 关闭按钮 -->
<view
@click.stop="closeVideo"
class="absolute top-4 right-4 w-10 h-10 bg-black bg-opacity-50 rounded-full flex items-center justify-center"
style="z-index: 10000;"
>
<Close size="24" class="text-white" />
</view>
<!-- 视频播放器 -->
<video
v-if="currentVideo"
:id="'album-video-' + videoId"
:src="currentVideo.url"
:poster="currentVideo.thumbnail"
:controls="true"
:autoplay="false"
:show-center-play-btn="true"
:show-play-btn="true"
:object-fit="'contain'"
:show-fullscreen-btn="true"
style="width: 100vw; height: 100vh; position: absolute; top: 0; left: 0;"
@click.stop
@play="handleVideoPlay"
@pause="handleVideoPause"
@error="handleVideoError"
@fullscreenchange="handleFullscreenChange"
/>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import Taro from '@tarojs/taro';
import { Left, Service, Photograph, Close } from '@nutui/icons-vue-taro';
// 响应式数据
const albumList = ref([]);
const previewVisible = ref(false);
const previewImages = ref([]);
const previewIndex = ref(0);
const videoVisible = ref(false);
const currentVideo = ref(null);
const videoId = ref(Date.now());
/**
* 页面加载时设置标题和初始化数据
*/
onMounted(() => {
Taro.setNavigationBarTitle({ title: '家庭相册' });
initMockData();
});
/**
* 初始化模拟数据
*/
const initMockData = () => {
albumList.value = [
{
type: 'image',
url: 'https://img.yzcdn.cn/vant/cat.jpeg',
createTime: '2024-01-15 10:30'
},
{
type: 'video',
url: 'https://vjs.zencdn.net/v/oceans.mp4',
thumbnail: 'https://img.yzcdn.cn/vant/apple-1.jpg',
duration: 125, // 秒
createTime: '2024-01-14 16:20'
},
{
type: 'image',
url: 'https://img.yzcdn.cn/vant/apple-2.jpg',
createTime: '2024-01-13 14:15'
},
{
type: 'image',
url: 'https://img.yzcdn.cn/vant/apple-3.jpg',
createTime: '2024-01-12 09:45'
},
{
type: 'video',
url: 'https://vjs.zencdn.net/v/oceans.mp4',
thumbnail: 'https://img.yzcdn.cn/vant/apple-4.jpg',
duration: 30,
createTime: '2024-01-11 18:30'
},
{
type: 'image',
url: 'https://img.yzcdn.cn/vant/tree.jpg',
createTime: '2024-01-10 12:00'
},
{
type: 'video',
url: 'https://vjs.zencdn.net/v/oceans.mp4',
thumbnail: 'https://img.yzcdn.cn/vant/leaf.jpg',
duration: 60,
createTime: '2024-01-09 15:20'
},
{
type: 'image',
url: 'https://img.yzcdn.cn/vant/sand.jpg',
createTime: '2024-01-08 11:10'
}
];
};
/**
* 处理项目点击事件
* @param {Object} item - 相册项目
* @param {number} index - 项目索引
*/
const handleItemClick = (item, index) => {
if (item.type === 'image') {
// 图片预览
const imageItems = albumList.value.filter(item => item.type === 'image');
previewImages.value = imageItems.map(img => ({ src: img.url }));
// 计算当前图片在图片列表中的索引
const imageIndex = imageItems.findIndex(img => img.url === item.url);
previewIndex.value = imageIndex;
previewVisible.value = true;
} else if (item.type === 'video') {
// 视频播放
currentVideo.value = item;
videoId.value = Date.now(); // 生成新的视频ID
videoVisible.value = true;
}
};
/**
* 格式化视频时长
* @param {number} seconds - 秒数
* @returns {string} 格式化后的时长
*/
const formatDuration = (seconds) => {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
};
/**
* 关闭图片预览
*/
const closePreview = () => {
previewVisible.value = false;
};
/**
* 关闭视频播放
*/
const closeVideo = () => {
videoVisible.value = false;
currentVideo.value = null;
};
/**
* 处理视频播放
*/
const handleVideoPlay = () => {
console.log('视频开始播放');
};
/**
* 处理视频暂停
*/
const handleVideoPause = () => {
console.log('视频暂停播放');
};
/**
* 处理全屏状态变化
* @param {Event} event - 全屏事件
*/
const handleFullscreenChange = (event) => {
console.log('全屏状态变化:', event.detail);
};
/**
* 处理视频播放错误
* @param {Event} error - 错误事件
*/
const handleVideoError = (error) => {
console.error('视频播放错误:', error);
Taro.showToast({
title: '视频播放失败',
icon: 'error',
duration: 2000
});
// 关闭视频弹框
closeVideo();
};
/**
* 返回上一页
*/
const goBack = () => {
Taro.navigateBack();
};
</script>
<style scoped>
.grid {
display: grid;
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.gap-3 {
gap: 0.75rem;
}
.aspect-square {
aspect-ratio: 1 / 1;
}
</style>
......@@ -65,7 +65,7 @@
<view class="p-5 mt-6 mb-6 bg-white rounded-xl shadow-md mx-4">
<view class="flex justify-between items-center mb-2">
<h2 class="font-medium text-lg">家庭相册</h2>
<view class="text-blue-500 flex items-center" @click="goToActivities">
<view class="text-blue-500 flex items-center" @click="openAlbumList">
打开相册 <Right size="16" />
</view>
</view>
......@@ -146,7 +146,7 @@ const goToProfile = () => {
Taro.navigateTo({ url: '/pages/EditFamily/index' });
};
const goToActivities = () => {
const openAlbumList = () => {
Taro.navigateTo({ url: '/pages/AlbumList/index' });
};
</script>
......