一、简介

做个简单的生成二维码海报分享,我做的时候也找简单的方法看能不能实现页面直接截图那种生成图片,原生小程序不支持,不多介绍下面有全部代码有注释、参数自行替换运行看看,还有需要优化的地方,有问题可以咨询我,我写的已经上线

如图:

微信小程序canvas type=2d生成海报保存到相册、文字换行溢出显示...、文字删除线、分享面板-LMLPHP微信小程序canvas type=2d生成海报保存到相册、文字换行溢出显示...、文字删除线、分享面板-LMLPHP微信小程序canvas type=2d生成海报保存到相册、文字换行溢出显示...、文字删除线、分享面板-LMLPHP微信小程序canvas type=2d生成海报保存到相册、文字换行溢出显示...、文字删除线、分享面板-LMLPHP

二、 js:

// 产品详情
import {
  getProductDetails,
  getDataList,
  getShareData,
  getUnlimitedQRCode
} from "../../../../../api/dsxapi";
const ui = require("../../../../../utils/ui");

Page({
  data: {
    id: null, //跳转传过来的产品id
    datas: null, //详情数据
    images: null, //轮播图
    current: 0,
    proTags: null, //产品标签
    images2: null, //详情图片
    routeId: null, //哪里打开的
    show: false, //遮罩层
    showShare: false, //分享面板
    shareImg: false, //控制分享图标
    options: [{
        name: '微信',
        icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
        openType: 'share'
      },
      {
        name: '生成分享图',
        icon: 'https://wx.applet.style.51dsx.cn/img/share_button_fxt.png'
      },
    ],
    posterDatas: {
      width: 281, //画布宽度
      height: 460, //画布高度
      // 缓冲区,无需手动设定
      pic: null,
      buttonType: 1,
      show: false, // 显示隐藏跳转设置
      success: false, // 是否成功生成过海报
      canvas: null, // 画布的节点
      ctx: null, // 画布的上下文
      dpr: 1, // 设备的像素比
    },
    QRcodeImg: null, //小程序二维码
    photoWidth: null //产品图片宽度
  },

  onLoad(options) {
    this.getlength()
    this.setData({
      id: options.id
    })
    if (options.routeId) {
      this.setData({
        routeId: options.routeId
      })
    }
    if (options.scene) { //扫二维码进来的
      this.setData({
        id: decodeURIComponent(options.scene).split('id=')[1].split('&')[0],
        routeId: decodeURIComponent(options.scene).split('routeId=')[1]
      })
    }
    let that = this;
    //生成海报初始化
    let posterDatas = that.data.posterDatas
    const query = wx.createSelectorQuery()
    query.select('#firstCanvas').fields({
        node: true,
        size: true
      },
      function (res) {
        const canvas = res.node
        const ctx = canvas.getContext('2d')
        const dpr = wx.getSystemInfoSync().pixelRatio
        canvas.width = posterDatas.width * dpr
        canvas.height = posterDatas.height * dpr
        ctx.scale(dpr, dpr)
        posterDatas.canvas = canvas
        posterDatas.ctx = ctx
        posterDatas.dpr = dpr
        //存储
        that.setData({
          posterDatas
        })
      }).exec()
    this._getProductDetails(this.data.id)
  },
  onShow() {
    // 判断分享打开并且没登录的跳转登录
    if (this.data.routeId) {
      if (wx.getStorageSync("appuser") == undefined || wx.getStorageSync("appuser") == '') {
        wx.navigateTo({
          url: "/subPackag/pages/me_jump/login/login",
        });
      }
    }
    this.getlength()
    this._getProductDetails(this.data.id)
    this.setData({
      show: false
    })
  },

  //自定义导航栏计算
  getlength() {
    let windowWidth = wx.getSystemInfoSync().windowWidth;
    const statusBarHeight = wx.getSystemInfoSync().statusBarHeight;
    const menuButton = wx.getMenuButtonBoundingClientRect();
    let navHeight = (menuButton.height + (menuButton.top - statusBarHeight) * 2) * (750 / windowWidth);
    let statusBarTop = statusBarHeight * (750 / windowWidth);
    this.setData({
      navHeight: navHeight + statusBarTop,
      statusBarTop: menuButton.top,
    })
  },
  //自定义返回按钮
  onBack() {
    if (this.data.routeId && this.data.routeId == 5 || this.data.routeId == 797 || this.data.routeId == 6) { //5好友里打开797朋友圈打开1未登录点击登录后大师兄页面返回
      // wx.navigateTo({
      //   url: `/subPackag/pages/consult_jump/EnterprisePiece/market/market`,
      // });
      // 判断从分享和扫码打开点击为关闭小程序
      wx.exitMiniProgram({
        success: function () {},
        fail: function () {}
      })
    } else {
      wx.navigateBack();
    }
  },

  //跳转企业/园区详情
  goEnterprise(event) {
    if (this.data.datas.type == 2) {
      //园区
      wx.navigateTo({
        url: `/subPackag/pages/consult_jump/EnterprisePiece/park/park?id=${event.currentTarget.dataset.id}`,
      });
    } else {
      //企业
      wx.navigateTo({
        url: `/subPackag/pages/consult_jump/EnterprisePiece/enterprise/enterprise?id=${event.currentTarget.dataset.id}`,
      });
    }
  },
  //跳转案例详情
  goCase(event) {
    let params = {
      type: 4,
      id: event.currentTarget.dataset.id,
    };
    getShareData(params)
      .then((res) => {
        wx.navigateTo({
          url: `/sDsxPackag/pages/webview/webview?url=${res.data.shareUrl}&title=${res.data.shareTitle}`
        })
      })
  },

  //跳转大师兄详情
  goDSXdetails(event) {
    getDataList({
      brotherName: event.currentTarget.dataset.item.realName,
      current: 1,
      size: 10
    }).then((res) => {
      console.log(1111111111111, res);
      const nowdata = {
        item: res.data.records[0],
      };
      console.log(111, nowdata);
      var queryBean = JSON.stringify(nowdata);
      if (wx.getStorageSync('queryBean') !== undefined) {
        let qb = wx.getStorageSync("queryBean");
        qb = queryBean;
        wx.setStorageSync("queryBean", qb);
      } else {
        wx.setStorageSync("queryBean", queryBean);
      }
      wx.navigateTo({
        url: `/sDsxPackag/pages/dsx/dsxcard/dsxcard?queryBean=${encodeURIComponent(
            queryBean
          )}`,
      });
    });
  },

  //产品详情
  _getProductDetails(id) {
    let params = {
      id: id
    };
    getProductDetails(params).then((res) => {
      this.setData({
        datas: res.data,
        images: res.data.proImgs.split(","),
        proTags: res.data.proTags.split(","),
        images2: res.data.proDetailsImgs.split(","),
        shareImg: true
      });
    }).catch(function (imError) {
      console.log(imError);
    })
  },

  //客服电话
  servicePhone() {
    if (this.data.datas.phone) {
      wx.makePhoneCall({
        phoneNumber: this.data.datas.phone
      })
    } else {
      wx.makePhoneCall({
        phoneNumber: '4001512051'
      })
    }
  },

  //轮播图预览
  tapAvatar(event) {
    console.log(event);
    wx.previewImage({
      current: event.currentTarget.dataset.item,
      urls: this.data.images,
    })
    console.log(event);
  },

  //轮播数字指示
  swiperChange(e) {
    var that = this;
    if (e.detail.source == 'touch') {
      that.setData({
        current: e.detail.current
      })
    }
  },
  //获取二维码
  //产品详情
  _getUnlimitedQRCode() {
    let params = {
      id: this.data.id,
      type: 1,
      envVersion: 'release' //正式版为 "release",体验版为 "trial",开发版为 "develop
    };
    getUnlimitedQRCode(params).then((res) => {
      const base64 = res.data.qrcode;
      const time = new Date().getTime();
      //USER_DATA_PATH:文件系统中的用户目录路径 (本地路径)
      const imgPath = wx.env.USER_DATA_PATH + "/poster" + time + "" + ".png";
      const imageData = base64.replace(/^data:image\/\w+;base64,/, "");
      const file = wx.getFileSystemManager();
      file.writeFileSync(imgPath, imageData, "base64");
      console.log(imgPath);
      this.setData({
        QRcodeImg: imgPath,
      });
    }).catch(function (imError) {
      console.log(imError);
    })
  },

  //引导打开相册权限取消按钮
  onClickHide1() {
    let posterDatas = this.data.posterDatas;
    posterDatas["buttonType"] = 1;
    this.setData({
      show: false,
      showShare: false,
      posterDatas,
      options: [{
          name: '微信',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
          openType: 'share'
        },
        {
          name: '生成分享图',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_fxt.png'
        },
      ],
    });
  },

  //分享按钮
  onClick(event) {
    if (!this.data.QRcodeImg) {
      //获取二维码
      this._getUnlimitedQRCode()
    }

    //获取产品图片宽度
    wx.getImageInfo({
      src: this.data.datas.proCover,
      success: res => {
        this.setData({
          photoWidth: res.width
        })
      }
    })
    this.setData({
      showShare: true,
      options2: [{
          name: '微信',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
          openType: 'share'
        },
        {
          name: '生成分享图',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_fxt.png'
        },
      ],
    });
  },

  //隐藏分享面板
  onClose() {
    this.onIsCanvas()
    this.data.posterDatas["buttonType"] = 1;
    this.setData({
      showShare: false,
      options: [{
          name: '微信',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
          openType: 'share'
        },
        {
          name: '生成分享图',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_fxt.png'
        },
      ],
    });
  },

  //分享面板里面的点击事件
  onSelect(event) {
    console.log(event);
    if (event.detail.index == 0) {
      this.setData({
        showShare: false
      })
      this.onIsCanvas()
    } else if (event.detail.index == 1) {
      if (this.data.posterDatas.buttonType == 1) {
        if (this.data.QRcodeImg) {
          this.data.posterDatas["buttonType"] = 2;
          this.onBuildPosterSaveAlbum()
          this.setData({
            options: [{
                name: '微信',
                icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
                openType: 'share'
              },
              {
                name: '保存到相册',
                icon: 'https://wx.applet.style.51dsx.cn/img/share_button_down.png'
              },
            ]
          })
        }
      } else if (this.data.posterDatas.buttonType == 2) {
        this.onDownloadImges()
        this.setData({
          options: [{
              name: '微信',
              icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
              openType: 'share'
            },
            {
              name: '保存到相册',
              icon: 'https://wx.applet.style.51dsx.cn/img/share_button_down.png'
            },
          ]
        });
      } else if (this.data.posterDatas.buttonType == 3) {
        let posterDatas = this.data.posterDatas;
        posterDatas["show"] = false;
        this.setData({
          posterDatas,
          show: true
        })
      }
    }
  },
  //海报生成
  onBuildPosterSaveAlbum() {
    let that = this;
    let posterDatas = that.data.posterDatas
    let canvas = posterDatas.canvas
    let ctx = posterDatas.ctx
    //已生成过海报的直接显示弹窗
    if (posterDatas.success) {
      posterDatas["show"] = true;
      that.setData({
        posterDatas
      })
      return;
    }
    posterDatas.show = true;
    that.setData({
      posterDatas
    })
    wx.showLoading({
      title: '海报生成中',
      mask: true
    });
    //二维码
    let promise1 = new Promise(function (resolve, reject) {
      const photo = canvas.createImage();
      photo.src = that.data.QRcodeImg;
      photo.onload = (e) => {
        resolve(photo);
      }
    });
    // 背景图
    let promise2 = new Promise(function (resolve, reject) {
      const photo = canvas.createImage();
      photo.src = "https://wx.applet.style.51dsx.cn/img/share_ig_bg.png";
      photo.onload = (e) => {
        resolve(photo);
      }
    });
    // 产品图
    let promise3 = new Promise(function (resolve, reject) {
      const photo = canvas.createImage();
      photo.src = that.data.datas.proCover;
      photo.onload = (e) => {
        resolve(photo);
      }
    });
    //获取图片信息
    Promise.all(
      [promise1, promise2, promise3]
    ).then(res => {
      //背景图
      ctx.drawImage(res[1], 0, 0, posterDatas.width, posterDatas.height);
      // 产品图
      // ctx.drawImage(res[2], 18, 18, posterDatas.width-36, 245);
      ctx.drawImage(res[2], 0, 0, that.data.photoWidth, that.data.photoWidth, 18, 18, posterDatas.width - 36, posterDatas.width - 36);
      //二维码
      ctx.drawImage(res[0], posterDatas.width - 82, posterDatas.height - 82, 64, 64);
      if (that.data.datas.payType == 2) {
        //面议
        ctx.font = "bold 20px sans-serif";
        ctx.fillStyle = "#EF3822";
        ctx.fillText('面议', 18, 290);
        ctx.fill();
      } else {
        //现价
        ctx.font = "14px"; //字体大小
        ctx.fillStyle = "#EF3822"; //字体颜色
        ctx.fillText('¥', 18, 290);
        ctx.font = "bold 20px sans-serif";
        ctx.fillStyle = "#EF3822";
        const proPrice = ctx.measureText(that.data.datas.proPrice)
        ctx.fillText(that.data.datas.proPrice, 26, 290);
        ctx.fill();
        // 原价
        let text = '¥' + that.data.datas.proOriginalPrice
        ctx.font = "10px sans-serif";
        ctx.fillStyle = "#9A9A9A";
        ctx.fillText(text, proPrice.width + 32, 290);
        ctx.fillStyle = '#9A9A9A';
        ctx.beginPath();
        const textWidth = ctx.measureText(text).width;
        ctx.rect(proPrice.width + 32, 286, textWidth, 1);
        ctx.fill();
      }
      //地区
      ctx.font = "10px sans-serif";
      ctx.fillStyle = "#9A9A9A";
      //画布宽度减去文字长度
      ctx.fillText(that.data.datas.serviceAreaText, posterDatas.width - ctx.measureText(that.data.datas.serviceAreaText).width - 18, 290);
      ctx.fill();
      //标题
      ctx.fillStyle = "#333333";
      ctx.font = "bold 14px sans-serif";
      // ctx.fillText('专精特新企业股权融资方案设计', 18, 310);
      that.toFormateStr(ctx, that.data.datas.proName, 245, 1, 18, 312, 16, 1) // 绘制文字并换行
      ctx.fill();
      //板块
      ctx.font = "11px sans-serif";
      ctx.fillStyle = "#646464";
      ctx.fillText(that.data.datas.proDesc, 18, 332);
      ctx.fill();
      //机构信息
      ctx.font = "11px";
      ctx.fillStyle = "#9D9D9D";
      ctx.fillText('机构信息:' + that.data.datas.institutionName, 18, 352);
      ctx.fill();
      //线条
      ctx.save();
      ctx.rect(18, 390, 140, 0.5);
      ctx.strokeStyle = "#9A9A9A"
      ctx.fill();

      ctx.font = "10px";
      ctx.fillStyle = "#9D9D9D";
      ctx.fillText('实际价格以扫码页面展示为准', 18, 410);
      ctx.fill();
      ctx.font = "10px";
      ctx.fillStyle = "#9D9D9D";
      ctx.fillText('长按识别查看、联系', 18, 426);
      ctx.fill();
      // 关闭loading
      wx.hideLoading();
      //显示海报
      posterDatas.success = true;
      that.setData({
        posterDatas
      })
    }).catch(err => {
      console.log(err)
      wx.hideLoading();
      wx.showToast({
        icon: 'none',
        title: '海报生成失败,请稍后再试.',
      })
    })
  },

  // 文字换行
  toFormateStr(ctx, str, draw_width, lineNum, startX, startY, steps, number) {
    //ctx:canvas的 2d 对象,str:绘制的文字,startX,startY:文字坐标,draw_width:文字最大宽度,lineNum:需要的行数,steps:行高,number:减少最后几个字变成...
    let strWidth = ctx.measureText(str).width; // 测量文本源尺寸信息(宽度)
    let startpoint = startY,
      keyStr = '',
      sreLN = strWidth / draw_width; // 文本长度除以换行的宽 得到一共生成多少行
    let liner = Math.ceil(sreLN); // 计算文本源一共能生成多少行
    let strlen = parseInt(str.length / sreLN); // 等比缩放测量一行文本显示多少个字符
    // 若文本不足一行,则直接绘制,反之大于传入的最多行数(lineNum)以省略号(...)代替
    if (strWidth < draw_width) {
      ctx.fillText(str, startX, startpoint);
    } else {
      for (let i = 1; i < liner + 1; i++) {
        let startPoint = strlen * (i - 1);
        if (i < lineNum || lineNum == -1) {
          keyStr = str.substr(startPoint, strlen);
          ctx.fillText(keyStr, startX, startpoint);
        } else {
          keyStr = str.substr(startPoint, strlen - number) + '...';
          ctx.fillText(keyStr, startX, startpoint);
          break;
        }
        startpoint = startpoint + steps;
      }
    }
  },

  //画布转图片
  onCanvasBuildImges() {
    let that = this;
    let posterDatas = that.data.posterDatas;
    wx.canvasToTempFilePath({
      canvas: posterDatas.canvas,
      width: posterDatas.width,
      height: posterDatas.height,
      // destWidth: posterDatas.width * 3,
      // destHeight: posterDatas.height * 3,
      quality: 1,
      success: function success(res) {
        posterDatas["pic"] = res.tempFilePath;
        that.setData({
          posterDatas
        })
        that.onDownloadImges();
      },
      fail: function complete(e) {
        wx.hideLoading();
        wx.showToast({
          icon: 'none',
          title: 'sorry 保存失败,请稍后再试.',
        })
        return;
      }
    });
  },

  //下载图片
  onDownloadImges() {
    wx.showLoading({
      title: '保存中',
      mask: true
    });
    let that = this;
    let posterDatas = that.data.posterDatas;
    if (!posterDatas.pic) {
      that.onCanvasBuildImges();
      return;
    }
    wx.saveImageToPhotosAlbum({
      filePath: posterDatas.pic,
      success(res) {
        wx.hideLoading();
        wx.showToast({
          icon: 'none',
          title: '已保存到相册',
        })
        that.onIsCanvas()
        posterDatas["buttonType"] = 1;
        that.setData({
          showShare: false,
          posterDatas
        })
      },
      fail: function (res) {
        wx.hideLoading();
        wx.showToast({
          icon: 'none',
          title: '已取消',
        })
        posterDatas["buttonType"] = 3;
        that.setData({
          posterDatas
        })
        return;
      }
    })
  },

  //在打开授权设置页后回调
  onBindOpenSetting() {
    let that = this;
    let posterDatas = that.data.posterDatas;
    posterDatas["buttonType"] = 1;
    that.setData({
      posterDatas
    })
  },

  //隐藏海报
  onIsCanvas() {
    let that = this;
    let posterDatas = that.data.posterDatas;
    posterDatas["buttonType"] = 1;
    posterDatas["show"] = false;
    that.setData({
      posterDatas
    })
  },

  onShareAppMessage: function (res) { //分享给好友
    var that = this
    return {
      title: that.data.datas.proName + '—产品详情',
      path: 'subPackag/pages/consult_jump/EnterprisePiece/product/product?id=' + that.data.datas.id + '&routeId=' + 5,
      imageUrl: '',
      success: function (res) {
        console.log(res);
        wx.showToast({
          title: '分享成功',
          icon: "none"
        });
      },
      fail: function (res) {
        wx.showToast({
          title: '分享失败',
          icon: "none"
        })
      }
    }
  },
  onShareTimeline: function () { //分享朋友圈
    var tha = this
    return {
      title: tha.data.datas.proName + '—产品详情',
      query: 'id=' + tha.data.datas.id + '&routeId=' + 797,
      imageUrl: tha.data.datas.proCover,
      success: function (res) {
        wx.showToast({
          title: '分享成功',
          icon: "none"
        });
      },
      fail: function (res) {
        wx.showToast({
          title: '分享失败',
          icon: "none"
        })
      }
    }
  },
})

 三、 wxml:

