| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- <template>
- <view class="container">
- <view class="header">
- <text class="title">邀请家人加入</text>
- <text class="desc">分享给好友,好友注册后自动成为家庭成员</text>
- </view>
- <!-- 邀请码展示 -->
- <view class="invite-card" v-if="inviteCode">
- <view class="invite-code">{{ inviteCode }}</view>
- <text class="invite-tip">好友也可输入邀请码加入</text>
- </view>
- <!-- 分享按钮区域 -->
- <view class="share-section">
- <button class="btn-share-large" open-type="share">
- <text class="share-icon">📤</text>
- <text class="share-text">发送给好友</text>
- </button>
- <text class="share-hint">点击上方按钮,分享到微信好友</text>
- </view>
- <!-- 其他方式 -->
- <view class="other-methods">
- <view class="method-title">其他方式</view>
- <view class="method-item" @click="copyCode">
- <text class="method-icon">📋</text>
- <text class="method-text">复制邀请码</text>
- </view>
- <view class="method-item" @click="generatePoster">
- <text class="method-icon">🖼️</text>
- <text class="method-text">生成邀请海报</text>
- </view>
- </view>
- <!-- 成长规划师邀请说明 -->
- <view class="guide-tip" v-if="role === 'teacher'">
- <text class="tip-title">成长规划师权限说明</text>
- <text class="tip-text">通过邀请码绑定家庭后,您将获得:</text>
- <text class="tip-item">• 查看家庭成员任务完成情况</text>
- <text class="tip-item">• 审核任务执行</text>
- <text class="tip-item">• 制定任务</text>
- </view>
- <!-- 成长规划师绑定家庭 -->
- <view class="section" v-if="role === 'teacher'">
- <view class="section-title">绑定已有家庭</view>
- <input
- class="input"
- v-model="familyCode"
- placeholder="输入家庭邀请码"
- />
- <button class="btn-primary" @click="bindFamily">绑定家庭</button>
- </view>
- <!-- 海报弹窗 -->
- <view class="poster-mask" v-if="showPoster" @click="showPoster = false">
- <view class="poster-content" @click.stop>
- <canvas canvas-id="posterCanvas" class="poster-canvas"></canvas>
- <button class="btn-save" @click="savePoster">保存到相册</button>
- <text class="poster-hint">保存图片后发送给好友</text>
- </view>
- </view>
- </view>
- </template>
- <script>
- import { getInviteCode, generateFamilyInviteCode, bindGuideFamily, generateQrCodeFromText } from '@/utils/api.js'
- export default {
- data() {
- return {
- inviteCode: '',
- familyCode: '',
- role: 'parent',
- showPoster: false,
- familyName: ''
- }
- },
- onLoad() {
- this.role = uni.getStorageSync('role') || 'parent'
- this.loadInviteCode()
- this.loadFamilyName()
- },
- onShareAppMessage() {
- var code = this.inviteCode || ''
- var path = '/pages/invite/join'
- if (code) {
- path = path + '?inviteCode=' + code
- }
- return {
- title: '我在浠艾福发现了好用的家庭成长工具,快来加入!',
- path: path,
- imageUrl: ''
- }
- },
- methods: {
- async loadInviteCode() {
- try {
- const res = await getInviteCode()
- if (res.data) {
- this.inviteCode = res.data
- } else {
- const genRes = await generateFamilyInviteCode()
- this.inviteCode = genRes.data
- }
- } catch (e) {
- console.error('获取邀请码失败', e)
- }
- },
- async loadFamilyName() {
- try {
- const userInfo = uni.getStorageSync('userInfo')
- if (userInfo && userInfo.nickname) {
- this.familyName = userInfo.nickname + '的家庭'
- } else {
- this.familyName = '我的家庭'
- }
- } catch (e) {
- this.familyName = '我的家庭'
- }
- },
- copyCode() {
- uni.setClipboardData({
- data: this.inviteCode,
- success: () => {
- uni.showToast({ title: '已复制邀请码', icon: 'success' })
- }
- })
- },
- async generatePoster() {
- this.showPoster = true
- // 先获取二维码
- var qrTempPath = ''
- if (this.inviteCode) {
- var invitePath = '/pages/invite/join?inviteCode=' + this.inviteCode
- try {
- var res = await generateQrCodeFromText(invitePath)
- if (res.code === 200 && res.data) {
- var rawBase64 = res.data.qrCodeBase64
- if (rawBase64.indexOf('base64,') >= 0) {
- rawBase64 = rawBase64.split('base64,')[1]
- }
- var fsm = uni.getFileSystemManager()
- var path = wx.env.USER_DATA_PATH + '/qr_poster_' + Date.now() + '.png'
- fsm.writeFileSync(path, rawBase64, 'base64')
- qrTempPath = path
- }
- } catch (e) {
- console.error('生成二维码失败', e)
- }
- }
- this.$nextTick(function() {
- this.drawPoster(qrTempPath)
- }.bind(this))
- },
- drawPoster(qrTempPath) {
- const ctx = uni.createCanvasContext('posterCanvas')
- const width = 300
- const height = 450
- var self = this
-
- // 背景
- ctx.setFillStyle('#667eea')
- ctx.fillRect(0, 0, width, height)
-
- // 标题
- ctx.setFillStyle('#ffffff')
- ctx.setFontSize(20)
- ctx.setTextAlign('center')
- ctx.fillText('邀请加入家庭', width / 2, 40)
-
- // 家庭名
- ctx.setFontSize(14)
- ctx.fillText(self.familyName, width / 2, 70)
-
- // 邀请码
- ctx.setFillStyle('#ffffff')
- ctx.setFontSize(32)
- ctx.setTextAlign('center')
- ctx.fillText(self.inviteCode, width / 2, 130)
-
- // 提示
- ctx.setFontSize(12)
- ctx.setFillStyle('rgba(255,255,255,0.8)')
- ctx.setTextAlign('center')
- ctx.fillText('输入邀请码加入家庭', width / 2, 170)
-
- // 二维码区域
- if (qrTempPath) {
- ctx.drawImage(qrTempPath, 75, 210, 150, 150)
- } else {
- ctx.setFillStyle('#ffffff')
- ctx.fillRect(75, 210, 150, 150)
- ctx.setFillStyle('#667eea')
- ctx.setFontSize(12)
- ctx.setTextAlign('center')
- ctx.fillText('扫码注册', width / 2, 300)
- }
-
- ctx.draw()
- },
- savePoster() {
- uni.canvasToTempFilePath({
- canvasId: 'posterCanvas',
- success: (res) => {
- uni.saveImageToPhotosAlbum({
- filePath: res.tempFilePath,
- success: () => {
- uni.showToast({ title: '已保存到相册', icon: 'success' })
- this.showPoster = false
- },
- fail: () => {
- uni.showToast({ title: '保存失败', icon: 'none' })
- }
- })
- },
- fail: () => {
- uni.showToast({ title: '生成失败', icon: 'none' })
- }
- })
- },
- async bindFamily() {
- if (!this.familyCode) {
- uni.showToast({ title: '请输入家庭邀请码', icon: 'none' })
- return
- }
- try {
- await bindGuideFamily(this.familyCode)
- uni.showToast({ title: '绑定成功', icon: 'success' })
- } catch (e) {
- uni.showToast({ title: e.message || '绑定失败', icon: 'none' })
- }
- }
- }
- }
- </script>
- <style scoped>
- .container { padding: 30rpx; min-height: 100vh; background: #f5f5f5; }
- .header { text-align: center; padding: 40rpx 0; }
- .title { font-size: 40rpx; font-weight: bold; display: block; margin-bottom: 16rpx; color: #333; }
- .desc { font-size: 28rpx; color: #666; }
- .invite-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20rpx; padding: 50rpx; text-align: center; margin: 30rpx 0; }
- .invite-code { font-size: 56rpx; font-weight: bold; color: #fff; letter-spacing: 8rpx; margin-bottom: 16rpx; }
- .invite-tip { font-size: 24rpx; color: rgba(255,255,255,0.8); }
- .share-section { background: #fff; border-radius: 20rpx; padding: 40rpx; margin: 30rpx 0; text-align: center; }
- .btn-share-large { background: #07c160; color: #fff; border-radius: 60rpx; padding: 24rpx 60rpx; display: inline-flex; align-items: center; justify-content: center; }
- .btn-share-large::after { border: none; }
- .share-icon { font-size: 36rpx; margin-right: 16rpx; }
- .share-text { font-size: 32rpx; font-weight: bold; }
- .share-hint { display: block; margin-top: 20rpx; font-size: 24rpx; color: #999; }
- .other-methods { background: #fff; border-radius: 20rpx; padding: 30rpx; margin: 30rpx 0; }
- .method-title { font-size: 28rpx; color: #999; margin-bottom: 20rpx; }
- .method-item { display: flex; align-items: center; padding: 20rpx 0; border-bottom: 1rpx solid #f0f0f0; }
- .method-item:last-child { border-bottom: none; }
- .method-icon { font-size: 40rpx; margin-right: 20rpx; }
- .method-text { font-size: 28rpx; color: #333; }
- .guide-tip { background: #fff3e0; border-radius: 16rpx; padding: 30rpx; margin: 30rpx 0; }
- .tip-title { font-size: 28rpx; font-weight: bold; color: #ff9800; display: block; margin-bottom: 16rpx; }
- .tip-text { font-size: 24rpx; color: #666; display: block; margin-bottom: 12rpx; }
- .tip-item { font-size: 24rpx; color: #666; display: block; margin-left: 20rpx; line-height: 1.8; }
- .section { background: #fff; border-radius: 20rpx; padding: 30rpx; margin: 30rpx 0; }
- .section-title { font-size: 28rpx; font-weight: bold; margin-bottom: 20rpx; }
- .input { border: 1rpx solid #ddd; border-radius: 10rpx; padding: 20rpx; margin-bottom: 20rpx; font-size: 28rpx; }
- .poster-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8); display: flex; align-items: center; justify-content: center; z-index: 999; }
- .poster-content { background: #fff; border-radius: 20rpx; padding: 30rpx; text-align: center; }
- .poster-canvas { width: 300px; height: 450px; margin: 0 auto; }
- .btn-save { background: #07c160; color: #fff; border-radius: 40rpx; padding: 20rpx; margin: 20rpx 0; }
- .poster-hint { font-size: 24rpx; color: #999; display: block; }
- </style>
|