index.vue 7.56 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.is_my"
            class="absolute top-2 right-2 px-2 py-1 bg-blue-500 bg-opacity-80 rounded text-white text-xs"
          >
            我的
          </view> -->

          <!-- 删除按钮 -->
          <view
            v-if="item.is_my"
            @click.stop="handleDeleteClick(item)"
            class="absolute bottom-2 right-2 px-2 py-1 bg-red-500 bg-opacity-80 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>



    <!-- 视频播放器 -->
    <view
      v-if="videoVisible"
      class="fixed inset-0 bg-black"
      style="z-index: 9999;"
      @click="closeVideo"
    >
      <!-- 关闭按钮 -->
      <view
        @tap.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: 50vh; position: absolute; top: 20vh; left: 0;"
        @tap.stop
        @play="handleVideoPlay"
        @pause="handleVideoPause"
        @error="handleVideoError"
        @fullscreenchange="handleFullscreenChange"
      />
    </view>


  </view>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import Taro, { useDidShow } from '@tarojs/taro';
import { Photograph, Close } from '@nutui/icons-vue-taro';

import { getPhotoListAPI, deletePhotoAPI } from '@/api/photo';

// 响应式数据
const albumList = ref([]);

// 视频播放相关状态
const videoVisible = ref(false);
const currentVideo = ref(null);
const videoId = ref(0);

/**
 * 关闭视频播放器
 */
const closeVideo = () => {
  videoVisible.value = false;
  currentVideo.value = null;
};

/**
 * 视频播放事件处理
 */
const handleVideoPlay = () => {
  console.log('视频开始播放');
};

/**
 * 视频暂停事件处理
 */
const handleVideoPause = () => {
  console.log('视频暂停播放');
};

/**
 * 视频全屏变化事件处理
 */
const handleFullscreenChange = () => {
  console.log('视频全屏状态变化');
};

/**
 * 视频错误事件处理
 */
const handleVideoError = (error) => {
  console.error('视频播放错误:', error);
  Taro.showToast({
    title: '视频播放失败',
    icon: 'none'
  });
};

// 删除相关状态
const currentDeleteItem = ref(null);

/**
 * 页面加载时设置标题和初始化数据
 */
onMounted(() => {
  Taro.setNavigationBarTitle({ title: '家庭相册' });
  // fetchAlbumList();
});

useDidShow(() => {
  fetchAlbumList();
});

/**
 * 获取相册列表数据
 */
const fetchAlbumList = async () => {
  try {
    Taro.showLoading({ title: '加载中...' });
    const response = await getPhotoListAPI({ page: 0, limit: 50 });

    if (response.code) {
      // 转换接口数据格式为组件需要的格式
      albumList.value = response.data.map(item => ({
        id: item.id,
        type: item.media_type === 'IMAGE' ? 'image' : 'video',
        url: item.media_url,
        thumbnail: item.thumbnail,
        is_my: item.is_my,
        media_type: item.media_type,
        media_url: item.media_url
      }));
    }
  } catch (error) {
    console.error('获取相册列表失败:', error);
    Taro.showToast({
      title: '获取相册列表失败',
      icon: 'none'
    });
  } finally {
    Taro.hideLoading();
  }
};

/**
 * 处理项目点击事件
 * @param {Object} item - 相册项目
 * @param {number} index - 项目索引
 */
const handleItemClick = (item, index) => {
  if (item.type === 'video') {
    // 播放视频
    currentVideo.value = {
      url: item.url,
      thumbnail: item.thumbnail
    };
    videoId.value = Date.now();
    videoVisible.value = true;
  } else {
    // 预览图片
    const imageUrls = albumList.value
      .filter(media => media.type === 'image')
      .map(media => media.url);
    
    Taro.previewImage({
      urls: imageUrls,
      current: item.url
    });
  }
};

/**
 * 处理删除按钮点击事件
 * @param {Object} item - 要删除的相册项目
 */
const handleDeleteClick = (item) => {
  currentDeleteItem.value = item;

  Taro.showModal({
    title: '删除确认',
    content: '确定要删除这张照片吗?删除后无法恢复。',
    success: (res) => {
      if (res.confirm) {
        confirmDelete();
      } else if (res.cancel) {
        cancelDelete();
      }
    }
  });
};

/**
 * 确认删除相册项目
 */
const confirmDelete = async () => {
  if (!currentDeleteItem.value) return;

  try {
    Taro.showLoading({ title: '删除中...' });
    const response = await deletePhotoAPI({ ids: [currentDeleteItem.value.id] });

    if (response.code) {
      // 从列表中移除已删除的项目
      const index = albumList.value.findIndex(item => item.id === currentDeleteItem.value.id);
      if (index > -1) {
        albumList.value.splice(index, 1);
      }

      Taro.showToast({
        title: '删除成功',
        icon: 'success'
      });
    } else {
      throw new Error(response.msg || '删除失败');
    }
  } catch (error) {
    console.error('删除相册项目失败:', error);
    Taro.showToast({
      title: '删除失败',
      icon: 'none'
    });
  } finally {
    Taro.hideLoading();
    currentDeleteItem.value = null;
  }
};

/**
 * 取消删除
 */
const cancelDelete = () => {
  currentDeleteItem.value = null;
};
</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>