<!-- 自定义导航 -->
<view class="nav-back" style="height: {{navHeight}}rpx;">
  <image class="imgbackw" style="margin-top: {{statusBarTop}}px" src="https://wx.applet.style.51dsx.cn/img/icon_navigation_return.png" catchtap="onBack" />
  <view class="nav-name" style="margin-top: {{statusBarTop}}px">产品详情</view>
</view>

<view class="product" wx:if="{{datas}}" style="margin-top: {{navHeight}}rpx;">
  <!-- 轮播 -->
  <view wx:if="{{images[0]!==''}}" class="swiper1" style="overflow: hidden;">
    <swiper bindchange="swiperChange" class="swiper" indicator-active-color="white" current="0" indicator-color="#fff6" display-multiple-items circular="{{true}}">
      <swiper-item wx:for="{{images}}" wx:key="index">
        <image data-item="{{item}}" bindtap="tapAvatar" src="{{item}}" mode="aspectFill" />
      </swiper-item>
    </swiper>
    <view class="imageCount" wx:if="{{images.length>1}}">{{current+1}}/{{images.length}}</view>
  </view>
  <view class="middle">
    <view class="price">
      <view class="price-div">
      <!-- 价钱、地区 -->
        <view style="display: inline-block;">
          {{ datas.payType == 2 ? "" : "¥"
          }}<span class="item1">{{
            datas.payType == 2 ? "面议" : datas.proPrice
            }}</span>
          <span class="item3">{{
            datas.payType == 2 || datas.proOriginalPrice == 0
            ? ""
            : "¥" + datas.proOriginalPrice
            }}</span>
        </view>
        <view class="area">
          {{datas.serviceAreaText}}
        </view>
      </view>
      <view class="info">
        <!-- 标题、板块、标签 -->
        <view class="item1">{{ datas.proName }}</view>
        <view class="item2" wx:if="{{datas.proDesc}}">{{ datas.proDesc }}</view>
        <view wx:if="{{proTags[0]!==''}}">
          <view class="item3" wx:for="{{proTags}}" wx:key="index">
            {{item}}
          </view>
        </view>
      </view>
    </view>
    <view class="institution">
      <view class="title">机构信息</view>
      <!-- 机构信息 -->
      <view class="info2" data-id="{{datas.companyId}}" bindtap="goEnterprise">
        <image src="{{datas.institutionLogo}}" class="img1" mode="aspectFit" />
        <view class="right">
          <view class="item1">
            {{ datas.institutionName }}
          </view>
          <view class="item2">
            {{ datas.institutionAddress }}
          </view>
        </view>
      </view>
      <view wx:if="{{datas.userNames.length > 0}}">
      <!-- 大师兄左右滑动列表 -->
        <van-divider customStyle="margin:0;" />
        <view class="BigMasterBox">
          <view class="BigMasterItem" data-item="{{item}}" bindtap="goDSXdetails" wx:for="{{datas.userNames}}" wx:key="index">
            <image src="{{item.photo}}" class="img" mode="aspectFill"/>
            <view class="right">
              <view class="item1">
                {{ item.realName }}
              </view>
              <view class="{{item.online == 1 ? 'item2' : 'red'}}">
                {{
                item.online == 1
                ? "可咨询"
                : "可预约"
                }}
              </view>
              <view class="item3">
                {{ item.roleName }} |
                {{ item.unitStartTimeText }}
              </view>
            </view>
          </view>
        </view>
        <view class="prompt">
          以上大师兄由产品方所列,由用户自主选择
        </view>
      </view>
    </view>
    <view class="case" wx:if="{{datas.serviceNames.length > 0}}">
      <view class="title">服务案例</view>
      <view class="caseBox">
        <view class="caseItem" data-id="{{item.id}}" bindtap="goCase" wx:for="{{datas.serviceNames}}" wx:key="index">
          <!-- 案例左右滑动列表 -->
          <image src="https://wx.applet.style.51dsx.cn/img/store_icon_fwal.png" class="img" mode='widthFix' />
          <view class="right">
            <view class="item1">
              {{ item.customerName }}
            </view>
            <view class="item2">
              {{ item.sectionText }}
            </view>
            <view class="item2">
              {{ item.serviceTime }}年
            </view>
          </view>
        </view>
      </view>
    </view>
    <view class="details">
      <view class="median"></view>
      <span> 产品详情 </span>
      <view class="median"></view>
    </view>
  </view>
  <!-- 产品介绍长图列 -->
  <view class="introduce">
    {{ datas.proDetails }}
  </view>
  <image wx:if="{{images2[0]!==''}}" class="img3" wx:for="{{images2}}" wx:key="index" src="{{item}}" mode='widthFix' />
  <view class="bottomBox">
    <view class="FixedBottom">
      <view>
        <!-- <view class="item1" bindtap="servicePhone">
          <image class="img" src="https://image-cos.51dsx.cn/images/2023-05-08/store_button_service.png" />
          <view class="text">客服</view>
        </view> -->
        <view class="item2" data-id="{{datas.companyId}}" bindtap="goEnterprise">
          <image class="img" src="https://image-cos.51dsx.cn/images/2023-05-08/store_button_store.png" />
          <view class="text">店铺</view>
        </view>
      </view>
      <view class="item3" bindtap="servicePhone">
        联系机构
      </view>
    </view>
  </view>
