CheckInDialog.vue 3.79 KB
<template>
  <van-popup
    :show="show"
    @update:show="$emit('update:show', $event)"
    round
    position="bottom"
    :style="{ minHeight: '30%', maxHeight: '80%', width: '100%' }"
  >
    <div class="p-4">
      <div class="flex justify-between items-center mb-3">
        <h3 class="font-medium">
            <span :class="{ 'text-green-500' : active_tab === 'today' }" @click="active_tab = 'today'">今日打卡</span>&nbsp;&nbsp;&nbsp;&nbsp;
            <span v-if="has_history" :class="{ 'text-green-500' : active_tab === 'history' }" @click="active_tab = 'history'">历史打卡</span>
        </h3>
        <van-icon name="cross" @click="handleClose" />
      </div>
      <CheckInList :items="active_list" @submit-success="handleListSuccess" />
    </div>
  </van-popup>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import CheckInList from '@/components/ui/CheckInList.vue'
import { getTaskListAPI } from "@/api/checkin";

// 签到列表
const checkInTypes = ref([]);

const route = useRoute()
const router = useRouter()

const props = defineProps({
    /** 弹窗显隐 */
    show: { type: Boolean, required: true, default: false },
    /** 今日打卡任务(外部传入,可选) */
    items_today: { type: Array, default: () => [] },
    /** 历史打卡任务(外部传入,可选) */
    items_history: { type: Array, default: () => [] }
})

const emit = defineEmits(['update:show', 'check-in-success', 'check-in-data'])

/**
 * @var {import('vue').Ref<'today'|'history'>} active_tab
 * @description 当前选中的任务标签页:今日或历史。
 */
const active_tab = ref('today')

/**
 * @function has_history
 * @description 是否存在历史任务(用于显示“历史打卡”标签)。
 * @returns {boolean}
 */
const has_history = computed(() => {
    const list = Array.isArray(props.items_history) ? props.items_history : []
    return list.length > 0
})

/**
 * @function active_list
 * @description 当前展示的任务列表:优先使用外部传入,未传时回退为组件内部获取的列表。
 * @returns {Array}
 */
const active_list = computed(() => {
    const today = Array.isArray(props.items_today) ? props.items_today : []
    const history = Array.isArray(props.items_history) ? props.items_history : []
    if (active_tab.value === 'today') {
        return today.length ? today : checkInTypes.value
    }
    return history
})

/**
 * @function refresh_checkin_list
 * @description 重新获取打卡任务列表,用于提交成功后更新置灰状态;同时向父组件透出最新数据。
 * @returns {Promise<void>}
 */
const refresh_checkin_list = async () => {
    const task = await getTaskListAPI()
    if (task?.code) {
        // 重建本地签到任务列表(当未传入 props.items_today 时用于展示)
        checkInTypes.value = (task.data || []).map(item => ({
            id: item.id,
            name: item.title,
            task_type: item.task_type,
            is_gray: item.is_gray
        }))
        // 向父组件透出最新数据,便于父组件自行刷新其持有的数据源
        emit('check-in-data', task.data)
    }
}

/**
 * @function handleListSuccess
 * @description 子组件提交成功后,刷新任务列表并通知外部,然后关闭弹窗。
 * @returns {Promise<void>}
 */
const handleListSuccess = async () => {
    await refresh_checkin_list()
    emit('check-in-success')
    setTimeout(() => emit('update:show', false), 1500)
}

const handleClose = () => {
  emit('update:show', false)
}

onMounted(async () => {
    // 当未从外部传入“今日任务”时,回退为组件内部获取的通用任务列表
    if (!Array.isArray(props.items_today) || props.items_today.length === 0) {
        await refresh_checkin_list()
    }
})
</script>