wode-jihua-tianjia-tancuang.vue 8.1 KB
<template>
  <!-- 具体计划的模板列表(在模板详情和我的计划,还有黄色小球中挂载) 如果没有输入计划id,就是显示最后一个添加的计划-->
  <up-popup :show="props.visible" mode="bottom" round="24rpx" @close="emit('update:visible', false)">
    <view class="up-popup-contain">
      <!-- 顶部滑块 -->
      <view class="popup-indicator"></view>
      <!-- 标题 -->
      <view class="popup-title" @click.stop="openPlanList">
        <view class="title-name">
          {{ isAdd ? (MyPlanDetail.name || '训练计划') : (planTitle || '模板') }}
        </view>
        <template v-if="isAdd">
          <up-icon name="arrow-down" class="arrow-icon" />
        </template>
      </view>
      <!-- 模板列表容器 -->
      <view class="plan-moban-list" v-if="MyPlanDetail.templates && MyPlanDetail.templates.length > 0">
        <view class="plan-card" v-for="(template, index) in MyPlanDetail.templates" :key="template.id">
          <image class="plan-image" :src="template.urlCover" mode="aspectFill" />
          <view class="plan-info">
            <view class="plan-top">
              <view class="plan-name">{{ template.name ?? '暂无名称' }}</view>
              <template v-if="isAdd">
                <button class="add-btn" @click.stop="addTemplateToToday(template.id)">添加</button>
              </template>
              <template v-else>
                <button class="add-btn"
                  @click="uni.navigateTo({ url: `/pages4/pages/xunji/xunji-dongzuo-lianxi?id=${template.id}&type=3` })">
                  去训练
                </button>
              </template>
            </view>

            <view class="plan-meta">
              <text>{{ template.exerciseCount || 0 }}动作</text>
              <text>{{ template.totalSets || 0 }}组</text>
              <text>{{ template.totalWeight || 0 }} kg</text>
            </view>
            <view class="plan-muscles">
              {{ template.primaryMuscleNames?.join(' ') || '无目标肌群' }}
            </view>
          </view>
        </view>
      </view>

      <!-- 空状态提示 -->
      <view class="empty-tip" v-else>
        <text>该计划暂无模板,请点击上方选择其他计划</text>
      </view>
    </view>
  </up-popup>

  <!-- 计划列表弹窗,显示计划列表,选择后会回到当前计划包含的模板列表,可以去训练或者添加模板到日历 -->
  <WodeJihuaLibiaoTancuang v-if="showPlanList && isAdd" ref="planListRef" @close="showPlanList = false"
    :show="showPlanList" :MyPlanList=MyPlanList @select="onSelectPlan" />

</template>

<script setup>
import { ref, onMounted, watch } from 'vue';
import QueryPlanApi from '@/sheep/api/plan/queryplan';
import WodeJihuaLibiaoTancuang from '@/pages/xunji/components/wode-jihua-libiao-tancuang.vue'

const showPlanList = ref(false);
const planListRef = ref(null);
const MyPlanList = ref([])
const activePlanId = ref(null) // 当前选中计划ID

const props = defineProps({
  visible: {
    type: Boolean,
    default: false
  },
  isAdd: {
    type: Boolean,
    default: false
  },
  planTitle: {
    type: String,
    default: '模板'
  },
  planId: {
    type: Number,
    default: null
  },
  isMyPlan: {
    type: Boolean,
    default: false
  }
})
const emit = defineEmits(['update:visible', 'getPlanListLength'])

// 计划详情
const MyPlanDetail = ref({})

// ====================== 核心修复:获取计划详情 ======================
const getMyPlanDetail = async (planId) => {
  if (!planId) return;
  let res;
  try {
    if (props.isMyPlan) {
      res = await QueryPlanApi.getMyPlanDetail(planId);
      console.log('✅ 我的计划详情获取成功 planId=', planId, res.data);
    }
    else {
      res = await QueryPlanApi.getPlanDetail(planId);
      console.log('✅ 不是我的计划,官方计划,详情获取成功 planId=', planId, res.data);
    }

    if (res.data) {
      MyPlanDetail.value = res.data;
    } else {
      uni.showToast({ title: '获取计划详情失败', icon: 'none' });
    }
  } catch (error) {
    console.error('请求失败:', error);
    uni.showToast({ title: '网络异常', icon: 'none' });
  }
};