</view>
<!-- 海报 -->
<view class="canvasMain" hidden="{{!posterDatas.show}}">
  <canvas type="2d" id="firstCanvas" class="firstCanvas" style="width:{{posterDatas.width}}px;height:{{posterDatas.height}}px;"></canvas>
</view>
<!-- 分享图标 -->
<image wx:if="{{shareImg}}" class="shareImg" bindtap="onClick" src="https://wx.applet.style.51dsx.cn/img/xiangqing_button_share.png" mode="aspectFill" />
<!-- 弹窗去打开相册权限 -->
<van-overlay z-index="999999" show="{{ show }}">
  <view class="wrapper">
    <view class="block" catch:tap="noop">
      <view class="popup-box">
        <view class="telephone">进入设置页,开启“保存到相册”</view>
        <van-divider customStyle="margin:0;" />
        <view class="ncontent">
          <button class='button' bindtap='onClickHide1'>取消</button>
          <button class='button' open-type='openSetting' bindopensetting='onBindOpenSetting'>确定</button>
        </view>
      </view>
    </view>
  </view>
</van-overlay>
<!-- 分享面板 -->
<van-share-sheet show="{{ showShare }}" title="" options="{{ options }}" bind:select="onSelect" bind:close="onClose" />

 四、wxss:

.nav-back {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  text-align: center;
  background: #fff;
  z-index: 9999999;
}

