CheckInList.vue
4.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<template>
<!-- 列表主体 -->
<div :class="wrapper_class" :style="scroll_style">
<button
v-for="item in items"
:key="item.id"
class="CheckInListItem flex flex-col items-center p-2 rounded-lg border transition-colors bg-white/70 border-gray-100 hover:bg-white"
:class="{ 'is-active': selected_item?.id === item.id }"
@click="handle_select(item)"
>
<div class="Icon w-12 h-12 rounded-full flex items-center justify-center mb-1 transition-colors bg-gray-100"
:class="{ 'is-active': selected_item?.id === item.id }"
>
<van-icon v-if="item.task_type === 'checkin'" name="edit" size="1.5rem" :color="item.is_gray ? 'gray' : ''" />
<van-icon v-if="item.task_type === 'upload'" name="tosend" size="1.5rem" :color="item.is_gray ? 'gray' : ''" />
</div>
<span :class="['text-xs', item.is_gray ? 'text-gray-500' : '']">{{ item.name }}</span>
</button>
</div>
<!-- 提交按钮 -->
<div v-if="selected_item" class="mt-3">
<button
class="SubmitBtn 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="handle_submit"
:disabled="submitting"
>
<template v-if="submitting">
<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>
<script setup>
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import { checkinTaskAPI } from '@/api/checkin'
import { showToast } from 'vant'
/**
* @typedef {Object} CheckInItem
* @property {number|string} id - 任务ID。
* @property {string} name - 任务名称。
* @property {string} task_type - 任务类型,`checkin` 或 `upload`。
* @property {boolean} [is_gray] - 是否置灰,表示今日已完成。
*/
/**
* @function props
* @description 组件接收的属性定义。
*/
const props = defineProps({
items: { type: Array, default: () => [] },
dense: { type: Boolean, default: false },
scroll: { type: Boolean, default: false },
})
/**
* @function emits
* @description 组件对外抛出的事件。
*/
const emit = defineEmits(['submit-success'])
const router = useRouter()
const selected_item = ref(null)
const submitting = ref(false)
/**
* @function wrapper_class
* @description 计算列表容器类名。
* @returns {string[]}
*/
const wrapper_class = computed(() => [
'CheckInListWrapper',
props.dense ? 'grid grid-cols-2 gap-2 py-2' : 'grid grid-cols-2 gap-4 py-2',
])
/**
* @function scroll_style
* @description 当 `scroll` 为真时启用滚动区域样式。
* @returns {Object}
*/
const scroll_style = computed(() => {
if (!props.scroll) return {}
return { maxHeight: '13rem', overflow: 'auto' }
})
/**
* @function handle_select
* @description 处理打卡类型选择:已完成提示;上传型跳转;否则选中。
* @param {CheckInItem} item - 当前点击的打卡项。
* @returns {void}
*/
const handle_select = (item) => {
if (item.is_gray && item.task_type === 'checkin') {
showToast('您已经完成了今天的打卡')
return
}
if (item.task_type === 'upload') {
router.push({
path: '/checkin/index',
query: { id: item.id },
})
return
}
selected_item.value = item
}
/**
* @function handle_submit
* @description 提交打卡调用接口,成功后抛出事件并复位。
* @returns {Promise<void>}
*/
const handle_submit = async () => {
if (!selected_item.value) {
showToast('请选择打卡项目')
return
}
submitting.value = true
try {
const { code } = await checkinTaskAPI({ task_id: selected_item.value.id })
if (code) {
emit('submit-success')
showToast('打卡成功')
selected_item.value = null
}
} catch (e) {
// showToast('打卡失败,请重试')
} finally {
submitting.value = false
}
}
</script>
<style lang="less" scoped>
@import './CheckInList.less';
</style>