|
|
@@ -32,9 +32,47 @@
|
|
|
<view
|
|
|
v-for="item in myCircles"
|
|
|
:key="getCircleKey(item)"
|
|
|
- class="circle-item"
|
|
|
- @tap="onCircleClick(item)">
|
|
|
- <CircleCard :circle="item" compact />
|
|
|
+ class="circle-item">
|
|
|
+ <view class="circle-card-wrap" @tap="onCircleClick(item)">
|
|
|
+ <CircleCard :circle="item" compact />
|
|
|
+ </view>
|
|
|
+ <view class="circle-edit-btn" v-if="item.role === 'admin'" @tap="openEdit(item)">
|
|
|
+ <text class="circle-edit-icon">✏️</text>
|
|
|
+ <text class="circle-edit-text">编辑</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 待审核成员 -->
|
|
|
+ <view class="section" v-if="pendingMembers.length > 0">
|
|
|
+ <view class="section-header">
|
|
|
+ <text class="section-title">⏳ 待审核成员</text>
|
|
|
+ </view>
|
|
|
+ <view class="pending-list">
|
|
|
+ <view
|
|
|
+ v-for="pm in pendingMembers"
|
|
|
+ :key="getPendingKey(pm)"
|
|
|
+ class="pending-item">
|
|
|
+ <image
|
|
|
+ class="pending-avatar"
|
|
|
+ :src="pm.applicantAvatar || defaultAvatar"
|
|
|
+ mode="aspectFill" />
|
|
|
+ <view class="pending-info">
|
|
|
+ <view class="pending-name">
|
|
|
+ <text class="pending-name-text">{{ pm.applicantName || '圈友' }}</text>
|
|
|
+ <text class="pending-circle">申请加入「{{ pm.circleName || '圈子' }}」</text>
|
|
|
+ </view>
|
|
|
+ <text class="pending-time">{{ formatAppliedTime(pm.appliedAt) }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="pending-actions">
|
|
|
+ <view class="approve-btn" @tap="onApprove(pm.circleId, pm.memberId, true)">
|
|
|
+ <text class="approve-btn-text">同意</text>
|
|
|
+ </view>
|
|
|
+ <view class="reject-btn" @tap="onApprove(pm.circleId, pm.memberId, false)">
|
|
|
+ <text class="reject-btn-text">拒绝</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -67,13 +105,46 @@
|
|
|
@close="detailVisible = false"
|
|
|
@join="onJoin"
|
|
|
@leave="onLeave" />
|
|
|
+
|
|
|
+ <!-- 编辑圈子弹窗 -->
|
|
|
+ <view class="modal-mask" v-if="editVisible" @tap="closeEdit">
|
|
|
+ <view class="edit-content" @tap.stop>
|
|
|
+ <view class="modal-header">
|
|
|
+ <text class="modal-title">编辑圈子</text>
|
|
|
+ <text class="modal-close" @tap="closeEdit">✕</text>
|
|
|
+ </view>
|
|
|
+ <view class="form-item">
|
|
|
+ <text class="form-label">圈子名称</text>
|
|
|
+ <input class="form-input" v-model="editForm.name" placeholder="请输入圈子名称" maxlength="30" />
|
|
|
+ </view>
|
|
|
+ <view class="form-item">
|
|
|
+ <text class="form-label">圈子简介</text>
|
|
|
+ <textarea class="form-textarea" v-model="editForm.description" placeholder="介绍一下这个圈子" maxlength="200" />
|
|
|
+ </view>
|
|
|
+ <view class="form-item">
|
|
|
+ <text class="form-label">加入方式</text>
|
|
|
+ <view class="cond-options">
|
|
|
+ <view class="cond-opt" :class="{ 'cond-active': editForm.joinCondition === 0 }" @tap="editForm.joinCondition = 0">
|
|
|
+ <text class="cond-opt-text">自由加入</text>
|
|
|
+ </view>
|
|
|
+ <view class="cond-opt" :class="{ 'cond-active': editForm.joinCondition === 1 }" @tap="editForm.joinCondition = 1">
|
|
|
+ <text class="cond-opt-text">审核制</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="modal-footer">
|
|
|
+ <button class="btn-save" :disabled="editing || !editForm.name.trim()" @tap="saveEdit">{{ editing ? '保存中...' : '保存' }}</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import CircleCard from '../../components/CircleCard.vue'
|
|
|
import CircleDetail from '../../components/CircleDetail.vue'
|
|
|
-import { discoverCircles, getMyCircles, joinGeneralCircle, leaveGeneralCircle } from '../../utils/api.js'
|
|
|
+import { discoverCircles, getMyCircles, joinGeneralCircle, leaveGeneralCircle, getPendingCircleMembers, approveCircleMember, updateCircle } from '../../utils/api.js'
|
|
|
+import { parseDate } from '@/utils/format.js'
|
|
|
|
|
|
export default {
|
|
|
components: { CircleCard, CircleDetail },
|
|
|
@@ -88,9 +159,19 @@ export default {
|
|
|
refreshing: false,
|
|
|
myCircles: [],
|
|
|
discoverList: [],
|
|
|
+ pendingMembers: [],
|
|
|
+ defaultAvatar: '/static/default-avatar.png',
|
|
|
detailVisible: false,
|
|
|
selectedCircle: null,
|
|
|
- selectedIsMember: false
|
|
|
+ selectedIsMember: false,
|
|
|
+ editVisible: false,
|
|
|
+ editing: false,
|
|
|
+ editForm: {
|
|
|
+ circleId: null,
|
|
|
+ name: '',
|
|
|
+ description: '',
|
|
|
+ joinCondition: 0
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
onLoad: function() {
|
|
|
@@ -100,6 +181,31 @@ export default {
|
|
|
getCircleKey: function(item) {
|
|
|
return item.id || item.circleId
|
|
|
},
|
|
|
+ getPendingKey: function(item) {
|
|
|
+ return (item.circleId || '') + '_' + (item.memberId || '')
|
|
|
+ },
|
|
|
+ formatAppliedTime: function(timeStr) {
|
|
|
+ var d = parseDate(timeStr)
|
|
|
+ if (!d) return ''
|
|
|
+ var pad = function(n) { return n < 10 ? '0' + n : '' + n }
|
|
|
+ return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes())
|
|
|
+ },
|
|
|
+ onApprove: function(circleId, memberId, approved) {
|
|
|
+ var self = this
|
|
|
+ var adminMemberId = uni.getStorageSync('currentChildId') || ''
|
|
|
+ if (!circleId || !memberId || !adminMemberId) return
|
|
|
+ approveCircleMember(circleId, memberId, approved, adminMemberId).then(function(res) {
|
|
|
+ if (res.code === 200) {
|
|
|
+ uni.showToast({ title: approved ? '已同意加入' : '已拒绝申请', icon: 'success' })
|
|
|
+ self.loadData()
|
|
|
+ } else {
|
|
|
+ uni.showToast({ title: res.message || '操作失败', icon: 'none' })
|
|
|
+ }
|
|
|
+ }).catch(function(e) {
|
|
|
+ console.log('审批成员失败', e)
|
|
|
+ uni.showToast({ title: '操作失败', icon: 'none' })
|
|
|
+ })
|
|
|
+ },
|
|
|
loadData: function() {
|
|
|
var self = this
|
|
|
self.loading = true
|
|
|
@@ -131,6 +237,16 @@ export default {
|
|
|
self.discoverList = []
|
|
|
resolve()
|
|
|
})
|
|
|
+ }),
|
|
|
+ new Promise(function(resolve) {
|
|
|
+ getPendingCircleMembers(memberId, 'child').then(function(res) {
|
|
|
+ self.pendingMembers = res.code === 200 && Array.isArray(res.data) ? res.data : []
|
|
|
+ resolve()
|
|
|
+ }).catch(function(e) {
|
|
|
+ console.log('获取待审核成员失败', e)
|
|
|
+ self.pendingMembers = []
|
|
|
+ resolve()
|
|
|
+ })
|
|
|
})
|
|
|
]).then(function() {
|
|
|
self.loading = false
|
|
|
@@ -193,6 +309,42 @@ export default {
|
|
|
},
|
|
|
goCreateCircle: function() {
|
|
|
uni.navigateTo({ url: '/pages/discover/create-circle' })
|
|
|
+ },
|
|
|
+ openEdit: function(item) {
|
|
|
+ this.editForm = {
|
|
|
+ circleId: item.id || item.circleId,
|
|
|
+ name: item.name || '',
|
|
|
+ description: item.description || '',
|
|
|
+ joinCondition: item.joinCondition === 1 ? 1 : 0
|
|
|
+ }
|
|
|
+ this.editVisible = true
|
|
|
+ },
|
|
|
+ closeEdit: function() {
|
|
|
+ this.editVisible = false
|
|
|
+ },
|
|
|
+ saveEdit: function() {
|
|
|
+ var self = this
|
|
|
+ if (!self.editForm.circleId) return
|
|
|
+ self.editing = true
|
|
|
+ updateCircle({
|
|
|
+ circleId: self.editForm.circleId,
|
|
|
+ name: self.editForm.name.trim(),
|
|
|
+ description: self.editForm.description.trim(),
|
|
|
+ joinCondition: self.editForm.joinCondition
|
|
|
+ }).then(function(res) {
|
|
|
+ self.editing = false
|
|
|
+ if (res.code === 200) {
|
|
|
+ uni.showToast({ title: '已保存', icon: 'success' })
|
|
|
+ self.editVisible = false
|
|
|
+ self.loadData()
|
|
|
+ } else {
|
|
|
+ uni.showToast({ title: res.message || '保存失败', icon: 'none' })
|
|
|
+ }
|
|
|
+ }).catch(function(e) {
|
|
|
+ self.editing = false
|
|
|
+ console.log('更新圈子失败', e)
|
|
|
+ uni.showToast({ title: '保存失败', icon: 'none' })
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -260,11 +412,227 @@ export default {
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
.circle-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
margin-bottom: 12rpx;
|
|
|
}
|
|
|
.circle-item:active {
|
|
|
opacity: 0.8;
|
|
|
}
|
|
|
+.circle-card-wrap {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+.circle-edit-btn {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 88rpx;
|
|
|
+ height: 88rpx;
|
|
|
+ margin-left: 12rpx;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+.circle-edit-btn:active {
|
|
|
+ opacity: 0.7;
|
|
|
+}
|
|
|
+.circle-edit-icon {
|
|
|
+ font-size: 28rpx;
|
|
|
+}
|
|
|
+.circle-edit-text {
|
|
|
+ font-size: 20rpx;
|
|
|
+ color: #FF9800;
|
|
|
+ margin-top: 4rpx;
|
|
|
+}
|
|
|
+/* 编辑圈子弹窗 */
|
|
|
+.modal-mask {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background: rgba(0,0,0,0.5);
|
|
|
+ z-index: 999;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+.edit-content {
|
|
|
+ width: 620rpx;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ padding: 32rpx;
|
|
|
+}
|
|
|
+.modal-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 28rpx;
|
|
|
+}
|
|
|
+.modal-title {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+.modal-close {
|
|
|
+ font-size: 36rpx;
|
|
|
+ color: #999;
|
|
|
+ padding: 0 8rpx;
|
|
|
+}
|
|
|
+.form-item {
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+}
|
|
|
+.form-label {
|
|
|
+ display: block;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #666;
|
|
|
+ margin-bottom: 12rpx;
|
|
|
+}
|
|
|
+.form-input {
|
|
|
+ width: 100%;
|
|
|
+ height: 76rpx;
|
|
|
+ background: #f7f7f7;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ padding: 0 20rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+.form-textarea {
|
|
|
+ width: 100%;
|
|
|
+ height: 150rpx;
|
|
|
+ background: #f7f7f7;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ padding: 16rpx 20rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+.cond-options {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+.cond-opt {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 72rpx;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ background: #f7f7f7;
|
|
|
+ margin-right: 16rpx;
|
|
|
+}
|
|
|
+.cond-opt:last-child {
|
|
|
+ margin-right: 0;
|
|
|
+}
|
|
|
+.cond-opt-text {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+.cond-active {
|
|
|
+ background: #FFF3E0;
|
|
|
+ border: 1rpx solid #FF9800;
|
|
|
+}
|
|
|
+.cond-active .cond-opt-text {
|
|
|
+ color: #FF9800;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+.modal-footer {
|
|
|
+ margin-top: 8rpx;
|
|
|
+}
|
|
|
+.btn-save {
|
|
|
+ width: 100%;
|
|
|
+ height: 80rpx;
|
|
|
+ line-height: 80rpx;
|
|
|
+ background: #FF9800;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 30rpx;
|
|
|
+ border-radius: 40rpx;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+.btn-save[disabled] {
|
|
|
+ opacity: 0.5;
|
|
|
+}
|
|
|
+/* 待审核成员卡片 */
|
|
|
+.pending-list {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ padding: 8rpx 20rpx;
|
|
|
+}
|
|
|
+.pending-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 20rpx 0;
|
|
|
+ border-bottom: 1rpx solid #f5f5f5;
|
|
|
+}
|
|
|
+.pending-item:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+.pending-avatar {
|
|
|
+ width: 72rpx;
|
|
|
+ height: 72rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ flex-shrink: 0;
|
|
|
+ background: #f0f0f0;
|
|
|
+}
|
|
|
+.pending-info {
|
|
|
+ flex: 1;
|
|
|
+ margin-left: 16rpx;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+.pending-name {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+.pending-name-text {
|
|
|
+ font-size: 28rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+ max-width: 200rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+.pending-circle {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #999;
|
|
|
+ margin-left: 12rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+.pending-time {
|
|
|
+ display: block;
|
|
|
+ font-size: 22rpx;
|
|
|
+ color: #ccc;
|
|
|
+ margin-top: 6rpx;
|
|
|
+}
|
|
|
+.pending-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-left: 12rpx;
|
|
|
+}
|
|
|
+.approve-btn {
|
|
|
+ background: #FF9800;
|
|
|
+ border-radius: 30rpx;
|
|
|
+ padding: 10rpx 26rpx;
|
|
|
+ margin-right: 12rpx;
|
|
|
+}
|
|
|
+.approve-btn-text {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+.reject-btn {
|
|
|
+ background: #f5f5f5;
|
|
|
+ border-radius: 30rpx;
|
|
|
+ padding: 10rpx 26rpx;
|
|
|
+}
|
|
|
+.reject-btn-text {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
/* 空状态 */
|
|
|
.empty-state {
|
|
|
padding: 80rpx 0;
|