效果图:

 

wxml:

<view>
        <view class="search_box">
            <view class="selectBox_time">
                <picker class="time-tilte-text" mode="date" fields="day" value="{{date}}" start="2020-01-01" bindchange="getDateTime" bindcancel="cancelDate">
                    <view class="selectDate">
                        <text>{{queryDay}}</text>
                        <text class="selectIcon">﹀</text>
                    </view>
                </picker>
            </view>
            <view class="btn" bindtap="saveExcel"><i-icon type="search" />导出清单</view>
            <view class="btn" style="background: #15B628;color: #fff;" bindtap="saveImg"><i-icon type="add" />保存图片</view>
        </view>
    <canvas id="myCanvas" type="2d" style=" width: 100vw; height: calc(100vh - 80px); background: #fff;" />

</view>
.intro {
    margin: 30px;
    text-align: center;
  }


  .search_box {
    width: 96%;
    margin: 10px 2% 10px 2%;
    height: 40px;
    /* padding-top: 10px; */
    line-height: 40px;
  }
  .selectBox_time {
    width: 44%;
    float: left;
    font-size: 28rpx;
    border: 1rpx solid #eee;
    border-radius: 10px;
    position: relative;
  }
  .selectDate text {
    margin-left: 4px;
  }
  .selectIcon{
    position: absolute;
    right: 10px;
    color: #c0c4cc;
    font-size: 12px;
    top: 6rpx;
  }

  .btn {
    width: 23%;
    background: #eee;
    text-align: center;
    margin-left: 10px;
    float: right;
    font-size: 26rpx;
    color: #000;
    border-radius: 8px;
  }

js:

