|
@@ -0,0 +1,267 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <view class="circles-container">
|
|
|
|
|
+ <!-- 自定义导航栏 -->
|
|
|
|
|
+ <view class="custom-nav">
|
|
|
|
|
+ <view class="nav-header">
|
|
|
|
|
+ <view class="nav-back" @click="goBack">
|
|
|
|
|
+ <text class="nav-back-icon">‹</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="nav-title">发现圈子</text>
|
|
|
|
|
+ <view class="nav-placeholder"></view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 加载状态 -->
|
|
|
|
|
+ <view class="loading-state" v-if="loading">
|
|
|
|
|
+ <text class="loading-text">加载中...</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 圈子列表 -->
|
|
|
|
|
+ <scroll-view
|
|
|
|
|
+ v-else
|
|
|
|
|
+ class="circle-list"
|
|
|
|
|
+ scroll-y
|
|
|
|
|
+ refresher-enabled
|
|
|
|
|
+ :refresher-triggered="refreshing"
|
|
|
|
|
+ @refresherrefresh="onRefresh"
|
|
|
|
|
+ @refresherrestore="refreshing = false">
|
|
|
|
|
+ <!-- 我的圈子 -->
|
|
|
|
|
+ <view class="section" v-if="myCircles.length > 0">
|
|
|
|
|
+ <view class="section-header">
|
|
|
|
|
+ <text class="section-title">🤝 我的圈子</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="circle-grid">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="item in myCircles"
|
|
|
|
|
+ :key="item.id || item.circleId"
|
|
|
|
|
+ class="circle-item"
|
|
|
|
|
+ @click="onCircleClick(item)">
|
|
|
|
|
+ <CircleCard :circle="item" compact />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 发现推荐 -->
|
|
|
|
|
+ <view class="section">
|
|
|
|
|
+ <view class="section-header">
|
|
|
|
|
+ <text class="section-title">🔍 推荐圈子</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="circle-grid" v-if="discoverList.length > 0">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="item in discoverList"
|
|
|
|
|
+ :key="item.id || item.circleId"
|
|
|
|
|
+ class="circle-item"
|
|
|
|
|
+ @click="onCircleClick(item)">
|
|
|
|
|
+ <CircleCard :circle="item" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="empty-state" v-else-if="!loading">
|
|
|
|
|
+ <text class="empty-text">暂无推荐圈子</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </scroll-view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 圈子详情弹窗 -->
|
|
|
|
|
+ <CircleDetail
|
|
|
|
|
+ :visible="detailVisible"
|
|
|
|
|
+ :circle="selectedCircle || {}"
|
|
|
|
|
+ :isMember="selectedIsMember"
|
|
|
|
|
+ @close="detailVisible = false"
|
|
|
|
|
+ @join="onJoin"
|
|
|
|
|
+ @leave="onLeave" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+import CircleCard from '../../components/CircleCard.vue'
|
|
|
|
|
+import CircleDetail from '../../components/CircleDetail.vue'
|
|
|
|
|
+import { discoverCircles, getMyCircles, joinGeneralCircle, leaveGeneralCircle } from '../../utils/api.js'
|
|
|
|
|
+
|
|
|
|
|
+export default {
|
|
|
|
|
+ components: { CircleCard, CircleDetail },
|
|
|
|
|
+ data: function() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ loading: true,
|
|
|
|
|
+ refreshing: false,
|
|
|
|
|
+ myCircles: [],
|
|
|
|
|
+ discoverList: [],
|
|
|
|
|
+ detailVisible: false,
|
|
|
|
|
+ selectedCircle: null,
|
|
|
|
|
+ selectedIsMember: false
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ onLoad: function() {
|
|
|
|
|
+ this.loadData()
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ loadData: function() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ self.loading = true
|
|
|
|
|
+ var childId = uni.getStorageSync('currentChildId') || ''
|
|
|
|
|
+ Promise.all([
|
|
|
|
|
+ new Promise(function(resolve) {
|
|
|
|
|
+ getMyCircles(childId, 'child').then(function(res) {
|
|
|
|
|
+ self.myCircles = res.code === 200 && Array.isArray(res.data) ? res.data : []
|
|
|
|
|
+ resolve()
|
|
|
|
|
+ }).catch(function() {
|
|
|
|
|
+ self.myCircles = []
|
|
|
|
|
+ resolve()
|
|
|
|
|
+ })
|
|
|
|
|
+ }),
|
|
|
|
|
+ new Promise(function(resolve) {
|
|
|
|
|
+ discoverCircles(childId).then(function(res) {
|
|
|
|
|
+ self.discoverList = res.code === 200 && Array.isArray(res.data) ? res.data : []
|
|
|
|
|
+ resolve()
|
|
|
|
|
+ }).catch(function() {
|
|
|
|
|
+ self.discoverList = []
|
|
|
|
|
+ resolve()
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ ]).then(function() {
|
|
|
|
|
+ self.loading = false
|
|
|
|
|
+ self.refreshing = false
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ onRefresh: function() {
|
|
|
|
|
+ this.refreshing = true
|
|
|
|
|
+ this.loadData()
|
|
|
|
|
+ },
|
|
|
|
|
+ onCircleClick: function(circle) {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ var isMember = self.myCircles.some(function(c) {
|
|
|
|
|
+ return (c.id || c.circleId) === (circle.id || circle.circleId)
|
|
|
|
|
+ })
|
|
|
|
|
+ self.selectedCircle = circle
|
|
|
|
|
+ self.selectedIsMember = isMember
|
|
|
|
|
+ self.detailVisible = true
|
|
|
|
|
+ },
|
|
|
|
|
+ onJoin: function(circleId) {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ var childId = uni.getStorageSync('currentChildId') || ''
|
|
|
|
|
+ joinGeneralCircle(circleId, childId, 'child').then(function(res) {
|
|
|
|
|
+ if (res.code === 200) {
|
|
|
|
|
+ uni.showToast({ title: '加入成功', icon: 'success' })
|
|
|
|
|
+ self.detailVisible = false
|
|
|
|
|
+ self.loadData()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: res.message || '加入失败', icon: 'none' })
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function(e) {
|
|
|
|
|
+ console.log('加入圈子失败', e)
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ onLeave: function(circleId) {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ var childId = uni.getStorageSync('currentChildId') || ''
|
|
|
|
|
+ leaveGeneralCircle(circleId, childId, 'child').then(function(res) {
|
|
|
|
|
+ if (res.code === 200) {
|
|
|
|
|
+ uni.showToast({ title: '已退出', icon: 'success' })
|
|
|
|
|
+ self.detailVisible = false
|
|
|
|
|
+ self.loadData()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: res.message || '退出失败', icon: 'none' })
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function(e) {
|
|
|
|
|
+ console.log('退出圈子失败', e)
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ goBack: function() {
|
|
|
|
|
+ uni.navigateBack()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.circles-container {
|
|
|
|
|
+ min-height: 100vh;
|
|
|
|
|
+ background: #FFF7ED;
|
|
|
|
|
+}
|
|
|
|
|
+/* 自定义导航栏 */
|
|
|
|
|
+.custom-nav {
|
|
|
|
|
+ padding-top: var(--status-bar-height);
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+.nav-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ height: 88rpx;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+}
|
|
|
|
|
+.nav-back {
|
|
|
|
|
+ width: 88rpx;
|
|
|
|
|
+ height: 88rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+.nav-back:active {
|
|
|
|
|
+ opacity: 0.6;
|
|
|
|
|
+}
|
|
|
|
|
+.nav-back-icon {
|
|
|
|
|
+ font-size: 48rpx;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+}
|
|
|
|
|
+.nav-title {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 34rpx;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+}
|
|
|
|
|
+.nav-placeholder {
|
|
|
|
|
+ width: 88rpx;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+/* 加载 */
|
|
|
|
|
+.loading-state {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ padding: 120rpx 0;
|
|
|
|
|
+}
|
|
|
|
|
+.loading-text {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+}
|
|
|
|
|
+/* 列表 */
|
|
|
|
|
+.circle-list {
|
|
|
|
|
+ height: calc(100vh - var(--status-bar-height) - 88rpx);
|
|
|
|
|
+}
|
|
|
|
|
+.section {
|
|
|
|
|
+ margin: 20rpx 20rpx 0;
|
|
|
|
|
+}
|
|
|
|
|
+.section-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 16rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.section-title {
|
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+}
|
|
|
|
|
+.circle-grid {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 0;
|
|
|
|
|
+}
|
|
|
|
|
+.circle-item {
|
|
|
|
|
+ margin-bottom: 12rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.circle-item:active {
|
|
|
|
|
+ opacity: 0.8;
|
|
|
|
|
+}
|
|
|
|
|
+/* 空状态 */
|
|
|
|
|
+.empty-state {
|
|
|
|
|
+ padding: 80rpx 0;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+.empty-text {
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ color: #ccc;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|