Browse Source

添加分享海报功能

wkw 3 tuần trước cách đây
mục cha
commit
221a272fb2

+ 8 - 0
api/other.js

@@ -63,6 +63,14 @@ export function couponDetail(coupon_id) {
     method: 'GET'
   });
 }
+//生成分享海报
+export function sharePoster() {
+  return request({
+    loading: false,
+    url: `/api/wechat/share/poster`,
+    method: 'GET'
+  });
+}
 
 
 

+ 9 - 0
api/user.js

@@ -36,3 +36,12 @@ export function behaviorReport(data) {
     data
   });
 }
+// 登出
+export function logout(data) {
+  return request({
+    loading: false,
+    url: '/api/logout',
+    method: 'POST',
+    data
+  });
+}

+ 1 - 34
custom-tab-bar/index.js

@@ -24,13 +24,11 @@ Component({
         selectedIconPath: "/static/tabbar/me-active.png"
       }
     ],
-    showShare: false,
     showAgreementModal:false,
-    showPoster:false,
     programConfig: {}
   },
   lifetimes: {
-    attached() {
+    ready() {
       this.setData({
         programConfig: app.globalData.programConfig
       });
@@ -42,39 +40,8 @@ Component({
       wx.switchTab({ url: path })
       this.setData({ selected: index })
     },
-    onCloseShare() {
-      this.setData({ showShare: false })
-    },
-    openShare() {
-      this.setData({ showShare: true })
-    },
     openShowAgreementModal(){
       this.setData({ showAgreementModal: true })
     },
-    onClosePoster(){
-      this.setData({ showPoster: true })
-    },
-    openPoster() {
-      this.setData({
-        showShare:false,
-        showPoster: true
-      });
-    },
-    // 分享逻辑
-    onShareAppMessage(res) {
-      const shareType = res.target.dataset.type;
-      if (shareType === 'poster') {
-        // return {
-        //   title: '分享我的专属海报',
-        //   path: '/pages/index/index',
-        //   imageUrl: app.globalData.programConfig.share_img
-        // }
-      } else if (shareType === 'friend') {
-        return {
-          path: '/pages/index/index',
-          imageUrl: app.globalData.programConfig.share_img
-        }
-      }
-    }
   }
 })

+ 0 - 1
custom-tab-bar/index.json

@@ -1,7 +1,6 @@
 {
   "component": true,
   "usingComponents": {
-    "van-popup": "@vant/weapp/popup/index",
     "van-dialog": "@vant/weapp/dialog/index"
   }
 }

+ 1 - 38
custom-tab-bar/index.wxml

@@ -6,32 +6,6 @@
     </view>
   </block>
 </view>
-<!-- 分享弹框 -->
-<van-popup
-  show="{{ showShare }}"
-  position="bottom"
-  round
-  z-index="9999"
->
-  <view class="share-container">
-    <view class="share-options">
-      <button class="share-item" open-type="share" data-type="friend">
-        <image class="share-icon" src="/static/tabbar/weixin.png" mode="aspectFit" />
-        <text class="share-text">微信好友</text>
-      </button>
-
-      <button class="share-item" bindtap="openPoster">
-        <image class="share-icon" src="/static/tabbar/tp.png" mode="aspectFit" />
-        <text class="share-text">海报转发</text>
-      </button>
-    </view>
-    <!-- 取消按钮 -->
-    <view class="share-cancel" bindtap="onCloseShare" hover-class="btn-hover" hover-start-time="50">
-      <image class="share-cancel-img" src="/static/tabbar/cancel.png" mode="aspectFit"/>
-    </view>
-  </view>
-</van-popup>
-
 <!-- 客服弹窗 -->
 <van-dialog
   confirm-button-class="confirm-btn"
@@ -45,15 +19,4 @@
     <view class="box-text" bindtap="onPhone" wx:if="{{programConfig.customer_phone}}">客服电话:{{programConfig.customer_phone}}</view>
     <view class="box-text" wx:if="{{programConfig.customer_email}}">客服邮箱:{{programConfig.customer_email}}</view>
   </view>
-</van-dialog>
-
-<!-- 弹框展示海报 -->
-<van-popup show="{{showPoster}}" position="center" bind:close="onClosePoster" close-on-click-overlay="{{true}}">
-  <view class="poster-container">
-    <image src="/static/image/address.png" mode="widthFix" class="poster-img" />
-    <view class="poster-actions">
-      <button bindtap="savePoster">保存到相册</button>
-      <button open-type="share" data-type="poster">发送朋友</button>
-    </view>
-  </view>
-</van-popup>
+</van-dialog>

+ 0 - 46
custom-tab-bar/index.wxss

@@ -26,52 +26,6 @@
   color: #FFA100;
   font-weight: 500;
 }
