diff --git a/project.private.config.json b/project.private.config.json index 8c3d1c9..336503e 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -4,11 +4,18 @@ "miniprogram": { "list": [ { + "name": "我要反馈", + "pathName": "pages/feedback/index", + "query": "", + "scene": null, + "launchMode": "default" + }, + { "name": "活动报名成功页", "pathName": "pages/actResult/index", "query": "id=59", - "scene": null, - "launchMode": "default" + "launchMode": "default", + "scene": null }, { "name": "活动分享页", @@ -165,7 +172,7 @@ "urlCheck": true, "coverView": true, "lazyloadPlaceholderEnable": false, - "skylineRenderEnable": false, + "skylineRenderEnable": true, "preloadBackgroundData": false, "autoAudits": false, "useApiHook": true, diff --git a/src/app.json b/src/app.json index 443e9b9..1d72841 100644 --- a/src/app.json +++ b/src/app.json @@ -20,7 +20,8 @@ "pages/myAct/index", "pages/myAgent/index", "pages/myComment/index", - "pages/schedule/index" + "pages/schedule/index", + "pages/feedback/index" ], "window": { "backgroundTextStyle": "light", @@ -65,5 +66,15 @@ "navbar": "/components/navbar/index" }, "requiredPrivateInfos": [], - "permission": {} + "permission": {}, + "lazyCodeLoading": "requiredComponents", + "rendererOptions": { + "skyline": { + "defaultDisplayBlock": true, + "disableABTest": true, + "sdkVersionBegin": "3.0.1", + "sdkVersionEnd": "15.255.255" + } + }, + "componentFramework": "glass-easel" } diff --git a/src/app.ts b/src/app.ts index 91b1bd1..2a13cbe 100644 --- a/src/app.ts +++ b/src/app.ts @@ -26,6 +26,7 @@ App({ accessToken: '', needBind: true, + loginPromise: Promise.resolve(), }, onLaunch() { this.autoUpdate() @@ -38,62 +39,61 @@ App({ if (options.query?.scene) { this.globalData.scene = parseScene(options.query!.scene) as any } - this.startLogin() + this.globalData.loginPromise = this.startLogin() }, - startLogin(callback?: () => void) { - wx.login({ - success: (res) => { - // 调用静默登录接口 - wx.ajax({ - method: 'POST', - url: '/auth/silent-login', - showMsg: false, // 隐藏错误提示 - data: { - code: res.code, - }, - }) - .then((response: any) => { - const { accessToken, user, needBind } = response + startLogin(callback?: () => void): Promise { + return new Promise((resolve, reject) => { + wx.login({ + success: (res) => { + // 调用静默登录接口 + wx.ajax({ + method: 'POST', + url: '/auth/silent-login', + showMsg: false, // 隐藏错误提示 + data: { + code: res.code, + }, + }) + .then((response: any) => { + const { accessToken, user, needBind } = response - // 存储 accessToken - this.globalData.accessToken = accessToken - this.globalData.needBind = needBind + // 存储 accessToken + this.globalData.accessToken = accessToken + this.globalData.needBind = needBind - // 存储用户信息 - if (user) { - this.globalData.userInfo = user - } + // 存储用户信息 + if (user) { + this.globalData.userInfo = user + } - if (callback) { - callback() - } - }) - .catch((err: any) => { - // 静默失败,不提示用户 - console.error('静默登录请求失败:', err) - }) - }, - fail: (err) => { - // 静默失败,不提示用户 - console.error('wx.login 失败:', err) - }, + if (callback) { + callback() + } + resolve() + }) + .catch((err: any) => { + // 静默失败,不提示用户 + console.error('静默登录请求失败:', err) + reject(err) + }) + }, + fail: (err) => { + // 静默失败,不提示用户 + console.error('wx.login 失败:', err) + reject(err) + }, + }) }) }, waitLogin({ type }: { type?: 0 | 1 } = { type: 0 }) { - return new Promise((resolve) => { - const checkLogin = () => { - // type = 0:不需要登录即可访问 - if (type === 0) { - resolve() - return - } - if (type === 1 && this.globalData.needBind) { - this.redirectToLogin() - } - - resolve() + return this.globalData.loginPromise.then(() => { + // type = 0:不需要登录即可访问 + if (type === 0) { + return + } + if (type === 1 && this.globalData.needBind) { + this.redirectToLogin() } - checkLogin() }) }, diff --git a/src/custom-tab-bar/index.scss b/src/custom-tab-bar/index.scss index 6e99e06..a326bd0 100644 --- a/src/custom-tab-bar/index.scss +++ b/src/custom-tab-bar/index.scss @@ -10,4 +10,18 @@ color: rgba(74, 184, 253, 1); } } + .hot { + position: absolute; + top: 8rpx; + right: 50%; + margin-right: -36rpx; + width: 16rpx; + height: 16rpx; + border-radius: 50%; + background-color: rgba(253, 91, 89, 1); + opacity: 0; + &.show { + opacity: 1; + } + } } diff --git a/src/custom-tab-bar/index.ts b/src/custom-tab-bar/index.ts index 198afdf..6e7e024 100644 --- a/src/custom-tab-bar/index.ts +++ b/src/custom-tab-bar/index.ts @@ -9,6 +9,7 @@ Component({ isChild: 0, active: 0, + showNoticeDot: false, list: [ { pagePath: '/pages/index/index', @@ -51,6 +52,9 @@ Component({ active, anyWhere: app.globalData.anyWhere, }) + + // 获取未读数 + this.fetchUnreadCount() }, }, methods: { @@ -63,5 +67,22 @@ Component({ url: pagePath, }) }, + + // 获取通知未读数 + async fetchUnreadCount() { + try { + await app.waitLogin({ type: 1 }) + const res = await wx.ajax({ + url: '/notification/unread-count', + method: 'GET', + data: {}, + }) + if (res) { + this.setData({ showNoticeDot: res.totalUnread > 0 }) + } + } catch (err) { + console.error('获取未读数失败:', err) + } + }, }, }) diff --git a/src/custom-tab-bar/index.wxml b/src/custom-tab-bar/index.wxml index f6fc74c..906f718 100644 --- a/src/custom-tab-bar/index.wxml +++ b/src/custom-tab-bar/index.wxml @@ -8,7 +8,7 @@ icon="{{imageUrl}}{{active==index ? item.iconActive : item.icon}}.png" > {{item.text}} - + diff --git a/src/images/bg7.png b/src/images/bg7.png index 792303a..c633153 100644 Binary files a/src/images/bg7.png and b/src/images/bg7.png differ diff --git a/src/images/icon58.png b/src/images/icon58.png index 0aa2792..2f9a9c0 100644 Binary files a/src/images/icon58.png and b/src/images/icon58.png differ diff --git a/src/images/icon59.png b/src/images/icon59.png index 8dc88e4..023779b 100644 Binary files a/src/images/icon59.png and b/src/images/icon59.png differ diff --git a/src/images/icon60.png b/src/images/icon60.png index fa98f78..ef97e37 100644 Binary files a/src/images/icon60.png and b/src/images/icon60.png differ diff --git a/src/images/icon90.png b/src/images/icon90.png new file mode 100644 index 0000000..e133f72 Binary files /dev/null and b/src/images/icon90.png differ diff --git a/src/images/icon91.png b/src/images/icon91.png new file mode 100644 index 0000000..b2f8641 Binary files /dev/null and b/src/images/icon91.png differ diff --git a/src/pages/actAdd/index.json b/src/pages/actAdd/index.json index ef5b8d3..630a043 100644 --- a/src/pages/actAdd/index.json +++ b/src/pages/actAdd/index.json @@ -3,6 +3,8 @@ "navigationStyle": "default", "usingComponents": { "upload-file": "/components/uploadFile/index", - "van-icon": "@vant/weapp/icon/index" + "van-icon": "@vant/weapp/icon/index", + "van-popup": "@vant/weapp/popup/index", + "van-datetime-picker": "@vant/weapp/datetime-picker/index" } } diff --git a/src/pages/actAdd/index.ts b/src/pages/actAdd/index.ts index a37997a..073efe3 100644 --- a/src/pages/actAdd/index.ts +++ b/src/pages/actAdd/index.ts @@ -26,7 +26,6 @@ interface ITagItem { } interface AgendaItem { - agendaDate: string agendaTime: string title: string description: string @@ -91,10 +90,22 @@ Page({ checkinStartTime: '', checkinEndTime: '', + // datetime-picker 相关 + showDatePicker: false, + datePickerField: '', // 当前选择的时间字段 + datePickerValue: new Date().getTime(), // 当前选择的日期时间戳 + datePickerMinDate: new Date(2020, 0, 1).getTime(), // 最小日期 + datePickerMaxDate: new Date(2030, 11, 31).getTime(), // 最大日期 + // 步骤3 活动议程 - agendas: [{ agendaDate: '', agendaTime: '', title: '', description: '', sort: 0 }] as AgendaItem[], + agendas: [{ agendaTime: '', title: '', description: '', sort: 0 }] as AgendaItem[], nextAgendaId: 2, + // 议程时间选择器相关 + showAgendaDatePicker: false, + agendaDatePickerIndex: -1, // 当前编辑的议程索引 + agendaDatePickerValue: new Date().getTime(), + // 提交状态 submitting: false, }, @@ -308,7 +319,6 @@ Page({ checkinWay, checkinStartTime, checkinEndTime, - agendas, } = this.data // 步骤1:基本信息 @@ -361,12 +371,9 @@ Page({ } } - // 步骤3:活动议程 + // 步骤3:活动议程(可选,不做必填验证) if (currentStep === 3) { - if (!agendas.length || !agendas[0].title.trim()) { - wx.showToast({ title: '请添加活动议程', icon: 'none' }) - return false - } + // 议程可选,不做验证 } return true @@ -421,9 +428,53 @@ Page({ }, // ========== 时间选择 ========== - onPickTime(e: WechatMiniprogram.PickerChange) { + // 格式化日期时间 + formatDateTime(timestamp: number): string { + const date = new Date(timestamp) + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + return `${year}-${month}-${day} ${hours}:${minutes}` + }, + + // 打开时间选择器 + onOpenDatePicker(e: WechatMiniprogram.TouchEvent) { const { field } = e.currentTarget.dataset - this.setAndSave({ [field]: e.detail.value }) + const currentValue = (this.data as any)[field] || '' + + // 根据当前值设置 picker 的初始值 + let pickerValue = new Date().getTime() + if (currentValue) { + const parsedDate = new Date(currentValue.replace(/-/g, '/')) + if (!Number.isNaN(parsedDate.getTime())) { + pickerValue = parsedDate.getTime() + } + } + + this.setData({ + showDatePicker: true, + datePickerField: field, + datePickerValue: pickerValue, + }) + }, + + // 关闭时间选择器 + onCloseDatePicker() { + this.setData({ showDatePicker: false }) + }, + + // 确认时间选择 + onConfirmDatePicker(e: WechatMiniprogram.CustomEvent) { + const { datePickerField } = this.data + const timestamp = e.detail + const formattedTime = this.formatDateTime(timestamp) + + this.setAndSave({ + showDatePicker: false, + [datePickerField]: formattedTime, + }) }, // ========== 活动类型选择 ========== @@ -482,7 +533,6 @@ Page({ onAddAgenda() { const agendas = this.data.agendas agendas.push({ - agendaDate: '', agendaTime: '', title: '', description: '', @@ -504,11 +554,45 @@ Page({ this.setAndSave({ agendas }) }, - onAgendaTime(e: WechatMiniprogram.PickerChange) { - const { index, field } = e.currentTarget.dataset + // 打开议程时间选择器 + onOpenAgendaDatePicker(e: WechatMiniprogram.TouchEvent) { + const { index } = e.currentTarget.dataset + const agenda = this.data.agendas[index] + const currentValue = agenda.agendaTime || '' + + let pickerValue = new Date().getTime() + if (currentValue) { + const parsedDate = new Date(currentValue.replace(/-/g, '/')) + if (!Number.isNaN(parsedDate.getTime())) { + pickerValue = parsedDate.getTime() + } + } + + this.setData({ + showAgendaDatePicker: true, + agendaDatePickerIndex: index, + agendaDatePickerValue: pickerValue, + }) + }, + + // 关闭议程时间选择器 + onCloseAgendaDatePicker() { + this.setData({ showAgendaDatePicker: false }) + }, + + // 确认议程时间选择 + onConfirmAgendaDatePicker(e: WechatMiniprogram.CustomEvent) { + const { agendaDatePickerIndex } = this.data + const timestamp = e.detail + const formattedTime = this.formatDateTime(timestamp) + const agendas = this.data.agendas - agendas[index][field] = e.detail.value as string - this.setAndSave({ agendas }) + agendas[agendaDatePickerIndex].agendaTime = formattedTime + + this.setAndSave({ + showAgendaDatePicker: false, + agendas, + }) }, // ========== 报名签到设置 ========== @@ -552,6 +636,7 @@ Page({ type, typeOther, summary, + detail, startTime, endTime, location, @@ -623,11 +708,7 @@ Page({ } } - // 校验议程 - if (!agendas.length || !agendas[0].title.trim()) { - wx.showToast({ title: '请添加活动议程', icon: 'error' }) - return - } + // 议程可选,不做必填验证 this.setData({ submitting: true }) wx.showLoading({ title: activityStatus === 1 ? '保存中...' : '提交中...' }) @@ -646,6 +727,7 @@ Page({ typeOther: type === 6 ? typeOther : '', summary, detailImages, + description: detail, checkinType: checkinTypeMap[checkinWay], regType: needRegister ? 1 : 2, regCondition: registerCondition, @@ -661,10 +743,17 @@ Page({ tagIds: selectedTagIds, categoryIds: selectedCategoryIds, levelId, - agendas: agendas.map((item, index) => ({ - ...item, - sort: index, - })), + checkinStartAt: checkinWay !== 'none' ? checkinStartTime : '', + checkinEndAt: checkinWay !== 'none' ? checkinEndTime : '', + // 过滤掉空的议程(没有标题的议程不提交) + agendas: agendas + .filter((item) => item.title.trim()) + .map((item, index) => ({ + agendaTime: item.agendaTime, + title: item.title, + description: item.description, + sort: index, + })), activityStatus, } diff --git a/src/pages/actAdd/index.wxml b/src/pages/actAdd/index.wxml index e3d06e1..1638a81 100644 --- a/src/pages/actAdd/index.wxml +++ b/src/pages/actAdd/index.wxml @@ -71,18 +71,14 @@ 活动时间 * - - - {{startTime || '请选择开始时间'}} - - - - - - {{endTime || '请选择结束时间'}} - - - + + {{startTime || '开始时间'}} + + + + {{endTime || '结束时间'}} + + @@ -246,34 +242,26 @@ 报名时间 * - - - - {{registerStartTime || '请选择开始时间'}} - - + + {{registerStartTime || '开始时间'}} - - + + - - - {{registerEndTime || '请选择结束时间'}} - - + + {{registerEndTime || '结束时间'}} - + + @@ -388,34 +376,26 @@ 签到时间 * - - - - {{checkinStartTime || '请选择签到开始时间'}} - - + + {{checkinStartTime || '开始时间'}} - - + + - - - {{checkinEndTime || '请选择签到结束时间'}} - - + + {{checkinEndTime || '结束时间'}} - + + @@ -463,20 +443,12 @@ 议程时间 * - - - - {{item.agendaTime || '请选择时间'}} - - + + + {{item.agendaTime || '请选择时间'}} - + + @@ -502,13 +474,13 @@ + 提交 + diff --git a/src/pages/my/index.ts b/src/pages/my/index.ts index 1ff77c9..dcc8158 100644 --- a/src/pages/my/index.ts +++ b/src/pages/my/index.ts @@ -27,6 +27,11 @@ Page({ url: '/pages/myComment/index', }) }, + handleFeedback() { + wx.navigateTo({ + url: '/pages/feedback/index', + }) + }, }) export {} diff --git a/src/pages/my/index.wxml b/src/pages/my/index.wxml index f9323eb..2e5bb38 100644 --- a/src/pages/my/index.wxml +++ b/src/pages/my/index.wxml @@ -16,13 +16,13 @@ - + 我的活动 已参与10个活动 - + 我的智能体 已启用20个智能体 @@ -30,18 +30,18 @@ - + 我的评论 - - 我的收藏 + + 个人信息 - - - 我的评论 + + + 我要反馈 diff --git a/src/pages/myAct/index.json b/src/pages/myAct/index.json index 062154f..ae07e01 100644 --- a/src/pages/myAct/index.json +++ b/src/pages/myAct/index.json @@ -1,9 +1,11 @@ { "navigationBarTitleText": "我的活动", "navigationStyle": "default", + "enablePullDownRefresh": true, "usingComponents": { "van-tab": "@vant/weapp/tab/index", "van-tabs": "@vant/weapp/tabs/index", - "van-icon": "@vant/weapp/icon/index" + "van-icon": "@vant/weapp/icon/index", + "pagination": "/components/pagination/index" } } diff --git a/src/pages/myAct/index.scss b/src/pages/myAct/index.scss index 330121e..e85110e 100644 --- a/src/pages/myAct/index.scss +++ b/src/pages/myAct/index.scss @@ -3,6 +3,7 @@ page { } .page { + padding-bottom: calc(60px + env(safe-area-inset-bottom)); .tabs { .van-tabs__line { width: 42rpx !important; @@ -13,6 +14,14 @@ page { } .page0 { padding: 43rpx 30rpx; + .empty { + padding: 100rpx 0; + text-align: center; + .empty-text { + font-size: 28rpx; + color: rgba(148, 163, 184, 1); + } + } .card { margin-bottom: 24rpx; padding: 24rpx; @@ -40,19 +49,49 @@ page { padding: 8rpx 16rpx; font-size: 22rpx; border-radius: 16rpx 0rpx 16rpx 0rpx; - color: rgba(255, 255, 255, 1); + display: flex; + align-items: center; + gap: 8rpx; + .icon { + width: 24rpx; + height: 24rpx; + } &.status1 { - background: #feb54a; + color: rgba(74, 184, 253, 1); + background: rgba(226, 244, 255, 1); } &.status2 { - background: rgba(74, 184, 253, 1); + color: rgba(255, 255, 255, 1); + background: rgba(254, 181, 74, 1); } &.status3 { + color: rgba(255, 255, 255, 1); background: rgba(111, 220, 174, 1); } &.status4 { + color: rgba(255, 255, 255, 1); + background: rgba(253, 91, 89, 1); + } + &.status5 { + color: rgba(255, 255, 255, 1); + background: rgba(111, 220, 174, 1); + } + &.status6 { + color: rgba(255, 255, 255, 1); + background: rgba(254, 181, 74, 1); + } + &.status7 { + color: rgba(255, 255, 255, 1); background: rgba(203, 213, 225, 1); } + &.status8 { + color: rgba(100, 116, 139, 1); + background: rgba(233, 239, 245, 1); + } + &.status9 { + color: rgba(255, 255, 255, 1); + background: rgba(74, 184, 253, 1); + } } .p-img { border-radius: 16rpx; diff --git a/src/pages/myAct/index.ts b/src/pages/myAct/index.ts index 067b6bb..46455a1 100644 --- a/src/pages/myAct/index.ts +++ b/src/pages/myAct/index.ts @@ -1,8 +1,232 @@ -const _app = getApp(); +const app = getApp() + +interface IActivityItem { + id: number + name: string + mainImages: string[] + startAt: string + endAt: string + status: string + activityStatus: number + activityStatusName: string + checkinType: string + regCount: number + checkinCount: number + isRegistered: boolean + isChecked: boolean + isReviewed: boolean + location?: string +} + +interface IPagination { + page: number + pageSize: number + pages: number + count: number +} Page({ - data: {}, - onLoad() {}, -}); + data: { + active: 0, + imageUrl: app.globalData.imageUrl, + Timestamp: app.globalData.Timestamp, + + // 我发布的 + createdList: [] as IActivityItem[], + createdPagination: { page: 1, pageSize: 10, pages: 0, count: 0 } as IPagination, + createdLoading: false, + + // 我参与的 + registeredList: [] as IActivityItem[], + registeredPagination: { page: 1, pageSize: 10, pages: 0, count: 0 } as IPagination, + registeredLoading: false, + }, + + onLoad() { + app.waitLogin({ type: 1 }).then(() => { + this.fetchCreatedList() + }) + }, + + // 切换 tab + onChange(e: WechatMiniprogram.CustomEvent) { + const active = e.detail.name || e.detail.index + this.setData({ active }) + + if (active === 0 && this.data.createdList.length === 0) { + this.fetchCreatedList() + } else if (active === 1 && this.data.registeredList.length === 0) { + this.fetchRegisteredList() + } + }, + + // 获取我发布的活动 + async fetchCreatedList(isRefresh = false) { + if (this.data.createdLoading) return + + const { createdPagination } = this.data + const page = isRefresh ? 1 : createdPagination.page + + this.setData({ createdLoading: true }) + + try { + const res = await wx.ajax({ + url: '/me/my-activities', + method: 'GET', + data: { type: 'created', page, pageSize: createdPagination.pageSize }, + }) + + if (res) { + const newList = isRefresh ? res.list : [...this.data.createdList, ...res.list] + this.setData({ + createdList: newList, + createdPagination: { + page: res.pagination?.page || page, + pageSize: res.pagination?.pageSize || createdPagination.pageSize, + pages: res.pagination?.totalPages || 0, + count: res.pagination?.total || 0, + }, + }) + } + } catch (err) { + console.error('获取我发布的活动失败:', err) + } finally { + this.setData({ createdLoading: false }) + } + }, + + // 获取我参与的活动 + async fetchRegisteredList(isRefresh = false) { + if (this.data.registeredLoading) return + + const { registeredPagination } = this.data + const page = isRefresh ? 1 : registeredPagination.page + + this.setData({ registeredLoading: true }) + + try { + const res = await wx.ajax({ + url: '/me/my-activities', + method: 'GET', + data: { type: 'registered', page, pageSize: registeredPagination.pageSize }, + }) + + if (res) { + const newList = isRefresh ? res.list : [...this.data.registeredList, ...res.list] + this.setData({ + registeredList: newList, + registeredPagination: { + page: res.pagination?.page || page, + pageSize: res.pagination?.pageSize || registeredPagination.pageSize, + pages: res.pagination?.totalPages || 0, + count: res.pagination?.total || 0, + }, + }) + } + } catch (err) { + console.error('获取我参与的活动失败:', err) + } finally { + this.setData({ registeredLoading: false }) + } + }, + + // 删除活动 + async handleDelete(e: WechatMiniprogram.TouchEvent) { + const { id } = e.currentTarget.dataset + + wx.showModal({ + title: '提示', + content: '确定要删除该活动吗?', + success: async (res) => { + if (res.confirm) { + try { + await wx.ajax({ + url: `/activity/delete?id=${id}`, + method: 'POST', + }) + wx.showToast({ title: '删除成功', icon: 'success' }) + this.fetchCreatedList(true) + } catch (err: any) { + wx.showToast({ title: err?.message || '删除失败', icon: 'error' }) + } + } + }, + }) + }, + + // 编辑活动 + handleEdit(e: WechatMiniprogram.TouchEvent) { + const { id } = e.currentTarget.dataset + wx.navigateTo({ url: `/pages/actEdit/index?id=${id}` }) + }, + + // 查看详情 + handleDetail(e: WechatMiniprogram.TouchEvent) { + const { id } = e.currentTarget.dataset + wx.navigateTo({ url: `/pages/actDetail/index?id=${id}` }) + }, + + // 取消活动 + async handleCancel(e: WechatMiniprogram.TouchEvent) { + const { id } = e.currentTarget.dataset + + wx.showModal({ + title: '提示', + content: '确定要取消该活动吗?取消后已报名用户将收到通知。', + success: async (res) => { + if (res.confirm) { + try { + await wx.ajax({ + url: `/activity/cancel?id=${id}`, + method: 'POST', + }) + wx.showToast({ title: '取消成功', icon: 'success' }) + this.fetchCreatedList(true) + } catch (err: any) { + wx.showToast({ title: err?.message || '取消失败', icon: 'error' }) + } + } + }, + }) + }, + + // 签到二维码 + handleQrcode(e: WechatMiniprogram.TouchEvent) { + const { id } = e.currentTarget.dataset + wx.navigateTo({ url: `/pages/actQrcode/index?id=${id}` }) + }, + + // 下拉刷新 + onPullDownRefresh() { + const { active } = this.data + if (active === 0) { + this.fetchCreatedList(true) + } else { + this.fetchRegisteredList(true) + } + wx.stopPullDownRefresh() + }, + + // 上拉加载更多 + onReachBottom() { + const { active, createdPagination, registeredPagination } = this.data + + if (active === 0) { + if (createdPagination.page < createdPagination.pages) { + this.setData({ + createdPagination: { ...createdPagination, page: createdPagination.page + 1 }, + }) + this.fetchCreatedList() + } + } else { + if (registeredPagination.page < registeredPagination.pages) { + this.setData({ + registeredPagination: { ...registeredPagination, page: registeredPagination.page + 1 }, + }) + this.fetchRegisteredList() + } + } + }, +}) export {} diff --git a/src/pages/myAct/index.wxml b/src/pages/myAct/index.wxml index 5e63078..c52ac39 100644 --- a/src/pages/myAct/index.wxml +++ b/src/pages/myAct/index.wxml @@ -9,41 +9,102 @@ > - - + + - 进行中 - 已发布 - 报名中 - 草稿 - 已取消 - 已结束 - - 128人已报名 + + + {{item.activityStatusName}} + + + {{item.regCount}}人已报名 - 深职大第十五届校园歌手大赛 + {{item.name}} - 2026.04.01-2026.05.30 + {{item.startAt}} - {{item.endAt}} - + - 留仙洞校区音乐厅 + {{item.location}} - 86人已报名 - 删除 - - - - 编辑 + {{item.regCount}}人已报名 + + + 删除 + 编辑 + + + + 签到二维码 + 取消 + 编辑 + + + + 签到二维码 + 详情 + + + + 删除 + 详情 + + + + + + + + + + + + + + {{item.activityStatusName}} + + + {{item.regCount}}人已报名 + + + {{item.name}} + + + {{item.startAt}} - {{item.endAt}} + + + + {{item.location}} + + + + + + 已签到 + 已评价 + 已报名 + + + + 去签到 + + + + 去评价 + + + + 详情 + + - 内容 2 diff --git a/src/pages/myComment/index.wxml b/src/pages/myComment/index.wxml index ebc4e43..cc7d98a 100644 --- a/src/pages/myComment/index.wxml +++ b/src/pages/myComment/index.wxml @@ -7,7 +7,7 @@ active="{{ active }}" bind:change="onChange" > - + @@ -52,7 +52,6 @@ - 内容 2 - 内容 2 + 内容 2 diff --git a/src/pages/notice/index.json b/src/pages/notice/index.json index 0e929f8..90cf718 100644 --- a/src/pages/notice/index.json +++ b/src/pages/notice/index.json @@ -1,9 +1,12 @@ { "navigationBarTitleText": "通知", "navigationStyle": "default", + "enablePullDownRefresh": true, "usingComponents": { "van-tab": "@vant/weapp/tab/index", "van-tabs": "@vant/weapp/tabs/index", - "van-icon": "@vant/weapp/icon/index" + "van-icon": "@vant/weapp/icon/index", + "pagination": "/components/pagination/index", + "mp-html": "mp-html" } } diff --git a/src/pages/notice/index.scss b/src/pages/notice/index.scss index 87c9258..f883f8a 100644 --- a/src/pages/notice/index.scss +++ b/src/pages/notice/index.scss @@ -11,6 +11,12 @@ page { --tab-font-size: 32rpx; } } + .page-empty { + padding: 100rpx 0; + text-align: center; + font-size: 28rpx; + color: rgba(148, 163, 184, 1); + } .page0 { padding: 0 32rpx; .module { @@ -31,6 +37,7 @@ page { font-weight: bold; } .content { + display: block; margin-top: 24rpx; font-size: 28rpx; color: rgba(100, 116, 139, 1); diff --git a/src/pages/notice/index.ts b/src/pages/notice/index.ts index becaafa..58a6c9e 100644 --- a/src/pages/notice/index.ts +++ b/src/pages/notice/index.ts @@ -1,15 +1,225 @@ -const _app = getApp() +const app = getApp() + +// 公告列表项 +interface IAnnouncementItem { + id: number + title: string + content: string + displayPosition: number + priority: number + redDot: boolean + jumpType: number + jumpTarget: string + buttonText: string + startAt: string + endAt: string + createdAt: string + isRead: boolean +} + +// 通知列表项 +interface INoticeItem { + id: number + taskId: number + title: string + subtitle: string + subType: string + subTypeName: string + coverImage: string + jumpType: number + jumpTarget: string + buttonText: string + isRead: boolean + scheduleAt: string +} + +interface IPagination { + page: number + pageSize: number + pages: number + count: number +} Page({ data: { - active: 2, + active: 0, + imageUrl: app.globalData.imageUrl, + Timestamp: app.globalData.Timestamp, + + // 公告 + announcementList: [] as IAnnouncementItem[], + announcementPagination: { page: 1, pageSize: 20, pages: 0, count: 0 } as IPagination, + announcementLoading: false, + + // 重要通知 + importantList: [] as INoticeItem[], + importantPagination: { page: 1, pageSize: 20, pages: 0, count: 0 } as IPagination, + importantLoading: false, + + // 活动通知 + activityList: [] as INoticeItem[], + activityPagination: { page: 1, pageSize: 20, pages: 0, count: 0 } as IPagination, + activityLoading: false, }, - onLoad() {}, - handleDetail() { + + onLoad() { + app.waitLogin({ type: 1 }).then(() => { + this.fetchAnnouncementList() + }) + }, + + onShow() { + // 刷新 tabbar 红点 + const tabBar = this.getTabBar() + if (tabBar && typeof tabBar.fetchUnreadCount === 'function') { + tabBar.fetchUnreadCount() + } + }, + + // 切换 tab + onChange(e: WechatMiniprogram.CustomEvent) { + const active = e.detail.index ?? 0 + this.setData({ active }) + + if (active === 0 && this.data.announcementList.length === 0) { + this.fetchAnnouncementList() + } else if (active === 1 && this.data.importantList.length === 0) { + this.fetchNoticeList('important') + } else if (active === 2 && this.data.activityList.length === 0) { + this.fetchNoticeList('activity') + } + }, + + // 获取公告列表(专用接口) + async fetchAnnouncementList(isRefresh = false) { + if (this.data.announcementLoading) return + + const { announcementPagination } = this.data + const page = isRefresh ? 1 : announcementPagination.page + + this.setData({ announcementLoading: true }) + + try { + const res = await wx.ajax({ + url: '/notification/announcement-list', + method: 'GET', + data: { page, pageSize: announcementPagination.pageSize }, + }) + + if (res) { + const newList = isRefresh ? res.list : [...this.data.announcementList, ...res.list] + this.setData({ + announcementList: newList, + announcementPagination: { + page: res.pagination?.page || page, + pageSize: res.pagination?.pageSize || announcementPagination.pageSize, + pages: res.pagination?.totalPages || 0, + count: res.pagination?.total || 0, + }, + }) + } + } catch (err) { + console.error('获取公告列表失败:', err) + } finally { + this.setData({ announcementLoading: false }) + } + }, + + // 获取通知列表(重要通知/活动通知) + async fetchNoticeList(type: 'important' | 'activity', isRefresh = false) { + const loadingKey = `${type}Loading` as const + const listKey = `${type}List` as const + const paginationKey = `${type}Pagination` as const + + if (this.data[loadingKey]) return + + const pagination = this.data[paginationKey] + const page = isRefresh ? 1 : pagination.page + + this.setData({ [loadingKey]: true } as any) + + try { + const res = await wx.ajax({ + url: '/notification/list', + method: 'GET', + data: { type, page, pageSize: pagination.pageSize }, + }) + + if (res) { + const newList = isRefresh ? res.list : [...this.data[listKey], ...res.list] + this.setData({ + [listKey]: newList, + [paginationKey]: { + page: res.pagination?.page || page, + pageSize: res.pagination?.pageSize || pagination.pageSize, + pages: res.pagination?.totalPages || 0, + count: res.pagination?.total || 0, + }, + } as any) + } + } catch (err) { + console.error('获取通知列表失败:', err) + } finally { + this.setData({ [loadingKey]: false } as any) + } + }, + + // 查看公告详情 + handleAnnouncementDetail(e: WechatMiniprogram.TouchEvent) { + const { id } = e.currentTarget.dataset wx.navigateTo({ - url: '/pages/noticeDetail/index', + url: `/pages/noticeDetail/index?id=${id}&type=announcement`, }) }, + + // 查看通知详情 + handleNoticeDetail(e: WechatMiniprogram.TouchEvent) { + const { taskId } = e.currentTarget.dataset + wx.navigateTo({ + url: `/pages/noticeDetail/index?taskId=${taskId}&type=notice`, + }) + }, + + // 下拉刷新 + onPullDownRefresh() { + const { active } = this.data + if (active === 0) { + this.fetchAnnouncementList(true) + } else if (active === 1) { + this.fetchNoticeList('important', true) + } else if (active === 2) { + this.fetchNoticeList('activity', true) + } + wx.stopPullDownRefresh() + }, + + // 上拉加载更多 + onReachBottom() { + const { active, announcementPagination, importantPagination, activityPagination } = this.data + + if (active === 0) { + if (announcementPagination.page < announcementPagination.pages) { + this.setData({ + announcementPagination: { ...announcementPagination, page: announcementPagination.page + 1 }, + }) + this.fetchAnnouncementList() + } + } else if (active === 1) { + if (importantPagination.page < importantPagination.pages) { + this.setData({ + importantPagination: { ...importantPagination, page: importantPagination.page + 1 }, + }) + this.fetchNoticeList('important') + } + } else if (active === 2) { + if (activityPagination.page < activityPagination.pages) { + this.setData({ + activityPagination: { ...activityPagination, page: activityPagination.page + 1 }, + }) + this.fetchNoticeList('activity') + } + } + }, }) -export {} +export {} \ No newline at end of file diff --git a/src/pages/notice/index.wxml b/src/pages/notice/index.wxml index d1bd56c..d94dc5b 100644 --- a/src/pages/notice/index.wxml +++ b/src/pages/notice/index.wxml @@ -7,15 +7,47 @@ active="{{ active }}" bind:change="onChange" > + - - 03-01 13:01 + + {{item.createdAt}} - 2025-2026学年暑期社会实践报名启动 - - 本次活动聚焦社会服务、乡村振兴、红色教育、志愿服务等方向,引导青年以实干践行使命,用行... + {{item.title}} + + + 公告 + + 查看详情 + + + + + + + + + + + + + {{item.scheduleAt}} + + {{item.title}} + 重要通知 @@ -25,26 +57,28 @@ + - 内容 2 + + - - 03-01 13:01 + + {{item.scheduleAt}} - 2025-2026学年暑期社会实践报名启动 - - 本次活动聚焦社会服务、乡村振兴、红色教育、志愿服务等方向,引导青年以实干践行使命,用行... - - + {{item.title}} + {{item.subtitle}} - - - - - - 重要通知 + + {{item.subTypeName}} + 查看详情 @@ -52,8 +86,13 @@ + - 内容 2 + + + + 暂未开放 + diff --git a/src/pages/noticeDetail/index.ts b/src/pages/noticeDetail/index.ts index 067b6bb..e97ebe0 100644 --- a/src/pages/noticeDetail/index.ts +++ b/src/pages/noticeDetail/index.ts @@ -1,8 +1,119 @@ -const _app = getApp(); +const app = getApp() + +// 分类名称映射 +const categoryNameMap: Record = { + announcement: '公告', + important: '重要通知', + activity: '活动通知', + agent: '智能体通知', +} Page({ - data: {}, - onLoad() {}, -}); + data: { + imageUrl: app.globalData.imageUrl, + Timestamp: app.globalData.Timestamp, + type: '', // announcement | notice + id: 0, + taskId: 0, + detail: null as any, + categoryName: '', + }, + + onLoad(options: { id?: string, taskId?: string, type?: string }) { + const type = options.type || 'notice' + const id = Number(options.id || 0) + const taskId = Number(options.taskId || 0) + + if (type === 'announcement' && !id) { + wx.showToast({ title: '参数错误', icon: 'error' }) + setTimeout(() => wx.navigateBack(), 1500) + return + } + + if (type === 'notice' && !taskId) { + wx.showToast({ title: '参数错误', icon: 'error' }) + setTimeout(() => wx.navigateBack(), 1500) + return + } + + this.setData({ type, id, taskId }) + + app.waitLogin({ type: 1 }).then(() => { + if (type === 'announcement') { + this.fetchAnnouncementDetail() + } else { + this.fetchNoticeDetail() + } + }) + }, + + // 获取公告详情(专用接口) + async fetchAnnouncementDetail() { + try { + const res = await wx.ajax({ + url: '/notification/announcement-detail', + method: 'GET', + data: { id: this.data.id }, + }) + + if (res) { + this.setData({ + detail: res, + categoryName: '公告', + }) + } + } catch (err) { + console.error('获取公告详情失败:', err) + wx.showToast({ title: '获取详情失败', icon: 'error' }) + } + }, + + // 获取通知详情 + async fetchNoticeDetail() { + try { + const res = await wx.ajax({ + url: '/notification/detail', + method: 'GET', + data: { taskId: this.data.taskId }, + }) + + if (res) { + this.setData({ + detail: res, + categoryName: categoryNameMap[res.category] || '通知', + }) + } + } catch (err) { + console.error('获取通知详情失败:', err) + wx.showToast({ title: '获取详情失败', icon: 'error' }) + } + }, + + // 跳转 + handleJump() { + const { detail } = this.data + if (!detail || detail.jumpType === 0) return + + if (detail.jumpType === 1) { + // 不跳转 + + } else if (detail.jumpType === 2) { + // 跳转活动 + wx.navigateTo({ url: `/pages/actDetail/index?id=${detail.jumpTarget}` }) + } else if (detail.jumpType === 3) { + // 跳转小程序页面 + wx.navigateTo({ url: detail.jumpTarget }) + } else if (detail.jumpType === 4) { + // 跳转H5链接 + wx.navigateTo({ + url: `/pages/webview/index?url=${encodeURIComponent(detail.jumpTarget)}`, + }) + } + }, + + handleBack() { + wx.navigateBack() + }, +}) -export {} +export {} \ No newline at end of file diff --git a/src/pages/noticeDetail/index.wxml b/src/pages/noticeDetail/index.wxml index 6b3338a..4a01c52 100644 --- a/src/pages/noticeDetail/index.wxml +++ b/src/pages/noticeDetail/index.wxml @@ -1,38 +1,23 @@ - + - 通知 + {{categoryName}} - 已发布 - - 关于开展校园歌手大赛的通知 - - 校园活动 + {{detail.title}} - 2026-05-20 14:30:00 - - - - 浏览人数:128 + {{type === 'announcement' ? detail.createdAt : detail.scheduleAt}} - - - - - - 温馨提示 - 未尽事宜,请联系校团委文艺部,联系电话:0755-12345678。 - + - - 查看活动详情 + + {{detail.buttonText}} - + \ No newline at end of file diff --git a/typings/index.d.ts b/typings/index.d.ts index 62e9382..028b105 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -33,11 +33,13 @@ interface IAppOption { openidSession?: string // 临时凭证(用于绑定) userInfo?: UserInfo | {} loginRedirectUrl?: string // 登录后返回的页面路径 + needBind: boolean + loginPromise: Promise [propName: string]: any } getUserInfo: (type?: 0 | 1 | 2) => Promise - startLogin: (callback?: () => void) => void + startLogin: (callback?: () => void) => Promise waitLogin: (params?: { type?: 0 | 1 }) => Promise [propName: string]: any }