hookehuyr

feat(打卡功能): 添加打卡对话框组件并集成到个人主页

- 新增 `CheckInDialog.vue` 组件,支持用户选择打卡类型并提交打卡内容
- 在 `ProfilePage.vue` 中集成打卡对话框,处理打卡成功后的逻辑
- 更新 `vite.config.js` 和 `components.d.ts` 以支持 Vant 组件的自动导入
...@@ -11,6 +11,7 @@ declare module 'vue' { ...@@ -11,6 +11,7 @@ declare module 'vue' {
11 ActivityCard: typeof import('./components/ui/ActivityCard.vue')['default'] 11 ActivityCard: typeof import('./components/ui/ActivityCard.vue')['default']
12 AppLayout: typeof import('./components/layout/AppLayout.vue')['default'] 12 AppLayout: typeof import('./components/layout/AppLayout.vue')['default']
13 BottomNav: typeof import('./components/layout/BottomNav.vue')['default'] 13 BottomNav: typeof import('./components/layout/BottomNav.vue')['default']
14 + CheckInDialog: typeof import('./components/ui/CheckInDialog.vue')['default']
14 ConfirmDialog: typeof import('./components/ui/ConfirmDialog.vue')['default'] 15 ConfirmDialog: typeof import('./components/ui/ConfirmDialog.vue')['default']
15 CourseCard: typeof import('./components/ui/CourseCard.vue')['default'] 16 CourseCard: typeof import('./components/ui/CourseCard.vue')['default']
16 FrostedGlass: typeof import('./components/ui/FrostedGlass.vue')['default'] 17 FrostedGlass: typeof import('./components/ui/FrostedGlass.vue')['default']
...@@ -22,6 +23,7 @@ declare module 'vue' { ...@@ -22,6 +23,7 @@ declare module 'vue' {
22 SearchBar: typeof import('./components/ui/SearchBar.vue')['default'] 23 SearchBar: typeof import('./components/ui/SearchBar.vue')['default']
23 SummerCampCard: typeof import('./components/ui/SummerCampCard.vue')['default'] 24 SummerCampCard: typeof import('./components/ui/SummerCampCard.vue')['default']
24 VanDatePicker: typeof import('vant/es')['DatePicker'] 25 VanDatePicker: typeof import('vant/es')['DatePicker']
26 + VanIcon: typeof import('vant/es')['Icon']
25 VanList: typeof import('vant/es')['List'] 27 VanList: typeof import('vant/es')['List']
26 VanPickerGroup: typeof import('vant/es')['PickerGroup'] 28 VanPickerGroup: typeof import('vant/es')['PickerGroup']
27 VanPopup: typeof import('vant/es')['Popup'] 29 VanPopup: typeof import('vant/es')['Popup']
......
1 +<template>
2 + <van-popup
3 + :show="show"
4 + @update:show="$emit('update:show', $event)"
5 + round
6 + position="bottom"
7 + :style="{ minHeight: '30%', maxHeight: '80%', width: '100%' }"
8 + >
9 + <div class="p-4">
10 + <div class="flex justify-between items-center mb-3">
11 + <h3 class="font-medium">今日打卡</h3>
12 + <van-icon name="cross" @click="handleClose" />
13 + </div>
14 +
15 + <div v-if="checkInSuccess" class="bg-green-50 border border-green-200 rounded-lg p-4 text-center">
16 + <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">
17 + <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" />
18 + </svg>
19 + <h4 class="text-green-700 font-medium mb-1">打卡成功!</h4>
20 + <p class="text-green-600 text-sm">+5 积分已添加到您的账户</p>
21 + </div>
22 + <template v-else>
23 + <div class="flex space-x-2 py-2">
24 + <button
25 + v-for="checkInType in checkInTypes"
26 + :key="checkInType.id"
27 + :class="[
28 + 'flex-1 flex flex-col items-center p-2 rounded-lg transition-colors',
29 + selectedCheckIn?.id === checkInType.id
30 + ? 'bg-green-100 border border-green-200'
31 + : 'bg-white/70 border border-gray-100 hover:bg-white'
32 + ]"
33 + @click="handleCheckInSelect(checkInType)"
34 + >
35 + <div :class="[
36 + 'w-12 h-12 rounded-full flex items-center justify-center mb-1 transition-colors',
37 + selectedCheckIn?.id === checkInType.id
38 + ? 'bg-green-500 text-white'
39 + : 'bg-gray-100 text-gray-500'
40 + ]">
41 + <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">
42 + <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" />
43 + </svg>
44 + <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">
45 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
46 + </svg>
47 + <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">
48 + <path d="M12 14l9-5-9-5-9 5 9 5z" />
49 + <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" />
50 + <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" />
51 + </svg>
52 + <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">
53 + <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" />
54 + </svg>
55 + </div>
56 + <span class="text-xs">{{ checkInType.name }}</span>
57 + </button>
58 + </div>
59 +
60 + <div v-if="selectedCheckIn" class="mt-3">
61 + <textarea
62 + :placeholder="`请输入${selectedCheckIn.name}内容...`"
63 + v-model="checkInContent"
64 + class="w-full p-3 border border-gray-200 rounded-lg text-sm resize-none h-24"
65 + />
66 + <button
67 + 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"
68 + @click="handleCheckInSubmit"
69 + :disabled="isCheckingIn"
70 + >
71 + <template v-if="isCheckingIn">
72 + <div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2"></div>
73 + 提交中...
74 + </template>
75 + <template v-else>提交打卡</template>
76 + </button>
77 + </div>
78 + </template>
79 + </div>
80 + </van-popup>
81 +</template>
82 +
83 +<script setup>
84 +import { ref } from 'vue'
85 +import { showToast } from 'vant'
86 +import 'vant/lib/toast/style'
87 +import { checkInTypes } from '@/utils/mockData'
88 +
89 +const props = defineProps({
90 + show: {
91 + type: Boolean,
92 + required: true,
93 + default: false
94 + }
95 +})
96 +
97 +const emit = defineEmits(['update:show', 'check-in-success'])
98 +
99 +const selectedCheckIn = ref(null)
100 +const checkInContent = ref('')
101 +const isCheckingIn = ref(false)
102 +const checkInSuccess = ref(false)
103 +
104 +const handleCheckInSelect = (type) => {
105 + selectedCheckIn.value = type
106 +}
107 +
108 +const handleCheckInSubmit = async () => {
109 + if (!selectedCheckIn.value) {
110 + showToast('请选择打卡项目')
111 + return
112 + }
113 + if (!checkInContent.value.trim()) {
114 + showToast('请输入打卡内容')
115 + return
116 + }
117 +
118 + isCheckingIn.value = true
119 + try {
120 + // 模拟API调用
121 + await new Promise(resolve => setTimeout(resolve, 1000))
122 + checkInSuccess.value = true
123 + emit('check-in-success')
124 +
125 + // 重置表单
126 + setTimeout(() => {
127 + checkInSuccess.value = false
128 + selectedCheckIn.value = null
129 + checkInContent.value = ''
130 + emit('update:show', false)
131 + }, 1500)
132 + } catch (error) {
133 + showToast('打卡失败,请重试')
134 + } finally {
135 + isCheckingIn.value = false
136 + }
137 +}
138 +
139 +const handleClose = () => {
140 + selectedCheckIn.value = null
141 + checkInContent.value = ''
142 + checkInSuccess.value = false
143 + emit('update:show', false)
144 +}
145 +</script>
1 <template> 1 <template>
2 <AppLayout title="我的" :right-content="rightContent"> 2 <AppLayout title="我的" :right-content="rightContent">
3 - <div class="bg-gradient-to-br from-green-50 via-green-100/30 to-blue-50/30 min-h-screen"> 3 + <div
4 + class="bg-gradient-to-br from-green-50 via-green-100/30 to-blue-50/30 min-h-screen"
5 + >
4 <!-- User Profile Header with Enhanced Design --> 6 <!-- User Profile Header with Enhanced Design -->
5 <div class="pt-6 pb-8 relative"> 7 <div class="pt-6 pb-8 relative">
6 - <div class="absolute inset-0 bg-gradient-to-r from-green-500 to-blue-500 opacity-15"></div> 8 + <div
9 + class="absolute inset-0 bg-gradient-to-r from-green-500 to-blue-500 opacity-15"
10 + ></div>
7 <div class="relative z-10 flex flex-col items-center"> 11 <div class="relative z-10 flex flex-col items-center">
8 - <div class="w-24 h-24 rounded-full overflow-hidden border-4 border-white shadow-lg mb-4"> 12 + <div
13 + class="w-24 h-24 rounded-full overflow-hidden border-4 border-white shadow-lg mb-4"
14 + >
9 <img 15 <img
10 :src="profile.avatar || '/assets/images/user-avatar-1.jpg'" 16 :src="profile.avatar || '/assets/images/user-avatar-1.jpg'"
11 :alt="profile.name" 17 :alt="profile.name"
...@@ -32,21 +38,31 @@ ...@@ -32,21 +38,31 @@
32 38
33 <div class="flex justify-between"> 39 <div class="flex justify-between">
34 <div class="flex flex-col items-center"> 40 <div class="flex flex-col items-center">
35 - <div class="text-2xl font-bold text-gray-800">{{ profile.checkIns?.totalDays || 0 }}</div> 41 + <div class="text-2xl font-bold text-gray-800">
42 + {{ profile.checkIns?.totalDays || 0 }}
43 + </div>
36 <div class="text-xs text-gray-500 mt-1">累计打卡</div> 44 <div class="text-xs text-gray-500 mt-1">累计打卡</div>
37 </div> 45 </div>
38 <div class="flex flex-col items-center"> 46 <div class="flex flex-col items-center">
39 - <div class="text-2xl font-bold text-green-600">{{ profile.checkIns?.currentStreak || 0 }}</div> 47 + <div class="text-2xl font-bold text-green-600">
48 + {{ profile.checkIns?.currentStreak || 0 }}
49 + </div>
40 <div class="text-xs text-gray-500 mt-1">连续打卡</div> 50 <div class="text-xs text-gray-500 mt-1">连续打卡</div>
41 </div> 51 </div>
42 <div class="flex flex-col items-center"> 52 <div class="flex flex-col items-center">
43 - <div class="text-2xl font-bold text-blue-600">{{ profile.checkIns?.longestStreak || 0 }}</div> 53 + <div class="text-2xl font-bold text-blue-600">
54 + {{ profile.checkIns?.longestStreak || 0 }}
55 + </div>
44 <div class="text-xs text-gray-500 mt-1">最长连续</div> 56 <div class="text-xs text-gray-500 mt-1">最长连续</div>
45 </div> 57 </div>
46 <div> 58 <div>
47 - <button class="bg-gradient-to-r from-green-500 to-green-600 text-white py-2 px-6 rounded-full text-sm shadow-sm"> 59 + <div @click="showCheckInDialog = true" class="cursor-pointer">
48 - 立即打卡 60 + <button
49 - </button> 61 + class="bg-gradient-to-r from-green-500 to-green-600 text-white py-2 px-6 rounded-full text-sm shadow-sm"
62 + >
63 + 立即打卡
64 + </button>
65 + </div>
50 </div> 66 </div>
51 </div> 67 </div>
52 </FrostedGlass> 68 </FrostedGlass>
...@@ -57,21 +73,78 @@ ...@@ -57,21 +73,78 @@
57 <FrostedGlass class="p-4 rounded-xl"> 73 <FrostedGlass class="p-4 rounded-xl">
58 <h3 class="font-semibold text-base mb-4">打卡项目</h3> 74 <h3 class="font-semibold text-base mb-4">打卡项目</h3>
59 <div class="grid grid-cols-4 gap-2"> 75 <div class="grid grid-cols-4 gap-2">
60 - <div v-for="type in checkInTypes" :key="type.id" class="flex flex-col items-center cursor-pointer" @click="handleCheckInClick(type.path)"> 76 + <div
61 - <div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center mb-1"> 77 + v-for="type in checkInTypes"
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"> 78 + :key="type.id"
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" /> 79 + class="flex flex-col items-center cursor-pointer"
80 + @click="handleCheckInClick(type.path)"
81 + >
82 + <div
83 + class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center mb-1"
84 + >
85 + <svg
86 + v-if="type.icon === 'book'"
87 + xmlns="http://www.w3.org/2000/svg"
88 + class="h-6 w-6 text-green-600"
89 + fill="none"
90 + viewBox="0 0 24 24"
91 + stroke="currentColor"
92 + >
93 + <path
94 + stroke-linecap="round"
95 + stroke-linejoin="round"
96 + stroke-width="2"
97 + 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"
98 + />
64 </svg> 99 </svg>
65 - <svg v-if="type.icon === 'running'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> 100 + <svg
66 - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /> 101 + v-if="type.icon === 'running'"
102 + xmlns="http://www.w3.org/2000/svg"
103 + class="h-6 w-6 text-green-600"
104 + fill="none"
105 + viewBox="0 0 24 24"
106 + stroke="currentColor"
107 + >
108 + <path
109 + stroke-linecap="round"
110 + stroke-linejoin="round"
111 + stroke-width="2"
112 + d="M13 10V3L4 14h7v7l9-11h-7z"
113 + />
67 </svg> 114 </svg>
68 - <svg v-if="type.icon === 'graduation-cap'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> 115 + <svg
116 + v-if="type.icon === 'graduation-cap'"
117 + xmlns="http://www.w3.org/2000/svg"
118 + class="h-6 w-6 text-green-600"
119 + fill="none"
120 + viewBox="0 0 24 24"
121 + stroke="currentColor"
122 + >
69 <path d="M12 14l9-5-9-5-9 5 9 5z" /> 123 <path d="M12 14l9-5-9-5-9 5 9 5z" />
70 - <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.998 12.078 12.078 0 01.665-6.479L12 14z" /> 124 + <path
71 - <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.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222" /> 125 + 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.998 12.078 12.078 0 01.665-6.479L12 14z"
126 + />
127 + <path
128 + stroke-linecap="round"
129 + stroke-linejoin="round"
130 + stroke-width="2"
131 + 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.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222"
132 + />
72 </svg> 133 </svg>
73 - <svg v-if="type.icon === 'pencil-alt'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> 134 + <svg
74 - <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" /> 135 + v-if="type.icon === 'pencil-alt'"
136 + xmlns="http://www.w3.org/2000/svg"
137 + class="h-6 w-6 text-green-600"
138 + fill="none"
139 + viewBox="0 0 24 24"
140 + stroke="currentColor"
141 + >
142 + <path
143 + stroke-linecap="round"
144 + stroke-linejoin="round"
145 + stroke-width="2"
146 + 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"
147 + />
75 </svg> 148 </svg>
76 </div> 149 </div>
77 <span class="text-xs">{{ type.name }}</span> 150 <span class="text-xs">{{ type.name }}</span>
...@@ -110,9 +183,7 @@ ...@@ -110,9 +183,7 @@
110 </FrostedGlass> 183 </FrostedGlass>
111 184
112 <!-- Version Info --> 185 <!-- Version Info -->
113 - <div class="text-center text-xs text-gray-400 mb-4"> 186 + <div class="text-center text-xs text-gray-400 mb-4">亲子教育 App v1.2.0</div>
114 - 亲子教育 App v1.2.0
115 - </div>
116 187
117 <!-- Logout Button --> 188 <!-- Logout Button -->
118 <button 189 <button
...@@ -124,125 +195,147 @@ ...@@ -124,125 +195,147 @@
124 </div> 195 </div>
125 </div> 196 </div>
126 </AppLayout> 197 </AppLayout>
198 +
199 + <CheckInDialog
200 + v-model:show="showCheckInDialog"
201 + @check-in-success="handleCheckInSuccess"
202 + />
127 </template> 203 </template>
128 204
129 <script setup> 205 <script setup>
130 -import { ref, h } from 'vue' 206 +import { ref, h } from "vue";
131 -import { useRoute, useRouter } from 'vue-router' 207 +import { useRoute, useRouter } from "vue-router";
132 -import AppLayout from '@/components/layout/AppLayout.vue' 208 +import AppLayout from "@/components/layout/AppLayout.vue";
133 -import FrostedGlass from '@/components/ui/FrostedGlass.vue' 209 +import FrostedGlass from "@/components/ui/FrostedGlass.vue";
134 -import MenuItem from '@/components/ui/MenuItem.vue' 210 +import MenuItem from "@/components/ui/MenuItem.vue";
135 -import { useAuth } from '@/contexts/auth' 211 +import { useAuth } from "@/contexts/auth";
136 -import { userProfile, checkInTypes } from '@/utils/mockData' 212 +import { userProfile, checkInTypes } from "@/utils/mockData";
137 -import { useTitle } from '@vueuse/core'; 213 +import CheckInDialog from "@/components/ui/CheckInDialog.vue";
138 -const router = useRouter() 214 +import { useTitle } from "@vueuse/core";
215 +const router = useRouter();
139 const $route = useRoute(); 216 const $route = useRoute();
140 const $router = useRouter(); 217 const $router = useRouter();
141 useTitle($route.meta.title); 218 useTitle($route.meta.title);
142 -const { currentUser, logout } = useAuth() 219 +const { currentUser, logout } = useAuth();
143 -const profile = ref(userProfile) 220 +const profile = ref(userProfile);
221 +const showCheckInDialog = ref(false);
222 +
223 +// 处理打卡成功
224 +const handleCheckInSuccess = () => {
225 + profile.value.checkIns.totalDays++;
226 + profile.value.checkIns.currentStreak++;
227 + profile.value.checkIns.longestStreak = Math.max(
228 + profile.value.checkIns.longestStreak,
229 + profile.value.checkIns.currentStreak
230 + );
231 +};
144 232
145 // Handle logout 233 // Handle logout
146 const handleLogout = () => { 234 const handleLogout = () => {
147 - logout() 235 + logout();
148 - router.push('/login') 236 + router.push("/login");
149 -} 237 +};
150 238
151 // Handle menu item click 239 // Handle menu item click
152 const handleMenuClick = (path) => { 240 const handleMenuClick = (path) => {
153 - router.push(path) 241 + router.push(path);
154 -} 242 +};
155 243
156 // Handle image error 244 // Handle image error
157 const handleImageError = (e) => { 245 const handleImageError = (e) => {
158 - e.target.onerror = null 246 + e.target.onerror = null;
159 - e.target.src = '/assets/images/user-avatar-1.jpg' 247 + e.target.src = "/assets/images/user-avatar-1.jpg";
160 -} 248 +};
161 249
162 // Handle check-in type click 250 // Handle check-in type click
163 const handleCheckInClick = (path) => { 251 const handleCheckInClick = (path) => {
164 - router.push(path) 252 + router.push(path);
165 -} 253 +};
166 254
167 // Right content component 255 // Right content component
168 -const rightContent = h('div', { class: 'flex items-center' }, [ 256 +const rightContent = h("div", { class: "flex items-center" }, [
169 - h('button', { class: 'p-2' }, [ 257 + h("button", { class: "p-2" }, [
170 - h('svg', { 258 + h(
171 - xmlns: 'http://www.w3.org/2000/svg', 259 + "svg",
172 - class: 'h-6 w-6 text-gray-700', 260 + {
173 - fill: 'none', 261 + xmlns: "http://www.w3.org/2000/svg",
174 - viewBox: '0 0 24 24', 262 + class: "h-6 w-6 text-gray-700",
175 - stroke: 'currentColor' 263 + fill: "none",
176 - }, [ 264 + viewBox: "0 0 24 24",
177 - h('path', { 265 + stroke: "currentColor",
178 - 'stroke-linecap': 'round', 266 + },
179 - 'stroke-linejoin': 'round', 267 + [
180 - 'stroke-width': '2', 268 + h("path", {
181 - d: 'M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9' 269 + "stroke-linecap": "round",
182 - }) 270 + "stroke-linejoin": "round",
183 - ]) 271 + "stroke-width": "2",
184 - ]) 272 + d:
185 -]) 273 + "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9",
274 + }),
275 + ]
276 + ),
277 + ]),
278 +]);
186 279
187 // Menu items 280 // Menu items
188 const menuItems1 = [ 281 const menuItems1 = [
189 { 282 {
190 - icon: 'clock', 283 + icon: "clock",
191 - title: '学习记录', 284 + title: "学习记录",
192 - path: '/profile/learning-records', 285 + path: "/profile/learning-records",
193 - badge: 'NEW' 286 + badge: "NEW",
194 }, 287 },
195 { 288 {
196 - icon: 'user', 289 + icon: "user",
197 - title: '我的活动', 290 + title: "我的活动",
198 - path: '/profile/activities' 291 + path: "/profile/activities",
199 }, 292 },
200 { 293 {
201 - icon: 'book', 294 + icon: "book",
202 - title: '我的课程', 295 + title: "我的课程",
203 - path: '/profile/courses' 296 + path: "/profile/courses",
204 }, 297 },
205 { 298 {
206 - icon: 'heart', 299 + icon: "heart",
207 - title: '我的收藏', 300 + title: "我的收藏",
208 - path: '/profile/favorites' 301 + path: "/profile/favorites",
209 - } 302 + },
210 -] 303 +];
211 304
212 const menuItems2 = [ 305 const menuItems2 = [
213 { 306 {
214 - icon: 'wallet', 307 + icon: "wallet",
215 - title: '我的钱包', 308 + title: "我的钱包",
216 - path: '/profile/wallet' 309 + path: "/profile/wallet",
217 }, 310 },
218 { 311 {
219 - icon: 'document', 312 + icon: "document",
220 - title: '我的订单', 313 + title: "我的订单",
221 - path: '/profile/orders' 314 + path: "/profile/orders",
222 }, 315 },
223 { 316 {
224 - icon: 'chat', 317 + icon: "chat",
225 - title: '我的圈子', 318 + title: "我的圈子",
226 - path: '/profile/community' 319 + path: "/profile/community",
227 - } 320 + },
228 -] 321 +];
229 322
230 const menuItems3 = [ 323 const menuItems3 = [
231 { 324 {
232 - icon: 'email', 325 + icon: "email",
233 - title: '消息中心', 326 + title: "消息中心",
234 - path: '/profile/messages', 327 + path: "/profile/messages",
235 - badge: '3' 328 + badge: "3",
236 }, 329 },
237 { 330 {
238 - icon: 'question', 331 + icon: "question",
239 - title: '帮助中心', 332 + title: "帮助中心",
240 - path: '/profile/help' 333 + path: "/profile/help",
241 }, 334 },
242 { 335 {
243 - icon: 'settings', 336 + icon: "settings",
244 - title: '设置', 337 + title: "设置",
245 - path: '/profile/settings' 338 + path: "/profile/settings",
246 - } 339 + },
247 -] 340 +];
248 </script> 341 </script>
......
1 /* 1 /*
2 * @Date: 2025-03-20 19:53:12 2 * @Date: 2025-03-20 19:53:12
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-03-20 23:44:51 4 + * @LastEditTime: 2025-03-21 14:21:42
5 * @FilePath: /mlaj/vite.config.js 5 * @FilePath: /mlaj/vite.config.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
...@@ -31,6 +31,7 @@ export default ({ command, mode }) => { ...@@ -31,6 +31,7 @@ export default ({ command, mode }) => {
31 vue(), 31 vue(),
32 vueJsx(), 32 vueJsx(),
33 AutoImport({ 33 AutoImport({
34 + resolvers: [VantResolver()],
34 imports: ['vue', 'vue-router'], 35 imports: ['vue', 'vue-router'],
35 dts: 'src/auto-imports.d.ts', 36 dts: 'src/auto-imports.d.ts',
36 // 解决eslint报错问题 37 // 解决eslint报错问题
......