CheckInDialog.vue 5.9 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="flex space-x-2 py-2">
          <button
            v-for="checkInType in checkInTypes"
            :key="checkInType.id"
            :class="[
              'flex-1 flex flex-col items-center p-2 rounded-lg transition-colors',
              selectedCheckIn?.id === checkInType.id
                ? 'bg-green-100 border border-green-200'
                : 'bg-white/70 border border-gray-100 hover:bg-white'
            ]"
            @click="handleCheckInSelect(checkInType)"
          >
            <div :class="[
              'w-12 h-12 rounded-full flex items-center justify-center mb-1 transition-colors',
              selectedCheckIn?.id === checkInType.id
                ? 'bg-green-500 text-white'
                : 'bg-gray-100 text-gray-500'
            ]">
              <svg v-if="checkInType.id === 'reading'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
              </svg>
              <svg v-if="checkInType.id === 'exercise'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
              </svg>
              <svg v-if="checkInType.id === 'study'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path d="M12 14l9-5-9-5-9 5 9 5z" />
                <path d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998a12.078 12.078 0 01.665-6.479L12 14z" />
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998a12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222" />
              </svg>
              <svg v-if="checkInType.id === 'reflection'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
              </svg>
            </div>
            <span class="text-xs">{{ 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 'vant/lib/toast/style'
import { checkInTypes } from '@/utils/mockData'

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

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

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

const handleCheckInSelect = (type) => {
  selectedCheckIn.value = type
}

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

  isCheckingIn.value = true
  try {
    // 模拟API调用
    await new Promise(resolve => setTimeout(resolve, 1000))
    checkInSuccess.value = true
    emit('check-in-success')

    // 重置表单
    setTimeout(() => {
      checkInSuccess.value = false
      selectedCheckIn.value = null
      checkInContent.value = ''
      emit('update:show', false)
    }, 1500)
  } catch (error) {
    showToast('打卡失败,请重试')
  } finally {
    isCheckingIn.value = false
  }
}

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