CheckInDialog.vue 5.28 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">今日打卡</h3>
        <van-icon name="cross" @click="handleClose" />
      </div>

      <div v-if="checkInSuccess" class="bg-green-50 border border-green-200 rounded-lg p-4 text-center">
        <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-green-500 mx-auto mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
        </svg>
        <h4 class="text-green-700 font-medium mb-1">打卡成功!</h4>
        <!-- <p class="text-green-600 text-sm">+5 积分已添加到您的账户</p> -->
      </div>
      <template v-else>
        <div class="grid grid-cols-2 gap-4 py-2"> <!-- grid-cols-2 强制每行2列,gap控制间距 -->
          <button
            v-for="checkInType in checkInTypes"
            :key="checkInType.id"
            class="flex flex-col items-center p-2 rounded-lg border transition-colors
                  bg-white/70 border-gray-100 hover:bg-white"
            :class="{
              'bg-green-100 border-green-200': selectedCheckIn?.id === checkInType.id
            }"
            @click="handleCheckInSelect(checkInType)"
          >
            <div class="w-12 h-12 rounded-full flex items-center justify-center mb-1 transition-colors
                        bg-gray-100 text-gray-500"
                :class="{
                  'bg-green-500 text-white': selectedCheckIn?.id === checkInType.id
                }"
            >
              <van-icon v-if="checkInType.task_type === 'checkin'" name="edit" size="1.5rem" :color="checkInType.is_gray ? 'gray' : ''" />
              <van-icon v-if="checkInType.task_type === 'upload'" name="tosend" size="1.5rem" :color="checkInType.is_gray ? 'gray' : ''" />
            </div>
            <span :class="['text-xs', checkInType.is_gray ? 'text-gray-500' : '']">{{ checkInType.name }}</span>
          </button>
        </div>

        <div v-if="selectedCheckIn" class="mt-3">
          <!-- <textarea
            :placeholder="`请输入${selectedCheckIn.name}内容...`"
            v-model="checkInContent"
            class="w-full p-3 border border-gray-200 rounded-lg text-sm resize-none h-24"
          /> -->
          <button
            class="mt-2 w-full bg-gradient-to-r from-green-500 to-green-600 text-white py-2 rounded-lg flex items-center justify-center"
            @click="handleCheckInSubmit"
            :disabled="isCheckingIn"
          >
            <template v-if="isCheckingIn">
              <div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2"></div>
              提交中...
            </template>
            <template v-else>提交打卡</template>
          </button>
        </div>
      </template>
    </div>
  </van-popup>
</template>

<script setup>
import { ref } from 'vue'
import { showToast } from 'vant'
import { useRoute, useRouter } from 'vue-router'
import { getTaskListAPI, checkinTaskAPI } from "@/api/checkin";

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

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

const props = defineProps({
  show: {
    type: Boolean,
    required: true,
    default: false
  }
})

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

const selectedCheckIn = ref(null)
const checkInContent = ref('')
const isCheckingIn = ref(false)
const checkInSuccess = ref(false)

const handleCheckInSelect = (type) => {
  if (type.is_gray && type.task_type === 'checkin') {
    showToast('您已经完成了今天的打卡')
    return
  }
  if (type.task_type === 'upload') {
    router.push({
      path: '/checkin/index',
      query: {
        id: type.id
      }
    })
  } else {
    selectedCheckIn.value = type;
  }
}

const handleCheckInSubmit = async () => {
  if (!selectedCheckIn.value) {
    showToast('请选择打卡项目')
    return
  }
  // if (!checkInContent.value.trim()) {
  //   showToast('请输入打卡内容')
  //   return
  // }

  isCheckingIn.value = true
  try {
    // API调用
    const { code, data } = await checkinTaskAPI({ task_id: selectedCheckIn.value.id });
    if (code) {
      checkInSuccess.value = true
      // 重置表单
      setTimeout(() => {
        checkInSuccess.value = false
        selectedCheckIn.value = null
        checkInContent.value = ''
        emit('update:show', false)
      }, 1500)
      emit('check-in-success')
    }
  } catch (error) {
    // showToast('打卡失败,请重试')
  } finally {
    isCheckingIn.value = false
  }
}

const handleClose = () => {
  selectedCheckIn.value = null
  checkInContent.value = ''
  checkInSuccess.value = false
  emit('update:show', false)
}

onMounted(async () => {
  // 获取签到列表
  const task = await getTaskListAPI()
  if (task.code) {
    emit('check-in-data', task.data)
    task.data.forEach(item => {
      checkInTypes.value.push({
        id: item.id,
        name: item.title,
        task_type: item.task_type,
        is_gray: item.is_gray
      })
    })
  }
})
</script>