diff --git a/components/tab-bar/postList.vue b/components/tab-bar/postList.vue
index 5799399..edbc7a2 100644
--- a/components/tab-bar/postList.vue
+++ b/components/tab-bar/postList.vue
@@ -60,6 +60,7 @@
>
+ {{formatBadgeCount(fishUnread)}}
{{item.name}}
{{item.action == 'publishSell' ? '点我发布' : item.desc}}
@@ -114,7 +115,7 @@
{{item.xiangyao}}
@@ -133,6 +134,17 @@
+
+
+
+
+
@@ -141,7 +153,8 @@
listFishCategories,
pageFishGoods,
pageFishWants,
- toggleFishLike
+ toggleFishLike,
+ getFishUnreadSummary
} from '@/common/fishMarketApi.js'
const POST_LIST_IMAGE_CACHE_KEY = 'post_list_image_cache_v1'
@@ -169,7 +182,7 @@
desc: '大家想要的',
category: '求购',
imageKey: 'wanted',
- action: 'category'
+ action: 'wantSquare'
}, {
name: '拼团购',
desc: '暂未开放',
@@ -195,14 +208,18 @@
imageKey: 'graduation',
action: 'mine'
}],
- baseCategories: ['推荐', '附近', '最新', '求购'],
+ baseCategories: ['推荐', '最新'],
categoryList: [],
goodsList: [],
pageNum: 1,
pageSize: 10,
totalPages: 1,
loading: false,
- sortType: 'hot'
+ sortType: 'hot',
+ likeEffectSeq: 0,
+ likeEffectTimers: {},
+ pageLikeEffects: [],
+ fishUnread: 0
}
},
computed: {
@@ -232,6 +249,14 @@
},
created() {
this.applyCachedPostListImages()
+ uni.$on('fishUnreadChange', this.handleFishUnreadChange)
+ },
+ beforeDestroy() {
+ Object.keys(this.likeEffectTimers || {}).forEach(key => {
+ clearTimeout(this.likeEffectTimers[key])
+ })
+ this.likeEffectTimers = {}
+ uni.$off('fishUnreadChange', this.handleFishUnreadChange)
},
methods: {
getPostListImage(key) {
@@ -375,8 +400,32 @@
}
}
this.loadCategories()
+ this.loadFishUnread()
this.reloadGoods()
},
+ formatBadgeCount(count) {
+ const value = Number(count) || 0
+ return value > 99 ? '99+' : value
+ },
+ loadFishUnread() {
+ const token = uni.getStorageSync('hiver_token')
+ if (!token) {
+ this.fishUnread = 0
+ return
+ }
+ getFishUnreadSummary().then(res => {
+ this.fishUnread = Number(res && res.fishUnread) || 0
+ }).catch(() => {
+ this.fishUnread = 0
+ })
+ },
+ handleFishUnreadChange(summary) {
+ if (summary && summary.fishUnread !== undefined) {
+ this.fishUnread = Number(summary.fishUnread) || 0
+ return
+ }
+ this.loadFishUnread()
+ },
handleSearch() {
uni.hideKeyboard()
this.reloadGoods()
@@ -410,6 +459,10 @@
this.goFishSessions()
return
}
+ if (item.action == 'wantSquare') {
+ this.goWantSquare()
+ return
+ }
const index = this.categories.indexOf(item.category)
if (index > -1) {
this.categoryIndex = index
@@ -555,12 +608,36 @@
uni.showToast({ title: '商品ID不能为空', icon: 'none' })
return
}
+ const shouldAnimate = !item.liked
+ if (shouldAnimate) {
+ this.playLikeEffect(item)
+ }
toggleFishLike({ goodsId: item.id, likeType: 'goods' }).then(res => {
if (!res) return
item.liked = !!res.liked
item.xiangyao = Number(res.count) || 0
})
},
+ playLikeEffect(item) {
+ const burstId = Date.now() + '-' + (++this.likeEffectSeq)
+ const tracks = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight']
+ const effects = tracks.map((name, index) => ({
+ id: burstId + '-' + index,
+ className: 'page-like-heart--' + name,
+ style: `left:${6 + ((index * 59) % 86)}%; animation-delay:${index * 95}ms;`
+ }))
+ this.pageLikeEffects = effects
+ const timerKey = 'page'
+ if (this.likeEffectTimers[timerKey]) {
+ clearTimeout(this.likeEffectTimers[timerKey])
+ }
+ this.likeEffectTimers[timerKey] = setTimeout(() => {
+ if (this.pageLikeEffects.length && String(this.pageLikeEffects[0].id).indexOf(burstId) === 0) {
+ this.pageLikeEffects = []
+ }
+ delete this.likeEffectTimers[timerKey]
+ }, 3050)
+ },
goDetail(item) {
if (!item) return
if (item.type == 'want') {
@@ -585,6 +662,11 @@
uni.navigateTo({
url: '/package2/IdleTrad/sessionList'
})
+ },
+ goWantSquare() {
+ uni.navigateTo({
+ url: '/package2/IdleTrad/wantList'
+ })
}
}
}
@@ -816,7 +898,7 @@
}
.shortcut-item--publish {
- margin-top: -8rpx;
+ margin-top: 4rpx;
}
.shortcut-item--publish::before {
@@ -836,6 +918,7 @@
width: 84rpx;
height: 84rpx;
margin: 0 auto 10rpx;
+ position: relative;
}
.shortcut-item--publish .shortcut-icon {
@@ -852,6 +935,24 @@
display: block;
}
+ .shortcut-badge {
+ position: absolute;
+ right: -8rpx;
+ top: -8rpx;
+ min-width: 30rpx;
+ height: 30rpx;
+ padding: 0 8rpx;
+ border-radius: 18rpx;
+ background: #ff4d5f;
+ color: #fff;
+ font-size: 18rpx;
+ font-weight: 900;
+ line-height: 30rpx;
+ text-align: center;
+ box-shadow: 0 6rpx 14rpx rgba(255, 77, 95, .26);
+ box-sizing: border-box;
+ }
+
.shortcut-name {
color: #29342f;
font-size: 21rpx;
@@ -1084,6 +1185,7 @@
}
.want-count {
+ position: relative;
display: flex;
align-items: center;
gap: 3rpx;
@@ -1091,6 +1193,142 @@
font-size: 17rpx;
}
+ .page-like-effect {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ overflow: hidden;
+ pointer-events: none;
+ z-index: 99999;
+ }
+
+ .page-like-heart {
+ position: absolute;
+ bottom: -230rpx;
+ width: 120rpx;
+ height: 120rpx;
+ opacity: 0;
+ transform-origin: center;
+ animation-duration: 2600ms;
+ animation-timing-function: cubic-bezier(.14, .66, .22, 1);
+ animation-fill-mode: forwards;
+ }
+
+ .page-like-heart-shape {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ width: 72%;
+ height: 72%;
+ background: linear-gradient(135deg, rgba(255, 176, 207, .72), rgba(255, 88, 148, .36));
+ border-radius: 18rpx 18rpx 10rpx 18rpx;
+ box-shadow:
+ 0 18rpx 42rpx rgba(255, 104, 164, .28),
+ inset 14rpx 18rpx 28rpx rgba(255, 255, 255, .32);
+ filter: blur(.2rpx);
+ transform: translate(-50%, -50%) rotate(45deg);
+ }
+
+ .page-like-heart-shape::before,
+ .page-like-heart-shape::after {
+ content: '';
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ background: inherit;
+ box-shadow: inherit;
+ }
+
+ .page-like-heart-shape::before {
+ left: -50%;
+ top: 0;
+ }
+
+ .page-like-heart-shape::after {
+ left: 0;
+ top: -50%;
+ }
+
+ .page-like-heart--one { width: 154rpx; height: 154rpx; animation-name: pageHeartFloatOne; }
+ .page-like-heart--two { width: 112rpx; height: 112rpx; animation-name: pageHeartFloatTwo; }
+ .page-like-heart--three { width: 188rpx; height: 188rpx; animation-name: pageHeartFloatThree; }
+ .page-like-heart--four { width: 92rpx; height: 92rpx; animation-name: pageHeartFloatFour; }
+ .page-like-heart--five { width: 136rpx; height: 136rpx; animation-name: pageHeartFloatFive; }
+ .page-like-heart--six { width: 82rpx; height: 82rpx; animation-name: pageHeartFloatSix; }
+ .page-like-heart--seven { width: 166rpx; height: 166rpx; animation-name: pageHeartFloatSeven; }
+ .page-like-heart--eight { width: 104rpx; height: 104rpx; animation-name: pageHeartFloatEight; }
+
+ .page-like-heart--three .page-like-heart-shape,
+ .page-like-heart--seven .page-like-heart-shape {
+ background: linear-gradient(135deg, rgba(255, 198, 220, .66), rgba(255, 103, 158, .28));
+ filter: blur(1rpx);
+ }
+
+ .page-like-heart--four .page-like-heart-shape,
+ .page-like-heart--six .page-like-heart-shape,
+ .page-like-heart--eight .page-like-heart-shape {
+ background: linear-gradient(135deg, rgba(255, 211, 226, .74), rgba(255, 126, 174, .32));
+ }
+
+ @keyframes pageHeartFloatOne {
+ 0% { opacity: 0; transform: translate3d(0, 0, 0) scale(.68) rotate(-8deg); }
+ 14% { opacity: .82; transform: translate3d(-18rpx, -92rpx, 0) scale(1) rotate(5deg); }
+ 62% { opacity: .52; transform: translate3d(-72rpx, -370rpx, 0) scale(.94) rotate(-12deg); }
+ 100% { opacity: 0; transform: translate3d(-112rpx, -650rpx, 0) scale(.72) rotate(-22deg); }
+ }
+
+ @keyframes pageHeartFloatTwo {
+ 0% { opacity: 0; transform: translate3d(0, 0, 0) scale(.62) rotate(6deg); }
+ 16% { opacity: .7; transform: translate3d(28rpx, -112rpx, 0) scale(.92) rotate(-6deg); }
+ 64% { opacity: .44; transform: translate3d(96rpx, -350rpx, 0) scale(.82) rotate(12deg); }
+ 100% { opacity: 0; transform: translate3d(146rpx, -600rpx, 0) scale(.62) rotate(22deg); }
+ }
+
+ @keyframes pageHeartFloatThree {
+ 0% { opacity: 0; transform: translate3d(0, 0, 0) scale(.64) rotate(-4deg); }
+ 12% { opacity: .64; transform: translate3d(8rpx, -86rpx, 0) scale(.94) rotate(7deg); }
+ 60% { opacity: .38; transform: translate3d(-36rpx, -420rpx, 0) scale(.78) rotate(-12deg); }
+ 100% { opacity: 0; transform: translate3d(-68rpx, -710rpx, 0) scale(.58) rotate(-24deg); }
+ }
+
+ @keyframes pageHeartFloatFour {
+ 0% { opacity: 0; transform: translate3d(0, 0, 0) scale(.58) rotate(12deg); }
+ 18% { opacity: .68; transform: translate3d(-26rpx, -124rpx, 0) scale(.86) rotate(-6deg); }
+ 66% { opacity: .4; transform: translate3d(-112rpx, -320rpx, 0) scale(.72) rotate(14deg); }
+ 100% { opacity: 0; transform: translate3d(-166rpx, -560rpx, 0) scale(.54) rotate(26deg); }
+ }
+
+ @keyframes pageHeartFloatFive {
+ 0% { opacity: 0; transform: translate3d(0, 0, 0) scale(.64) rotate(-10deg); }
+ 14% { opacity: .72; transform: translate3d(34rpx, -96rpx, 0) scale(.96) rotate(6deg); }
+ 62% { opacity: .44; transform: translate3d(68rpx, -380rpx, 0) scale(.78) rotate(-10deg); }
+ 100% { opacity: 0; transform: translate3d(104rpx, -650rpx, 0) scale(.58) rotate(-20deg); }
+ }
+
+ @keyframes pageHeartFloatSix {
+ 0% { opacity: 0; transform: translate3d(0, 0, 0) scale(.52) rotate(8deg); }
+ 20% { opacity: .64; transform: translate3d(6rpx, -132rpx, 0) scale(.8) rotate(-8deg); }
+ 68% { opacity: .36; transform: translate3d(44rpx, -290rpx, 0) scale(.64) rotate(12deg); }
+ 100% { opacity: 0; transform: translate3d(70rpx, -500rpx, 0) scale(.48) rotate(22deg); }
+ }
+
+ @keyframes pageHeartFloatSeven {
+ 0% { opacity: 0; transform: translate3d(0, 0, 0) scale(.62) rotate(-8deg); }
+ 13% { opacity: .62; transform: translate3d(-28rpx, -82rpx, 0) scale(.88) rotate(6deg); }
+ 58% { opacity: .38; transform: translate3d(-86rpx, -390rpx, 0) scale(.72) rotate(-12deg); }
+ 100% { opacity: 0; transform: translate3d(-132rpx, -680rpx, 0) scale(.52) rotate(-22deg); }
+ }
+
+ @keyframes pageHeartFloatEight {
+ 0% { opacity: 0; transform: translate3d(0, 0, 0) scale(.56) rotate(10deg); }
+ 18% { opacity: .66; transform: translate3d(38rpx, -118rpx, 0) scale(.84) rotate(-8deg); }
+ 64% { opacity: .4; transform: translate3d(122rpx, -338rpx, 0) scale(.68) rotate(14deg); }
+ 100% { opacity: 0; transform: translate3d(178rpx, -580rpx, 0) scale(.5) rotate(26deg); }
+ }
+
.empty-state {
padding: 100rpx 0;
color: #77827d;
diff --git a/components/tab-bar/tab-bar.vue b/components/tab-bar/tab-bar.vue
index 0372803..8159510 100644
--- a/components/tab-bar/tab-bar.vue
+++ b/components/tab-bar/tab-bar.vue
@@ -7,6 +7,9 @@
{{formatBadgeCount(indexWorkerCount)}}
+
+ {{formatBadgeCount(indexFishCount)}}
+
{{formatBadgeCount(indexMyCount)}}
@@ -60,6 +63,7 @@
}],
indexZhipaiCount:0,
indexWorkerCount:0,
+ indexFishCount:0,
indexMyCount:0,
countRefreshId:0
}
@@ -90,10 +94,12 @@
},
mounted() {
uni.$on('deliveryStatusCountsChange', this.setDeliveryStatusCounts)
+ uni.$on('fishUnreadChange', this.setFishUnreadCounts)
this.setDeliveryStatusCounts(uni.getStorageSync('deliveryStatusCounts') || {})
},
beforeDestroy() {
uni.$off('deliveryStatusCountsChange', this.setDeliveryStatusCounts)
+ uni.$off('fishUnreadChange', this.setFishUnreadCounts)
},
methods: {
hasLogin() {
@@ -115,6 +121,14 @@
this.indexWorkerCount = (Number(counts.daiqu) || 0) + (Number(counts.daisong) || 0)
this.$forceUpdate()
},
+ setFishUnreadCounts(summary) {
+ if (summary && summary.fishUnread !== undefined) {
+ this.indexFishCount = Number(summary.fishUnread) || 0
+ this.$forceUpdate()
+ return
+ }
+ this.getWorkerCounts()
+ },
//点击tab切换
clickBar(index) {
if (index !== 0 && !this.hasLogin()) {
@@ -145,7 +159,11 @@
const refreshId = ++this.countRefreshId
this.indexZhipaiCount = 0
this.indexWorkerCount = 0
+ this.indexFishCount = 0
this.indexMyCount = 0
+ if (!this.hasLogin()) {
+ return Promise.resolve()
+ }
let deliveryStatusCounts = {
daiqu: 0,
daisong: 0
@@ -161,6 +179,12 @@
}
let shopCounts = 0
let tasks = []
+ tasks.push(this.tui.request("/app/fishmarket/unread/summary", "GET", {}, false, true).then((res) => {
+ if (res && res.success !== false) {
+ const result = res.result || res
+ this.indexFishCount = Number(result.fishUnread) || 0
+ }
+ }).catch(() => {}))
if(worker){
const area = uni.getStorageSync('area') ? JSON.parse(uni.getStorageSync('area')) : {}
tasks.push(this.tui.request("/mall/delivery/countOrderByStatus", "POST", {workerId:worker.workerId,regionId:area.id}, false, true).then((res) => {