create-circle.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <template>
  2. <view class="create-circle-page">
  3. <view class="nav-bar">
  4. <view class="nav-back" @tap="goBack"><text class="nav-back-icon">‹</text></view>
  5. <text class="nav-title">创建圈子</text>
  6. <view class="nav-placeholder"></view>
  7. </view>
  8. <scroll-view class="content" scroll-y>
  9. <view class="form-card">
  10. <view class="form-item">
  11. <text class="form-label">圈子名称 <text class="required">*</text></text>
  12. <input class="form-input" placeholder="给圈子起个名字" v-model="form.name" maxlength="30" />
  13. </view>
  14. <view class="form-item">
  15. <text class="form-label">圈子类型</text>
  16. <view class="type-grid">
  17. <view class="type-opt" :class="{ 'type-active': form.type === t.value }" v-for="t in circleTypes" :key="t.value" @tap="form.type = t.value">
  18. <text class="to-icon">{{ t.icon }}</text>
  19. <text class="to-label">{{ t.label }}</text>
  20. </view>
  21. </view>
  22. </view>
  23. <view class="form-item">
  24. <text class="form-label">圈子描述</text>
  25. <textarea class="form-textarea" placeholder="简单介绍一下这个圈子..." v-model="form.description" maxlength="200" />
  26. </view>
  27. <view class="form-item">
  28. <text class="form-label">加入方式</text>
  29. <view class="join-options">
  30. <view class="join-opt" :class="{ 'join-active': form.joinCondition === 0 }" @tap="form.joinCondition = 0">
  31. <text class="jo-icon">🌐</text>
  32. <text class="jo-label">公开加入</text>
  33. <text class="jo-desc">任何人都可以加入</text>
  34. </view>
  35. <view class="join-opt" :class="{ 'join-active': form.joinCondition === 1 }" @tap="form.joinCondition = 1">
  36. <text class="jo-icon">🔒</text>
  37. <text class="jo-label">需审核</text>
  38. <text class="jo-desc">管理员审核后加入</text>
  39. </view>
  40. <view class="join-opt" :class="{ 'join-active': form.joinCondition === 2 }" @tap="form.joinCondition = 2">
  41. <text class="jo-icon">🔗</text>
  42. <text class="jo-label">邀请制</text>
  43. <text class="jo-desc">仅邀请可加入</text>
  44. </view>
  45. </view>
  46. </view>
  47. <view class="form-item">
  48. <view class="switch-row">
  49. <text class="form-label">公开可见</text>
  50. <switch :checked="form.isPublic === 1" color="#FF8C42" @change="form.isPublic = $event.detail.value ? 1 : 0" />
  51. </view>
  52. <text class="switch-desc">其他用户可以在发现页看到此圈子</text>
  53. </view>
  54. </view>
  55. <view class="bottom-space"></view>
  56. </scroll-view>
  57. <view class="fixed-bottom">
  58. <button class="btn-create" :disabled="!form.name.trim()" @tap="handleCreate">创建圈子</button>
  59. </view>
  60. </view>
  61. </template>
  62. <script>
  63. import { createGeneralCircle } from '@/utils/api.js'
  64. export default {
  65. data: function() {
  66. return {
  67. form: {
  68. name: '',
  69. type: 'topic',
  70. description: '',
  71. joinCondition: 0,
  72. isPublic: 1
  73. },
  74. circleTypes: [
  75. { value: 'topic', label: '话题', icon: '💬' },
  76. { value: 'activity', label: '活动', icon: '🎯' },
  77. { value: 'hobby', label: '爱好', icon: '🎨' },
  78. { value: 'health', label: '健康', icon: '💪' },
  79. { value: 'ability', label: '能力', icon: '🧠' },
  80. { value: 'product', label: '商品', icon: '🛒' }
  81. ]
  82. }
  83. },
  84. methods: {
  85. goBack: function() {
  86. uni.navigateBack()
  87. },
  88. handleCreate: function() {
  89. var self = this
  90. if (!self.form.name.trim()) return
  91. var memberId = uni.getStorageSync('currentChildId') || ''
  92. if (!memberId) {
  93. uni.showToast({ title: '请先登录', icon: 'none' })
  94. return
  95. }
  96. uni.showLoading({ title: '创建中...' })
  97. createGeneralCircle({
  98. name: self.form.name.trim(),
  99. type: self.form.type,
  100. description: self.form.description,
  101. joinCondition: self.form.joinCondition,
  102. isPublic: self.form.isPublic,
  103. creatorId: memberId,
  104. creatorType: 'child'
  105. }).then(function(res) {
  106. uni.hideLoading()
  107. if (res.code === 200 && res.data) {
  108. uni.showToast({ title: '创建成功', icon: 'success' })
  109. setTimeout(function() {
  110. uni.redirectTo({ url: '/pages/discover/circle-feed?circleId=' + res.data.id + '&name=' + encodeURIComponent(self.form.name) + '&memberCount=1' })
  111. }, 1000)
  112. } else {
  113. uni.showToast({ title: res.message || '创建失败', icon: 'none' })
  114. }
  115. }).catch(function() {
  116. uni.hideLoading()
  117. uni.showToast({ title: '创建失败,请重试', icon: 'none' })
  118. })
  119. }
  120. }
  121. }
  122. </script>
  123. <style scoped>
  124. .create-circle-page { min-height: 100vh; background: #F5F7FA; padding-bottom: 160rpx; }
  125. .nav-bar {
  126. display: flex; align-items: center; justify-content: space-between;
  127. height: 88rpx; padding-top: env(safe-area-inset-top);
  128. background: #fff; position: sticky; top: 0; z-index: 100;
  129. border-bottom: 1rpx solid #f0f0f0;
  130. }
  131. .nav-back { width: 80rpx; padding-left: 24rpx; }
  132. .nav-back-icon { font-size: 48rpx; color: #333; }
  133. .nav-title { font-size: 32rpx; font-weight: 600; color: #333; }
  134. .nav-placeholder { width: 80rpx; }
  135. .content { padding: 24rpx 32rpx; }
  136. .form-card { background: #fff; border-radius: 20rpx; padding: 24rpx; }
  137. .form-item { padding: 24rpx 0; border-bottom: 1rpx solid #f5f5f5; }
  138. .form-item:last-child { border-bottom: none; }
  139. .form-label { font-size: 28rpx; font-weight: 600; color: #333; display: block; margin-bottom: 16rpx; }
  140. .required { color: #F44336; }
  141. .form-input {
  142. width: 100%; height: 72rpx; background: #F5F5F5; border-radius: 12rpx;
  143. padding: 0 20rpx; font-size: 28rpx; box-sizing: border-box;
  144. }
  145. .form-textarea {
  146. width: 100%; height: 160rpx; background: #F5F5F5; border-radius: 12rpx;
  147. padding: 16rpx 20rpx; font-size: 26rpx; line-height: 1.6; box-sizing: border-box;
  148. }
  149. .type-grid { display: flex; flex-wrap: wrap; }
  150. .type-opt {
  151. display: flex; flex-direction: column; align-items: center;
  152. padding: 20rpx 28rpx; margin-right: 16rpx; margin-bottom: 16rpx;
  153. background: #F5F5F5; border-radius: 16rpx; border: 2rpx solid transparent;
  154. }
  155. .type-active { border-color: #FF8C42; background: #FFF0E0; }
  156. .to-icon { font-size: 40rpx; margin-bottom: 8rpx; }
  157. .to-label { font-size: 24rpx; color: #666; }
  158. .join-options { display: flex; flex-direction: column; }
  159. .join-opt {
  160. display: flex; align-items: center; padding: 20rpx;
  161. background: #FAFAFA; border-radius: 12rpx; border: 2rpx solid transparent;
  162. }
  163. .join-active { border-color: #FF8C42; background: #FFF8F0; }
  164. .jo-icon { font-size: 36rpx; margin-right: 16rpx; }
  165. .jo-label { font-size: 26rpx; font-weight: 600; color: #333; margin-right: 16rpx; }
  166. .jo-desc { font-size: 22rpx; color: #999; }
  167. .switch-row { display: flex; align-items: center; justify-content: space-between; }
  168. .switch-desc { font-size: 22rpx; color: #999; margin-top: 8rpx; display: block; }
  169. .fixed-bottom {
  170. position: fixed; bottom: 0; left: 0; right: 0;
  171. padding: 20rpx 32rpx; padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
  172. background: #fff; border-top: 1rpx solid #f0f0f0;
  173. }
  174. .btn-create {
  175. background: linear-gradient(135deg, #FF8C42, #FFB366);
  176. color: #fff; border: none; border-radius: 40rpx;
  177. font-size: 30rpx; font-weight: 600; padding: 24rpx 0;
  178. }
  179. .btn-create[disabled] { opacity: 0.4; }
  180. .bottom-space { height: 40rpx; }
  181. </style>