index.vue 7.07 KB
<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'
    },
    {
      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>