.nav-back .nav-name {
  font-size: 32rpx;
  font-family: PingFang SC-Semibold, PingFang SC;
  font-weight: 600;
  color: #000000;
  line-height: 40rpx;
  width: 100%;
  color: #000;
  height: 60rpx;
  line-height: 60rpx;
}

.imgbackw {
  width: 60rpx;
  height: 60rpx;
  position: absolute;
  left: 0rpx;
  z-index: 100;
}

.imageCount {
  padding: 4rpx 20rpx;
  font-size: 24rpx;
  border-radius: 40rpx;
  background-color: rgba(0, 0, 0, 0.6);
  color:#fff;
  position:absolute;
  right:16rpx;
  bottom:16rpx;
}
.swiper1 {
  width: 100vw;
  height: 100vw;
  transform: translateY(0);
}

/* 广告轮播图 */
.swiper {
  /*再设置个transform的属性*/
  transform: translateY(0);

}

.swiper image,
.swiper {
  width: 100vw;
  height: 100vw;
}

.swiper swiper-item {
  position: relative;
}
.product {
  padding-bottom: 164rpx;
  width: 100%;
  height: auto;
}

.product .FixedBottom .item3 {
  /* width: 2.47rpx; */
  /* width: 70%; */
  width: 88%;
  height: 88rpx;
  background: linear-gradient(138deg, #5092f7 0%, #3171e8 100%);
  border-radius: 80rpx;
  line-height: 88rpx;
  font-size: 32rpx;
  font-weight: 500;
  color: #ffffff;
}

.product .FixedBottom .img {
  width: 48rpx;
  height: 48rpx;
}

.product .FixedBottom .text {
  font-size: 24rpx;
  font-weight: 400;
  color: #000000;
  line-height: 36rpx;
}

.product .FixedBottom .item1,
.product .FixedBottom .item2 {
  font-size: 24rpx;
  color: #000000;
  line-height: 36rpx;
}

.product .FixedBottom .item1,
.product .FixedBottom .item2,
.product .FixedBottom .item3 {
  display: inline-block;
  text-align: center;
}

.product .bottomBox {
  width: 100%;
  background: #fff;
  position: fixed;
  left: 50%;
  transform: translate(-50%, 0);
  bottom: 0;
  padding: 20rpx 0rpx 52rpx 0rpx;
  border-top: 1px solid rgb(247, 246, 246);
}

.product .FixedBottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 24rpx 0rpx 40rpx;
  /* margin: 0 auto; */
}

