wkw 3 nedēļas atpakaļ
vecāks
revīzija
8278566415

+ 9 - 0
api/other.js

@@ -79,6 +79,15 @@ export function registrationRecord() {
     method: 'GET'
   });
 }
+// 保存完赛证明
+export function finishCertificate(data) {
+  return request({
+    loading: false,
+    url: '/api/upload/finish-certificate',
+    method: 'POST',
+    data
+  });
+}
 
 
 

+ 24 - 11
pages/competitionRecords/competitionRecords.js

@@ -1,4 +1,4 @@
-import { registrationRecord } from '../../api/other';
+import { registrationRecord,finishCertificate } from '../../api/other';
 import { uploadImage } from '../../utils/upload.js';
 Page({
   data: {
@@ -11,6 +11,7 @@ Page({
     activeItem:{},
     filePath: '',  // 上传成功的文件路径
     fileType: '',   // image / pdf
+    competition_image:''
   },
 
   onLoad() {
@@ -24,17 +25,11 @@ Page({
     this.setData({ loadingMore: true });
     try {
       const res = await registrationRecord({ page, pageSize: this.data.pageSize });
-      const statusImgMap = {
-        0: '/static/image/register/wks.png',
-        1: '/static/image/register/yjs.png',
-        2: '/static/image/register/jxz.png'
-      };
-
       const newList = res.data.list || [];
       let allList = isRefresh ? newList : [...this.data.cardList, ...newList];
       allList = allList.map(item => ({
         ...item,
-        img: statusImgMap[item.status]
+        img: '/static/image/register/yjs.png'
       }));
 
       this.setData({
@@ -117,13 +112,15 @@ Page({
       return;
     }
     try {
-      const res = await uploadImage({filePath:path,marathonId:this.data.activeItem.mv_id,competitionNo:this.data.activeItem.competition_no},'/api/upload/finish-certificate','file');
+      const res = await uploadImage(path);
       console.log('上传成功:', res);
       this.setData({
         filePath: res.url,
         fileType: type,
-        "formData.competition_image": res.path
+        competition_image: res.path,//后端需要的参数
       });
+      // 请求保存接口
+      this.getfinishCertificate();
     } catch (err) {
       console.error('上传失败:', err);
     }
@@ -133,9 +130,25 @@ Page({
     this.setData({
       filePath: '',
       fileType: '',
-      "formData.competition_image": ''
+      competition_image: ''
     });
   },
+  // 保存赛事证明
+  async getfinishCertificate(){
+    const res = await finishCertificate({
+      marathon_id:this.data.activeItem.mv_id,
+      competition_no:this.data.activeItem.competition_no,
+      file:this.data.competition_image
+    })
+    if(res.code == 200){
+      this.loadData(true);
+    }else{
+      wx.showToast({
+        title: res.message || '提交失败',
+        icon: 'none'
+      });
+    }
+  },
   //预览图片
   previewImage(e){
     const current = e.currentTarget.dataset.src;

+ 5 - 2
pages/competitionRecords/competitionRecords.wxml

@@ -14,7 +14,7 @@
       <view class="card-box {{ index === 0 ? 'first-card' : '' }}" wx:for="{{cardList}}" wx:key="index">
         <view class="card-top">
           <view class="card-title-box">
-            <view class="card-title">{{item.marathon_event.doings_name}}</view>
+            <view class="card-title">{{item.marathon_name}}</view>
             <view class="card-time">{{item.register_time}}</view>
           </view>
           <image class="card-img" src="{{item.img}}" mode="aspectFit"/>
@@ -32,7 +32,10 @@
               <image class="cell-img" src="/static/image/register/zm.png" mode="aspectFit"/>
               <view class="cell-bg"></view>
             </view>
-            <view class="uploader">
+            <view class="uploader" wx:if="{{item.competition_image}}">
+              <image src="{{item.competition_image}}" class="preview-img" mode="aspectFill" data-src="{{item.competition_image}}" bindtap="previewImage"/>
+            </view>
+            <view class="uploader" wx:if="{{!item.competition_image}}">
               <!-- 已选择文件 -->
               <block wx:if="{{filePath}}">
                 <block wx:if="{{fileType === 'image'}}">

+ 2 - 2
pages/index/index - 副本.wxml

@@ -36,7 +36,7 @@
           </view>
           <!-- 日历 -->
           <block wx:if="{{start_time && end_time}}">
-            <view class="countdown">倒计时开跑时间</view>
+            <view class="countdown">距离活动开跑剩余</view>
             <countdown end-time="{{start_time}}" />
           </block>
         </view>
@@ -78,7 +78,7 @@
   </van-popup>
   <view class="poster-container" wx:if="{{showPoster}}" bindtap="closePoster">
     <view class="poster-content">
-      <canvas canvas-id="posterCanvas" style="width: 300px; height: 600px;"></canvas>
+      <canvas canvas-id="posterCanvas" style="width: 600rpx; height: 1200rpx;"></canvas>
       <!-- <image src="data:image/png;base64,{{posterImg}}" mode="aspectFit" class="poster-img" /> -->
       <view class="poster-actions">
         <button class="poster-lf" bindtap="savePoster">保存到相册</button>

+ 112 - 37
pages/index/index.js

@@ -99,25 +99,115 @@ Page({
     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();
+  async openPoster() {
+    const userInfo = app.globalData.userInfo;
+    const programConfig = app.globalData.programConfig;
+    console.log(userInfo)
+    const posterKey = `posterImg_${userInfo.avatar}_${userInfo.nickname}_${programConfig.share_img}_${programConfig.share_qrcode}`;
+    const cachedData = wx.getStorageSync('posterCache') || {};
+    if (cachedData.key === posterKey && cachedData.path) {
+      this.setData({ posterImg: cachedData.path, showPoster: true });
+    } else {
+      this.setData({ showPoster: true }, async () => {
+        const path = await this.drawPoster();
+        wx.setStorageSync('posterCache', { key: posterKey, path });
+      });
+    }
+  },
+
+  async drawPoster() {
+    const ctx = wx.createCanvasContext('posterCanvas', this);
+    const canvasWidth = 300;
+    const canvasHeight = 600;
+    const bottomHeight = 80;
+    const radius = 16; // 圆角半径
+  
+    ctx.save();
+    ctx.setFillStyle('#fff');
+    this.drawRoundRect(ctx, 0, 0, canvasWidth, canvasHeight, radius);
+  
+    // 背景图
+    const bgUrl = app.globalData.programConfig.share_img;
+    if (bgUrl) {
+      const bgPath = bgUrl.startsWith('http') ? await this.downloadImage(bgUrl) : bgUrl;
+      ctx.drawImage(bgPath, 0, 0, canvasWidth, canvasHeight - bottomHeight);
+    }
+  
+    // 底部白色区域
+    ctx.setFillStyle('#fff');
+    ctx.fillRect(0, canvasHeight - bottomHeight, canvasWidth, bottomHeight);
+  
+    // 用户头像(圆形)
+    const avatarUrl = app.globalData.userInfo.avatar;
+    if (avatarUrl) {
+      const avatarPath = avatarUrl.startsWith('http') ? await this.downloadImage(avatarUrl) : avatarUrl;
+      const avatarSize = 40;
+      const avatarX = 20;
+      const avatarY = canvasHeight - bottomHeight + (bottomHeight - avatarSize) / 2;
+      ctx.save();
+      ctx.beginPath();
+      ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2);
+      ctx.clip();
+      ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize);
+      ctx.restore();
+    }
+  
+    // 昵称
+    const nickName = app.globalData.userInfo.nickname || '游客';
+    ctx.setFontSize(16);
+    ctx.setFillStyle('#000');
+    const textX = 20 + 40 + 12;
+    const textY = canvasHeight - bottomHeight + bottomHeight / 2 + 6;
+    ctx.fillText(nickName, textX, textY);
+  
+    // 二维码
+    const qrUrl = app.globalData.programConfig.share_qrcode;
+    if (qrUrl) {
+      const qrPath = qrUrl.startsWith('http') ? await this.downloadImage(qrUrl) : qrUrl;
+      const qrSize = 50;
+      const qrX = canvasWidth - qrSize - 20;
+      const qrY = canvasHeight - bottomHeight + (bottomHeight - qrSize) / 2;
+      ctx.drawImage(qrPath, qrX, qrY, qrSize, qrSize);
     }
+    ctx.restore(); // 恢复裁剪
+    return new Promise((resolve, reject) => {
+      ctx.draw(true, () => {
+        wx.canvasToTempFilePath({
+          canvasId: 'posterCanvas',
+          success: res => resolve(res.tempFilePath),
+          fail: err => {
+            console.error('生成临时文件失败:', err);
+            reject(err);
+          }
+        }, this);
+      });
+    });
+  },
+  
+  // 下载网络图片到临时路径
+  downloadImage(url) {
+    return new Promise((resolve, reject) => {
+      wx.downloadFile({
+        url,
+        success(res) {
+          if (res.statusCode === 200) resolve(res.tempFilePath);
+          else reject(new Error('下载失败'));
+        },
+        fail(err) { reject(err); }
+      });
+    });
+  },
+  // 绘制圆角矩形函数
+  drawRoundRect(ctx, x, y, w, h, r) {
+    ctx.beginPath();
+    ctx.moveTo(x + r, y);
+    ctx.arcTo(x + w, y, x + w, y + h, r);
+    ctx.arcTo(x + w, y + h, x, y + h, r);
+    ctx.arcTo(x, y + h, x, y, r);
+    ctx.arcTo(x, y, x + w, y, r);
+    ctx.closePath();
+    ctx.fill();
+    ctx.clip(); // 关键:裁剪出圆角
   },
   closePoster(){
     this.setData({ showPoster: false});
@@ -128,16 +218,8 @@ Page({
       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,
+      filePath: this.data.posterImg,
       success() { wx.showToast({ title: '保存成功' }); },
       fail(err) {
         if (err.errMsg.includes('auth')) wx.openSetting();
@@ -146,18 +228,11 @@ Page({
   },  
   // 发送朋友
   sendImg(){
-    REPORT_BEHAVIOR('分享');
-    // 将 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');
+    if (!this.data.posterImg) return;
     wx.showShareImageMenu({
-      path: filePath,
+      path: this.data.posterImg,
       success() {},
       fail: console.error,
-    })
+    });
   }
 })

+ 3 - 1
pages/index/index.wxml

@@ -81,7 +81,9 @@
   </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" />
+      <image wx:if="{{posterImg}}" src="{{posterImg}}" mode="aspectFit" class="poster-img" />
+      <canvas wx:if="{{!posterImg}}" canvas-id="posterCanvas" width="300" height="600" style="height:600px;"></canvas>
+
       <view class="poster-actions">
         <button class="poster-lf" bindtap="savePoster">保存到相册</button>
         <button class="poster-ri" bindtap="sendImg" data-type="poster">发送朋友</button>

+ 7 - 5
pages/index/index.wxss

@@ -170,11 +170,13 @@
   background-color: rgba(0,0,0,0.7);
 }
 .poster-content{
-  position:absolute;
+  position: absolute;
   top: 10%;
-  height: 60%;
-  left: 80rpx;
-  right: 80rpx;
+  left: 0;
+  right: 0;
+  width: 300px;
+  height: 600px;
+  margin: 0 auto;
 }
 .poster-img{
   width: 100%;
@@ -185,7 +187,7 @@
   display: flex;
   align-items: center;
   gap: 70rpx;
-  margin-top: 32rpx;
+  margin-top: 55rpx;
 }
 .poster-lf,.poster-ri{
   width: 50% !important;

+ 10 - 10
pages/mine/mine.js

@@ -7,8 +7,8 @@ const app = getApp();
 Page({
   data: {
     menuList: [
-      { icon: '/static/image/me-1.png', title: '活动规则' },
-      { icon: '/static/image/me-2.png', title: '免责声明' },
+      // { icon: '/static/image/me-1.png', title: '活动规则' },
+      { icon: '/static/image/me-2.png', title: '活动须知' },
       { icon: '/static/image/me-3.png', title: '报名记录' },
       { icon: '/static/image/me-4.png', title: '优惠券' },
       { icon: '/static/image/me-5.png', title: '设置' },
@@ -39,12 +39,12 @@ Page({
   goPage(e){
     const index = e.currentTarget.dataset.index;
     const pageMap = {
-      0: `/pages/rules/rules?type=${index}`,
-      1: `/pages/rules/rules?type=${index}`,
-      2: `/pages/registrationRecords/registrationRecords`,
-      3: `/pages/coupon/coupon`,
-      4: `/pages/setting/setting`,
-      5:`/pages/competitionRecords/competitionRecords`,
+      0: `/pages/rules/rules?type=1`,
+      // 1: `/pages/rules/rules?type=${index}`,
+      1: `/pages/registrationRecords/registrationRecords`,
+      2: `/pages/coupon/coupon`,
+      3: `/pages/setting/setting`,
+      4:`/pages/competitionRecords/competitionRecords`,
     };
     if (pageMap[index]) {
       wx.navigateTo({ url: pageMap[index] });
@@ -59,7 +59,7 @@ Page({
   // 头像选择
   async onChooseAvatar(e) {
     const { avatarUrl } = e.detail;
-    const res = await uploadImage({filePath:avatarUrl});
+    const res = await uploadImage(avatarUrl);
     this.setData({ "userInfo.avatar": avatarUrl });
     this.onSave(res.path);
   },
@@ -82,7 +82,7 @@ Page({
 
   handleActionWithLogin(e) {
     const index = e.currentTarget.dataset.index;
-    if (index >= 2 && index <= 5 && !isLoggedIn()) {
+    if (index >= 1 && index <= 4 && !isLoggedIn()) {
       return;
     }
     this.doAction(index);

+ 1 - 1
pages/mine/mine.wxml

@@ -27,7 +27,7 @@
 
             <!-- 未登录覆盖授权按钮 -->
             <button 
-              wx:if="{{!loggedIn && index >= 2 && index <= 5}}" 
+              wx:if="{{!loggedIn && index >= 1 && index <= 4}}" 
               class="cover-btn" 
               open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber"
               data-index="{{index}}"></button>

+ 5 - 5
pages/register/register.js

@@ -99,13 +99,13 @@ Page({
       return;
     }
     try {
-      const res = await uploadImage({filePath:path});
+      const res = await uploadImage(path);
       console.log('上传成功:', res);
       this.setData({
         filePath: res.url,
         fileType: type,
         "formData.competition_image": res.path
-      });
+      }, () => this.checkFormValid());
     } catch (err) {
       console.error('上传失败:', err);
     }
@@ -116,7 +116,7 @@ Page({
       filePath: '',
       fileType: '',
       "formData.competition_image": ''
-    });
+    },() => this.checkFormValid());
   },
   // 输入框数据绑定
   onInput(e) {
@@ -153,8 +153,8 @@ Page({
 
   // 校验表单是否可提交
   checkFormValid() {
-    const { phone, captcha, nickname, gender, race_number } = this.data.formData;
-    const valid = phone && captcha && nickname && gender && race_number && this.data.checked && !this.data.raceNumberError && !this.data.phoneError;
+    const { phone, captcha, nickname, gender, race_number,competition_image } = this.data.formData;
+    const valid = phone && captcha && nickname && gender && race_number && this.data.checked && !this.data.raceNumberError && !this.data.phoneError && competition_image;
     this.setData({ canSubmit: valid });
   },
   // 获取验证码

+ 3 - 2
pages/setting/setting.js

@@ -6,7 +6,7 @@ Page({
     userInfo:{},
     pathUrl:'',//保存接口需要传的值
   },
-  onLoad(options) {
+  onReady() {
     this.setData({
       userInfo: app.globalData.userInfo
     });
@@ -21,7 +21,7 @@ Page({
   async onChooseAvatar(e) {
     const { avatarUrl } = e.detail;
     // 上传到服务器
-    const res = await uploadImage({filePath:avatarUrl});
+    const res = await uploadImage(avatarUrl);
     this.setData({
       "userInfo.avatar":avatarUrl,
       pathUrl: res.path
@@ -52,6 +52,7 @@ Page({
       });
       wx.removeStorageSync('token');
       wx.removeStorageSync('expires_in');
+      wx.removeStorageSync('posterCache');
       app.globalData.userInfo = null;
       app.globalData.programConfig = null;
       wx.navigateBack({ delta: 1 });

+ 14 - 19
utils/upload.js

@@ -1,20 +1,17 @@
-import { BASE_URL } from './request';
-
+import {BASE_URL} from './request';
 /**
- * 上传报名证明图片
- * @param {Object} params 参数对象
- * @param {string} params.filePath 本地文件路径
- * @param {number} params.marathonId 比赛ID
- * @param {string} params.competitionNo 参赛号
- * @param {string} [url='/api/upload/image'] 上传接口路径
- * @param {string} [name='file'] 后端接收文件字段名
+ * 上传图片
+ * @param {string} filePath 本地文件路径
+ * @param {string} url 上传接口路径(默认 /api/upload/image)
+ * @param {string} name 后端接收文件字段名(默认 file)
  */
-export function uploadImage(
-  { filePath, marathonId = '', competitionNo = '' },
-  url = '/api/upload/image',
-  name = 'file'
-) {
+export function uploadImage(filePath, url = '/api/upload/image', name = 'file') {
   return new Promise((resolve, reject) => {
+    // wx.showLoading({
+    //   title: '上传中...',
+    //   mask: true
+    // });
+
     wx.uploadFile({
       url: BASE_URL + url,
       filePath,
@@ -22,14 +19,11 @@ export function uploadImage(
       header: {
         'Authorization': wx.getStorageSync('token') || ''
       },
-      formData: {
-        marathon_id: marathonId,
-        competition_no: competitionNo,
-        file:filePath
-      },
       success(res) {
+        // wx.hideLoading();
         try {
           const data = JSON.parse(res.data);
+          console.log(data)
           if (data.code === 200 && data.ret) {
             wx.showToast({
               title: '上传成功',
@@ -48,6 +42,7 @@ export function uploadImage(
         }
       },
       fail(err) {
+        // wx.hideLoading();
         wx.showToast({
           title: '网络异常,请重试',
           icon: 'none'