week-select-popup.vue 6.04 KB
<template>
  <view class="popup-overlay" v-if="innerVisible" @click="handleClose">
    <view class="popup-bottom" @click.stop>
      <view class="popup-indicator"></view>
      <text class="popup-title">训练日设置</text>
      <view class="popup-header">
        <text class="tip-left">请点选训练日</text>
        <!-- 1. 加 ?? 0 兜底,防止 selectedList 为 undefined 报错 -->
        <text class="tip-right">请选择 {{ selectedList?.length ?? 0 }}/{{ maxCount }}</text>
      </view>
      <view class="week-grid">
        <!-- 2. includes 也加兜底,防止 selectedList 为 undefined -->
        <view v-for="item in weekOptions" :key="item.value" class="week-day"
          :class="{ selected: (selectedList || []).includes(item.value) }" @click="toggleWeek(item.value)">
          <text class="day-text">{{ item.shortName }}</text>
          <text class="rest-text">{{ (selectedList || []).includes(item.value) ? '练' : '休' }}</text>
        </view>
      </view>
      <view class="popup-desc">
        <text>此计划为 一周{{ maxCount }}练,设置好后计划排课将同步到课程小组件和日历中,后续也可根据实际情况进行更改。</text>
      </view>
      <!-- 3. 按钮禁用判断加兜底 -->
      <button class="confirm-btn" :class="{ disabled: (selectedList?.length ?? 0) !== maxCount }"
        :disabled="(selectedList?.length ?? 0) !== maxCount" @click="handleConfirm">
        确定日期并加入
      </button>
    </view>
  </view>
</template>

<script setup>
import { ref, watch, defineProps, defineEmits } from 'vue'
import QueryPlanApi from '@/sheep/api/plan/queryplan'

const props = defineProps({
  visible: {
    type: Boolean,
    default: false
  },
  maxCount: {
    type: Number,
    required: true
  },
  planDetail: {
    type: Object,
    required: true
  },
  isUpdate: {
    type: Boolean,
    default: false
  }
})

const emit = defineEmits(['update:visible', 'success'])

const innerVisible = ref(false)
// 4. 初始化为空数组,保证永远是数组类型
const selectedList = ref([])

watch(() => props.visible, (val) => {
  innerVisible.value = val
  if (val) {
    // 5. 强制兜底,保证 selectedList.value 永远是数组
    selectedList.value = props.isUpdate
      ? (props.planDetail?.weekList ?? [])
      : []
  }
}, { immediate: true })

const weekOptions = [
  { shortName: '一', fullName: '周一', value: 1 },
  { shortName: '二', fullName: '周二', value: 2 },
  { shortName: '三', fullName: '周三', value: 3 },
  { shortName: '四', fullName: '周四', value: 4 },
  { shortName: '五', fullName: '周五', value: 5 },
  { shortName: '六', fullName: '周六', value: 6 },
  { shortName: '日', fullName: '周日', value: 7 },
]

const handleClose = () => {
  innerVisible.value = false
  emit('update:visible', false)
}

const toggleWeek = (val) => {
  // 6. 强制兜底,防止 selectedList.value 为 undefined
  const list = selectedList.value || []
  const index = list.indexOf(val)
  if (index > -1) {
    selectedList.value = list.filter(item => item !== val)
  } else {
    if (list.length < props.maxCount) {
      selectedList.value = [...list, val]
    } else {
      uni.showToast({ title: `每周只能选 ${props.maxCount} 天`, icon: 'none' })
    }
  }
}

const handleConfirm = async () => {
  if (!props.planDetail?.id) {
    uni.showToast({ title: '数据异常', icon: 'none' })
    return
  }
  const list = selectedList.value || []
  if (list.length !== props.maxCount) {
    uni.showToast({ title: `请选择 ${props.maxCount} 个训练日`, icon: 'none' })
    return
  }

  const pd = props.planDetail
  const params = {
    id: pd.id,
    durationWeeks: pd.durationWeeks,
    frequencyPerWeek: pd.frequencyPerWeek,
    templateList: pd.templates?.map(item => item.id) || [],
    weekList: list
  }

  try {
    if (props.isUpdate === true) {
      await QueryPlanApi.reStartPlan(params)
      console.log('接口请求成功')
      uni.showToast({ title: '已重新开始计划!', icon: 'success' })
    } else {
      await QueryPlanApi.arrangePlan(params)
      console.log('接口请求成功')
      // uni.showToast({ title: '已成功加入日历!', icon: 'success' })
      uni.showToast({ title: '已成功加入日历!', icon: 'none' })
    }
    emit('success')
    handleClose()
  } catch (err) {
    uni.showToast({ title: '加入失败', icon: 'none' })
    console.error(err)
  }
}
</script>

<style scoped lang="scss">
/* 样式保持不变 */
.popup-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.6);
  z-index: 9999;
  display: flex;
  align-items: flex-end;
}

.popup-bottom {
  width: 100%;
  background: #1e1e1e;
  border-radius: 24rpx 24rpx 0 0;
  padding: 30rpx;
  box-sizing: border-box;
}

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

.popup-title {
  font-size: 34rpx;
  color: #fff;
  text-align: center;
  font-weight: bold;
  display: block;
  margin-bottom: 30rpx;
}

.popup-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 30rpx;

  .tip-left {
    font-size: 28rpx;
    color: #fff;
  }

  .tip-right {
    font-size: 28rpx;
    color: #ffd700;
  }
}

.week-grid {
  display: flex;
  justify-content: space-between;
  margin-bottom: 40rpx;
}

.week-day {
  width: 80rpx;
  height: 100rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #333;
  border-radius: 12rpx;
  color: #fff;
  font-size: 28rpx;

  .rest-text {
    font-size: 24rpx;
    color: #999;
    margin-top: 8rpx;
  }
}

.week-day.selected {
  background: #ffd700;
  color: #000;

  .rest-text {
    color: #000;
  }
}

.popup-desc {
  font-size: 26rpx;
  color: #999;
  line-height: 1.6;
  margin-bottom: 40rpx;
}

.confirm-btn {
  width: 100%;
  height: 80rpx;
  background: #ffd700;
  color: #000;
  border-radius: 40rpx;
  font-size: 30rpx;
  font-weight: bold;
  border: none;
}

.confirm-btn.disabled {
  background: #444;
  color: #666;
  pointer-events: none;
}
</style>