You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
457 lines
16 KiB
457 lines
16 KiB
<template>
|
|
<view class="my-campus-page">
|
|
<view class="header" :style="{ paddingTop: statusBarTop + 'px' }">
|
|
<view class="back" @tap="back"><uni-icons type="left" size="22" color="#173b32"></uni-icons></view>
|
|
<view class="title">我的圈子</view>
|
|
<view class="tabs">
|
|
<view
|
|
class="tab"
|
|
:class="{ active: activeTab === item.key }"
|
|
v-for="item in tabs"
|
|
:key="item.key"
|
|
@tap="switchTab(item.key)"
|
|
>
|
|
<text>{{item.name}}</text>
|
|
<view class="tab-badge" v-if="item.key === 'messages' && commentUnread > 0">{{formatBadgeCount(commentUnread)}}</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="search-bar">
|
|
<uni-icons type="search" size="18" color="#8d9693"></uni-icons>
|
|
<input
|
|
v-model.trim="keyword"
|
|
class="search-input"
|
|
confirm-type="search"
|
|
:placeholder="searchPlaceholder"
|
|
@confirm="handleSearch"
|
|
/>
|
|
<view class="search-clear" v-if="keyword" @tap="clearSearch">清空</view>
|
|
<view class="search-btn" @tap="handleSearch">搜索</view>
|
|
</view>
|
|
|
|
<view class="swipe-tip" v-if="activeTab === 'posts'">
|
|
<uni-icons type="info" size="15" color="#24b979"></uni-icons>
|
|
<text>左滑卡片可编辑、上下架或删除</text>
|
|
</view>
|
|
<view class="swipe-tip" v-else-if="activeTab === 'messages'">
|
|
<uni-icons type="info" size="15" color="#24b979"></uni-icons>
|
|
<text>有人评论我的发布会显示角标,点开详情后自动已读</text>
|
|
</view>
|
|
|
|
<scroll-view scroll-y class="list" :class="{ 'list--with-tip': activeTab !== 'comments' }" @scrolltolower="loadMore">
|
|
<u-swipe-action v-if="activeTab === 'posts'">
|
|
<u-swipe-action-item
|
|
v-for="(item,index) in list"
|
|
:key="item.id"
|
|
:name="index"
|
|
:options="swipeOptions(item)"
|
|
@click="handleSwipeAction"
|
|
>
|
|
<view class="card" @tap="openPost(item)">
|
|
<image class="cover" :src="coverText(item)" mode="aspectFill"></image>
|
|
<view class="main">
|
|
<view class="name">{{item.title || '校园圈发布'}}</view>
|
|
<view class="desc">{{descText(item)}}</view>
|
|
<view class="meta">
|
|
<text>{{typeText(item)}}</text>
|
|
<text v-if="item.categoryName">{{item.categoryName}}</text>
|
|
<text>{{item.createTime || ''}}</text>
|
|
</view>
|
|
</view>
|
|
<view class="status" :class="{ 'status--off': isOffShelf(item) }">{{statusText(item)}}</view>
|
|
</view>
|
|
</u-swipe-action-item>
|
|
</u-swipe-action>
|
|
|
|
<block v-else-if="activeTab === 'messages'">
|
|
<view class="card message-card" v-for="item in list" :key="item.id" @tap="openMessagePost(item)">
|
|
<image class="cover" :src="coverText(item)" mode="aspectFill"></image>
|
|
<view class="main">
|
|
<view class="name-row">
|
|
<view class="name">{{item.title || '校园圈发布'}}</view>
|
|
<view class="card-badge" v-if="unreadCount(item) > 0">{{formatBadgeCount(unreadCount(item))}}</view>
|
|
</view>
|
|
<view class="desc">{{messageDesc(item)}}</view>
|
|
<view class="meta">
|
|
<text>{{typeText(item)}}</text>
|
|
<text>{{item.commentCount || 0}} 条评论</text>
|
|
<text>{{item.lastActiveTime || item.createTime || ''}}</text>
|
|
</view>
|
|
</view>
|
|
<view class="status status--action">查看</view>
|
|
</view>
|
|
</block>
|
|
|
|
<block v-else>
|
|
<view class="card" v-for="item in list" :key="item.id" @tap="openComment(item)">
|
|
<image class="cover" :src="commentCoverText(item)" mode="aspectFill"></image>
|
|
<view class="main">
|
|
<view class="name">{{item.displayTitle || '校园圈发布'}}</view>
|
|
<view class="desc">{{commentDesc(item)}}</view>
|
|
<view class="meta">
|
|
<text>我的评论</text>
|
|
<text>{{item.createTime || ''}}</text>
|
|
</view>
|
|
</view>
|
|
<view class="status status--danger" @tap.stop="deleteCommentItem(item)">删除</view>
|
|
</view>
|
|
</block>
|
|
|
|
<view class="empty" v-if="!list.length && !loading">{{emptyText}}</view>
|
|
<view class="load-more">{{loading ? '加载中...' : (hasMore ? '上滑加载更多' : (list.length ? '已经到底了' : ''))}}</view>
|
|
<view class="bottom-space"></view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
pageMyCampusPosts,
|
|
pageMyCampusComments,
|
|
updateMyCampusPostStatus,
|
|
getCampusUnreadSummary,
|
|
deleteCampusComment
|
|
} from '@/common/campusApi.js'
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
statusBarTop: 24,
|
|
activeTab: 'posts',
|
|
keyword: '',
|
|
list: [],
|
|
pageNum: 1,
|
|
pageSize: 10,
|
|
totalPages: 1,
|
|
loading: false,
|
|
loadedOnce: false,
|
|
chatUnread: 0,
|
|
commentUnread: 0,
|
|
defaultCover: '/static/images/img/songshu.png',
|
|
tabs: [
|
|
{ key: 'posts', name: '我的发布' },
|
|
{ key: 'messages', name: '我的消息' },
|
|
{ key: 'comments', name: '我的评论' }
|
|
]
|
|
}
|
|
},
|
|
computed: {
|
|
hasMore() {
|
|
return this.pageNum < this.totalPages
|
|
},
|
|
searchPlaceholder() {
|
|
if (this.activeTab === 'comments') return '按评论内容搜索'
|
|
return '按标题或内容搜索'
|
|
},
|
|
emptyText() {
|
|
if (this.activeTab === 'messages') return '暂无评论消息'
|
|
if (this.activeTab === 'comments') return '暂无我的评论'
|
|
return '暂无发布内容'
|
|
}
|
|
},
|
|
onLoad(options = {}) {
|
|
if (options.tab && this.tabs.some(item => item.key === options.tab)) this.activeTab = options.tab
|
|
if (uni.getMenuButtonBoundingClientRect) {
|
|
this.statusBarTop = uni.getMenuButtonBoundingClientRect().top || this.statusBarTop
|
|
}
|
|
this.load()
|
|
},
|
|
onShow() {
|
|
if (this.loadedOnce) this.reload()
|
|
},
|
|
methods: {
|
|
back() {
|
|
uni.navigateBack()
|
|
},
|
|
getArea() {
|
|
const area = uni.getStorageSync('area')
|
|
if (!area) return {}
|
|
if (typeof area === 'string') {
|
|
try { return JSON.parse(area) } catch (e) { return {} }
|
|
}
|
|
return area
|
|
},
|
|
switchTab(key) {
|
|
if (this.activeTab === key) return
|
|
this.activeTab = key
|
|
this.keyword = ''
|
|
this.reload()
|
|
},
|
|
swipeOptions(item) {
|
|
const offShelf = this.isOffShelf(item)
|
|
return [{
|
|
text: '编辑',
|
|
style: {
|
|
backgroundColor: '#24b979',
|
|
color: '#fff',
|
|
fontSize: '26rpx'
|
|
}
|
|
}, {
|
|
text: offShelf ? '上架' : '下架',
|
|
style: {
|
|
backgroundColor: offShelf ? '#2f8cff' : '#ff9f2d',
|
|
color: '#fff',
|
|
fontSize: '26rpx'
|
|
}
|
|
}, {
|
|
text: '删除',
|
|
style: {
|
|
backgroundColor: '#ef4c3f',
|
|
color: '#fff',
|
|
fontSize: '26rpx'
|
|
}
|
|
}]
|
|
},
|
|
handleSwipeAction(e) {
|
|
const item = this.list[Number(e.name)]
|
|
if (!item) return
|
|
if (e.index == 0) {
|
|
this.editItem(item)
|
|
return
|
|
}
|
|
if (e.index == 1) {
|
|
this.toggleShelf(item)
|
|
return
|
|
}
|
|
this.deleteItem(item)
|
|
},
|
|
handleSearch() {
|
|
uni.hideKeyboard()
|
|
this.reload()
|
|
},
|
|
clearSearch() {
|
|
this.keyword = ''
|
|
this.reload()
|
|
},
|
|
reload() {
|
|
this.pageNum = 1
|
|
this.totalPages = 1
|
|
this.list = []
|
|
this.load()
|
|
},
|
|
loadMore() {
|
|
if (this.loading || !this.hasMore) return
|
|
this.pageNum += 1
|
|
this.load()
|
|
},
|
|
load() {
|
|
if (this.loading) return
|
|
this.loading = true
|
|
this.loadUnreadSummary()
|
|
const area = this.getArea()
|
|
const query = {
|
|
pageNum: this.pageNum,
|
|
pageSize: this.pageSize,
|
|
regionId: area.id || '',
|
|
keyword: this.keyword,
|
|
sortType: 'latest'
|
|
}
|
|
const request = this.activeTab === 'comments' ? pageMyCampusComments(query) : pageMyCampusPosts(query)
|
|
request.then(page => {
|
|
const records = page && (page.records || page.content) ? (page.records || page.content) : []
|
|
this.totalPages = Number(page && (page.pages || page.totalPages)) || 1
|
|
const normalized = this.activeTab === 'messages' ? this.sortMessages(records) : records
|
|
this.list = this.pageNum === 1 ? normalized : this.list.concat(normalized)
|
|
}).finally(() => {
|
|
this.loading = false
|
|
this.loadedOnce = true
|
|
})
|
|
},
|
|
loadUnreadSummary() {
|
|
getCampusUnreadSummary().then(summary => {
|
|
this.chatUnread = 0
|
|
this.commentUnread = Number(summary && summary.campusCommentUnread) || 0
|
|
uni.$emit('campusUnreadChange', {
|
|
...summary,
|
|
campusChatUnread: 0,
|
|
myCircleUnread: this.commentUnread,
|
|
campusUnread: this.commentUnread
|
|
})
|
|
}).catch(() => {})
|
|
},
|
|
sortMessages(records) {
|
|
return (Array.isArray(records) ? records : []).slice().sort((a, b) => {
|
|
const unreadDiff = this.unreadCount(b) - this.unreadCount(a)
|
|
if (unreadDiff) return unreadDiff
|
|
return String(b.lastActiveTime || b.createTime || '').localeCompare(String(a.lastActiveTime || a.createTime || ''))
|
|
})
|
|
},
|
|
openPost(item) {
|
|
if (!item || !item.id) return
|
|
uni.navigateTo({ url: '/package1/post/postDetail?id=' + item.id + '&source=campus' })
|
|
},
|
|
openMessagePost(item) {
|
|
if (!item || !item.id) return
|
|
const count = this.unreadCount(item)
|
|
uni.navigateTo({
|
|
url: '/package1/post/postDetail?id=' + item.id + '&source=campus',
|
|
success: () => {
|
|
if (count <= 0) return
|
|
this.$set(item, 'unreadCommentCount', 0)
|
|
this.commentUnread = Math.max(0, this.commentUnread - count)
|
|
uni.$emit('campusUnreadChange', {
|
|
campusChatUnread: 0,
|
|
campusCommentUnread: this.commentUnread,
|
|
myCircleUnread: this.commentUnread,
|
|
campusUnread: this.commentUnread
|
|
})
|
|
}
|
|
})
|
|
},
|
|
openComment(item) {
|
|
const postId = item && item.postId
|
|
if (!postId) return
|
|
uni.navigateTo({ url: '/package1/post/postDetail?id=' + postId + '&source=campus' })
|
|
},
|
|
editItem(item) {
|
|
if (!item || !item.id) return
|
|
const type = item.postType === 'partner' ? (item.partnerType || 'daily') : 'wall'
|
|
uni.navigateTo({ url: `/package1/campus/campusRelease?mode=edit&type=${type}&id=${item.id}` })
|
|
},
|
|
isOffShelf(item) {
|
|
return Number(item.status) === 2 || Number(item.offShelf) === 1
|
|
},
|
|
toggleShelf(item) {
|
|
const nextStatus = this.isOffShelf(item) ? 1 : 2
|
|
this.updateItemStatus(item, nextStatus)
|
|
},
|
|
deleteItem(item) {
|
|
uni.showModal({
|
|
title: '删除确认',
|
|
content: '删除后不可恢复,确定删除这条发布吗?',
|
|
confirmText: '删除',
|
|
confirmColor: '#ef4c3f',
|
|
success: (res) => {
|
|
if (!res.confirm) return
|
|
this.updateItemStatus(item, 3)
|
|
}
|
|
})
|
|
},
|
|
deleteCommentItem(item) {
|
|
if (!item || !item.id) return
|
|
uni.showModal({
|
|
title: '删除评论',
|
|
content: '确定删除这条评论吗?',
|
|
confirmText: '删除',
|
|
confirmColor: '#ef4c3f',
|
|
success: (res) => {
|
|
if (!res.confirm) return
|
|
deleteCampusComment(item.id).then(() => {
|
|
uni.showToast({ title: '已删除', icon: 'none' })
|
|
this.reload()
|
|
})
|
|
}
|
|
})
|
|
},
|
|
updateItemStatus(item, status) {
|
|
return updateMyCampusPostStatus({
|
|
postId: item.id,
|
|
status
|
|
}).then(() => {
|
|
uni.showToast({ title: status === 3 ? '已删除' : (status === 2 ? '已下架' : '已上架'), icon: 'none' })
|
|
uni.$emit('campusPostPublished')
|
|
this.reload()
|
|
})
|
|
},
|
|
statusText(item) {
|
|
if (Number(item.status) === 2) return '已下架'
|
|
if (Number(item.status) === 3) return '已删除'
|
|
if (Number(item.auditStatus) === 2) return '未通过'
|
|
if (Number(item.auditStatus) === 0) return '审核中'
|
|
return '展示中'
|
|
},
|
|
unreadCount(item) {
|
|
return Number(item && (item.unreadCommentCount || item.unreadCount)) || 0
|
|
},
|
|
messageDesc(item) {
|
|
const count = this.unreadCount(item)
|
|
if (count > 0) return count + ' 条新评论,点击查看并标记已读'
|
|
return '暂无新评论,点击查看评论区'
|
|
},
|
|
typeText(item) {
|
|
if (item.postType === 'wall') return '校园圈'
|
|
const map = { game: '游戏搭子', daily: '日常搭子', sport: '运动搭子' }
|
|
return map[item.partnerType] || '校园搭子'
|
|
},
|
|
descText(item) {
|
|
return this.plainText(item.contentText || item.content || '')
|
|
},
|
|
commentDesc(item) {
|
|
const prefix = item.replyToUserName ? '回复 ' + item.replyToUserName + ':' : ''
|
|
return prefix + (item.content || '')
|
|
},
|
|
coverText(item) {
|
|
return this.firstImage(item.imageList)
|
|
|| this.firstImage(item.images)
|
|
|| item.coverImage
|
|
|| this.defaultCover
|
|
},
|
|
commentCoverText(item) {
|
|
return item.displayCover || this.defaultCover
|
|
},
|
|
firstImage(value) {
|
|
if (!value) return ''
|
|
if (Array.isArray(value)) return value[0] || ''
|
|
try {
|
|
const parsed = JSON.parse(value)
|
|
return Array.isArray(parsed) ? (parsed[0] || '') : ''
|
|
} catch (e) {
|
|
return ''
|
|
}
|
|
},
|
|
plainText(value) {
|
|
return String(value || '')
|
|
.replace(/<img[^>]*>/gi, '[图片]')
|
|
.replace(/<[^>]+>/g, '')
|
|
.replace(/ /g, ' ')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/&/g, '&')
|
|
.trim()
|
|
},
|
|
formatBadgeCount(count) {
|
|
const value = Number(count) || 0
|
|
return value > 99 ? '99+' : value
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
page { background: #f7f9f7; }
|
|
.my-campus-page { min-height: 100vh; background: #f7f9f7; color: #17362f; }
|
|
.header { padding: 0 26rpx 18rpx; background: linear-gradient(180deg, #e5fbf3, #f7f9f7); box-sizing: border-box; }
|
|
.back { width: 68rpx; height: 68rpx; display: flex; align-items: center; }
|
|
.title { margin-top: 8rpx; font-size: 40rpx; font-weight: 900; color: #17362f; }
|
|
.tabs { margin-top: 20rpx; display: flex; align-items: center; gap: 12rpx; }
|
|
.tab { position: relative; flex-shrink: 0; min-width: 132rpx; height: 58rpx; padding: 0 20rpx; display: flex; align-items: center; justify-content: center; border-radius: 30rpx; background: #fff; color: #758782; font-size: 23rpx; box-shadow: 0 8rpx 20rpx rgba(20, 60, 48, 0.045); box-sizing: border-box; }
|
|
.tab.active { background: linear-gradient(135deg, #38c897, #19af7e); color: #fff; font-weight: 900; }
|
|
.tab-badge, .card-badge { 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-sizing: border-box; }
|
|
.tab-badge { margin-left: 8rpx; }
|
|
.card-badge { margin-left: 8rpx; flex-shrink: 0; }
|
|
.search-bar { height: 70rpx; margin: 0 24rpx 16rpx; padding: 0 12rpx 0 22rpx; display: flex; align-items: center; border-radius: 38rpx; background: #fff; box-shadow: 0 8rpx 22rpx rgba(20, 60, 48, 0.045); box-sizing: border-box; }
|
|
.search-input { flex: 1; height: 68rpx; margin-left: 10rpx; color: #293632; font-size: 24rpx; line-height: 68rpx; }
|
|
.search-clear { padding: 0 12rpx; color: #9aa8a4; font-size: 22rpx; }
|
|
.search-btn { width: 86rpx; height: 54rpx; border-radius: 28rpx; background: linear-gradient(135deg, #38c897, #19af7e); color: #fff; font-size: 23rpx; font-weight: 800; line-height: 54rpx; text-align: center; }
|
|
.swipe-tip { height: 52rpx; margin: 0 24rpx 16rpx; padding: 0 20rpx; display: flex; align-items: center; border-radius: 26rpx; background: #eafaf3; color: #4f7368; font-size: 22rpx; box-sizing: border-box; }
|
|
.swipe-tip text { margin-left: 8rpx; }
|
|
.list { height: calc(100vh - 286rpx); }
|
|
.list--with-tip { height: calc(100vh - 354rpx); }
|
|
.card { position: relative; margin: 18rpx 24rpx 0; padding: 18rpx; display: flex; align-items: center; border-radius: 24rpx; background: #fff; box-shadow: 0 8rpx 22rpx rgba(20, 60, 48, 0.05); }
|
|
.message-card { border: 2rpx solid rgba(36, 185, 121, .10); }
|
|
.cover { width: 126rpx; height: 126rpx; border-radius: 18rpx; background: #edf3ef; flex-shrink: 0; }
|
|
.main { flex: 1; min-width: 0; margin-left: 18rpx; }
|
|
.name-row { display: flex; align-items: center; min-width: 0; }
|
|
.name { overflow: hidden; font-size: 28rpx; font-weight: 900; text-overflow: ellipsis; white-space: nowrap; }
|
|
.name-row .name { flex: 1; }
|
|
.desc { margin-top: 8rpx; overflow: hidden; color: #71817c; font-size: 23rpx; text-overflow: ellipsis; white-space: nowrap; }
|
|
.meta { margin-top: 8rpx; display: flex; gap: 14rpx; color: #a0aaa6; font-size: 20rpx; }
|
|
.meta text:first-child { color: #22a879; font-weight: 900; }
|
|
.status { margin-left: 12rpx; color: #22a879; font-size: 22rpx; font-weight: 800; white-space: nowrap; }
|
|
.status--off { color: #9aa8a4; }
|
|
.status--action { padding: 8rpx 14rpx; border-radius: 18rpx; background: #eafaf3; color: #18a878; font-size: 20rpx; }
|
|
.status--danger { padding: 8rpx 14rpx; border-radius: 18rpx; background: #fff2f2; color: #ef4c3f; font-size: 20rpx; }
|
|
.empty, .load-more { padding: 70rpx 0; color: #9aa8a4; text-align: center; font-size: 24rpx; }
|
|
.bottom-space { height: 120rpx; }
|
|
</style>
|
|
|