Răsfoiți Sursa

feat: Add invitation QR code modal to parent index page

Xiaogang Liao 1 lună în urmă
părinte
comite
a769c901e1
1 a modificat fișierele cu 176 adăugiri și 1 ștergeri
  1. 176 1
      cfc-frontend/pages/home-pages/parent-index.vue

+ 176 - 1
cfc-frontend/pages/home-pages/parent-index.vue

@@ -294,6 +294,26 @@
         :visible="showImportModal"
         @close="onCloseImport"
         @success="onImportSuccess" />
+      
+      <!-- 邀请码二维码弹窗 -->
+      <view class="qr-code-modal" v-if="showQrCodeModal" @touchmove.stop.prevent @click="onCloseQrCodeModal">
+        <view class="qr-code-modal-content" @click.stop>
+          <view class="qr-code-modal-header">
+            <text class="qr-code-modal-title">邀请二维码</text>
+            <text class="qr-code-modal-close" @click="onCloseQrCodeModal">×</text>
+          </view>
+          <view class="qr-code-modal-body">
+            <view class="qr-code-image-container">
+              <image :src="qrCodeData?.qrCodeBase64 || ''" class="qr-code-image" mode="aspectFit" />
+            </view>
+            <view class="qr-code-referral-code">
+              <text class="qr-code-label">邀请码:</text>
+              <text class="qr-code-code">{{ qrCodeData?.referralCode || '' }}</text>
+              <text class="qr-code-copy" @click="onCopyReferralCode">复制</text>
+            </view>
+          </view>
+        </view>
+      </view>
 
       <!-- ===== 推荐商品+活动(混合信息流) ===== -->
       <RecommendedFeed
@@ -368,6 +388,13 @@
               </view>
               <text class="action-label">奖励中心</text>
             </view>
+            <!-- Added: QR Code button -->
+            <view class="action-item" @click="onShowQrCodeModal">
+              <view class="action-icon act-invite">
+                <text class="action-icon-text">📱</text>
+              </view>
+              <text class="action-label">邀请码</text>
+            </view>
           </view>
         </PlayfulCard>
       </view>
@@ -393,7 +420,7 @@
 </template>
 
 <script>
-import { getFamilyMembers, getChildren, getPendingReviewTasks, getPendingWishes, approveTask, rejectTask, getChildCompletionStats, getParentDashboard, getFamilyEnergySandbox, getEnergyOverview, getVisibleFamilyMembers, getActivityList, getProductsByDomain, getContactList, getFeaturedArticles, getActiveAlerts, dismissAlert, getFamilyRanking, getChallengeList, getCircleRanking } from '../../utils/api.js'
+import { getFamilyMembers, getChildren, getPendingReviewTasks, getPendingWishes, approveTask, rejectTask, getChildCompletionStats, getParentDashboard, getFamilyEnergySandbox, getEnergyOverview, getVisibleFamilyMembers, getActivityList, getProductsByDomain, getContactList, getFeaturedArticles, getActiveAlerts, dismissAlert, getFamilyRanking, getChallengeList, getCircleRanking, getInviteQrCode } from '../../utils/api.js'
 import PageBanner from '../../components/PageBanner.vue'
 import PlayfulCard from '../../components/PlayfulCard.vue'
 import PlayfulButton from '../../components/PlayfulButton.vue'
@@ -458,6 +485,9 @@ export default {
       loadingProducts: false,
       contactList: [],
       showImportModal: false,
+      // QR Code modal
+      showQrCodeModal: false,
+      qrCodeData: null,
       // 推荐阅读
       indexArticles: [],
       articlesLoading: false,
@@ -968,6 +998,38 @@ export default {
       this.showImportModal = false
       this.loadContacts()
     },
+    // QR Code modal methods
+    onShowQrCodeModal: async function() {
+      this.showQrCodeModal = true
+      this.qrCodeData = null
+      try {
+        const res = await this.getInviteQrCode()
+        if (res.code === 200) {
+          this.qrCodeData = res.data
+        }
+      } catch (e) {
+        console.error('获取二维码失败', e)
+        uni.showToast({ title: '获取二维码失败', icon: 'none' })
+      }
+    },
+    onCloseQrCodeModal: function() {
+      this.showQrCodeModal = false
+      this.qrCodeData = null
+    },
+    onCopyReferralCode: function() {
+      if (this.qrCodeData && this.qrCodeData.referralCode) {
+        uni.setClipboardData({
+          data: this.qrCodeData.referralCode,
+          success: () => {
+            uni.showToast({ title: '复制成功', icon: 'success' })
+          }
+        })
+      }
+    },
+    // 兼容旧引用
+    getInviteQrCode: function() {
+      return getInviteQrCode()
+    },
     // ===== 推荐阅读 =====
     loadFeaturedArticles: function() {
       var self = this
@@ -1718,4 +1780,117 @@ export default {
   font-weight: 600;
   margin-top: 4rpx;
 }
+
+/* ===== 邀请码二维码弹窗 ===== */
+.qr-code-modal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(0, 0, 0, 0.6);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 1000;
+  padding: 40rpx 20rpx;
+  box-sizing: border-box;
+}
+
+.qr-code-modal-content {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 40rpx 32rpx;
+  max-width: 600rpx;
+  width: 100%;
+  box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.2);
+  position: relative;
+}
+
+.qr-code-modal-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 32rpx;
+}
+
+.qr-code-modal-title {
+  font-size: 36rpx;
+  font-weight: 700;
+  color: #1E293B;
+}
+
+.qr-code-modal-close {
+  font-size: 48rpx;
+  color: #94A3B8;
+  line-height: 1;
+  padding: 8rpx;
+  cursor: pointer;
+}
+
+.qr-code-modal-body {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 32rpx;
+}
+
+.qr-code-image-container {
+  width: 280rpx;
+  height: 280rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: #f9fafb;
+  border-radius: 16rpx;
+  padding: 20rpx;
+}
+
+.qr-code-image {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+}
+
+.qr-code-referral-code {
+  display: flex;
+  align-items: center;
+  gap: 16rpx;
+  padding: 24rpx;
+  background: #f9fafb;
+  border-radius: 16rpx;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.qr-code-label {
+  font-size: 26rpx;
+  color: #64748b;
+  font-weight: 500;
+}
+
+.qr-code-code {
+  flex: 1;
+  font-size: 32rpx;
+  font-weight: 700;
+  color: #1E293B;
+  word-break: break-all;
+  letter-spacing: 2rpx;
+}
+
+.qr-code-copy {
+  font-size: 24rpx;
+  color: #F97316;
+  font-weight: 500;
+  padding: 8rpx 16rpx;
+  background: #fff;
+  border-radius: 8rpx;
+  border: 1px solid #F97316;
+  cursor: pointer;
+}
+
+.qr-code-copy:active {
+  opacity: 0.8;
+}
+
 </style>