feat: 添加axios依赖并初始化数据管理
refactor: 重构页面组件以使用本地数据 style: 调整组件代码结构以提高可读性 docs: 更新组件注释和文档
Showing
20 changed files
with
256 additions
and
72 deletions
| ... | @@ -11,6 +11,7 @@ | ... | @@ -11,6 +11,7 @@ |
| 11 | }, | 11 | }, |
| 12 | "dependencies": { | 12 | "dependencies": { |
| 13 | "@vitejs/plugin-vue-jsx": "4.1.2", | 13 | "@vitejs/plugin-vue-jsx": "4.1.2", |
| 14 | + "axios": "^1.8.4", | ||
| 14 | "pinia": "^2.1.7", | 15 | "pinia": "^2.1.7", |
| 15 | "vue": "^3.4.21", | 16 | "vue": "^3.4.21", |
| 16 | "vue-router": "^4.3.0" | 17 | "vue-router": "^4.3.0" | ... | ... |
This diff is collapsed. Click to expand it.
| ... | @@ -13,6 +13,7 @@ | ... | @@ -13,6 +13,7 @@ |
| 13 | * @Description: 文件描述 | 13 | * @Description: 文件描述 |
| 14 | */ | 14 | */ |
| 15 | <template> | 15 | <template> |
| 16 | + <AppProvider> | ||
| 16 | <div class="flex flex-col min-h-screen"> | 17 | <div class="flex flex-col min-h-screen"> |
| 17 | <Header /> | 18 | <Header /> |
| 18 | <main class="flex-grow"> | 19 | <main class="flex-grow"> |
| ... | @@ -20,12 +21,14 @@ | ... | @@ -20,12 +21,14 @@ |
| 20 | </main> | 21 | </main> |
| 21 | <Footer /> | 22 | <Footer /> |
| 22 | </div> | 23 | </div> |
| 24 | + </AppProvider> | ||
| 23 | </template> | 25 | </template> |
| 24 | 26 | ||
| 25 | <script setup> | 27 | <script setup> |
| 26 | import { onMounted } from 'vue' | 28 | import { onMounted } from 'vue' |
| 27 | import Header from './components/layout/Header.vue' | 29 | import Header from './components/layout/Header.vue' |
| 28 | import Footer from './components/layout/Footer.vue' | 30 | import Footer from './components/layout/Footer.vue' |
| 31 | +import AppProvider from './providers/AppProvider.vue' | ||
| 29 | 32 | ||
| 30 | // 更新文档标题 | 33 | // 更新文档标题 |
| 31 | onMounted(() => { | 34 | onMounted(() => { | ... | ... |
src/assets/images/avatars/default_avatar.svg
0 → 100644
| ... | @@ -42,7 +42,7 @@ | ... | @@ -42,7 +42,7 @@ |
| 42 | <div class="relative"> | 42 | <div class="relative"> |
| 43 | <button @click="toggleProfileDropdown" class="flex items-center space-x-2"> | 43 | <button @click="toggleProfileDropdown" class="flex items-center space-x-2"> |
| 44 | <div class="h-8 w-8 rounded-full overflow-hidden border-2 border-green-500"> | 44 | <div class="h-8 w-8 rounded-full overflow-hidden border-2 border-green-500"> |
| 45 | - <img :src="currentUser?.avatar || '/assets/images/avatars/default_avatar.png'" alt="User Avatar" class="h-full w-full object-cover" /> | 45 | + <img :src="currentUser?.avatar || '/src/assets/images/avatars/default_avatar.svg'" alt="User Avatar" class="h-full w-full object-cover" /> |
| 46 | </div> | 46 | </div> |
| 47 | <span class="hidden lg:block text-sm font-medium text-gray-700">{{ currentUser?.name || 'User' }}</span> | 47 | <span class="hidden lg:block text-sm font-medium text-gray-700">{{ currentUser?.name || 'User' }}</span> |
| 48 | <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | 48 | <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | ... | ... |
| ... | @@ -19,7 +19,7 @@ | ... | @@ -19,7 +19,7 @@ |
| 19 | </button> | 19 | </button> |
| 20 | </template> | 20 | </template> |
| 21 | 21 | ||
| 22 | -<script setup> | 22 | +<script> |
| 23 | import { computed } from 'vue' | 23 | import { computed } from 'vue' |
| 24 | 24 | ||
| 25 | const buttonVariants = { | 25 | const buttonVariants = { |
| ... | @@ -50,7 +50,10 @@ const buttonRounded = { | ... | @@ -50,7 +50,10 @@ const buttonRounded = { |
| 50 | 50 | ||
| 51 | const buttonBlock = 'w-full flex justify-center' | 51 | const buttonBlock = 'w-full flex justify-center' |
| 52 | 52 | ||
| 53 | -const props = defineProps({ | 53 | +const baseClasses = 'flex items-center justify-center font-medium border shadow-sm transition duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed' |
| 54 | + | ||
| 55 | +export default { | ||
| 56 | + props: { | ||
| 54 | variant: { | 57 | variant: { |
| 55 | type: String, | 58 | type: String, |
| 56 | default: 'primary', | 59 | default: 'primary', |
| ... | @@ -90,13 +93,20 @@ const props = defineProps({ | ... | @@ -90,13 +93,20 @@ const props = defineProps({ |
| 90 | type: String, | 93 | type: String, |
| 91 | default: 'button' | 94 | default: 'button' |
| 92 | } | 95 | } |
| 93 | -}) | 96 | + }, |
| 94 | - | 97 | + emits: ['click'], |
| 95 | -defineEmits(['click']) | 98 | + setup(props) { |
| 96 | - | 99 | + const variantClass = computed(() => buttonVariants[props.variant] || buttonVariants.primary) |
| 97 | -const baseClasses = 'flex items-center justify-center font-medium border shadow-sm transition duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed' | 100 | + const sizeClass = computed(() => buttonSizes[props.size] || buttonSizes.md) |
| 101 | + const roundedClass = computed(() => buttonRounded[props.rounded] || buttonRounded.md) | ||
| 98 | 102 | ||
| 99 | -const variantClass = computed(() => buttonVariants[props.variant] || buttonVariants.primary) | 103 | + return { |
| 100 | -const sizeClass = computed(() => buttonSizes[props.size] || buttonSizes.md) | 104 | + baseClasses, |
| 101 | -const roundedClass = computed(() => buttonRounded[props.rounded] || buttonRounded.md) | 105 | + variantClass, |
| 106 | + sizeClass, | ||
| 107 | + roundedClass, | ||
| 108 | + buttonBlock | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | +} | ||
| 102 | </script> | 112 | </script> | ... | ... |
| ... | @@ -51,7 +51,7 @@ | ... | @@ -51,7 +51,7 @@ |
| 51 | </div> | 51 | </div> |
| 52 | </template> | 52 | </template> |
| 53 | 53 | ||
| 54 | -<script setup> | 54 | +<script> |
| 55 | import { computed } from 'vue' | 55 | import { computed } from 'vue' |
| 56 | 56 | ||
| 57 | const inputSizes = { | 57 | const inputSizes = { |
| ... | @@ -60,7 +60,9 @@ const inputSizes = { | ... | @@ -60,7 +60,9 @@ const inputSizes = { |
| 60 | lg: 'px-4 py-3 text-lg' | 60 | lg: 'px-4 py-3 text-lg' |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | -const props = defineProps({ | 63 | +export default { |
| 64 | + name: 'Input', | ||
| 65 | + props: { | ||
| 64 | modelValue: { | 66 | modelValue: { |
| 65 | type: [String, Number], | 67 | type: [String, Number], |
| 66 | default: '' | 68 | default: '' |
| ... | @@ -114,10 +116,16 @@ const props = defineProps({ | ... | @@ -114,10 +116,16 @@ const props = defineProps({ |
| 114 | type: [String, Object], | 116 | type: [String, Object], |
| 115 | default: null | 117 | default: null |
| 116 | } | 118 | } |
| 117 | -}) | 119 | + }, |
| 118 | - | 120 | + emits: ['update:modelValue', 'blur'], |
| 119 | -defineEmits(['update:modelValue', 'blur']) | 121 | + setup(props) { |
| 122 | + const inputId = computed(() => `input-${props.name}`) | ||
| 123 | + const sizeClass = computed(() => inputSizes[props.size] || inputSizes.md) | ||
| 120 | 124 | ||
| 121 | -const inputId = computed(() => `input-${props.name}`) | 125 | + return { |
| 122 | -const sizeClass = computed(() => inputSizes[props.size] || inputSizes.md) | 126 | + inputId, |
| 127 | + sizeClass | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | +} | ||
| 123 | </script> | 131 | </script> | ... | ... |
| 1 | <script setup> | 1 | <script setup> |
| 2 | -import { ref, onMounted, onUnmounted, watch } from 'vue'; | 2 | +import { ref, onMounted, onUnmounted, watch, computed } from 'vue'; |
| 3 | + | ||
| 4 | +// Set sizes based on the size prop | ||
| 5 | +const sizeClasses = { | ||
| 6 | + sm: 'max-w-md', | ||
| 7 | + md: 'max-w-lg', | ||
| 8 | + lg: 'max-w-2xl', | ||
| 9 | + xl: 'max-w-4xl', | ||
| 10 | + full: 'max-w-full mx-4' | ||
| 11 | +}; | ||
| 3 | 12 | ||
| 4 | const props = defineProps({ | 13 | const props = defineProps({ |
| 5 | isOpen: { | 14 | isOpen: { |
| ... | @@ -33,15 +42,6 @@ const emit = defineEmits(['close']); | ... | @@ -33,15 +42,6 @@ const emit = defineEmits(['close']); |
| 33 | const modalRef = ref(null); | 42 | const modalRef = ref(null); |
| 34 | const isMounted = ref(false); | 43 | const isMounted = ref(false); |
| 35 | 44 | ||
| 36 | -// Set sizes based on the size prop | ||
| 37 | -const sizeClasses = { | ||
| 38 | - sm: 'max-w-md', | ||
| 39 | - md: 'max-w-lg', | ||
| 40 | - lg: 'max-w-2xl', | ||
| 41 | - xl: 'max-w-4xl', | ||
| 42 | - full: 'max-w-full mx-4' | ||
| 43 | -}; | ||
| 44 | - | ||
| 45 | const modalSize = computed(() => sizeClasses[props.size] || sizeClasses.md); | 45 | const modalSize = computed(() => sizeClasses[props.size] || sizeClasses.md); |
| 46 | 46 | ||
| 47 | // Handle ESC key press | 47 | // Handle ESC key press | ... | ... |
| ... | @@ -21,6 +21,12 @@ | ... | @@ -21,6 +21,12 @@ |
| 21 | </div> | 21 | </div> |
| 22 | </template> | 22 | </template> |
| 23 | 23 | ||
| 24 | +<script> | ||
| 25 | +// Define valid values for variant and size props | ||
| 26 | +const validVariants = ['underline', 'pills', 'bordered']; | ||
| 27 | +const validSizes = ['sm', 'md', 'lg']; | ||
| 28 | +</script> | ||
| 29 | + | ||
| 24 | <script setup> | 30 | <script setup> |
| 25 | import { ref, computed } from 'vue'; | 31 | import { ref, computed } from 'vue'; |
| 26 | 32 | ||
| ... | @@ -39,13 +45,13 @@ const props = defineProps({ | ... | @@ -39,13 +45,13 @@ const props = defineProps({ |
| 39 | }, | 45 | }, |
| 40 | variant: { | 46 | variant: { |
| 41 | type: String, | 47 | type: String, |
| 42 | - default: 'underline', // 'underline', 'pills', 'bordered' | 48 | + default: 'underline', |
| 43 | - validator: (value) => ['underline', 'pills', 'bordered'].includes(value) | 49 | + validator: (value) => validVariants.includes(value) |
| 44 | }, | 50 | }, |
| 45 | size: { | 51 | size: { |
| 46 | type: String, | 52 | type: String, |
| 47 | - default: 'md', // 'sm', 'md', 'lg' | 53 | + default: 'md', |
| 48 | - validator: (value) => ['sm', 'md', 'lg'].includes(value) | 54 | + validator: (value) => validSizes.includes(value) |
| 49 | } | 55 | } |
| 50 | }); | 56 | }); |
| 51 | 57 | ... | ... |
src/data/activities.json
0 → 100644
| 1 | +{ | ||
| 2 | + "activities": [ | ||
| 3 | + { | ||
| 4 | + "id": "A0001", | ||
| 5 | + "title": "Vue.js 3.0 读书会", | ||
| 6 | + "description": "每周四晚上8点,我们一起学习Vue.js 3.0的新特性和最佳实践。", | ||
| 7 | + "start_time": "2024-04-18 20:00:00", | ||
| 8 | + "end_time": "2024-04-18 22:00:00", | ||
| 9 | + "location": "线上会议室", | ||
| 10 | + "max_participants": 20, | ||
| 11 | + "current_participants": 5, | ||
| 12 | + "organizer_id": "U0001", | ||
| 13 | + "organizer_name": "张三", | ||
| 14 | + "status": "upcoming", | ||
| 15 | + "is_public": true, | ||
| 16 | + "tags": [ | ||
| 17 | + "Vue.js", | ||
| 18 | + "前端开发", | ||
| 19 | + "读书会" | ||
| 20 | + ], | ||
| 21 | + "requirements": "需要基本的JavaScript和Vue.js基础知识", | ||
| 22 | + "materials": [ | ||
| 23 | + "Vue.js 3.0官方文档", | ||
| 24 | + "示例代码仓库" | ||
| 25 | + ], | ||
| 26 | + "created_at": "2024-04-10 10:00:00", | ||
| 27 | + "updated_at": "2024-04-10 10:00:00" | ||
| 28 | + } | ||
| 29 | + ] | ||
| 30 | +} |
src/data/messages.json
0 → 100644
| 1 | +{ | ||
| 2 | + "messages": [ | ||
| 3 | + { | ||
| 4 | + "id": "M0001", | ||
| 5 | + "sender_id": "system", | ||
| 6 | + "recipient_id": "U0001", | ||
| 7 | + "title": "活动提醒", | ||
| 8 | + "content": "您报名的Vue.js 3.0读书会将在明天晚上8点开始,请准时参加。", | ||
| 9 | + "type": "notification", | ||
| 10 | + "priority": "normal", | ||
| 11 | + "read_status": false, | ||
| 12 | + "created_at": "2024-04-17 10:00:00", | ||
| 13 | + "updated_at": "2024-04-17 10:00:00" | ||
| 14 | + } | ||
| 15 | + ] | ||
| 16 | +} |
src/data/registrations.json
0 → 100644
| 1 | +{ | ||
| 2 | + "registrations": [ | ||
| 3 | + { | ||
| 4 | + "id": "R0001", | ||
| 5 | + "activity_id": "A0001", | ||
| 6 | + "user_id": "U0001", | ||
| 7 | + "registration_time": "2024-04-15 14:30:00", | ||
| 8 | + "status": "confirmed", | ||
| 9 | + "custom_fields": [ | ||
| 10 | + { | ||
| 11 | + "field_name": "experience_level", | ||
| 12 | + "field_type": "select", | ||
| 13 | + "field_label": "Vue.js经验水平", | ||
| 14 | + "field_options": ["入门", "进阶", "专家"] | ||
| 15 | + } | ||
| 16 | + ], | ||
| 17 | + "custom_answers": { | ||
| 18 | + "experience_level": "进阶" | ||
| 19 | + }, | ||
| 20 | + "notes": "期待参加读书会!", | ||
| 21 | + "created_at": "2024-04-15 14:30:00", | ||
| 22 | + "updated_at": "2024-04-15 14:30:00" | ||
| 23 | + } | ||
| 24 | + ] | ||
| 25 | +} |
src/data/users.json
0 → 100644
| 1 | +{ | ||
| 2 | + "users": [ | ||
| 3 | + { | ||
| 4 | + "id": "U0001", | ||
| 5 | + "name": "张三", | ||
| 6 | + "email": "zhangsan@example.com", | ||
| 7 | + "avatar": "/assets/images/avatars/default.png", | ||
| 8 | + "role": "member", | ||
| 9 | + "created_at": "2024-01-01 00:00:00", | ||
| 10 | + "updated_at": "2024-01-01 00:00:00", | ||
| 11 | + "last_login": "2024-04-17 10:00:00", | ||
| 12 | + "status": "active", | ||
| 13 | + "preferences": { | ||
| 14 | + "notification_email": true, | ||
| 15 | + "notification_web": true | ||
| 16 | + } | ||
| 17 | + } | ||
| 18 | + ] | ||
| 19 | +} |
| ... | @@ -379,17 +379,22 @@ const submitRegistration = async () => { | ... | @@ -379,17 +379,22 @@ const submitRegistration = async () => { |
| 379 | onMounted(async () => { | 379 | onMounted(async () => { |
| 380 | try { | 380 | try { |
| 381 | const activityId = route.params.id | 381 | const activityId = route.params.id |
| 382 | - const response = await store.fetchActivity(activityId) | 382 | + const foundActivity = activities.value.find(a => a.id === activityId) |
| 383 | - activity.value = response | 383 | + if (foundActivity) { |
| 384 | - loading.value = false | 384 | + activity.value = foundActivity |
| 385 | - | ||
| 386 | // Check registration status | 385 | // Check registration status |
| 387 | - const registration = await store.checkRegistration(activityId) | 386 | + const registration = registrations.value.find(r => r.activity_id === activityId) |
| 388 | hasRegistered.value = registration !== null | 387 | hasRegistered.value = registration !== null |
| 389 | registrationStatus.value = registration?.status | 388 | registrationStatus.value = registration?.status |
| 390 | 389 | ||
| 391 | - // Fetch similar activities | 390 | + // Find similar activities |
| 392 | - similarActivities.value = await store.fetchSimilarActivities(activityId) | 391 | + similarActivities.value = activities.value |
| 392 | + .filter(a => a.id !== activityId && a.category === foundActivity.category) | ||
| 393 | + .slice(0, 3) | ||
| 394 | + } else { | ||
| 395 | + error.value = '活动不存在' | ||
| 396 | + } | ||
| 397 | + loading.value = false | ||
| 393 | } catch (err) { | 398 | } catch (err) { |
| 394 | error.value = err.message | 399 | error.value = err.message |
| 395 | loading.value = false | 400 | loading.value = false | ... | ... |
| ... | @@ -258,12 +258,16 @@ | ... | @@ -258,12 +258,16 @@ |
| 258 | <script setup> | 258 | <script setup> |
| 259 | import { ref, onMounted, watch } from 'vue' | 259 | import { ref, onMounted, watch } from 'vue' |
| 260 | import { useRoute, useRouter } from 'vue-router' | 260 | import { useRoute, useRouter } from 'vue-router' |
| 261 | -import { useAppStore } from '../stores/app' | 261 | +import activitiesData from '../data/activities.json' |
| 262 | +import registrationsData from '../data/registrations.json' | ||
| 263 | +import usersData from '../data/users.json' | ||
| 262 | import Button from '../components/shared/Button.vue' | 264 | import Button from '../components/shared/Button.vue' |
| 263 | 265 | ||
| 264 | const route = useRoute() | 266 | const route = useRoute() |
| 265 | const router = useRouter() | 267 | const router = useRouter() |
| 266 | -const appStore = useAppStore() | 268 | +const activities = ref(activitiesData.activities) |
| 269 | +const registrations = ref(registrationsData.registrations) | ||
| 270 | +const currentUser = ref(usersData.users[0]) | ||
| 267 | 271 | ||
| 268 | const activityId = route.params.activityId | 272 | const activityId = route.params.activityId |
| 269 | const activity = ref(null) | 273 | const activity = ref(null) |
| ... | @@ -280,21 +284,19 @@ const isSubmitting = ref(false) | ... | @@ -280,21 +284,19 @@ const isSubmitting = ref(false) |
| 280 | // Fetch activity details and user registration | 284 | // Fetch activity details and user registration |
| 281 | const fetchData = async () => { | 285 | const fetchData = async () => { |
| 282 | try { | 286 | try { |
| 283 | - if (!appStore.currentUser) { | 287 | + if (!currentUser.value) { |
| 284 | error.value = '请先登录' | 288 | error.value = '请先登录' |
| 285 | loading.value = false | 289 | loading.value = false |
| 286 | return | 290 | return |
| 287 | } | 291 | } |
| 288 | 292 | ||
| 289 | - if (appStore.activities.length > 0) { | 293 | + const foundActivity = activities.value.find(a => a.id === activityId) |
| 290 | - const foundActivity = appStore.getActivityById(activityId) | ||
| 291 | - | ||
| 292 | if (foundActivity) { | 294 | if (foundActivity) { |
| 293 | activity.value = foundActivity | 295 | activity.value = foundActivity |
| 294 | 296 | ||
| 295 | // Find user registration for this activity | 297 | // Find user registration for this activity |
| 296 | - const registration = appStore.registrations.find( | 298 | + const registration = registrations.value.find( |
| 297 | - reg => reg.activity_id === activityId && reg.user_id === appStore.currentUser.id | 299 | + reg => reg.activity_id === activityId && reg.user_id === currentUser.value.id |
| 298 | ) | 300 | ) |
| 299 | 301 | ||
| 300 | if (registration) { | 302 | if (registration) { |
| ... | @@ -314,7 +316,7 @@ const fetchData = async () => { | ... | @@ -314,7 +316,7 @@ const fetchData = async () => { |
| 314 | } else { | 316 | } else { |
| 315 | error.value = '未找到活动信息' | 317 | error.value = '未找到活动信息' |
| 316 | } | 318 | } |
| 317 | - } | 319 | + |
| 318 | loading.value = false | 320 | loading.value = false |
| 319 | } catch (err) { | 321 | } catch (err) { |
| 320 | console.error('Failed to fetch data:', err) | 322 | console.error('Failed to fetch data:', err) | ... | ... |
| ... | @@ -86,12 +86,12 @@ | ... | @@ -86,12 +86,12 @@ |
| 86 | </template> | 86 | </template> |
| 87 | 87 | ||
| 88 | <script setup> | 88 | <script setup> |
| 89 | -import { ref, computed, watchEffect } from 'vue' | 89 | +import { ref, watchEffect } from 'vue' |
| 90 | -import { useAppStore } from '../stores/app' | 90 | +import activitiesData from '../data/activities.json' |
| 91 | import ActivityCard from '../components/shared/ActivityCard.vue' | 91 | import ActivityCard from '../components/shared/ActivityCard.vue' |
| 92 | 92 | ||
| 93 | -const store = useAppStore() | 93 | +const activities = ref(activitiesData.activities) |
| 94 | -const { activities, loading } = store | 94 | +const loading = ref(false) |
| 95 | 95 | ||
| 96 | const upcomingActivities = ref([]) | 96 | const upcomingActivities = ref([]) |
| 97 | const ongoingActivities = ref([]) | 97 | const ongoingActivities = ref([]) | ... | ... |
| ... | @@ -169,13 +169,15 @@ | ... | @@ -169,13 +169,15 @@ |
| 169 | <script setup> | 169 | <script setup> |
| 170 | import { ref, computed, onMounted } from 'vue' | 170 | import { ref, computed, onMounted } from 'vue' |
| 171 | import { useRoute, useRouter } from 'vue-router' | 171 | import { useRoute, useRouter } from 'vue-router' |
| 172 | -import { useAppStore } from '../stores/app' | 172 | +import activitiesData from '../data/activities.json' |
| 173 | +import registrationsData from '../data/registrations.json' | ||
| 173 | import Button from '../components/shared/Button.vue' | 174 | import Button from '../components/shared/Button.vue' |
| 174 | import Input from '../components/shared/Input.vue' | 175 | import Input from '../components/shared/Input.vue' |
| 175 | 176 | ||
| 176 | const route = useRoute() | 177 | const route = useRoute() |
| 177 | const router = useRouter() | 178 | const router = useRouter() |
| 178 | -const store = useAppStore() | 179 | +const activities = ref(activitiesData.activities) |
| 180 | +const registrations = ref(registrationsData.registrations) | ||
| 179 | 181 | ||
| 180 | const activityId = route.params.activityId | 182 | const activityId = route.params.activityId |
| 181 | const activity = ref(null) | 183 | const activity = ref(null) |
| ... | @@ -196,9 +198,7 @@ const formErrors = ref({}) | ... | @@ -196,9 +198,7 @@ const formErrors = ref({}) |
| 196 | // Fetch activity details | 198 | // Fetch activity details |
| 197 | onMounted(async () => { | 199 | onMounted(async () => { |
| 198 | try { | 200 | try { |
| 199 | - if (store.activities.length > 0) { | 201 | + const foundActivity = activities.value.find(a => a.id === activityId) |
| 200 | - const foundActivity = store.getActivityById(activityId) | ||
| 201 | - | ||
| 202 | if (foundActivity) { | 202 | if (foundActivity) { |
| 203 | activity.value = foundActivity | 203 | activity.value = foundActivity |
| 204 | 204 | ||
| ... | @@ -214,7 +214,7 @@ onMounted(async () => { | ... | @@ -214,7 +214,7 @@ onMounted(async () => { |
| 214 | } else { | 214 | } else { |
| 215 | error.value = '未找到活动信息' | 215 | error.value = '未找到活动信息' |
| 216 | } | 216 | } |
| 217 | - } | 217 | + |
| 218 | loading.value = false | 218 | loading.value = false |
| 219 | } catch (err) { | 219 | } catch (err) { |
| 220 | console.error('Failed to fetch activity details:', err) | 220 | console.error('Failed to fetch activity details:', err) | ... | ... |
| ... | @@ -17,7 +17,7 @@ | ... | @@ -17,7 +17,7 @@ |
| 17 | <div class="flex flex-col md:flex-row items-center"> | 17 | <div class="flex flex-col md:flex-row items-center"> |
| 18 | <div | 18 | <div |
| 19 | class="h-24 w-24 md:h-32 md:w-32 rounded-full overflow-hidden border-4 border-white mb-4 md:mb-0 md:mr-6"> | 19 | class="h-24 w-24 md:h-32 md:w-32 rounded-full overflow-hidden border-4 border-white mb-4 md:mb-0 md:mr-6"> |
| 20 | - <img :src="currentUser.avatar || '/assets/images/avatars/default_avatar.png'" | 20 | + <img :src="currentUser.avatar || '/src/assets/images/avatars/default_avatar.svg'" |
| 21 | :alt="currentUser.name" class="h-full w-full object-cover" /> | 21 | :alt="currentUser.name" class="h-full w-full object-cover" /> |
| 22 | </div> | 22 | </div> |
| 23 | <div class="text-center md:text-left"> | 23 | <div class="text-center md:text-left"> | ... | ... |
| 1 | import { defineStore } from 'pinia' | 1 | import { defineStore } from 'pinia' |
| 2 | import { ref, computed } from 'vue' | 2 | import { ref, computed } from 'vue' |
| 3 | +import usersData from '../data/users.json' | ||
| 4 | +import activitiesData from '../data/activities.json' | ||
| 5 | +import registrationsData from '../data/registrations.json' | ||
| 6 | +import messagesData from '../data/messages.json' | ||
| 3 | 7 | ||
| 4 | export const useAppStore = defineStore('app', () => { | 8 | export const useAppStore = defineStore('app', () => { |
| 5 | const currentUser = ref(null) | 9 | const currentUser = ref(null) |
| ... | @@ -10,29 +14,21 @@ export const useAppStore = defineStore('app', () => { | ... | @@ -10,29 +14,21 @@ export const useAppStore = defineStore('app', () => { |
| 10 | const userMessages = ref([]) | 14 | const userMessages = ref([]) |
| 11 | 15 | ||
| 12 | // 初始化应用数据 | 16 | // 初始化应用数据 |
| 13 | - async function fetchInitialData() { | 17 | + function fetchInitialData() { |
| 14 | try { | 18 | try { |
| 15 | loading.value = true | 19 | loading.value = true |
| 16 | 20 | ||
| 17 | // 获取用户数据 | 21 | // 获取用户数据 |
| 18 | - const usersResponse = await fetch('/data/users.json') | 22 | + currentUser.value = usersData.users[0] |
| 19 | - const usersData = await usersResponse.json() | ||
| 20 | - currentUser.value = usersData[0] | ||
| 21 | 23 | ||
| 22 | // 获取活动数据 | 24 | // 获取活动数据 |
| 23 | - const activitiesResponse = await fetch('/data/activities.json') | 25 | + activities.value = activitiesData.activities |
| 24 | - const activitiesData = await activitiesResponse.json() | ||
| 25 | - activities.value = activitiesData | ||
| 26 | 26 | ||
| 27 | // 获取报名数据 | 27 | // 获取报名数据 |
| 28 | - const registrationsResponse = await fetch('/data/registrations.json') | 28 | + registrations.value = registrationsData.registrations |
| 29 | - const registrationsData = await registrationsResponse.json() | ||
| 30 | - registrations.value = registrationsData | ||
| 31 | 29 | ||
| 32 | // 获取消息数据 | 30 | // 获取消息数据 |
| 33 | - const messagesResponse = await fetch('/data/messages.json') | 31 | + userMessages.value = messagesData.messages |
| 34 | - const messagesData = await messagesResponse.json() | ||
| 35 | - userMessages.value = messagesData | ||
| 36 | 32 | ||
| 37 | loading.value = false | 33 | loading.value = false |
| 38 | } catch (err) { | 34 | } catch (err) { | ... | ... |
src/utils/axios.js
0 → 100644
| 1 | +import axios from 'axios' | ||
| 2 | + | ||
| 3 | +// 创建axios实例 | ||
| 4 | +const instance = axios.create({ | ||
| 5 | + baseURL: '/src/data', // 基础URL,根据实际环境配置 | ||
| 6 | + timeout: 10000, // 请求超时时间 | ||
| 7 | + headers: { | ||
| 8 | + 'Content-Type': 'application/json', | ||
| 9 | + }, | ||
| 10 | +}) | ||
| 11 | + | ||
| 12 | +// 请求拦截器 | ||
| 13 | +instance.interceptors.request.use( | ||
| 14 | + (config) => { | ||
| 15 | + // 在发送请求之前做些什么 | ||
| 16 | + // 例如:添加token | ||
| 17 | + // const token = localStorage.getItem('token'); | ||
| 18 | + // if (token) { | ||
| 19 | + // config.headers.Authorization = `Bearer ${token}`; | ||
| 20 | + // } | ||
| 21 | + return config | ||
| 22 | + }, | ||
| 23 | + (error) => { | ||
| 24 | + // 对请求错误做些什么 | ||
| 25 | + console.error('Request error:', error) | ||
| 26 | + return Promise.reject(error) | ||
| 27 | + }, | ||
| 28 | +) | ||
| 29 | + | ||
| 30 | +// 响应拦截器 | ||
| 31 | +instance.interceptors.response.use( | ||
| 32 | + (response) => { | ||
| 33 | + // 对响应数据做点什么 | ||
| 34 | + return response.data | ||
| 35 | + }, | ||
| 36 | + (error) => { | ||
| 37 | + // 对响应错误做点什么 | ||
| 38 | + console.error('Response error:', error) | ||
| 39 | + if (error.response) { | ||
| 40 | + switch (error.response.status) { | ||
| 41 | + case 401: | ||
| 42 | + // 未授权处理 | ||
| 43 | + break | ||
| 44 | + case 404: | ||
| 45 | + // 资源不存在处理 | ||
| 46 | + break | ||
| 47 | + case 500: | ||
| 48 | + // 服务器错误处理 | ||
| 49 | + break | ||
| 50 | + default: | ||
| 51 | + break | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | + return Promise.reject(error) | ||
| 55 | + }, | ||
| 56 | +) | ||
| 57 | + | ||
| 58 | +export default instance |
-
Please register or login to post a comment