.product .img3 {
  width: 100vw;
}

.product .introduce {
  padding: 20rpx;
  font-size: 28rpx;
  color: #444444;
  line-height: 42rpx;
}

/* 内容 价钱信息、机构、案例 */
.middle .details span {
  margin: 0 20rpx;
}

.middle .details .median {
  display: inline-block;
  width: 96rpx;
  border-bottom: #d8d8d8 2rpx solid;
}

.middle .details {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 40rpx;
}

.middle .case .caseBox .caseItem .right .item2 {
  font-size: 24rpx;
  color: #6f6f6f;
  line-height: 36rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
}

.middle .case .caseBox .caseItem .right .item1 {
  font-size: 28rpx;
  font-weight: 600;
  color: #000000;
  line-height: 40rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
}

.middle .case .caseBox .caseItem .right {
  margin-left: 20rpx;
}

.middle .case .caseBox .caseItem .img {
  width: 112rpx;
  min-width: 112rpx;
  height: 112rpx;
  border-radius: 12rpx;
}

.middle .case .caseBox .caseItem {
  display: flex;
  align-items: center;
  margin-right: 16rpx;
  flex-shrink: 0;
  background: #f6f6f6;
  border-radius: 8rpx;
  height: 160rpx;
  padding: 0rpx 20rpx;
  min-width: 400rpx;
  max-width: 400rpx;
}