-/* 分享样式 */
-.share-container {
-  background: #FFFFFF;
-}
-.share-options {
-  display: flex;
-  justify-content: space-evenly;
-  margin: 70rpx 0 36rpx;
-}
-.share-item {
-  display: inline-flex;
-  flex-direction: column;
-  align-items: center;
-  background: transparent;
-  border: none;
-  padding: 0;
-  margin: 0;
-  width: 100% !important;
-}
-.share-icon {
-  width: 76rpx;
-  height: 76rpx;
-  margin-bottom: 20rpx;
-}
-.share-text {
-  font-family: PingFangSC, PingFang SC;
-  font-weight: 400;
-  font-size: 24rpx;
-  color: #666666;
-  line-height: 40rpx;
-}
-.share-cancel {
-  margin-bottom: 60rpx;
-  display: flex;
-}
-.share-cancel-img{
-  width: 444rpx;
-  height: 102rpx;
-  margin: 0 auto;
-}
-.btn-hover {
-  opacity: 0.7;
-}
-.share-item::after {
-  border: none !important;
-}
 /* 客服弹框样式 */
 .dialog-box{
   padding: 32rpx;

+ 75 - 10
pages/index/index.js

@@ -1,4 +1,4 @@
-import {miniProgramConfig} from '../../api/other';
+import {miniProgramConfig,sharePoster} from '../../api/other';
 import {BASE_URL} from '../../utils/request';
 import {isLoggedIn,doLogin} from '../../utils/auth';
 const app = getApp();
@@ -10,8 +10,12 @@ Page({
     start_time:'',
     end_time:'',
     ad_img:'',
+    share_img:'',
     pendingAction:null,//按钮类型
-    loggedIn:false
+    loggedIn:false,
+    showShare: false,
+    showPoster:false,
+    posterImg:'',//生成的海报
   },
   async onLoad(){
     const res = await miniProgramConfig();
@@ -21,7 +25,8 @@ Page({
       introduction:res.data.marathon_event.introduction,
       start_time:res.data.marathon_event.start_time,
       end_time:res.data.marathon_event.end_time,
-      ad_img:res.data.ad_img
+      ad_img:res.data.ad_img,
+      share_img:res.data.share_img
     })
   },
   onShow() {
@@ -70,7 +75,7 @@ Page({
         success: async loginRes => {
           await doLogin({
             code: loginRes.code,
-            // phone: { encryptedData, iv }
+            phone: { encryptedData, iv }
           });
           this.setData({ loggedIn: isLoggedIn() })
           this.doAction(action);
@@ -84,12 +89,72 @@ Page({
   doAction(action) {
     if (action === 'register') wx.navigateTo({ url: '/pages/register/register' });
     if (action === 'invite'){
-      const tabbar = this.getTabBar && this.getTabBar()
-      if (tabbar && tabbar.openShare) {
-        tabbar.openShare()
-      } else {
-        console.log("没找到 tabbar 组件")
-      }
+      this.setData({ showShare: true });
     };
+  },
+  // 弹框取消
+  onClose(){
+    this.setData({ showShare: false });
+  },
+  // 点击海报生成图片
+  async openPoster(){
+    this.setData({
+      showShare: false
+    })
+    wx.showLoading({
+      title: '海报生成中...',
+      mask: true
+    });
+    try {
+      const res = await sharePoster();
+      if(res.code == 200){
+        console.log(res.data);
+        this.setData({ showPoster:true,posterImg: res.data});
+      }
+    } catch (error) {
+      console.log(error)
+    } finally{
+      wx.hideLoading();
+    }
+  },
+  closePoster(){
+    this.setData({ showPoster: false});
+  },
+  // 保存相册
+  savePoster() {
+    if (!this.data.posterImg) {
+      wx.showToast({ title: '没有海报图片', icon: 'none' });
+      return;
+    }
+    // 将 base64 转临时文件路径
+    const base64Data = this.data.posterImg;
+    const filePath = `${wx.env.USER_DATA_PATH}/poster.png`;
+    const fs = wx.getFileSystemManager();
+    const buffer = wx.base64ToArrayBuffer(base64Data);
+  
+    fs.writeFileSync(filePath, buffer, 'binary');
+  
+    wx.saveImageToPhotosAlbum({
+      filePath,
+      success() { wx.showToast({ title: '保存成功' }); },
+      fail(err) {
+        if (err.errMsg.includes('auth')) wx.openSetting();
+      }
+    });
+  },  
+  // 发送朋友
+  sendImg(){
+    // 将 base64 转临时文件路径
+    const base64Data = this.data.posterImg;
+    const filePath = `${wx.env.USER_DATA_PATH}/poster.png`;
+    const fs = wx.getFileSystemManager();
+    const buffer = wx.base64ToArrayBuffer(base64Data);
+  
+    fs.writeFileSync(filePath, buffer, 'binary');
+    wx.showShareImageMenu({
+      path: filePath,
+      success() {},
+      fail: console.error,
+    })
   }
 })

+ 2 - 1
pages/index/index.json

@@ -1,6 +1,7 @@
 {
   "usingComponents": {
-    "countdown": "/components/Countdown/Countdown"
+    "countdown": "/components/Countdown/Countdown",
+    "van-popup": "@vant/weapp/popup/index"
   },
   "navigationStyle": "custom"
 }

+ 27 - 0
pages/index/index.wxml

@@ -59,4 +59,31 @@
       </view>
     </view>
   </view>
+  <van-popup show="{{showShare}}" position="bottom" round>
+    <view class="share-container">
+      <view class="share-options">
+        <button class="share-item" open-type="share" data-type="friend">
+          <image class="share-icon" src="/static/tabbar/weixin.png" mode="aspectFit" />
+          <text class="share-text">微信好友</text>
+        </button>
+
+        <button class="share-item" bindtap="openPoster">
+          <image class="share-icon" src="/static/tabbar/tp.png" mode="aspectFit" />
+          <text class="share-text">海报转发</text>
+        </button>
+      </view>
+      <view class="share-cancel" bindtap="onClose" hover-class="btn-hover" hover-start-time="50">
+        <image class="share-cancel-img" src="/static/tabbar/cancel.png" mode="aspectFit"/>
+      </view>
+    </view>
+  </van-popup>
+  <view class="poster-container" wx:if="{{showPoster}}" bindtap="closePoster">
+    <view class="poster-content">
+      <image src="data:image/png;base64,{{posterImg}}" mode="aspectFit" class="poster-img" />
+      <view class="poster-actions">
+        <button class="poster-lf" bindtap="savePoster">保存到相册</button>
+        <button class="poster-ri" bindtap="sendImg" data-type="poster">发送朋友</button>
+      </view>
+    </view>
+  </view>
 </view>

+ 92 - 0
pages/index/index.wxss

@@ -98,4 +98,96 @@
   left: 0;
   opacity: 0; /* 完全透明 */
   z-index: 10;
+}
+
+/* 分享样式 */
+.share-container {
+  background: #FFFFFF;
+  margin-bottom: 120rpx;
+}
+.share-options {
+  display: flex;
+  justify-content: space-evenly;
+  margin: 70rpx 0 36rpx;
+}
+.share-item {
+  display: inline-flex;
+  flex-direction: column;
+  align-items: center;
+  background: transparent;
+  border: none;
+  padding: 0;
+  margin: 0;
+  width: 100% !important;
+}
+.share-icon {
+  width: 76rpx;
+  height: 76rpx;
+  margin-bottom: 20rpx;
+}
+.share-text {
+  font-family: PingFangSC, PingFang SC;
+  font-weight: 400;
+  font-size: 24rpx;
+  color: #666666;
+  line-height: 40rpx;
+}
+.share-cancel {
+  margin-bottom: 60rpx;
+  display: flex;
+}
+.share-cancel-img{
+  width: 444rpx;
+  height: 102rpx;
+  margin: 0 auto;
+}
+.btn-hover {
+  opacity: 0.7;
+}
+.share-item::after {
+  border: none !important;
+}
+.poster-container{
+  height: 100%;
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 100;
+  width: 100%;
+  background-color: rgba(0,0,0,0.7);
+}
+.poster-content{
+  position:absolute;
+  top: 10%;
+  height: 60%;
+  left: 80rpx;
+  right: 80rpx;
+}
+.poster-img{
+  width: 100%;
+  height: 100%;
+  border-radius: 20rpx;
+}
+.poster-actions{
+  display: flex;
+  align-items: center;
+  gap: 70rpx;
+  margin-top: 32rpx;
+}
+.poster-lf,.poster-ri{
+  width: 50% !important;
+  margin: 0;
+  padding: 15rpx 30rpx;
+  font-size: 28rpx;
+  font-weight: 500;
+  border-radius: 120rpx;
+}
+.poster-lf{
+  color: #FFA100;
+}
+.poster-ri{
+  background-color: #FFA100;
+  color: #fff;
 }

+ 2 - 1
pages/mine/mine.js

@@ -12,6 +12,7 @@ Page({
       { icon: '/static/image/me-3.png', title: '报名记录' },
       { icon: '/static/image/me-4.png', title: '优惠券' },
       { icon: '/static/image/me-5.png', title: '设置' },
+      // { icon: '/static/image/me-5.png', title: '参赛管理' },
       { icon: '/static/image/me-6.png', title: '联系客服' }
     ],
     userInfo: {},
@@ -97,7 +98,7 @@ Page({
         success: async loginRes => {
           await doLogin({
             code: loginRes.code,
-            // phone: { encryptedData, iv } 可按接口需要传
+            phone: { encryptedData, iv }
           });
           this.setData({ loggedIn: true });
           this.doAction(index);

+ 1 - 0
pages/register/register.js

@@ -123,6 +123,7 @@ Page({
     console.log(e)
     const field = e.currentTarget.dataset.field;
     const value = e.detail;
+    console.log(value)
     // 手机号单独做校验
     if (field === 'phone') {
       const regPhone = /^1[3-9]\d{9}$/;

+ 1 - 1
pages/register/register.wxml

@@ -73,8 +73,8 @@
             placeholder="请输入身份证号"
             border="{{ false }}"
           >
-            <view class="error-text">{{raceNumberError}}</view>
           </van-field>
+          <view class="error-text">{{raceNumberError}}</view>
         </view>
 
         <!-- 参赛号码 -->

+ 16 - 1
pages/setting/setting.js

@@ -1,4 +1,4 @@
-import {userModify} from '../../api/user';
+import {userModify,logout} from '../../api/user';
 import { uploadImage } from '../../utils/upload.js';
 const app = getApp();
 Page({
@@ -42,6 +42,21 @@ Page({
       wx.showToast({ title: '授权失败', icon: 'none' });
     }
   },
+  // 退出登录
+  async onExit(){
+    const res = await logout();
+    if(res.code == 200){
+      wx.showToast({
+        title: res.code || '登出成功',
+        icon: 'none'
+      });
+      wx.removeStorageSync('token');
+      wx.removeStorageSync('expires_in');
+      app.globalData.userInfo = null;
+      app.globalData.programConfig = null;
+      wx.navigateBack({ delta: 1 });
+    }
+  },
   async onSave(){
     let params = {
       nickname:this.data.userInfo.nickname,

+ 2 - 2
pages/setting/setting.wxml

@@ -34,9 +34,9 @@
       </van-cell>
     </view>
     <view class="btn">
-      <!-- <view bindtap="onExit" hover-class="btn-hover" hover-start-time="10">
+      <view bindtap="onExit" hover-class="btn-hover" hover-start-time="10">
         <image  class="set-btn m20" src="/static/image/exit.png" mode="aspectFit"/>
-      </view> -->
+      </view>
       <view bindtap="onSave" hover-class="btn-hover" hover-start-time="10">
         <image class="set-btn" src="/static/image/save.png" mode="aspectFit"/>
       </view>

+ 2 - 2
utils/util.js

@@ -21,12 +21,12 @@ export async function FETCH_AND_FORMAT_USER_INFO() {
   getApp().globalData.userInfo = {
     ...data,
     avatar: data.avatar || '/static/image/cat.jpeg',
-    nickname: data.nickname || data.username.substring(0,6)
+    nickname: data.nickname || data.phone || data.username.substring(0,6)
   }
   return {
     ...data,
     avatar: data.avatar || '/static/image/cat.jpeg',
-    nickname: data.nickname || data.username.substring(0,6)
+    nickname: data.nickname || data.phone || data.username.substring(0,6)
   }
 }
 // 埋点上报