// ====================== 打开计划列表 ======================
const openPlanList = () => {
  if (showPlanList.value) return
  showPlanList.value = true
}

// ====================== 选择计划后刷新 ======================
const onSelectPlan = (planId) => {
  activePlanId.value = planId;
  getMyPlanDetail(planId); // 选中立即刷新
  setTimeout(() => {
    showPlanList.value = false
  }, 500)
}

// ====================== 添加到今日 ======================
const addTemplateToToday = async (templateId) => {
  if (!templateId) {
    uni.showToast({ title: '未找到训练模板', icon: 'none' });
    return;
  }
  const todayDate = new Date().toISOString().split('T')[0];
  const reqData = {
    id: templateId,
    trainDateList: [todayDate]
  };
  try {
    const res = await QueryPlanApi.addPlanToCalendar(reqData);
    if (res.code === 0) {
      uni.showToast({ title: '添加到今日成功', icon: 'success' });
      emit('update:visible', false);
    } else {
      uni.showToast({ title: res.msg || '添加失败', icon: 'none' });
    }
  } catch (error) {
    console.error('添加失败:', error);
    uni.showToast({ title: '网络异常', icon: 'none' });
  }
};

// ====================== 【关键修复】监听父组件传的 planId ======================
watch(() => props.planId, (newId) => {
  if (newId) {
    activePlanId.value = newId;
    getMyPlanDetail(newId); // 父传ID → 直接加载
  }
}, { immediate: true, deep: true });

// ====================== 弹窗打开时强制刷新(保险) ======================
watch(() => props.visible, (show) => {
  if (show && props.planId) {
    getMyPlanDetail(props.planId);
  }
});

// ====================== 只在 isAdd = true 时获取计划列表 ======================
const getMyPlanList = async () => {
  if (!props.isAdd) return;
  try {
    const res = await QueryPlanApi.getMyPlan();
    if (res.code === 0 && res.data) {
      MyPlanList.value = res.data;
      emit('getPlanListLength', MyPlanList.value.length);
    }
  } catch (err) {
    console.error('请求失败:', err);
  }
};

onMounted(() => {
  getMyPlanList();
  // console.log('props.planId=', planId);

});

</script>

<style lang="scss" scoped>
.up-popup-contain {
  width: 100%;
  min-height: 80vh;
  border-radius: 24rpx 24rpx 0 0;
  padding: 30rpx;
  box-sizing: border-box;
}

.popup-indicator {
  width: 80rpx;
  height: 8rpx;
  background: #ddd;
  border-radius: 4rpx;
  margin: 0 auto 30rpx;
}

.popup-title {
  font-size: 36rpx;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 40rpx;
  gap: 10rpx;

  .title-name {
    color: #333;
  }

  .arrow-icon {
    margin-left: 10rpx;
    font-size: 36rpx;
    font-weight: bold;
  }
}

.plan-moban-list {
  display: flex;
  flex-direction: column;
  gap: 20rpx;
}

.plan-card {
  display: flex;
  align-items: center;
  gap: 20rpx;
  padding-right: 20rpx
}

.plan-image {
  width: 200rpx;
  height: 200rpx;
  border-radius: 16rpx;
  flex-shrink: 0;
}

.plan-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 10rpx 0;

  .plan-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10rpx;
  }

  .plan-name {
    font-size: 32rpx;
    font-weight: 600;
    margin-bottom: 12rpx;
    white-space: nowrap;
    overflow: hidden;
    color: #333;
    text-overflow: ellipsis;
    max-width: 50%;
  }

  .plan-meta {
    font-size: 26rpx;
    color: #666;
    margin-bottom: 12rpx;

    text {
      margin-right: 20rpx;
    }
  }

  .plan-muscles {
    font-size: 24rpx;
    color: #999;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 50%;
  }
}

.add-btn {
  width: 120rpx;
  height: 60rpx;
  background: #ffd60a;
  color: #000;
  border: none;
  border-radius: 30rpx;
  font-size: 28rpx;
  font-weight: 500;
  flex-shrink: 0;
  margin: 0;
  padding: 0;
  line-height: 60rpx;
  box-sizing: border-box;

  &::after {
    border: none;
  }
}
</style>