.middle .case .caseBox .caseItem:last-child {
  margin-right: 0 !important;
}

.middle .case .caseBox {
  border-radius: 12rpx;
  /* padding: 0.12rpx 0.1rpx; */
  /* 设置超出滚动 */
  overflow-x: auto;
  display: flex;
  justify-content: space-between;
  margin-top: 20rpx;
}

.product .middle .case {
  margin-top: 16rpx;
  padding: 28rpx 20rpx 20rpx 20rpx;
  background: #fff;
  border-radius: 20rpx;
  border: 2rpx solid #ffffff;
}

::-webkit-scrollbar {
  /* 隐藏滚动条 */
  display: none;
}

.BigMasterItem .right {
  display: inline-block;
  font-size: 24rpx;
  color: #000000;
  line-height: 32rpx;
  margin-left: 8rpx;

}

.product .middle .institution .BigMasterBox .BigMasterItem .img {
  width: 60rpx;
  height: 60rpx;
  border-radius: 30rpx;
  object-fit: cover;
}

.product .middle .institution .prompt {
  margin-top: 16rpx;
  font-size: 24rpx;
  color: #9a9a9a;
  line-height: 36rpx;
}

.product .middle .institution .BigMasterBox .BigMasterItem:last-child {
  margin-right: 0 !important;
}

