index.vue 10.2 KB
<template>
  <view class="min-h-screen flex flex-col bg-white pb-16" style="background-color: #F9FAFB;">
    <!-- Hero section with family name and background image -->
    <view class="relative h-48">
      <image :src="familyCover" alt="Family background" class="w-full h-full object-cover" />
      <view class="absolute inset-0 bg-black bg-opacity-30 flex flex-col justify-end p-5">
        <view class="absolute top-4 right-4 text-white flex items-center" @click="goToProfile">
          <Setting size="24" />
          <text class="ml-2">家庭设置</text>
        </view>
        <h1 class="text-white text-2xl font-bold">{{ familyName }}</h1>
        <p class="text-white opacity-90">{{ familySlogn }}</p>
      </view>
    </view>

    <!-- 微信步数授权组件 -->
    <WeRunAuth
      ref="weRunAuthRef"
      @auth-change="handleAuthChange"
      @steps-update="handleStepsUpdate"
    >
      <template #default="{ steps, isLoading }">
        <!-- Today's steps section -->
        <!-- TODO: 今日获取的步数怎么同步到下面那个积分池里面去 -->
        <view class="px-5 py-6 bg-white rounded-xl shadow-md mx-4 mt-4">
          <view class="flex justify-between items-center mb-1">
            <span class="text-gray-500">今日</span>
          </view>
          <view class="flex justify-between items-center">
            <view class="flex items-baseline">
              <span class="text-4xl font-bold">
                {{ isLoading ? '...' : steps.toLocaleString() }}
              </span>
              <span class="ml-1 text-gray-500">步</span>
            </view>
            <view class="flex gap-2">
              <!-- <view class="bg-green-500 text-white px-4 py-2 rounded-full text-xs" @click="handleRefreshSteps">
                更新步数
              </view> -->
              <view class="bg-blue-500 text-white px-4 py-2 rounded-full text-sm" @click="handleCollectAll">
                一键收取
              </view>
            </view>
          </view>
        </view>

        <!-- Points circles -->
        <view class="flex justify-between px-5 py-6 my-4 bg-white rounded-xl shadow-md mx-4">
          <PointsCollector ref="pointsCollectorRef" height="30vh" />
        </view>

        <!-- Photo button -->
        <view class="px-5 mb-4">
          <view @tap="openCamera" class="w-full bg-blue-500 text-white py-3 rounded-lg flex flex-col items-center justify-center">
            <view class="flex items-center justify-center">
              <Photograph size="20" class="mr-2" />
              拍照留念,奖励积分
            </view>
          </view>
        </view>

        <!-- Family step ranking -->
        <view class="p-5 bg-white rounded-xl shadow-md mx-4">
          <view class="flex justify-between items-center mb-4">
            <h2 class="font-medium text-lg">今日家庭步数排行</h2>
            <span class="text-sm text-gray-500">
              总计 {{ getTotalSteps(steps).toLocaleString() }} 步
            </span>
          </view>
          <view class="grid grid-cols-4 gap-2">
            <view v-for="member in familyMembers" :key="member.id" class="flex flex-col items-center">
              <image :src="member.avatar" :alt="member.name" class="w-16 h-16 rounded-full mb-1" />
              <span class="text-sm text-gray-700">
                {{ member.steps.toLocaleString() }}步
              </span>
            </view>
          </view>
        </view>
      </template>
    </WeRunAuth>

    <!-- 活动排行榜 -->
    <view class="px-5 mb-4 mt-4">
      <view @tap="openFamilyRank" class="w-full bg-blue-500 text-white py-3 rounded-lg flex flex-col items-center justify-center">
        <view class="flex items-center justify-center">
          活动排行榜
        </view>
      </view>
    </view>

    <!-- Family album -->
    <view class="p-5 mt-4 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="openAlbumList">
          打开相册
        </view>
      </view>
      <p class="text-sm text-gray-500 mb-3">记录每一个家庭活动瞬间</p>
      <view class="grid grid-cols-2 gap-3">
        <view
          v-for="(item, index) in albumData"
          :key="index"
          class="rounded-lg overflow-hidden h-32 relative cursor-pointer"
          @click="handleMediaClick(item, albumData)"
        >
          <image
            :src="item.type === 'video' ? item.thumbnail : item.url"
            alt="家庭活动照片"
            class="w-full h-full object-cover rounded-lg"
          />
          <!-- 视频标识 -->
          <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>
      </view>
    </view>

    <BottomNav />

    <!-- 图片预览 -->
    <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="'dashboard-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, computed, onMounted } from 'vue';
