From d241d0b11e70da96e6883ade30d6d474aaf1a1fa Mon Sep 17 00:00:00 2001 From: Shiro Date: Tue, 13 Jan 2026 23:30:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=AF=84=E4=BB=B7?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E7=BB=84=E4=BB=B6=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=A4=9A=E4=B8=AA=E5=89=8D=E7=AB=AF=E9=A1=B5=E9=9D=A2=E4=BA=A4?= =?UTF-8?q?=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/components.d.ts | 5 +- web/src/components/ReviewSubmitDialog.vue | 120 ++++ web/src/services/registration.ts | 7 + web/src/services/stats.ts | 2 +- web/src/utils/request.ts | 5 + web/src/views/activity/ActivityDetail.vue | 208 ++++++- web/src/views/admin/ActivityStats.vue | 13 +- web/src/views/admin/AdminDashboard.vue | 197 ++++++- web/src/views/home/Home.vue | 556 ++++++++++++++++-- .../views/registration/MyRegistrations.vue | 75 ++- 10 files changed, 1094 insertions(+), 94 deletions(-) create mode 100644 web/src/components/ReviewSubmitDialog.vue diff --git a/web/components.d.ts b/web/components.d.ts index cf69196..21e4a4f 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -11,6 +11,7 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + ReviewSubmitDialog: typeof import('./src/components/ReviewSubmitDialog.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] VanActionBar: typeof import('vant/es')['ActionBar'] @@ -18,12 +19,13 @@ declare module 'vue' { VanActionBarIcon: typeof import('vant/es')['ActionBarIcon'] VanButton: typeof import('vant/es')['Button'] VanCalendar: typeof import('vant/es')['Calendar'] - VanCard: typeof import('vant/es')['Card'] VanCell: typeof import('vant/es')['Cell'] VanCellGroup: typeof import('vant/es')['CellGroup'] VanConfigProvider: typeof import('vant/es')['ConfigProvider'] VanDatePicker: typeof import('vant/es')['DatePicker'] VanDialog: typeof import('vant/es')['Dialog'] + VanDropdownItem: typeof import('vant/es')['DropdownItem'] + VanDropdownMenu: typeof import('vant/es')['DropdownMenu'] VanEmpty: typeof import('vant/es')['Empty'] VanField: typeof import('vant/es')['Field'] VanForm: typeof import('vant/es')['Form'] @@ -42,7 +44,6 @@ declare module 'vue' { VanRate: typeof import('vant/es')['Rate'] VanSearch: typeof import('vant/es')['Search'] VanStepper: typeof import('vant/es')['Stepper'] - VanSwipeCell: typeof import('vant/es')['SwipeCell'] VanTab: typeof import('vant/es')['Tab'] VanTabbar: typeof import('vant/es')['Tabbar'] VanTabbarItem: typeof import('vant/es')['TabbarItem'] diff --git a/web/src/components/ReviewSubmitDialog.vue b/web/src/components/ReviewSubmitDialog.vue new file mode 100644 index 0000000..10cbfb2 --- /dev/null +++ b/web/src/components/ReviewSubmitDialog.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/web/src/services/registration.ts b/web/src/services/registration.ts index bac1a5d..56ee814 100644 --- a/web/src/services/registration.ts +++ b/web/src/services/registration.ts @@ -15,3 +15,10 @@ export const cancelRegistration = (id: number) => { export const getMyRegistrations = (params?: any) => { return request.get('/registrations/my', { params }); }; + +// Download ticket PDF +export const downloadTicket = (id: number): Promise => { + return request.get(`/registrations/${id}/ticket`, { + responseType: 'blob' + }); +}; diff --git a/web/src/services/stats.ts b/web/src/services/stats.ts index 590e387..b144397 100644 --- a/web/src/services/stats.ts +++ b/web/src/services/stats.ts @@ -32,7 +32,7 @@ export const getOverviewStats = () => { return request.get('/statistics/overview'); }; -export const exportActivityStats = (activityId: number, format = 'excel') => { +export const exportActivityStats = (activityId: number, format = 'csv') => { // Return blob or handle download return request.get(`/statistics/activity/${activityId}/export`, { params: { format }, diff --git a/web/src/utils/request.ts b/web/src/utils/request.ts index df151d7..1dd6071 100644 --- a/web/src/utils/request.ts +++ b/web/src/utils/request.ts @@ -24,6 +24,11 @@ service.interceptors.request.use( // Response Interceptor service.interceptors.response.use( (response: AxiosResponse) => { + // For blob responses (file downloads), return data directly + if (response.config.responseType === 'blob') { + return response.data; + } + const res = response.data; // According to API docs: code 200 is success if (res.code !== 200) { diff --git a/web/src/views/activity/ActivityDetail.vue b/web/src/views/activity/ActivityDetail.vue index c9b7769..1c3797f 100644 --- a/web/src/views/activity/ActivityDetail.vue +++ b/web/src/views/activity/ActivityDetail.vue @@ -33,31 +33,93 @@

活动简介

{{ activity.description || '暂无简介' }}

+ + +
+
+

活动评价

+
+ + {{ activity.averageRating?.toFixed(1) }} ({{ activity.reviewCount }}条评价) +
+
+ + + +
+ +
+
+
+ + {{ formatReviewTime(review.createdAt) }} +
+ +

{{ review.content }}

+
+
+
+
- + - + + + + + diff --git a/web/src/views/home/Home.vue b/web/src/views/home/Home.vue index 8b748c2..fe1c55e 100644 --- a/web/src/views/home/Home.vue +++ b/web/src/views/home/Home.vue @@ -2,57 +2,150 @@
- - - + + + + +
+ + + + +
+ - - - - +
+
+
+ + + +
+ + {{ formatStatus(item.status) }} + +
+
+ +
+

{{ item.title }}

+ +
+ + {{ formatTimeRange(item.startTime, item.endTime) }} +
+ +
+ + {{ item.location }} +
+ + +
+
+
- - + +
+ + + +
+
+ + {{ formatSelectedDate() }} 的活动 +
+
+
+
+

{{ item.title }}

+
+ + {{ formatActivityTime(item.startTime, item.endTime) }} +
+
+ + {{ item.location }} +
+ +
+
+
+
+ +
+ +
+
diff --git a/web/src/views/registration/MyRegistrations.vue b/web/src/views/registration/MyRegistrations.vue index f5f4e28..47d80e5 100644 --- a/web/src/views/registration/MyRegistrations.vue +++ b/web/src/views/registration/MyRegistrations.vue @@ -17,8 +17,8 @@

{{ item.activityTitle }}

- - {{ getStatusText(item.status) }} + + {{ getStatusText(item) }}
@@ -28,17 +28,26 @@

票码: {{ item.ticketCode }}

@@ -75,7 +84,7 @@