.product .middle .institution .BigMasterBox .BigMasterItem .item1 {
  display: inline-block;
  font-size: 28rpx;
  font-weight: 600;
  color: #444444;
  line-height: 36rpx;
}

.product .middle .institution .BigMasterBox .BigMasterItem .red {
  margin-left: 8rpx;
  display: inline-block;
  padding: 0 8rpx;
  height: 32rpx;
  background: #3a89ff;
  color: #fff;
  font-size: 24rpx;
  line-height: 32rpx;
  border-radius: 8rpx;
}

.product .middle .institution .BigMasterBox .BigMasterItem .item2 {
  margin-left: 8rpx;
  display: inline-block;
  padding: 0 8rpx;
  height: 32rpx;
  background: #4dc741;
  color: #fff;
  font-size: 24rpx;
  line-height: 32rpx;
  border-radius: 8rpx;
}

.product .middle .institution .BigMasterBox .BigMasterItem .item3 {
  margin-top: 4rpx;
  font-size: 24rpx;
  color: #9a9a9a;
  line-height: 28rpx;
}

.BigMasterItem {
  display: flex;
  align-items: center;
  margin-right: 16rpx;
  flex-shrink: 0;
  background: #f3f8ff;
  border-radius: 16rpx;
  padding: 28rpx 24rpx;
}

.BigMasterBox {
  /* 设置超出滚动 */
  overflow-x: auto;
  display: flex;
  justify-content: space-between;
  margin-top: 20rpx;
}

::v-deep .el-divider--horizontal {
  margin: 0;
  background-color: #e5e5e5 !important;
}

.product .middle .institution .info2 {
  margin-top: 24rpx;
  margin-bottom: 24rpx;
  display: flex;
  align-items: center;
}