import Taro, { useDidShow } from '@tarojs/taro';
import { Setting, Photograph, Right, Close } from '@nutui/icons-vue-taro';
import BottomNav from '../../components/BottomNav.vue';
import PointsCollector from '@/components/PointsCollector.vue'
import WeRunAuth from '@/components/WeRunAuth.vue'
import { useMediaPreview } from '@/composables/useMediaPreview';

const todaySteps = ref(0);
const isWeRunAuthorized = ref(false);
const pointsCollectorRef = ref(null)
const weRunAuthRef = ref(null)

const familyName = ref('')
const familySlogn = ref('')
const familyCover = ref('')

// 使用媒体预览 composable
const {
  previewVisible,
  previewImages,
  previewIndex,
  videoVisible,
  currentVideo,
  videoId,
  handleMediaClick,
  closePreview,
  closeVideo,
  handleVideoPlay,
  handleVideoPause,
  handleFullscreenChange,
  handleVideoError
} = useMediaPreview();

// 家庭相册数据
const albumData = ref([
  {
    type: 'image',
    url: 'https://cdn.ipadbiz.cn/hager/0513-1_FsxMk28AGz6N_D1zZFFOl_EaRdss.png',
    createTime: '2024-01-15 10:30'
  },
  {
    type: 'video',
    url: 'https://vjs.zencdn.net/v/oceans.mp4',
    thumbnail: 'https://cdn.ipadbiz.cn/hager/0513-1_FsxMk28AGz6N_D1zZFFOl_EaRdss.png',
    duration: 125,
    createTime: '2024-01-14 16:20'
  }
]);

/**
 * 触发积分收集组件的一键收取
 */
const handleCollectAll = () => {
  if (pointsCollectorRef.value) {
    pointsCollectorRef.value.collectAll()
  }
}

/**
 * 手动刷新微信步数
 */
const handleRefreshSteps = async () => {
  if (weRunAuthRef.value) {
    await weRunAuthRef.value.refreshSteps()
  }
}

/**
 * 处理微信步数授权状态变化
 * @param {boolean} authorized - 授权状态
 */
const handleAuthChange = (authorized) => {
  isWeRunAuthorized.value = authorized
  console.log('微信步数授权状态:', authorized)
}

/**
 * 处理步数数据更新
 * @param {number} steps - 步数
 */
const handleStepsUpdate = (steps) => {
  todaySteps.value = steps
  console.log('步数更新:', steps)
}

/**
 * 计算总步数(包含用户步数和家庭成员步数)
 * @param {number} userSteps - 用户步数
 * @returns {number} 总步数
 */
const getTotalSteps = (userSteps) => {
  return familyMembers.value.reduce((sum, member) => sum + member.steps, 0) + userSteps
}

// Mock data for family members
const familyMembers = ref([
  {
    id: 1,
    name: '妈妈',
    steps: 7000,
    avatar: 'https://randomuser.me/api/portraits/women/44.jpg'
  },
  {
    id: 2,
    name: '爸爸',
    steps: 6000,
    avatar: 'https://randomuser.me/api/portraits/men/32.jpg'
  },
  {
    id: 3,
    name: '儿子',
    steps: 5000,
    avatar: 'https://randomuser.me/api/portraits/men/22.jpg'
  },
  {
    id: 4,
    name: '女儿',
    steps: 4000,
    avatar: 'https://randomuser.me/api/portraits/women/29.jpg'
  }
]);

// 注意:totalSteps 计算逻辑已移至 getTotalSteps 方法中

const handleSyncSteps = () => {
  // In a real app, this would sync with a health API
  // For demo purposes, we'll just log and do nothing
  console.log('Syncing steps...');
};

const goToProfile = () => {
  Taro.navigateTo({ url: '/pages/EditFamily/index' });
};

const openAlbumList = () => {
  Taro.navigateTo({ url: '/pages/AlbumList/index' });
};

/**
 * 打开拍照上传页面
 */
const openCamera = () => {
  Taro.navigateTo({ url: '/pages/UploadMedia/index' });
}

/**
 * 打开活动排行榜
 */
const openFamilyRank = () => {
  Taro.navigateTo({ url: '/pages/FamilyRank/index' });
}

const initPageData = async () => {
  // 获取用户信息
  familyName.value = '张爷爷的家庭'
  familySlogn.value = '每日走万步,全家一起行'
  familyCover.value = 'https://placehold.co/800x400/e2f3ff/0369a1?text=LFX&font=roboto'
}

useDidShow(() => {
  // TODO: 等待真实接口获取用户是否加入家庭
  // const hasJoinedFamily = false; // Change to true to simulate having a family

  // if (!hasJoinedFamily) {
  //   Taro.redirectTo({ url: '/pages/Welcome/index' });
  // }
  initPageData();
})
</script>