Răsfoiți Sursa

chore: auto bump version and changelog [skip ci]

iwt 1 lună în urmă
părinte
comite
898640576b

+ 205 - 22
cfc-frontend/pages/profile-extra/family-members.vue

@@ -116,6 +116,9 @@
     <view v-if="loading" class="loading-wrap">
       <text class="loading-text">加载中...</text>
     </view>
+
+    <!-- ===== 分享卡片画布(离屏,仅用于生成分享缩略图) ===== -->
+    <canvas canvas-id="familyShareCard" id="familyShareCard" class="share-card-canvas"></canvas>
   </view>
 </template>
 
@@ -152,7 +155,8 @@ export default {
       userAvatar: '',
 
       inviteToken: '',
-      joinRequests: []
+      joinRequests: [],
+      shareCardImage: ''
     }
   },
   computed: {
@@ -187,52 +191,78 @@ export default {
     this.loadJoinRequests()
   },
   // 页面级分享:转发邀请卡片,接收人点击直接进入加入家庭页(信息补充、家庭申请)
-  onShareAppMessage() {
+  onShareAppMessage: function() {
     var self = this
     var title = '邀请你加入「' + (this.familyName || '我的家庭') + '」'
-    var buildResult = function(token) {
+    var buildResult = function(token, imageUrl) {
       return {
         title: title,
         path: '/pages/invite/join?token=' + (token || ''),
-        success: function() {},
-        fail: function() {}
+        imageUrl: imageUrl || self.shareCardImage || ''
       }
     }
-    if (this.inviteToken) {
-      return buildResult(this.inviteToken)
+    if (this.inviteToken && this.shareCardImage) {
+      return buildResult(this.inviteToken, this.shareCardImage)
     }
-    // 令牌未就绪时异步获取(基础库 >=2.6.0 支持 promise,超时约 3 秒)
+    // 令牌/卡片未就绪时异步获取
     return {
       title: title,
       path: '/pages/index/index',
       promise: new Promise(function(resolve) {
+        function done(token, image) {
+          resolve(buildResult(token || '', image || ''))
+        }
+        // 并行:令牌 + 分享卡片
+        var tokenDone = false
+        var cardDone = false
+        var tryResolve = function() {
+          if (!tokenDone || !cardDone) return
+          if (!self.inviteToken) { done('', self.shareCardImage) }
+          else { done(self.inviteToken, self.shareCardImage) }
+        }
         self.prepareInviteToken(function(token) {
-          resolve(token ? buildResult(token) : { path: '/pages/index/index' })
+          tokenDone = true
+          tryResolve()
+        })
+        self.drawShareCard(function(image) {
+          cardDone = true
+          self.shareCardImage = image
+          tryResolve()
         })
       })
     }
   },
-  onShareTimeline() {
+  onShareTimeline: function() {
     var self = this
     var title = '邀请你加入「' + (this.familyName || '我的家庭') + '」'
-    var buildResult = function(token) {
+    var buildResult = function(token, imageUrl) {
       return {
         title: title,
         query: 'token=' + (token || ''),
-        imageUrl: ''
+        imageUrl: imageUrl || self.shareCardImage || ''
       }
     }
-    if (this.inviteToken) {
-      return buildResult(this.inviteToken)
+    if (this.inviteToken && this.shareCardImage) {
+      return buildResult(this.inviteToken, this.shareCardImage)
     }
-    // 令牌未就绪时异步获取
     return {
       title: title,
       query: '',
       imageUrl: '',
       promise: new Promise(function(resolve) {
-        self.prepareInviteToken(function(token) {
-          resolve(token ? buildResult(token) : { query: '', imageUrl: '' })
+        var tokenDone = false
+        var cardDone = false
+        var tryResolve = function() {
+          if (!tokenDone || !cardDone) return
+          var token = self.inviteToken || ''
+          var image = self.shareCardImage || ''
+          resolve(buildResult(token, image))
+        }
+        self.prepareInviteToken(function() { tokenDone = true; tryResolve() })
+        self.drawShareCard(function(image) {
+          cardDone = true
+          self.shareCardImage = image
+          tryResolve()
         })
       })
     }
@@ -360,6 +390,152 @@ export default {
         done('')
       })
     },
+    // 生成家庭邀请分享卡片图片(5:4 比例,500x400px),返回 tempFilePath
+    drawShareCard: function(callback) {
+      var self = this
+      var W = 500
+      var H = 400
+      var canvasId = 'familyShareCard'
+      var members = self.members || []
+      var roundRect = function(ctx, x, y, w, h, r) {
+        ctx.beginPath()
+        ctx.moveTo(x + r, y)
+        ctx.arcTo(x + w, y, x + w, y + r, r)
+        ctx.arcTo(x + w, y + h, x + w - r, y + h, r)
+        ctx.arcTo(x, y + h, x, y + h - r, r)
+        ctx.arcTo(x, y, x + r, y, r)
+        ctx.closePath()
+      }
+      var ctx = uni.createCanvasContext(canvasId, self)
+      // 暖橙渐变背景
+      var bgGrad = ctx.createLinearGradient(0, 0, W * 0.6, H)
+      bgGrad.addColorStop(0, '#FF8C42')
+      bgGrad.addColorStop(1, '#FFB366')
+      ctx.setFillStyle(bgGrad)
+      ctx.fillRect(0, 0, W, H)
+      // 白色内容卡片
+      var cx = 30, cy = 70, cw = W - 60, ch = 250, cr = 20
+      ctx.setFillStyle('rgba(0,0,0,0.04)')
+      roundRect(ctx, cx + 2, cy + 4, cw, ch, cr)
+      ctx.fill()
+      ctx.setFillStyle('#FFFCF5')
+      roundRect(ctx, cx, cy, cw, ch, cr)
+      ctx.fill()
+      // 四角装饰框
+      var bl = 14, br = 10
+      ctx.setStrokeStyle('rgba(249,115,22,0.5)')
+      ctx.setLineWidth(1.5)
+      ctx.beginPath(); ctx.moveTo(cx+br, cy+br+bl); ctx.lineTo(cx+br, cy+br); ctx.lineTo(cx+br+bl, cy+br); ctx.stroke()
+      ctx.beginPath(); ctx.moveTo(cx+cw-br-bl, cy+br); ctx.lineTo(cx+cw-br, cy+br); ctx.lineTo(cx+cw-br, cy+br+bl); ctx.stroke()
+      ctx.beginPath(); ctx.moveTo(cx+br, cy+ch-br-bl); ctx.lineTo(cx+br, cy+ch-br); ctx.lineTo(cx+br+bl, cy+ch-br); ctx.stroke()
+      ctx.beginPath(); ctx.moveTo(cx+cw-br-bl, cy+ch-br); ctx.lineTo(cx+cw-br, cy+ch-br); ctx.lineTo(cx+cw-br, cy+ch-br-bl); ctx.stroke()
+      // 主文案
+      var title = '邀请你加入「' + (self.familyName || '我的家庭') + '」'
+      ctx.setFillStyle('#C2410C')
+      ctx.setFontSize(26)
+      ctx.setTextAlign('center')
+      ctx.setTextBaseline('top')
+      ctx.fillText(title, W / 2, cy + 36)
+      // 成员头像(最多3个,首字母占位)
+      var avatarY = cy + 115
+      var avatarR = 30
+      var avatarGap = 20
+      var avatars = []
+      var initials = []
+      for (var i = 0; i < Math.min(3, members.length); i++) {
+        avatars.push(members[i].avatar || '')
+        var name = members[i].nickname || ''
+        initials.push(name.substring(0, 1) || '?')
+      }
+      var totalW = avatars.length * (avatarR * 2) + Math.max(0, avatars.length - 1) * avatarGap
+      var startX = (W - totalW) / 2
+      for (var a = 0; a < avatars.length; a++) {
+        var ax = startX + a * (avatarR * 2 + avatarGap) + avatarR
+        ctx.save()
+        ctx.beginPath()
+        ctx.arc(ax, avatarY, avatarR, 0, 2 * Math.PI)
+        ctx.setFillStyle('#FFF0E0')
+        ctx.fill()
+        ctx.restore()
+        ctx.save()
+        ctx.beginPath()
+        ctx.arc(ax, avatarY, avatarR, 0, 2 * Math.PI)
+        ctx.clip()
+        ctx.setFillStyle('#FF8C42')
+        ctx.setFontSize(28)
+        ctx.setTextAlign('center')
+        ctx.setTextBaseline('middle')
+        ctx.fillText(initials[a] || '?', ax, avatarY)
+        ctx.restore()
+      }
+      // 成员数量
+      ctx.setFillStyle('#999999')
+      ctx.setFontSize(16)
+      ctx.setTextAlign('center')
+      ctx.fillText(members.length + ' 位家庭成员', W / 2, avatarY + avatarR + 18)
+      // CTA 按钮
+      var btnW = 200, btnH = 52
+      var btnX = (W - btnW) / 2
+      var btnY = cy + ch - 70
+      var btnGrad = ctx.createLinearGradient(btnX, btnY, btnX + btnW, btnY)
+      btnGrad.addColorStop(0, '#FF8C42')
+      btnGrad.addColorStop(1, '#FFB366')
+      ctx.setFillStyle(btnGrad)
+      roundRect(ctx, btnX, btnY, btnW, btnH, 26)
+      ctx.fill()
+      ctx.setFillStyle('#FFFFFF')
+      ctx.setFontSize(20)
+      ctx.setTextAlign('center')
+      ctx.setTextBaseline('middle')
+      ctx.fillText('点击加入', W / 2, btnY + btnH / 2)
+      // 底部品牌
+      ctx.setFillStyle('rgba(255,255,255,0.85)')
+      ctx.setFontSize(13)
+      ctx.fillText('浠艾福 · 家庭成长俱乐部', W / 2, H - 30)
+      // 五维点装饰
+      var dotColors = ['#FF8C42', '#6366F1', '#F59E0B', '#10B981', '#FF6B9D']
+      var dotY = H - 16
+      for (var d = 0; d < dotColors.length; d++) {
+        ctx.setFillStyle(dotColors[d])
+        ctx.beginPath()
+        ctx.arc(W / 2 + (d - 2) * 16, dotY, 3, 0, 2 * Math.PI)
+        ctx.fill()
+      }
+      ctx.draw(false, function() {
+        uni.canvasToTempFilePath({
+          canvasId: canvasId,
+          success: function(res) {
+            self.shareCardImage = res.tempFilePath
+            if (typeof callback === 'function') callback(res.tempFilePath)
+          },
+          fail: function() {
+            if (typeof callback === 'function') callback('')
+          }
+        }, self)
+      })
+    },
+                fail: function() {
+                  redraw()
+                }
+              })
+            }
+          } else {
+            // 导出 tempFilePath
+            uni.canvasToTempFilePath({
+              canvasId: canvasId,
+              success: function(res) {
+                self.shareCardImage = res.tempFilePath
+                if (typeof callback === 'function') callback(res.tempFilePath)
+              },
+              fail: function() {
+                if (typeof callback === 'function') callback('')
+              }
+            }, self)
+          }
+        })
+      }
+      render(true)
+    },
     onEditRelation(member) {
       this.showMemberDetailModal = false
       var params = 'memberId=' + member.id + '&nickname=' + encodeURIComponent(member.nickname || '')
@@ -668,11 +844,18 @@ export default {
   border: 2rpx solid #F97316;
 }
 .btn-invite {
-  background: linear-gradient(135deg, #F97316, #EA580C);
-  color: #fff;
-}
-
-/* ===== 弹窗遮罩 ===== */
+    background: linear-gradient(135deg, #F97316, #EA580C);
+    color: #fff;
+  }
+  /* 分享卡片画布:离屏不可见 */
+  .share-card-canvas {
+    position: fixed;
+    left: -9999px;
+    top: -9999px;
+    width: 500px;
+    height: 400px;
+  }
+  /* ===== 弹窗遮罩 ===== */
 .modal-mask {
   position: fixed;
   top: 0;

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-d36c0469b813cb2c71fab06a7cd90e86bdbb5083
+46ddf3550c0d5212a4ab2e430ee45af2c0f58daf

+ 1 - 1
cfc-web/package.json

@@ -1,6 +1,6 @@
 {
   "name": "cfc-web",
-  "version": "1.0.1377",
+  "version": "1.0.1378",
   "private": true,
   "scripts": {
     "dev": "vue-cli-service serve",

+ 6 - 0
cfc-web/public/CHANGELOG-v1.0.md

@@ -4,6 +4,12 @@
 
 ---
 
+## v1.0.1378 (2026-09-12)
+
+### Bug 修复
+- 审批加入时创建者切换家庭前置校验——原家庭仅一人免转让;多人时错误信息指向申请人并给转让指引
+
+
 ## v1.0.1377 (2026-09-12)
 
 ### 其他

+ 7 - 1
cfc-web/public/CHANGELOG.md

@@ -1,6 +1,6 @@
 # 更新日志
 
-> 当前版本: v1.0.1377
+> 当前版本: v1.0.1378
 
 ## 历史版本
 
@@ -8,6 +8,12 @@
 
 ---
 
+## v1.0.1378 (2026-09-12)
+
+### Bug 修复
+- 审批加入时创建者切换家庭前置校验——原家庭仅一人免转让;多人时错误信息指向申请人并给转让指引
+
+
 ## v1.0.1377 (2026-09-12)
 
 ### 其他