.product .middle .institution .img1 {
  display: inline-block;
  width: 88rpx;
  min-width: 88rpx;
  max-height: 88rpx;
  border-radius: 12rpx;
  border: 1rpx solid #ededed;
}

.product .middle .institution .info2 .right {
  margin-left: 16rpx;
  display: inline-block;
}

.product .middle .institution .info2 .right .item1 {
  font-size: 32rpx;
  font-weight: 600;
  color: #000000;
  line-height: 40rpx;
}

.product .middle .institution .info2 .right .item2 {
  margin-top: 6rpx;
  font-size: 26rpx;
  color: #9a9a9a;
  line-height: 36rpx;
  min-height: 36rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
}

.product .middle .institution {
  margin-top: 16rpx;
  padding: 28rpx 20rpx 20rpx 20rpx;
  background: linear-gradient(180deg, #e0edff 0%, #ffffff 23%, #ffffff 100%);
  border-radius: 20rpx;
  border: 2rpx solid #ffffff;
}

.product .middle .institution .title,
.product .middle .case .title {
  font-size: 30rpx;
  font-weight: 600;
  color: #000000;
  line-height: 40rpx;
}

.product .middle .info .item1 {
  width: 100%;
  font-size: 32rpx;
  font-weight: 600;
  color: #333333;
  line-height: 48rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.product .middle .info .item2 {
  width: 100%;
  font-size: 26rpx;
  color: #868686;
  line-height: 40rpx;
  margin-top: 8rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  margin-bottom: 8rpx;
}

.product .middle .info .item3 {
  display: inline-block;
  height: 32rpx;
  font-size: 24rpx;
  color: #9a9a9a;
  line-height: 32rpx;
  background: #f7f7f7;
  border-radius: 4rpx;
  padding: 0 12rpx;
  margin-right: 20rpx;
}

.product .middle .info .item3:last-child {
  margin-right: 0 !important;
}

.custom-indicator {
  position: absolute;
  right: 16rpx;
  bottom: 16rpx;
  padding: 4rpx 20rpx;
  font-size: 24rpx;
  border-radius: 40rpx;
  color: #fff;
  background: rgba(0, 0, 0, 0.6);
}

.swipeItem {
  height: 100vw;
  width: 100%;
}

.itemImg {
  height: 100%;
  width: 100%;
  object-fit: cover;
  background-color: #f2f2f2;
}

.product .middle {
  padding: 16rpx 12rpx 24rpx;
  background: #f7f7f7;
}

.product .middle .price {
  background: #fff1f1;
  border-radius: 20rpx;
  padding: 16rpx 0 0;
}

.product .middle .info {
  padding: 28rpx 20rpx 32rpx 20rpx;
  border-radius: 20rpx;
  background: #fff;
}

.middle .price .price-div {
  margin-bottom: 12rpx;
  margin-left: 20rpx;
  color: #ef3822;
  font-size: 28rpx;
  position: relative;
}

.middle .price .price-div .area {
  font-size: 26rpx;
  color: #808080;
  display: inline-block;
  position: absolute;
  right: 20rpx;
  top:50%;
  transform:translate(0,-50%);
  line-height: 80rpx;
}

.middle .price .price-div .item1 {
  color: #ef3822;
  font-size: 44rpx;
  font-weight: 600;
}

.middle .price .price-div .item2 {
  color: #ef3822;
  font-size: 24rpx;
}

.middle .price .price-div .item3 {
  text-decoration: line-through;
  margin-left: 20rpx;
  color: #9a9a9a;
  font-size: 24rpx;
}

/* 生成海报 */
.shareImg{
  width: 100rpx;
  height: 100rpx;
  position: fixed;
  right: 14rpx;
  top: 80%;
  z-index: 99;
}
.popup-box {
  position: fixed;
  top: 50vh;
  left: 50vw;
  transform:translate(-50%,-50%);
  width: 62vw;
  background:#fff;
  border-radius: 16rpx;
  opacity: 1;
  margin: auto;
  text-align: center;
  padding: 34rpx 32rpx 36rpx;
}
.telephone{
  margin-bottom: 20rpx;
}
.canvasMain{
  position: fixed;
  top: 8vh;
  width: 100vw;
  z-index: 999999 !important;
}
.firstCanvas{
  margin: 5vh auto 0;
  z-index: 9999 !important;
}
.ncontent{
  margin-top: 20rpx;
  display: flex;
  justify-content: space-between;
}

.van-share-sheet__cancel {
  font-size: 32rpx !important;
}

.van-share-sheet__name{
  font-size: 26rpx !important;
  color: #333333 !important;
}
.van-share-sheet__header, .van-share-sheet__options{
  background: #F5F5F5;
}
.van-share-sheet__icon {
  width: 128rpx !important;
  height: 128rpx !important;
}
.van-share-sheet__cancel:before{
  background-color: #F5F5F5 !important;
}
08-23 02:13