hookehuyr

refactor(components): 替换Tabs组件为内联实现

在ActivityDetail.vue和UserProfile.vue中,将Tabs组件替换为内联实现,以提高代码的可读性和维护性。同时修复了路径中的空格问题,并调整了路由参数的命名。
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
10 "preview": "vite preview" 10 "preview": "vite preview"
11 }, 11 },
12 "dependencies": { 12 "dependencies": {
13 - "@vitejs/plugin-vue-jsx": "4.1.2",
14 "axios": "^1.8.4", 13 "axios": "^1.8.4",
15 "pinia": "^2.1.7", 14 "pinia": "^2.1.7",
16 "vue": "^3.4.21", 15 "vue": "^3.4.21",
...@@ -18,6 +17,7 @@ ...@@ -18,6 +17,7 @@
18 }, 17 },
19 "devDependencies": { 18 "devDependencies": {
20 "@vitejs/plugin-vue": "^5.0.4", 19 "@vitejs/plugin-vue": "^5.0.4",
20 + "@vitejs/plugin-vue-jsx": "4.1.2",
21 "@vue/compiler-sfc": "^3.4.21", 21 "@vue/compiler-sfc": "^3.4.21",
22 "autoprefixer": "^10.4.20", 22 "autoprefixer": "^10.4.20",
23 "eslint": "^9.9.0", 23 "eslint": "^9.9.0",
......
...@@ -8,9 +8,6 @@ importers: ...@@ -8,9 +8,6 @@ importers:
8 8
9 .: 9 .:
10 dependencies: 10 dependencies:
11 - '@vitejs/plugin-vue-jsx':
12 - specifier: 4.1.2
13 - version: 4.1.2(vite@5.4.18)(vue@3.5.13)
14 axios: 11 axios:
15 specifier: ^1.8.4 12 specifier: ^1.8.4
16 version: 1.8.4 13 version: 1.8.4
...@@ -27,6 +24,9 @@ importers: ...@@ -27,6 +24,9 @@ importers:
27 '@vitejs/plugin-vue': 24 '@vitejs/plugin-vue':
28 specifier: ^5.0.4 25 specifier: ^5.0.4
29 version: 5.2.3(vite@5.4.18)(vue@3.5.13) 26 version: 5.2.3(vite@5.4.18)(vue@3.5.13)
27 + '@vitejs/plugin-vue-jsx':
28 + specifier: 4.1.2
29 + version: 4.1.2(vite@5.4.18)(vue@3.5.13)
30 '@vue/compiler-sfc': 30 '@vue/compiler-sfc':
31 specifier: ^3.4.21 31 specifier: ^3.4.21
32 version: 3.5.13 32 version: 3.5.13
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
16 16
17 <!-- Tab content --> 17 <!-- Tab content -->
18 <div class="pt-4"> 18 <div class="pt-4">
19 - <component :is="tabs[currentTab]?.content" /> 19 + <component :is="tabs[currentTab]?.component" />
20 </div> 20 </div>
21 </div> 21 </div>
22 </template> 22 </template>
......
1 +/*
2 + * @Date: 2025-04-17 14:26:17
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-04-21 13:26:32
5 + * @FilePath: /mlaj-reading-club/src/main.js
6 + * @Description: 文件描述
7 + */
1 import { createApp } from 'vue' 8 import { createApp } from 'vue'
2 import { createPinia } from 'pinia' 9 import { createPinia } from 'pinia'
3 import { createRouter, createWebHistory } from 'vue-router' 10 import { createRouter, createWebHistory } from 'vue-router'
...@@ -16,7 +23,7 @@ const router = createRouter({ ...@@ -16,7 +23,7 @@ const router = createRouter({
16 history: createWebHistory(), 23 history: createWebHistory(),
17 routes: [ 24 routes: [
18 { path: '/', component: () => import('./pages/HomePage.vue') }, 25 { path: '/', component: () => import('./pages/HomePage.vue') },
19 - { path: '/activity/:activityId', component: () => import('./pages/ActivityDetail.vue') }, 26 + { path: '/activity/:id', component: () => import('./pages/ActivityDetail.vue') },
20 { path: '/create-activity', component: () => import('./pages/CreateActivity.vue') }, 27 { path: '/create-activity', component: () => import('./pages/CreateActivity.vue') },
21 { path: '/profile', component: () => import('./pages/UserProfile.vue') }, 28 { path: '/profile', component: () => import('./pages/UserProfile.vue') },
22 { path: '/registration/:activityId', component: () => import('./pages/Registration.vue') }, 29 { path: '/registration/:activityId', component: () => import('./pages/Registration.vue') },
......
...@@ -75,7 +75,64 @@ ...@@ -75,7 +75,64 @@
75 </div> 75 </div>
76 76
77 <!-- Activity Tabs --> 77 <!-- Activity Tabs -->
78 - <Tabs :tabs="tabs" /> 78 + <div>
79 + <!-- Tab headers -->
80 + <div class="flex border-b border-gray-200">
81 + <button v-for="(tab, index) in tabs" :key="index"
82 + class="text-base py-2 px-4 font-medium transition-all duration-200 focus:outline-none border-b-2"
83 + :class="currentTab === index
84 + ? 'border-green-500 text-green-600'
85 + : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'"
86 + @click="currentTab = index" role="tab" :aria-selected="currentTab === index">
87 + {{ tab.label }}
88 + </button>
89 + </div>
90 +
91 + <!-- Tab content -->
92 + <div class="pt-4">
93 + <div v-if="tabs[currentTab].label === '活动详情'">
94 + <div class="space-y-4">
95 + <div v-for="(paragraph, idx) in activity.description.split('\n')" :key="idx"
96 + class="mb-4">
97 + {{ paragraph }}
98 + </div>
99 + </div>
100 + </div>
101 + <div v-if="tabs[currentTab].label === '参与须知'">
102 + <div class="space-y-4">
103 + <h3 class="font-medium text-lg">参与须知</h3>
104 + <ul class="list-disc pl-5 space-y-2">
105 + <li>请准时到达活动地点或登录线上会议</li>
106 + <li>请提前阅读相关书籍或材料</li>
107 + <li>活动开始后,请将手机调至静音模式</li>
108 + <li>尊重他人发言,不打断他人</li>
109 + <li>可携带笔记本进行记录</li>
110 + <li>如需取消参与,请提前24小时通知主办方</li>
111 + </ul>
112 + </div>
113 + </div>
114 + <div v-if="tabs[currentTab].label === '常见问题'">
115 +
116 + <div class="space-y-6">
117 + <div>
118 + <h4 class="font-medium text-gray-900">如何取消报名?</h4>
119 + <p class="mt-2 text-gray-600">
120 + 您可以在"我的活动"页面找到已报名的活动,点击"取消报名"按钮即可。请注意,活动开始前24小时内取消将无法获得退款。</p>
121 + </div>
122 + <div>
123 + <h4 class="font-medium text-gray-900">活动材料如何获取?</h4>
124 + <p class="mt-2 text-gray-600">报名成功后,您将在"我的活动"页面看到活动详情,相关材料可在页面底部下载或通过邮件接收。
125 + </p>
126 + </div>
127 + <div>
128 + <h4 class="font-medium text-gray-900">线上活动如何参加?</h4>
129 + <p class="mt-2 text-gray-600">
130 + 线上活动将在活动开始前30分钟发送会议链接到您的邮箱和手机短信,您也可以在"我的活动"页面找到入口链接。</p>
131 + </div>
132 + </div>
133 + </div>
134 + </div>
135 + </div>
79 </div> 136 </div>
80 137
81 <!-- Sidebar --> 138 <!-- Sidebar -->
...@@ -226,13 +283,17 @@ ...@@ -226,13 +283,17 @@
226 </template> 283 </template>
227 284
228 <script setup> 285 <script setup>
229 -import { ref, computed, onMounted } from 'vue' 286 +import { ref, computed, onMounted, h, defineComponent } from 'vue'
230 import { useRoute, useRouter } from 'vue-router' 287 import { useRoute, useRouter } from 'vue-router'
231 import { useAppStore } from '../stores/app' 288 import { useAppStore } from '../stores/app'
232 import Button from '../components/shared/Button.vue' 289 import Button from '../components/shared/Button.vue'
233 import Modal from '../components/shared/Modal.vue' 290 import Modal from '../components/shared/Modal.vue'
234 -import Tabs from '../components/shared/Tabs.vue'
235 import ActivityCard from '../components/shared/ActivityCard.vue' 291 import ActivityCard from '../components/shared/ActivityCard.vue'
292 +import activitiesData from '../data/activities.json'
293 +import registrationsData from '../data/registrations.json'
294 +
295 +const activities = ref(activitiesData.activities)
296 +const registrations = ref(registrationsData.registrations)
236 297
237 const route = useRoute() 298 const route = useRoute()
238 const router = useRouter() 299 const router = useRouter()
...@@ -246,6 +307,7 @@ const showRegisterModal = ref(false) ...@@ -246,6 +307,7 @@ const showRegisterModal = ref(false)
246 const similarActivities = ref([]) 307 const similarActivities = ref([])
247 const hasRegistered = ref(false) 308 const hasRegistered = ref(false)
248 const registrationStatus = ref(null) 309 const registrationStatus = ref(null)
310 +const currentTab = ref(0)
249 311
250 // Computed 312 // Computed
251 const activityStatus = computed(() => { 313 const activityStatus = computed(() => {
...@@ -305,44 +367,13 @@ const registrationStatusBadge = computed(() => { ...@@ -305,44 +367,13 @@ const registrationStatusBadge = computed(() => {
305 const tabs = computed(() => [ 367 const tabs = computed(() => [
306 { 368 {
307 label: '活动详情', 369 label: '活动详情',
308 - content: activity.value?.description.split('\n').map((paragraph, idx) => (
309 - `<p key="${idx}" class="mb-4">${paragraph}</p>`
310 - )).join('')
311 }, 370 },
312 { 371 {
313 label: '参与须知', 372 label: '参与须知',
314 - content: `
315 - <h3 class="font-medium text-lg mb-4">参与须知</h3>
316 - <ul class="list-disc pl-5 space-y-2">
317 - <li>请准时到达活动地点或登录线上会议</li>
318 - <li>请提前阅读相关书籍或材料</li>
319 - <li>活动开始后,请将手机调至静音模式</li>
320 - <li>尊重他人发言,不打断他人</li>
321 - <li>可携带笔记本进行记录</li>
322 - <li>如需取消参与,请提前24小时通知主办方</li>
323 - </ul>
324 - `
325 }, 373 },
326 { 374 {
327 label: '常见问题', 375 label: '常见问题',
328 - content: ` 376 + }
329 - <div className="py-4">
330 - <div className="space-y-6">
331 - <div>
332 - <h4 className="font-medium text-gray-900">如何取消报名?</h4>
333 - <p className="mt-2 text-gray-600">您可以在"我的活动"页面找到已报名的活动,点击"取消报名"按钮即可。请注意,活动开始前24小时内取消将无法获得退款。</p>
334 - </div>
335 - <div>
336 - <h4 className="font-medium text-gray-900">活动材料如何获取?</h4>
337 - <p className="mt-2 text-gray-600">报名成功后,您将在"我的活动"页面看到活动详情,相关材料可在页面底部下载或通过邮件接收。</p>
338 - </div>
339 - <div>
340 - <h4 className="font-medium text-gray-900">线上活动如何参加?</h4>
341 - <p className="mt-2 text-gray-600">线上活动将在活动开始前30分钟发送会议链接到您的邮箱和手机短信,您也可以在"我的活动"页面找到入口链接。</p>
342 - </div>
343 - </div>
344 - </div>
345 - `}
346 ]) 377 ])
347 378
348 // Functions 379 // Functions
...@@ -351,7 +382,7 @@ const goBack = () => { ...@@ -351,7 +382,7 @@ const goBack = () => {
351 } 382 }
352 383
353 const getActivityImage = (activityId) => { 384 const getActivityImage = (activityId) => {
354 - return `/ assets / images / activities / ${activityId}.jpg` 385 + return `/assets/images/activities/${activityId}.jpg`
355 } 386 }
356 387
357 const formatDate = (dateString) => { 388 const formatDate = (dateString) => {
......
This diff is collapsed. Click to expand it.