diff --git a/components/tab-bar/delivery.vue b/components/tab-bar/delivery.vue index 9838e35..e20329b 100644 --- a/components/tab-bar/delivery.vue +++ b/components/tab-bar/delivery.vue @@ -492,6 +492,8 @@ refreshLoading: false, refreshLoadingStartTime: 0, refreshLoadingTimer: null, + deliveryRequestId: 0, + loadStatus: 'more', pickupCode: '', pickupCodeContext: null, pickupCodeSubmitting: false, @@ -642,11 +644,12 @@ } uni.setStorageSync('deliveryStatusCounts', this.deliveryStatusCounts) }, - syncDeliveryStatusCountFromPage(total) { + syncDeliveryStatusCountFromPage(total, status) { const count = Number(total) || 0 - if (this.searchForm.status == 1) { + const currentStatus = status || this.searchForm.status + if (currentStatus == 1) { this.deliveryStatusCounts.daiqu = count - } else if (this.searchForm.status == 2) { + } else if (currentStatus == 2) { this.deliveryStatusCounts.daisong = count } uni.setStorageSync('deliveryStatusCounts', this.deliveryStatusCounts) @@ -723,6 +726,7 @@ if (uni.getStorageSync('worker')) { this.searchForm.workerId = uni.getStorageSync('worker').workerId } + this.searchForm.pageNum = 1 this.$forceUpdate() this.loadWorkerRules() this.getDelivery() @@ -897,31 +901,40 @@ getDelivery() { if (!this.ensureLogin()) return let that = this + const requestId = ++that.deliveryRequestId + const requestForm = Object.assign({}, that.searchForm) + const requestStatus = requestForm.status + const requestPageNum = Number(requestForm.pageNum) || 1 that.isLoadingPage = true; - this.tui.request("/mall/delivery/pagebyworker", "POST", this.searchForm, false, false).then((res) => { + that.loadStatus = 'loading'; + this.tui.request("/mall/delivery/pagebyworker", "POST", requestForm, false, false).then((res) => { + if (requestId != that.deliveryRequestId) return that.isLoadingPage = false; - that.loadStatus = 'nomore'; if (res.code == 200) { - that.syncDeliveryStatusCountFromPage(res.result && res.result.total) - if (this.searchForm.status == 1) { - if (that.searchForm.pageNum == 1) { - that.deliveryItem1 = res.result.records; + const result = res.result || {} + const records = Array.isArray(result.records) ? result.records : [] + that.syncDeliveryStatusCountFromPage(result.total, requestStatus) + if (requestStatus == 1) { + if (requestPageNum == 1) { + that.deliveryItem1 = records; } else { - that.deliveryItem1 = [...that.deliveryItem1, ...res.result.records] + that.deliveryItem1 = [...that.deliveryItem1, ...records] } that.pageData = that.deliveryItem1 } else { - if (that.searchForm.pageNum == 1) { - that.deliveryItem = res.result.records; + if (requestPageNum == 1) { + that.deliveryItem = records; } else { - that.deliveryItem = [...that.deliveryItem, ...res.result.records] + that.deliveryItem = [...that.deliveryItem, ...records] } that.pageData = that.deliveryItem } - that.totalPages = res.result.pages; // pages = 总页数 + that.totalPages = Number(result.pages) || 1; // pages = 总页数 + that.loadStatus = requestPageNum >= that.totalPages ? 'nomore' : 'more'; that.$forceUpdate(); } else { + that.loadStatus = 'more'; that.tui.toast(res.message); } if (that.refreshLoading) { @@ -930,7 +943,9 @@ uni.hideLoading(); } }).catch((res) => { + if (requestId != that.deliveryRequestId) return that.isLoadingPage = false; + that.loadStatus = 'more'; if (that.refreshLoading) { that.finishRefreshLoading() } diff --git a/components/tab-bar/myCenter.vue b/components/tab-bar/myCenter.vue index 9b60aee..6c208fd 100644 --- a/components/tab-bar/myCenter.vue +++ b/components/tab-bar/myCenter.vue @@ -5,11 +5,12 @@ - - + + + - + {{(userName == null || userName == undefined) ? '半径同学' : userName}} @@ -158,6 +159,31 @@ + + + + 编辑头像昵称 + 使用微信头像昵称填写能力完善资料 + +
+ + + 取消 + + +
+
+
@@ -230,6 +256,14 @@ worker: uni.getStorageSync('worker'), balanceRefreshId: 0, refreshing: false, + profileNickname: uni.getStorageSync('nickName') || uni.getStorageSync('nickname') || uni.getStorageSync('userName') || '半径同学', + profileAvatar: uni.getStorageSync('avatarUrl') || uni.getStorageSync('avatar') || uni.getStorageSync('headimgurl') || '', + editNickname: '', + editAvatar: '', + nicknameInputByKeyboard: false, + nicknameManualTipShown: false, + profileSaving: false, + profileUploading: false, couponCount: Number(uni.getStorageSync('couponCount')) || 8, blindBoxCount: Number(uni.getStorageSync('blindBoxCount')) || 8, orderCounts: { @@ -277,7 +311,10 @@ }) }, userName() { - return uni.getStorageSync('nickName') || uni.getStorageSync('nickname') || uni.getStorageSync('userName') || '半径同学' + return this.profileNickname || '半径同学' + }, + userAvatar() { + return this.profileAvatar || this.getMyCenterImage('avatar') }, avatarText() { return (this.userName || '张').slice(0, 1) @@ -402,6 +439,7 @@ }, mounted() { this.setMenuButtonInfo() + this.syncCachedUserProfile() uni.$on('myCenterCountsChange', this.setPendingCounts) }, beforeDestroy() { @@ -411,6 +449,120 @@ getMyCenterImage(key) { return this.myCenterImages[key] || MY_CENTER_IMAGE_URLS[key] || '' }, + openProfileEditor() { + this.editNickname = this.userName || '' + this.editAvatar = this.profileAvatar || '' + this.nicknameInputByKeyboard = false + this.nicknameManualTipShown = false + this.$refs.profilePopup.open() + }, + onProfileNicknameInput(e) { + const detail = e && e.detail ? e.detail : {} + const keyCode = detail.keyCode + const isKeyboardInput = keyCode !== undefined && keyCode !== 0 + this.nicknameInputByKeyboard = isKeyboardInput + if (isKeyboardInput && !this.nicknameManualTipShown) { + this.nicknameManualTipShown = true + this.tui.toast('只能选择微信昵称') + } + if (!isKeyboardInput) { + this.nicknameManualTipShown = false + } + }, + onChooseWechatAvatar(e) { + if (this.profileUploading) return + const filePath = e && e.detail ? e.detail.avatarUrl : '' + if (!filePath) return + this.uploadProfileAvatar(filePath).then((url) => { + if (url) this.editAvatar = url + }) + }, + uploadProfileAvatar(path) { + if (!path) return Promise.resolve('') + this.profileUploading = true + return this.tui.uploadFile('/upload/file', path).then((fileObj) => { + const url = this.extractUploadUrl(fileObj) + if (!url) this.tui.toast('头像上传失败') + return url + }).catch(() => { + this.tui.toast('头像上传失败,请稍后重试') + return '' + }).finally(() => { + this.profileUploading = false + }) + }, + extractUploadUrl(fileObj) { + if (!fileObj) return '' + if (typeof fileObj == 'string') return fileObj + return fileObj.url || fileObj.fileUrl || fileObj.path || fileObj.fullPath || '' + }, + saveProfile() { + if (this.profileSaving || this.profileUploading) return + const nickname = (this.editNickname || '').trim() + if (!nickname) { + this.tui.toast('请先获取微信昵称') + return + } + if (this.nicknameInputByKeyboard) { + this.tui.toast('只能选择微信昵称,不能手动输入') + return + } + if (!this.editAvatar) { + this.tui.toast('请先获取微信头像') + return + } + this.editNickname = nickname + this.submitProfile(nickname) + }, + submitProfile(nickname) { + this.profileSaving = true + this.tui.request('/user/profile', 'POST', { + nickname, + avatar: this.editAvatar || '' + }, false, false).then((res) => { + if (res.code == 200) { + const user = Object.assign({ + nickname, + avatar: this.editAvatar || this.profileAvatar + }, res.result || {}) + this.cacheUserProfile(user) + this.$refs.profilePopup.close() + this.tui.toast('修改成功', 1500, true) + this.$forceUpdate() + } else { + this.tui.toast(res.message || '修改失败') + } + }).catch(() => { + this.tui.toast('修改失败,请稍后重试') + }).finally(() => { + this.profileSaving = false + }) + }, + cacheUserProfile(user) { + if (!user) return + if (user.nickname !== undefined) { + this.profileNickname = user.nickname || '半径同学' + uni.setStorageSync('nickName', this.profileNickname) + uni.setStorageSync('nickname', this.profileNickname) + uni.setStorageSync('userName', this.profileNickname) + } + if (user.avatar !== undefined) { + this.profileAvatar = user.avatar || '' + uni.setStorageSync('avatarUrl', this.profileAvatar) + uni.setStorageSync('avatar', this.profileAvatar) + uni.setStorageSync('headimgurl', this.profileAvatar) + } + if (user.id !== undefined) uni.setStorageSync('id', user.id) + if (user.unionid !== undefined) uni.setStorageSync('unionid', user.unionid) + if (user.miniProgramOpenid !== undefined) uni.setStorageSync('miniProgramOpenid', user.miniProgramOpenid) + if (user.officialAccountOpenid !== undefined) uni.setStorageSync('officialAccountOpenid', user.officialAccountOpenid) + }, + syncCachedUserProfile() { + const nickname = uni.getStorageSync('nickName') || uni.getStorageSync('nickname') || uni.getStorageSync('userName') || '' + const avatar = uni.getStorageSync('avatarUrl') || uni.getStorageSync('avatar') || uni.getStorageSync('headimgurl') || '' + if (nickname) this.profileNickname = nickname + if (avatar) this.profileAvatar = avatar + }, getMyCenterImageCache() { const cache = uni.getStorageSync(MY_CENTER_IMAGE_CACHE_KEY) return cache && typeof cache === 'object' ? cache : {} @@ -646,6 +798,7 @@ }, init() { this.applyCachedMyCenterImages() + this.syncCachedUserProfile() const area = this.getArea() this.schoolName1 = area.title this.schoolShop = this.normalizeShopList(uni.getStorageSync('schoolShop')) @@ -917,6 +1070,21 @@ display: block; } + .avatar-edit { + position: absolute; + right: 0; + bottom: 0; + width: 32rpx; + height: 32rpx; + border-radius: 50%; + background: rgba(7, 61, 51, .72); + color: #FFFFFF; + font-size: 18rpx; + font-weight: 900; + line-height: 32rpx; + text-align: center; + } + .level-chip, .identity-pill, .saved-badge, @@ -1698,6 +1866,123 @@ background: rgba(255, 255, 255, .92); } + .profile-popup { + box-sizing: border-box; + width: 620rpx; + padding: 38rpx 34rpx 30rpx; + border: 1rpx solid rgba(255, 255, 255, .78); + border-radius: 42rpx; + background: rgba(255, 255, 255, .94); + box-shadow: 0 30rpx 86rpx rgba(7, 61, 51, .18); + text-align: center; + } + + .profile-popup-title { + color: #073D33; + font-size: 36rpx; + font-weight: 900; + line-height: 44rpx; + } + + .profile-popup-sub { + margin-top: 10rpx; + color: rgba(7, 61, 51, .45); + font-size: 23rpx; + line-height: 32rpx; + } + + .profile-avatar-picker { + position: relative; + width: 132rpx; + height: 132rpx; + margin: 30rpx auto 26rpx; + padding: 0; + border: 0; + border-radius: 38rpx; + overflow: hidden; + background: linear-gradient(145deg, #EAFBF4, #B7F3D5); + box-shadow: 0 12rpx 34rpx rgba(7, 61, 51, .12); + line-height: normal; + } + + .profile-avatar-picker::after { + border: 0; + } + + .profile-avatar-preview { + width: 100%; + height: 100%; + display: block; + } + + .profile-avatar-mask { + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 42rpx; + background: rgba(7, 61, 51, .62); + color: #FFFFFF; + font-size: 20rpx; + font-weight: 800; + line-height: 42rpx; + } + + .profile-name-input { + box-sizing: border-box; + width: 100%; + height: 88rpx; + padding: 0 26rpx; + border-radius: 28rpx; + background: #F8FCFA; + color: #073D33; + font-size: 28rpx; + font-weight: 800; + text-align: left; + } + + .profile-name-placeholder { + color: rgba(7, 61, 51, .32); + } + + .profile-popup-actions { + display: flex; + gap: 18rpx; + margin-top: 30rpx; + } + + .profile-popup-cancel, + .profile-popup-save { + flex: 1; + height: 78rpx; + margin: 0; + padding: 0; + border: 0; + border-radius: 999rpx; + font-size: 28rpx; + font-weight: 900; + line-height: 78rpx; + } + + .profile-popup-save::after { + border: 0; + } + + .profile-popup-cancel { + background: #F2F6F4; + color: rgba(7, 61, 51, .58); + } + + .profile-popup-save { + background: linear-gradient(90deg, #E3FF96, #A6FFEA); + color: #073D33; + box-shadow: 0 12rpx 28rpx rgba(13, 114, 82, .14); + } + + .profile-popup-save.disabled { + opacity: .58; + } + .popup-pay-title, .popup-area-title { margin-bottom: 24rpx; diff --git a/components/tab-bar/postList.vue b/components/tab-bar/postList.vue index 3c093a9..212bc5b 100644 --- a/components/tab-bar/postList.vue +++ b/components/tab-bar/postList.vue @@ -111,7 +111,7 @@ - 半径卖家 + {{item.shopName}} + + + + 校园热榜 + 热议中 + + 每天都有新鲜事,全校都在聊 + + + + + 搜索 + + + + + + + + 发布 + + + + + + + + {{formatBadge(myCircleUnread)}} + {{item.name}} + {{item.desc}} + + + + + + 全部 + 最新 + {{item.name}} + + + + + 随机找搭子 + {{matching ? '匹配中...' : '发起匹配'}} + + + + + + {{item.name}} + + + + + + {{sectionTitle}} + {{sectionSubTitle}} + + {{postSortType === 'latest' ? '最新优先' : (activeCategory === 'wall' ? '实时更新' : '热度优先')}} + + + + + + + + {{displayName(item)}} + {{item.createTime || ''}} · {{item.categoryName || '本校'}} + + 搭一下 + + {{item.title}} + {{item.contentText || stripHtml(item.content)}} + + + + + {{tag}} + + + {{item.location || '地点待定'}} + {{item.activityTime || '时间可商量'}} + {{item.joinedCount || 0}}/{{item.targetCount || '不限'}} 人联系 + + + 微信 {{item.contactWechat}} + 手机 {{item.contactPhone}} + + + + + + 暂无内容,来发布第一条吧 + {{loading ? '加载中...' : (hasMore ? '上滑加载更多' : '已经到底了')}} + + + + + {{effect.emoji}} + + + + + + 模糊匹配找搭子 + 按标题关键词和二级分类,从当前搭子列表里挑一个最合适的。 + + × + + 想找什么搭子 + + 选择二级分类 + + 不限 + {{item.name}} + + + 取消 + 开始匹配 + + + + + + + + + + + + 正在漂流匹配 + 根据关键词和标签寻找最合适的搭子... + + + + + + + + diff --git a/components/tab-bar/tab-bar.vue b/components/tab-bar/tab-bar.vue index 8159510..33f9ff6 100644 --- a/components/tab-bar/tab-bar.vue +++ b/components/tab-bar/tab-bar.vue @@ -7,6 +7,9 @@ {{formatBadgeCount(indexWorkerCount)}} + + {{formatBadgeCount(indexCampusCount)}} + {{formatBadgeCount(indexFishCount)}} @@ -16,10 +19,10 @@ - - + + - + {{item.tabName}} @@ -43,9 +46,9 @@ isCheck: false, shaking:false }, { - tabName: '发布', - tabIcon: '/static/images/tabbar/fabu222.png', - tabIconSelect: '/static/images/tabbar/shouye.png', + tabName: '校园圈', + tabIcon: '/static/images/tabbar/xiaoyuan.png', + tabIconSelect: '', isCheck: false, shaking:false }, { @@ -63,9 +66,13 @@ }], indexZhipaiCount:0, indexWorkerCount:0, + indexCampusCount:0, indexFishCount:0, indexMyCount:0, - countRefreshId:0 + countRefreshId:0, + countLoading:false, + countPromise:null, + lastCountAt:0 } }, props: { @@ -95,11 +102,13 @@ mounted() { uni.$on('deliveryStatusCountsChange', this.setDeliveryStatusCounts) uni.$on('fishUnreadChange', this.setFishUnreadCounts) + uni.$on('campusUnreadChange', this.setCampusUnreadCounts) this.setDeliveryStatusCounts(uni.getStorageSync('deliveryStatusCounts') || {}) }, beforeDestroy() { uni.$off('deliveryStatusCountsChange', this.setDeliveryStatusCounts) uni.$off('fishUnreadChange', this.setFishUnreadCounts) + uni.$off('campusUnreadChange', this.setCampusUnreadCounts) }, methods: { hasLogin() { @@ -124,6 +133,17 @@ setFishUnreadCounts(summary) { if (summary && summary.fishUnread !== undefined) { this.indexFishCount = Number(summary.fishUnread) || 0 + if (summary.campusUnread !== undefined || summary.myCircleUnread !== undefined) { + this.indexCampusCount = Number(summary.campusUnread || summary.myCircleUnread) || 0 + } + this.$forceUpdate() + return + } + this.getWorkerCounts() + }, + setCampusUnreadCounts(summary) { + if (summary && (summary.campusUnread !== undefined || summary.myCircleUnread !== undefined)) { + this.indexCampusCount = Number(summary.campusUnread || summary.myCircleUnread) || 0 this.$forceUpdate() return } @@ -139,7 +159,6 @@ for (let i = 0; i < this.tabList.length; i++) { if (index == i) { this.$emit('tab-index', i); - if(index == 2) return this.getWorkerCounts() this.tabList[i].isCheck = true if (this.tabList[i].shaking) return @@ -156,12 +175,24 @@ } }, getWorkerCounts(){ + const now = Date.now() + if (this.countLoading && this.countPromise) { + return this.countPromise + } + if (this.countPromise && now - this.lastCountAt < 600) { + return this.countPromise + } + this.countLoading = true + this.lastCountAt = now const refreshId = ++this.countRefreshId this.indexZhipaiCount = 0 this.indexWorkerCount = 0 + this.indexCampusCount = 0 this.indexFishCount = 0 this.indexMyCount = 0 if (!this.hasLogin()) { + this.countLoading = false + this.countPromise = null return Promise.resolve() } let deliveryStatusCounts = { @@ -183,6 +214,8 @@ if (res && res.success !== false) { const result = res.result || res this.indexFishCount = Number(result.fishUnread) || 0 + this.indexCampusCount = Number(result.campusUnread || result.myCircleUnread) || 0 + uni.$emit('campusUnreadChange', result) } }).catch(() => {})) if(worker){ @@ -269,7 +302,7 @@ }else{ uni.setStorageSync('shopCounts', 0) } - return Promise.all(tasks).then(() => { + const countPromise = Promise.all(tasks).then(() => { if (refreshId != this.countRefreshId) return this.indexZhipaiCount = workerResult.zhipaiCount this.indexWorkerCount = workerResult.workerTabCount @@ -283,7 +316,16 @@ shopCounts: shopCounts }) this.$forceUpdate() + }).finally(() => { + if (refreshId == this.countRefreshId) { + this.countLoading = false + setTimeout(() => { + if (this.countPromise === countPromise) this.countPromise = null + }, 600) + } }) + this.countPromise = countPromise + return countPromise } } } @@ -355,14 +397,11 @@ pointer-events: none; -webkit-tap-highlight-color: transparent; } - .big-img-box img{ - width: 80%; - height: 90%; - background-size: 100%; - margin: -20rpx 0 0 10%; - display: block; - pointer-events: none; - -webkit-tap-highlight-color: transparent; + + .uni-tab-icon { + display: flex; + align-items: center; + justify-content: center; } @keyframes simple-scale-shake { @@ -411,8 +450,4 @@ font-family: 'PingFang HK'; font-weight: bold; } - .big-img-box{ - width: 100%; - height: 100%; - } \ No newline at end of file