const app = getApp()
const utils = require('../../common/util.js');
Page({

    /**
     * 页面的初始数据
     */
    data: {
      shareImgSrc:'',
      deviceWidth:'',
      deviceHeight:'',

      tokenInfo:'',
      queryDay:'请选择时间',
      mgridArr: [],
      headers: []
    },
  
    /**
     * 生命周期函数--监听页面加载
     */
    onLoad() {
        let that = this

        var tokenInfo = wx.getStorageSync('tokenInfo')

        let month = utils.formatDate(new Date());
        const data = month.substring(0, 10) 
        this.setData({
          queryDay: data,
          tokenInfo:tokenInfo
        })

        wx.getSystemInfo({
            success(res) {
                that.setData({
                    deviceWidth: res.windowWidth,
                    deviceHeight: res.windowHeight
                })
            }
        })

        this.queryDossierDayDataTable()
          
    },
    // 选择日期
   getDateTime: function(e){
    this.setData({
      queryDay: e.detail.value
    })
    this.queryDossierDayDataTable()
  },
  // 取消时间
  cancelDate: function(){
    this.setData({
      queryDay: '请选择时间'
    })
  },
  // 站点日报
  queryDossierDayDataTable(){
    let queryDay = this.data.queryDay == '请选择时间' ? null : this.data.queryDay
    if(queryDay == '' || queryDay == null ){
      wx.showToast({
        title: '请选择日期',
        icon: 'none',
        duration: 2000
      })
    }else{
      let params = {
        queryDay: this.data.queryDay,
        isMonth: false
      }
      var that = this;
      app.$post.post(`地址`, params, true).then(res => {
        if(res.code == 0){
            if(res.data){
                let headers = res.data.headers
                let mgrid_COUNT =res.data.mgrid_COUNT

                let mgridArr = res.data.mgrid
                let len = mgridArr[0].length - headers.length
                mgridArr.forEach(item => {
                    item.splice(-len)
                })
                let lastArr = mgridArr.concat(mgrid_COUNT)
                that.setData({
                    mgridArr: lastArr,
                    headers: headers
                })
                that.drawCanvas()
            }else{
                wx.showToast({
                    title: "这一天没有检测数据",
                    icon: 'none'
                  })
                  
            }
            


        }else{
          wx.showToast({
            title: res.msg,
            icon: 'none'
          })
        }        
      })    
    }
  },
   //适配方法封装
    /**canvas绘制文字适配 
    * @params coordinate 坐标值或大小
    */
   fitSize: function (coordinate) {
        let that = this;
        let deviceWidth = that.data.deviceWidth;//这个windowWidth指的是该设备宽度,可以onLoad监听页面加载中获取
        let v = 375 / deviceWidth;//375是设计稿的大小,得到的v值是:设计稿和设备宽度的比例关系,也可理解成在设计稿的大小基础上放大或缩小的倍数
        return coordinate / v;//返回的是当前坐标值或者大小与v的比例
    },

  drawCanvas(){
    let that = this
    wx.createSelectorQuery()
    .select('#myCanvas') // 在 WXML 中填入的 id
    .fields({ node: true, size: true })
    .exec((res) => {
        // Canvas 对象
        const header = that.data.headers
        const mgrid = that.data.mgridArr

        const canvas = res[0].node
        // 渲染上下文
        const ctx = canvas.getContext('2d')
        // Canvas 画布的实际绘制宽高
        const width = res[0].width
        const height = res[0].height
        // const height = mgrid.length * 20 +150

        // 初始化画布大小
        const dpr = wx.getWindowInfo().pixelRatio
        canvas.width = width * dpr
        canvas.height = height * dpr
        ctx.scale(dpr, dpr)
        // 清空画布
        ctx.clearRect(0, 0, width, height)        
   
           canvas.width=455*2;
           canvas.height=mgrid.length*20+580;
        //    canvas.style.border="1px solid #ccc";
           var rectH=20; //单元格高度
        //    var rectW=65; //单元格宽度
           var rectW=canvas.width / header.length
           ctx.scale(1,1)
           ctx.lineWidth = 1;
           ctx.strokeStyle = "#ccc";
           ctx.textAlign = "center";
           ctx.fillStyle="#FFFFFF";
           

           //头部
           var title = that.data.tokenInfo.orgName + '  全车影像检测日报'
           ctx.font = `normal normal normal ${parseInt(that.fitSize(16))}px PingFang SC`;
           ctx.fillStyle="#000000";
           ctx.fillText(title,455, 40)
           ctx.font = `normal normal normal ${parseInt(that.fitSize(12))}px PingFang SC`;
           ctx.fillText(that.data.queryDay, 455, 60)

           ctx.font = `normal normal normal ${parseInt(that.fitSize(8))}px PingFang SC`;
           ctx.fillStyle = '#15B628'
           ctx.fillRect(30, 80, 40, 10)
           ctx.fillText('配件检测正常', 105, 88)
           ctx.fillStyle = '#267ef0'
           ctx.fillRect(150, 80, 40, 10)
           ctx.fillText('配件异常已销售', 235, 88)
           ctx.fillStyle = '#ff4d00'
           ctx.fillRect(280, 80, 40, 10)
           ctx.fillText('配件异常未销售', 365, 88)

           
           for(let i = 0; i < header.length; i++){
              ctx.fillStyle = "#000";
              ctx.font = `normal normal normal ${parseInt(that.fitSize(7))}px PingFang SC`;
              ctx.textAlign = "center"; 
              ctx.fillText(header[i], (rectW/2) + (rectW*i), 125);
            }           

           for (let i = 0; i < mgrid.length; i++) {
                
                if( i < mgrid.length-4){
                    for(let j = 0; j < mgrid[0].length; j++){
                        ctx.fillStyle = "#333";
                        ctx.font = `normal normal normal ${parseInt(that.fitSize(7))}px PingFang SC`;
                        ctx.textAlign = "center"; 
                        if(mgrid[i][j] == "-"){
                            ctx.fillText(" ", (rectW/2) + (rectW*j), rectH * i + 145)
                        }else{
                            if(mgrid[i][j] == 0){
                                ctx.fillStyle = '#15B628'
                                ctx.fillRect((rectW/4) + (rectW*j), rectH * i + 135, rectW/2, 10)   
                            }else if(mgrid[i][j] == 1){
                                ctx.fillStyle = '#267ef0'
                                ctx.fillRect((rectW/4) + (rectW*j), rectH * i + 135, rectW/2, 10)   
                            }else  if(mgrid[i][j] == 2){
                                ctx.fillStyle = '#ff4d00'
                                ctx.fillRect((rectW/4) + (rectW*j), rectH * i + 135, rectW/2, 10)  
                            }else{
                                ctx.fillText(mgrid[i][j], (rectW/2) + (rectW*j), rectH * i + 145)
                            }
                            // ctx.fillText(mgrid[i][j], 32 + (65*j), rectH * i + 35)
                            // ctx.drawImage(code, 32 + (65*j), rectH * i + 35, 14, 14)
                        }
                        
                    }
                }else{
                    for(let j = 0; j < mgrid[0].length; j++){
                        ctx.fillStyle = "#000";
                        ctx.font = `normal normal normal ${parseInt(that.fitSize(7))}px PingFang SC`;
                        ctx.textAlign = "center"; 
                        if(mgrid[i][j] == null){
                            ctx.fillText(" ", rectW/2 + (rectW*j), rectH * i + 145);
                        }else{
                            ctx.fillText(mgrid[i][j], rectW/2 + (rectW*j), rectH * i + 145);
                        }
                        
                    }
                }
            }    
            for(var i= 0;i<header.length+2;i++){
                // 画竖线
                ctx.moveTo(rectW*i,110);
                ctx.lineTo(rectW*i, rectH*(mgrid.length + 1)+110);
    
                // 画横线
                // ctx.moveTo(0,rectH*i+90);
                // ctx.lineTo(canvas.width,rectH*i+90);
                ctx.stroke();
            }
           for(var i= 0;i<mgrid.length+2;i++){
               // 画竖线
            //    ctx.moveTo(rectW*i,90);
            //    ctx.lineTo(rectW*i,canvas.height);
   
               // 画横线
               ctx.moveTo(0,rectH*i+110);
               ctx.lineTo(canvas.width,rectH*i+110);
               ctx.stroke();
           }
              // 生成图片
            wx.canvasToTempFilePath({
                canvas,
                success: res => {
                    // 生成的图片临时文件路径
                    const tempFilePath = res.tempFilePath
                    that.setData({
                        //关键 赋值给变量
                        shareImgSrc: res.tempFilePath
                      })
                },
            })            

    })
  },
  
  //保存图片 
  saveImg: function (){
      var that = this;
      wx.saveImageToPhotosAlbum({
        //shareImgSrc为canvas赋值的图片路径
        filePath: that.data.shareImgSrc,
        success(res) {
          wx.showModal({
            title: '保存成功',
            content: '图片成功保存到相册啦,去发图噻~',
            showCancel: false,
            confirmText: '确认',
            confirmColor: '#21e6c1',
            success: function (res) {
              if (res.confirm) {
                console.log('用户点击确定');
              }
            }
          })
        }
      })
    },
    saveExcel(){
        wx.showToast({
            title: "功能正在开发中,敬请期待",
            icon: 'none'
          })
    },
  
    /**
     * 生命周期函数--监听页面初次渲染完成
     */
    onReady() {
  
    },
  
    /**
     * 生命周期函数--监听页面显示
     */
    onShow() {
  
    },
  
    /**
     * 生命周期函数--监听页面隐藏
     */
    onHide() {
  
    },
  
    /**
     * 生命周期函数--监听页面卸载
     */
    onUnload() {
  
    },
  
    /**
     * 页面相关事件处理函数--监听用户下拉动作
     */
    onPullDownRefresh() {
  
    },
  
    /**
     * 页面上拉触底事件的处理函数
     */
    onReachBottom() {
  
    },
  
    /**
     * 用户点击右上角分享
     */
    // onShareAppMessage() {}
  })