|
@@ -28,7 +28,7 @@
|
|
|
<text class="empty-icon">👨👩👧👦</text>
|
|
<text class="empty-icon">👨👩👧👦</text>
|
|
|
<text class="empty-title">还没有家庭成员</text>
|
|
<text class="empty-title">还没有家庭成员</text>
|
|
|
<text class="empty-desc">邀请家人加入,一起记录成长</text>
|
|
<text class="empty-desc">邀请家人加入,一起记录成长</text>
|
|
|
- <button class="empty-invite-btn" @click="onInvite">📨 邀请家人</button>
|
|
|
|
|
|
|
+ <button class="empty-invite-btn" open-type="share">📨 邀请家人</button>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<!-- ===== 成员详情弹窗 ===== -->
|
|
<!-- ===== 成员详情弹窗 ===== -->
|
|
@@ -90,7 +90,7 @@
|
|
|
<!-- ===== 底部操作栏 ===== -->
|
|
<!-- ===== 底部操作栏 ===== -->
|
|
|
<view class="bottom-bar">
|
|
<view class="bottom-bar">
|
|
|
<button class="bottom-btn btn-add" @click="onAddMember">+ 添加成员</button>
|
|
<button class="bottom-btn btn-add" @click="onAddMember">+ 添加成员</button>
|
|
|
- <button class="bottom-btn btn-invite" @click="onInvite">📨 邀请成员</button>
|
|
|
|
|
|
|
+ <button class="bottom-btn btn-invite" open-type="share">📨 邀请成员</button>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<!-- ===== 加载状态 ===== -->
|
|
<!-- ===== 加载状态 ===== -->
|
|
@@ -109,7 +109,6 @@ import {
|
|
|
updateFamilyMember
|
|
updateFamilyMember
|
|
|
} from '../../utils/api.js'
|
|
} from '../../utils/api.js'
|
|
|
import store from '../../store/index.js'
|
|
import store from '../../store/index.js'
|
|
|
-import { ensureFamily } from '../../utils/api.js'
|
|
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
components: {},
|
|
components: {},
|
|
@@ -128,7 +127,9 @@ export default {
|
|
|
detailMember: null,
|
|
detailMember: null,
|
|
|
memberScores: {},
|
|
memberScores: {},
|
|
|
userNickname: '',
|
|
userNickname: '',
|
|
|
- userAvatar: ''
|
|
|
|
|
|
|
+ userAvatar: '',
|
|
|
|
|
+
|
|
|
|
|
+ inviteToken: ''
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
computed: {
|
|
computed: {
|
|
@@ -138,19 +139,6 @@ export default {
|
|
|
detailMemberScores() {
|
|
detailMemberScores() {
|
|
|
if (!this.detailMember) return {}
|
|
if (!this.detailMember) return {}
|
|
|
return this.memberScores[this.detailMember.id] || {}
|
|
return this.memberScores[this.detailMember.id] || {}
|
|
|
- },
|
|
|
|
|
- hasFamily: function() {
|
|
|
|
|
- if (this.members && this.members.length > 0) return true
|
|
|
|
|
- return this.$store.getters.hasFamily
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- onLoad(options) {
|
|
|
|
|
- // 无家庭时自动创建
|
|
|
|
|
- if (!this.hasFamily) {
|
|
|
|
|
- var self = this
|
|
|
|
|
- ensureFamily().then(function() {
|
|
|
|
|
- self.loadMembers()
|
|
|
|
|
- })
|
|
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
onShow() {
|
|
onShow() {
|
|
@@ -158,17 +146,57 @@ export default {
|
|
|
this.userId = (userInfo && userInfo.id) || 0
|
|
this.userId = (userInfo && userInfo.id) || 0
|
|
|
this.userNickname = uni.getStorageSync('nickname') || (userInfo && userInfo.nickname) || ''
|
|
this.userNickname = uni.getStorageSync('nickname') || (userInfo && userInfo.nickname) || ''
|
|
|
this.userAvatar = uni.getStorageSync('avatar') || (userInfo && userInfo.avatar) || ''
|
|
this.userAvatar = uni.getStorageSync('avatar') || (userInfo && userInfo.avatar) || ''
|
|
|
|
|
+ // 家庭在用户注册时已由后端创建(登录接口另有兜底),此处不再触发建家庭逻辑;
|
|
|
|
|
+ // 数据加载统一入口:单次 includeFamily 请求同时获取成员列表 + 家庭信息
|
|
|
this.loadMembers()
|
|
this.loadMembers()
|
|
|
|
|
+ // 预取邀请令牌(后端复用同家庭未用尽的有效令牌),保证点击分享时立即可用
|
|
|
|
|
+ this.prepareInviteToken()
|
|
|
|
|
+ },
|
|
|
|
|
+ // 页面级分享:转发邀请卡片,接收人点击直接进入加入家庭页(信息补充、家庭申请)
|
|
|
|
|
+ onShareAppMessage() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ var title = '邀请你加入「' + (this.familyName || '我的家庭') + '」'
|
|
|
|
|
+ var buildResult = function(token) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ title: title,
|
|
|
|
|
+ path: '/pages/invite/join?token=' + (token || ''),
|
|
|
|
|
+ success: function() {},
|
|
|
|
|
+ fail: function() {}
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.inviteToken) {
|
|
|
|
|
+ return buildResult(this.inviteToken)
|
|
|
|
|
+ }
|
|
|
|
|
+ // 令牌未就绪时异步获取(基础库 >=2.6.0 支持 promise,超时约 3 秒)
|
|
|
|
|
+ return {
|
|
|
|
|
+ title: title,
|
|
|
|
|
+ path: '/pages/index/index',
|
|
|
|
|
+ promise: new Promise(function(resolve) {
|
|
|
|
|
+ self.prepareInviteToken(function(token) {
|
|
|
|
|
+ resolve(token ? buildResult(token) : { path: '/pages/index/index' })
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
async loadMembers() {
|
|
async loadMembers() {
|
|
|
this.loading = true
|
|
this.loading = true
|
|
|
try {
|
|
try {
|
|
|
- var res = await getFamilyMemberList()
|
|
|
|
|
|
|
+ var res = await getFamilyMemberList({ includeFamily: true })
|
|
|
if (res.code === 200 && res.data) {
|
|
if (res.code === 200 && res.data) {
|
|
|
- this.members = res.data || []
|
|
|
|
|
- this.buildScoreMap(res.data)
|
|
|
|
|
- this.loadFamilyInfo()
|
|
|
|
|
|
|
+ var data = res.data
|
|
|
|
|
+ this.familyId = data.familyId
|
|
|
|
|
+ this.familyName = data.familyName
|
|
|
|
|
+ this.creatorId = data.creatorId
|
|
|
|
|
+ this.isAdmin = this.creatorId === this.userId
|
|
|
|
|
+ var arr = data.members
|
|
|
|
|
+ if (!(arr instanceof Array)) {
|
|
|
|
|
+ // 后端旧版本未返回 members 字段时的兼容:补一次无参请求
|
|
|
|
|
+ var legacy = await getFamilyMemberList()
|
|
|
|
|
+ arr = (legacy && legacy.code === 200 && legacy.data instanceof Array) ? legacy.data : []
|
|
|
|
|
+ }
|
|
|
|
|
+ this.members = arr || []
|
|
|
|
|
+ this.buildScoreMap(this.members)
|
|
|
}
|
|
}
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
|
@@ -190,26 +218,6 @@ export default {
|
|
|
}
|
|
}
|
|
|
this.memberScores = map
|
|
this.memberScores = map
|
|
|
},
|
|
},
|
|
|
- async loadFamilyInfo() {
|
|
|
|
|
- try {
|
|
|
|
|
- var res = await getFamilyMemberList({ includeFamily: true })
|
|
|
|
|
- if (res.code === 200 && res.data) {
|
|
|
|
|
- var data = res.data
|
|
|
|
|
- this.familyId = data.familyId
|
|
|
|
|
- this.familyName = data.familyName
|
|
|
|
|
- // inviteCode no longer needed
|
|
|
|
|
- this.creatorId = data.creatorId
|
|
|
|
|
- this.isAdmin = this.creatorId === this.userId
|
|
|
|
|
- } else if (res.code === 5001 || (res.message && res.message.indexOf('未加入家庭') >= 0)) {
|
|
|
|
|
- // 无家庭:自动创建
|
|
|
|
|
- ensureFamily().then(function() {
|
|
|
|
|
- self.loadMembers()
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- // 静默处理
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
showMemberDetail(member) {
|
|
showMemberDetail(member) {
|
|
|
this.detailMember = member
|
|
this.detailMember = member
|
|
|
this.showMemberDetailModal = true
|
|
this.showMemberDetailModal = true
|
|
@@ -268,46 +276,30 @@ export default {
|
|
|
uni.showToast({ title: '操作失败', icon: 'none' })
|
|
uni.showToast({ title: '操作失败', icon: 'none' })
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- async onInvite() {
|
|
|
|
|
- uni.showLoading({ title: '生成邀请...' })
|
|
|
|
|
- try {
|
|
|
|
|
- var res = await generateFamilyInvite()
|
|
|
|
|
- if (res.code === 200 && res.data) {
|
|
|
|
|
- var token = res.data.token
|
|
|
|
|
- uni.share({
|
|
|
|
|
- provider: 'weixin',
|
|
|
|
|
- scene: 'WXSceneSession',
|
|
|
|
|
- type: 5,
|
|
|
|
|
- title: '加入「' + (this.familyName || '我的家庭') + '」',
|
|
|
|
|
- miniProgram: {
|
|
|
|
|
- id: '',
|
|
|
|
|
- path: 'pages/invite/join?token=' + token,
|
|
|
|
|
- type: 0,
|
|
|
|
|
- webUrl: 'https://cfc.etotem.com.cn/invite/join?token=' + token
|
|
|
|
|
- },
|
|
|
|
|
- success: function() {
|
|
|
|
|
- uni.hideLoading()
|
|
|
|
|
- uni.showToast({ title: '分享成功', icon: 'success' })
|
|
|
|
|
- },
|
|
|
|
|
- fail: function(err) {
|
|
|
|
|
- uni.hideLoading()
|
|
|
|
|
- if (err.errMsg.indexOf('cancel') >= 0) return
|
|
|
|
|
- uni.setClipboardData({
|
|
|
|
|
- data: 'https://cfc.etotem.com.cn/invite/join?token=' + token,
|
|
|
|
|
- success: function() {
|
|
|
|
|
- uni.showToast({ title: '链接已复制', icon: 'success' })
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- } else {
|
|
|
|
|
- uni.showToast({ title: res.message || '生成失败', icon: 'none' })
|
|
|
|
|
- uni.hideLoading()
|
|
|
|
|
|
|
+ // 生成/复用邀请令牌(小程序原生分享卡片路径使用),完成后回调 callback(token)
|
|
|
|
|
+ prepareInviteToken(callback) {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ var done = function(token) {
|
|
|
|
|
+ if (typeof callback === 'function') {
|
|
|
|
|
+ callback(token)
|
|
|
}
|
|
}
|
|
|
- } catch (e) {
|
|
|
|
|
- uni.showToast({ title: '生成失败', icon: 'none' })
|
|
|
|
|
- uni.hideLoading()
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+ if (this.inviteToken) {
|
|
|
|
|
+ done(this.inviteToken)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ generateFamilyInvite().then(function(res) {
|
|
|
|
|
+ if (res && res.code === 200 && res.data && res.data.token) {
|
|
|
|
|
+ self.inviteToken = res.data.token
|
|
|
|
|
+ done(self.inviteToken)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: (res && res.message) || '生成邀请失败', icon: 'none' })
|
|
|
|
|
+ done('')
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {
|
|
|
|
|
+ uni.showToast({ title: '生成邀请失败', icon: 'none' })
|
|
|
|
|
+ done('')
|
|
|
|
|
+ })
|
|
|
},
|
|
},
|
|
|
onEditRelation(member) {
|
|
onEditRelation(member) {
|
|
|
this.showMemberDetailModal = false
|
|
this.showMemberDetailModal = false
|
|
@@ -481,6 +473,11 @@ export default {
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
border: none;
|
|
border: none;
|
|
|
}
|
|
}
|
|
|
|
|
+/* 微信小程序 button 默认边框来自 ::after 伪元素,分享按钮同样需要重置 */
|
|
|
|
|
+.empty-invite-btn::after,
|
|
|
|
|
+.bottom-btn::after {
|
|
|
|
|
+ border: none;
|
|
|
|
|
+}
|
|
|
.bottom-btn:active {
|
|
.bottom-btn:active {
|
|
|
opacity: 0.85;
|
|
opacity: 0.85;
|
|
|
}
|
|
}
|