hookehuyr

feat(打卡模块): 新增打卡功能相关页面和路由配置

添加打卡模块的四个页面(阅读、运动、学习、写作)及其路由配置,并在用户资料页面增加打卡项目点击跳转功能。同时更新了mock数据和组件类型声明以支持新功能。
...@@ -21,7 +21,10 @@ declare module 'vue' { ...@@ -21,7 +21,10 @@ declare module 'vue' {
21 RouterView: typeof import('vue-router')['RouterView'] 21 RouterView: typeof import('vue-router')['RouterView']
22 SearchBar: typeof import('./components/ui/SearchBar.vue')['default'] 22 SearchBar: typeof import('./components/ui/SearchBar.vue')['default']
23 SummerCampCard: typeof import('./components/ui/SummerCampCard.vue')['default'] 23 SummerCampCard: typeof import('./components/ui/SummerCampCard.vue')['default']
24 + VanDatePicker: typeof import('vant/es')['DatePicker']
24 VanList: typeof import('vant/es')['List'] 25 VanList: typeof import('vant/es')['List']
26 + VanPickerGroup: typeof import('vant/es')['PickerGroup']
27 + VanPopup: typeof import('vant/es')['Popup']
25 VanRate: typeof import('vant/es')['Rate'] 28 VanRate: typeof import('vant/es')['Rate']
26 VanTab: typeof import('vant/es')['Tab'] 29 VanTab: typeof import('vant/es')['Tab']
27 VanTabs: typeof import('vant/es')['Tabs'] 30 VanTabs: typeof import('vant/es')['Tabs']
......
1 +/*
2 + * @Date: 2025-03-21 13:28:30
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-03-21 13:33:06
5 + * @FilePath: /mlaj/src/router/checkin.js
6 + * @Description: 文件描述
7 + */
8 +export default [
9 + {
10 + path: '/checkin/reading',
11 + name: 'ReadingCheckIn',
12 + component: () => import('@/views/checkin/ReadingCheckInPage.vue'),
13 + meta: {
14 + title: '阅读打卡',
15 + requiresAuth: true
16 + }
17 + },
18 + {
19 + path: '/checkin/exercise',
20 + name: 'ExerciseCheckIn',
21 + component: () => import('@/views/checkin/ExerciseCheckInPage.vue'),
22 + meta: {
23 + title: '运动打卡',
24 + requiresAuth: true
25 + }
26 + },
27 + {
28 + path: '/checkin/study',
29 + name: 'StudyCheckIn',
30 + component: () => import('@/views/checkin/StudyCheckInPage.vue'),
31 + meta: {
32 + title: '学习打卡',
33 + requiresAuth: true
34 + }
35 + },
36 + {
37 + path: '/checkin/writing',
38 + name: 'WritingCheckIn',
39 + component: () => import('@/views/checkin/WritingCheckInPage.vue'),
40 + meta: {
41 + title: '反思打卡',
42 + requiresAuth: true
43 + }
44 + }
45 +]
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
8 import { createRouter, createWebHistory } from 'vue-router' 8 import { createRouter, createWebHistory } from 'vue-router'
9 +import checkinRoutes from './checkin'
9 10
10 const routes = [ 11 const routes = [
11 { 12 {
...@@ -114,6 +115,7 @@ const routes = [ ...@@ -114,6 +115,7 @@ const routes = [
114 component: () => import('../views/test.vue'), 115 component: () => import('../views/test.vue'),
115 meta: { title: 'test' } 116 meta: { title: 'test' }
116 }, 117 },
118 + ...checkinRoutes
117 ] 119 ]
118 120
119 const router = createRouter({ 121 const router = createRouter({
......
...@@ -185,10 +185,10 @@ export const userProfile = { ...@@ -185,10 +185,10 @@ export const userProfile = {
185 185
186 // Daily check-in data 186 // Daily check-in data
187 export const checkInTypes = [ 187 export const checkInTypes = [
188 - { id: 'reading', name: '阅读打卡', icon: 'book' }, 188 + { id: 'reading', name: '阅读打卡', icon: 'book', path: '/checkin/reading' },
189 - { id: 'exercise', name: '运动打卡', icon: 'running' }, 189 + { id: 'exercise', name: '运动打卡', icon: 'running', path: '/checkin/exercise' },
190 - { id: 'study', name: '学习打卡', icon: 'graduation-cap' }, 190 + { id: 'study', name: '学习打卡', icon: 'graduation-cap', path: '/checkin/study' },
191 - { id: 'reflection', name: '反思打卡', icon: 'pencil-alt' } 191 + { id: 'reflection', name: '反思打卡', icon: 'pencil-alt', path: '/checkin/writing' }
192 ]; 192 ];
193 193
194 // Community posts data 194 // Community posts data
......
1 +<!--
2 + * @Date: 2025-03-21 13:27:50
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-03-21 13:53:13
5 + * @FilePath: /mlaj/src/views/checkin/ExerciseCheckInPage.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <AppLayout title="运动打卡" :show-back="true">
10 + <div
11 + class="bg-gradient-to-br from-green-50 via-green-100/30 to-blue-50/30 min-h-screen p-4"
12 + >
13 + <!-- 时间筛选 -->
14 + <FrostedGlass class="p-4 rounded-xl mb-4">
15 + <div class="flex items-center justify-between mb-4">
16 + <div class="text-sm text-gray-600">选择时间范围</div>
17 + <div @click="showDatePicker = true" class="text-green-600 text-sm">
18 + {{ formatDateRange }}
19 + </div>
20 + </div>
21 + </FrostedGlass>
22 +
23 + <!-- 打卡列表 -->
24 + <van-list
25 + v-model:loading="loading"
26 + :finished="finished"
27 + finished-text="没有更多了"
28 + @load="onLoad"
29 + >
30 + <FrostedGlass v-for="item in list" :key="item.id" class="p-4 rounded-xl mb-4">
31 + <div class="flex justify-between items-start mb-2">
32 + <div class="text-gray-900 font-medium">{{ item.exerciseType }}</div>
33 + <div class="text-sm text-gray-500">{{ item.submitTime }}</div>
34 + </div>
35 + <div class="text-gray-600 text-sm">
36 + <div class="mb-1">
37 + 时长:{{ item.duration }}分钟 | 强度:{{ item.intensity }}
38 + </div>
39 + <div class="whitespace-pre-wrap">{{ item.thoughts }}</div>
40 + </div>
41 + </FrostedGlass>
42 + </van-list>
43 +
44 + <!-- 时间选择器 -->
45 + <van-popup v-model:show="showDatePicker" position="bottom">
46 + <van-picker-group
47 + title="预约日期"
48 + :tabs="['开始日期', '结束日期']"
49 + @confirm="onConfirmDate"
50 + @cancel="onCancelDate"
51 + >
52 + <van-date-picker v-model="startDate" :min-date="minDate" :max-date="maxDate" />
53 + <van-date-picker v-model="endDate" :min-date="minDate" :max-date="maxDate" />
54 + </van-picker-group>
55 + </van-popup>
56 + </div>
57 + </AppLayout>
58 +</template>
59 +
60 +<script setup>
61 +import { ref, computed } from "vue";
62 +import { DatePicker, List, Popup } from "vant";
63 +import AppLayout from "@/components/layout/AppLayout.vue";
64 +import FrostedGlass from "@/components/ui/FrostedGlass.vue";
65 +
66 +// 列表数据
67 +const list = ref([]);
68 +const loading = ref(false);
69 +const finished = ref(false);
70 +const pageSize = 10;
71 +const currentPage = ref(1);
72 +
73 +// 日期选择
74 +const showDatePicker = ref(false);
75 +const startDate = ref(["2022", "06", "01"]);
76 +const endDate = ref(["2023", "06", "01"]);
77 +const minDate = new Date(2020, 0, 1);
78 +const maxDate = new Date(2025, 5, 1);
79 +
80 +// 格式化日期范围显示
81 +const formatDateRange = computed(() => {
82 + return `${startDate.value.join("-")} ~ ${endDate.value.join("-")}`;
83 +});
84 +
85 +// 确认日期选择
86 +const onConfirmDate = (values) => {
87 + const [start, end] = values;
88 + startDate.value = start.selectedValues;
89 + endDate.value = end.selectedValues;
90 + showDatePicker.value = false;
91 + // 重置列表并重新加载
92 + list.value = [];
93 + finished.value = false;
94 + currentPage.value = 1;
95 + onLoad();
96 +};
97 +
98 +// 取消日期选择
99 +const onCancelDate = () => {
100 + showDatePicker.value = false;
101 +};
102 +
103 +// 加载数据
104 +const onLoad = () => {
105 + loading.value = true;
106 + // 模拟数据加载
107 + setTimeout(() => {
108 + const newItems = Array.from({ length: pageSize }, (_, index) => ({
109 + id: list.value.length + index + 1,
110 + exerciseType: ["跑步", "步行", "骑行", "游泳"][Math.floor(Math.random() * 4)],
111 + duration: Math.floor(Math.random() * 120) + 30,
112 + intensity: ["低强度", "中等强度", "高强度"][Math.floor(Math.random() * 3)],
113 + thoughts: "今天的运动很充实,感觉状态不错!",
114 + submitTime: "2024-03-21 14:30:00",
115 + }));
116 +
117 + list.value.push(...newItems);
118 + loading.value = false;
119 + currentPage.value += 1;
120 +
121 + if (list.value.length >= 30) {
122 + finished.value = true;
123 + }
124 + }, 1000);
125 +};
126 +</script>
1 +<!--
2 + * @Date: 2025-03-21 13:27:25
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-03-21 13:27:26
5 + * @FilePath: /mlaj/src/views/checkin/ReadingCheckInPage.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <AppLayout title="阅读打卡" :show-back="true">
10 + <div class="bg-gradient-to-br from-green-50 via-green-100/30 to-blue-50/30 min-h-screen p-4">
11 + <!-- 时间筛选 -->
12 + <FrostedGlass class="p-4 rounded-xl mb-4">
13 + <div class="flex items-center justify-between mb-4">
14 + <div class="text-sm text-gray-600">选择时间范围</div>
15 + <div @click="showDatePicker = true" class="text-green-600 text-sm">
16 + {{ formatDateRange }}
17 + </div>
18 + </div>
19 + </FrostedGlass>
20 +
21 + <!-- 打卡列表 -->
22 + <van-list
23 + v-model:loading="loading"
24 + :finished="finished"
25 + finished-text="没有更多了"
26 + @load="onLoad"
27 + >
28 + <FrostedGlass v-for="item in list" :key="item.id" class="p-4 rounded-xl mb-4">
29 + <div class="flex justify-between items-start mb-2">
30 + <div class="text-gray-900 font-medium">{{ item.bookTitle }}</div>
31 + <div class="text-sm text-gray-500">{{ item.submitTime }}</div>
32 + </div>
33 + <div class="text-gray-600 text-sm">
34 + <div class="mb-1">
35 + 阅读时间:{{ item.readingTime }}
36 + </div>
37 + <div class="whitespace-pre-wrap">{{ item.thoughts }}</div>
38 + </div>
39 + </FrostedGlass>
40 + </van-list>
41 +
42 + <!-- 时间选择器 -->
43 + <van-popup v-model:show="showDatePicker" position="bottom">
44 + <van-picker-group
45 + title="预约日期"
46 + :tabs="['开始日期', '结束日期']"
47 + @confirm="onConfirmDate"
48 + @cancel="onCancelDate"
49 + >
50 + <van-date-picker v-model="startDate" :min-date="minDate" :max-date="maxDate" />
51 + <van-date-picker v-model="endDate" :min-date="minDate" :max-date="maxDate" />
52 + </van-picker-group>
53 + </van-popup>
54 + </div>
55 + </AppLayout>
56 +</template>
57 +
58 +<script setup>
59 +import { ref, computed } from "vue";
60 +import { DatePicker, List, Popup } from "vant";
61 +import AppLayout from '@/components/layout/AppLayout.vue'
62 +import FrostedGlass from '@/components/ui/FrostedGlass.vue'
63 +
64 +// 列表数据
65 +const list = ref([]);
66 +const loading = ref(false);
67 +const finished = ref(false);
68 +const pageSize = 10;
69 +const currentPage = ref(1);
70 +
71 +// 日期选择
72 +const showDatePicker = ref(false);
73 +const startDate = ref(["2022", "06", "01"]);
74 +const endDate = ref(["2023", "06", "01"]);
75 +const minDate = new Date(2020, 0, 1);
76 +const maxDate = new Date(2025, 5, 1);
77 +
78 +// 格式化日期范围显示
79 +const formatDateRange = computed(() => {
80 + return `${startDate.value.join("-")} ~ ${endDate.value.join("-")}`;
81 +});
82 +
83 +// 确认日期选择
84 +const onConfirmDate = (values) => {
85 + const [start, end] = values;
86 + startDate.value = start.selectedValues;
87 + endDate.value = end.selectedValues;
88 + showDatePicker.value = false;
89 + // 重置列表并重新加载
90 + list.value = [];
91 + finished.value = false;
92 + currentPage.value = 1;
93 + onLoad();
94 +};
95 +
96 +// 取消日期选择
97 +const onCancelDate = () => {
98 + showDatePicker.value = false;
99 +};
100 +
101 +// 加载数据
102 +const onLoad = () => {
103 + loading.value = true;
104 + // 模拟数据加载
105 + setTimeout(() => {
106 + const newItems = Array.from({ length: pageSize }, (_, index) => ({
107 + id: list.value.length + index + 1,
108 + bookTitle: ["深入理解计算机系统", "JavaScript高级程序设计", "算法导论", "设计模式"][Math.floor(Math.random() * 4)],
109 + readingTime: "1小时30分钟",
110 + thoughts: "今天的阅读收获很多,对这个主题有了更深的理解!",
111 + submitTime: "2024-03-21 14:30:00",
112 + }));
113 +
114 + list.value.push(...newItems);
115 + loading.value = false;
116 + currentPage.value += 1;
117 +
118 + if (list.value.length >= 30) {
119 + finished.value = true;
120 + }
121 + }, 1000);
122 +};
123 +</script>
1 +<!--
2 + * @Date: 2025-03-21 13:28:06
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-03-21 13:28:08
5 + * @FilePath: /mlaj/src/views/checkin/StudyCheckInPage.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <AppLayout title="学习打卡" :show-back="true">
10 + <div class="bg-gradient-to-br from-green-50 via-green-100/30 to-blue-50/30 min-h-screen p-4">
11 + <!-- 时间筛选 -->
12 + <FrostedGlass class="p-4 rounded-xl mb-4">
13 + <div class="flex items-center justify-between mb-4">
14 + <div class="text-sm text-gray-600">选择时间范围</div>
15 + <div @click="showDatePicker = true" class="text-green-600 text-sm">
16 + {{ formatDateRange }}
17 + </div>
18 + </div>
19 + </FrostedGlass>
20 +
21 + <!-- 打卡列表 -->
22 + <van-list
23 + v-model:loading="loading"
24 + :finished="finished"
25 + finished-text="没有更多了"
26 + @load="onLoad"
27 + >
28 + <FrostedGlass v-for="item in list" :key="item.id" class="p-4 rounded-xl mb-4">
29 + <div class="flex justify-between items-start mb-2">
30 + <div class="text-gray-900 font-medium">{{ item.subject }}</div>
31 + <div class="text-sm text-gray-500">{{ item.submitTime }}</div>
32 + </div>
33 + <div class="text-gray-600 text-sm">
34 + <div class="mb-1">
35 + 学习时长:{{ item.duration }}分钟
36 + </div>
37 + <div class="mb-1">学习内容:{{ item.content }}</div>
38 + <div class="whitespace-pre-wrap">学习收获:{{ item.thoughts }}</div>
39 + </div>
40 + </FrostedGlass>
41 + </van-list>
42 +
43 + <!-- 时间选择器 -->
44 + <van-popup v-model:show="showDatePicker" position="bottom">
45 + <van-picker-group
46 + title="预约日期"
47 + :tabs="['开始日期', '结束日期']"
48 + @confirm="onConfirmDate"
49 + @cancel="onCancelDate"
50 + >
51 + <van-date-picker v-model="startDate" :min-date="minDate" :max-date="maxDate" />
52 + <van-date-picker v-model="endDate" :min-date="minDate" :max-date="maxDate" />
53 + </van-picker-group>
54 + </van-popup>
55 + </div>
56 + </AppLayout>
57 +</template>
58 +
59 +<script setup>
60 +import { ref, computed } from "vue";
61 +import { DatePicker, List, Popup } from "vant";
62 +import AppLayout from "@/components/layout/AppLayout.vue";
63 +import FrostedGlass from "@/components/ui/FrostedGlass.vue";
64 +
65 +// 列表数据
66 +const list = ref([]);
67 +const loading = ref(false);
68 +const finished = ref(false);
69 +const pageSize = 10;
70 +const currentPage = ref(1);
71 +
72 +// 日期选择
73 +const showDatePicker = ref(false);
74 +const startDate = ref(["2022", "06", "01"]);
75 +const endDate = ref(["2023", "06", "01"]);
76 +const minDate = new Date(2020, 0, 1);
77 +const maxDate = new Date(2025, 5, 1);
78 +
79 +// 格式化日期范围显示
80 +const formatDateRange = computed(() => {
81 + return `${startDate.value.join("-")} ~ ${endDate.value.join("-")}`;
82 +});
83 +
84 +// 确认日期选择
85 +const onConfirmDate = (values) => {
86 + const [start, end] = values;
87 + startDate.value = start.selectedValues;
88 + endDate.value = end.selectedValues;
89 + showDatePicker.value = false;
90 + // 重置列表并重新加载
91 + list.value = [];
92 + finished.value = false;
93 + currentPage.value = 1;
94 + onLoad();
95 +};
96 +
97 +// 取消日期选择
98 +const onCancelDate = () => {
99 + showDatePicker.value = false;
100 +};
101 +
102 +// 加载数据
103 +const onLoad = () => {
104 + loading.value = true;
105 + // 模拟数据加载
106 + setTimeout(() => {
107 + const newItems = Array.from({ length: pageSize }, (_, index) => ({
108 + id: list.value.length + index + 1,
109 + subject: ["数学", "英语", "物理", "化学"][Math.floor(Math.random() * 4)],
110 + duration: Math.floor(Math.random() * 120) + 30,
111 + content: "今天学习了很多知识点,收获颇丰。",
112 + thoughts: "通过今天的学习,加深了对知识的理解。",
113 + submitTime: "2024-03-21 14:30:00",
114 + }));
115 +
116 + list.value.push(...newItems);
117 + loading.value = false;
118 + currentPage.value += 1;
119 +
120 + if (list.value.length >= 30) {
121 + finished.value = true;
122 + }
123 + }, 1000);
124 +};
125 +</script>
1 +<!--
2 + * @Date: 2025-03-21 13:28:22
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-03-21 13:28:24
5 + * @FilePath: /mlaj/src/views/checkin/WritingCheckInPage.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <AppLayout title="写作打卡" :show-back="true">
10 + <div class="bg-gradient-to-br from-green-50 via-green-100/30 to-blue-50/30 min-h-screen p-4">
11 + <!-- 时间筛选 -->
12 + <FrostedGlass class="p-4 rounded-xl mb-4">
13 + <div class="flex items-center justify-between mb-4">
14 + <div class="text-sm text-gray-600">选择时间范围</div>
15 + <div @click="showDatePicker = true" class="text-green-600 text-sm">
16 + {{ formatDateRange }}
17 + </div>
18 + </div>
19 + </FrostedGlass>
20 +
21 + <!-- 打卡列表 -->
22 + <van-list
23 + v-model:loading="loading"
24 + :finished="finished"
25 + finished-text="没有更多了"
26 + @load="onLoad"
27 + >
28 + <FrostedGlass v-for="item in list" :key="item.id" class="p-4 rounded-xl mb-4">
29 + <div class="flex justify-between items-start mb-2">
30 + <div class="text-gray-900 font-medium">{{ item.topic }}</div>
31 + <div class="text-sm text-gray-500">{{ item.submitTime }}</div>
32 + </div>
33 + <div class="text-gray-600 text-sm">
34 + <div class="mb-1">
35 + 写作时长:{{ item.duration }}分钟
36 + </div>
37 + <div class="mb-1">写作内容:{{ item.content }}</div>
38 + <div class="whitespace-pre-wrap">感悟:{{ item.thoughts }}</div>
39 + </div>
40 + </FrostedGlass>
41 + </van-list>
42 +
43 + <!-- 时间选择器 -->
44 + <van-popup v-model:show="showDatePicker" position="bottom">
45 + <van-picker-group
46 + title="预约日期"
47 + :tabs="['开始日期', '结束日期']"
48 + @confirm="onConfirmDate"
49 + @cancel="onCancelDate"
50 + >
51 + <van-date-picker v-model="startDate" :min-date="minDate" :max-date="maxDate" />
52 + <van-date-picker v-model="endDate" :min-date="minDate" :max-date="maxDate" />
53 + </van-picker-group>
54 + </van-popup>
55 + </div>
56 + </AppLayout>
57 +</template>
58 +
59 +<script setup>
60 +import { ref, computed } from "vue";
61 +import { DatePicker, List, Popup } from "vant";
62 +import AppLayout from "@/components/layout/AppLayout.vue";
63 +import FrostedGlass from "@/components/ui/FrostedGlass.vue";
64 +
65 +// 列表数据
66 +const list = ref([]);
67 +const loading = ref(false);
68 +const finished = ref(false);
69 +const pageSize = 10;
70 +const currentPage = ref(1);
71 +
72 +// 日期选择
73 +const showDatePicker = ref(false);
74 +const startDate = ref(["2022", "06", "01"]);
75 +const endDate = ref(["2023", "06", "01"]);
76 +const minDate = new Date(2020, 0, 1);
77 +const maxDate = new Date(2025, 5, 1);
78 +
79 +// 格式化日期范围显示
80 +const formatDateRange = computed(() => {
81 + return `${startDate.value.join("-")} ~ ${endDate.value.join("-")}`;
82 +});
83 +
84 +// 确认日期选择
85 +const onConfirmDate = (values) => {
86 + const [start, end] = values;
87 + startDate.value = start.selectedValues;
88 + endDate.value = end.selectedValues;
89 + showDatePicker.value = false;
90 + // 重置列表并重新加载
91 + list.value = [];
92 + finished.value = false;
93 + currentPage.value = 1;
94 + onLoad();
95 +};
96 +
97 +// 取消日期选择
98 +const onCancelDate = () => {
99 + showDatePicker.value = false;
100 +};
101 +
102 +// 加载数据
103 +const onLoad = () => {
104 + loading.value = true;
105 + // 模拟数据加载
106 + setTimeout(() => {
107 + const newItems = Array.from({ length: pageSize }, (_, index) => ({
108 + id: list.value.length + index + 1,
109 + topic: ["每日随笔", "读书感悟", "生活记录", "技术博客"][Math.floor(Math.random() * 4)],
110 + duration: Math.floor(Math.random() * 120) + 30,
111 + content: "这是一段写作内容的示例...",
112 + thoughts: "今天的写作让我收获颇丰,继续加油!",
113 + submitTime: "2024-03-21 14:30:00",
114 + }));
115 +
116 + list.value.push(...newItems);
117 + loading.value = false;
118 + currentPage.value += 1;
119 +
120 + if (list.value.length >= 30) {
121 + finished.value = true;
122 + }
123 + }, 1000);
124 +};
125 +</script>
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
57 <FrostedGlass class="p-4 rounded-xl"> 57 <FrostedGlass class="p-4 rounded-xl">
58 <h3 class="font-semibold text-base mb-4">打卡项目</h3> 58 <h3 class="font-semibold text-base mb-4">打卡项目</h3>
59 <div class="grid grid-cols-4 gap-2"> 59 <div class="grid grid-cols-4 gap-2">
60 - <div v-for="type in checkInTypes" :key="type.id" class="flex flex-col items-center"> 60 + <div v-for="type in checkInTypes" :key="type.id" class="flex flex-col items-center cursor-pointer" @click="handleCheckInClick(type.path)">
61 <div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center mb-1"> 61 <div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center mb-1">
62 <svg v-if="type.icon === 'book'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> 62 <svg v-if="type.icon === 'book'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
63 <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" /> 63 <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" />
...@@ -159,6 +159,11 @@ const handleImageError = (e) => { ...@@ -159,6 +159,11 @@ const handleImageError = (e) => {
159 e.target.src = '/assets/images/user-avatar-1.jpg' 159 e.target.src = '/assets/images/user-avatar-1.jpg'
160 } 160 }
161 161
162 +// Handle check-in type click
163 +const handleCheckInClick = (path) => {
164 + router.push(path)
165 +}
166 +
162 // Right content component 167 // Right content component
163 const rightContent = h('div', { class: 'flex items-center' }, [ 168 const rightContent = h('div', { class: 'flex items-center' }, [
164 h('button', { class: 'p-2' }, [ 169 h('button', { class: 'p-2' }, [
......