微信小程式之匯出頁面為doc檔案

whitebang發表於2024-03-12

微信小程式:需求將一個類似報表的頁面點選下載匯出問xxx.doc檔案。

1.寫入檔案匯出
由於微信小程式的限制;將匯出的功能放到node服務上。使用fs直接將html文字模板寫入doc檔案後返回下載地址

	// 生成下載檔案,並返回名稱
    app.post('/getFile', (req, res) => {
      const data = req.body;
      const name = data.name;
      const html = data.html;
      fs.writeFile('./public/' + name, html, (err) => {
        if (err) throw err;
        res.json({ data: name });
      });
    });
    // 檔案下載路由
    app.get('/download/:filename', function (req, res) {
      const filename = req.params.filename;
      res.download(path.join(__dirname, 'public') + '/' + filename, filename);
    });
    app.get('/', (req, res) => {
      res.send({ data: '服務啟動成功!' });
    });

2.html模板內容:區分頁面中的元素
1.模板,帶有表格樣式

let template = '
<!DOCTYPE html><html><head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
      <style>
        table{border-collapse:collapse;border:1px solid #000;font-family:FangSong_GB2312}th{border:1px solid #000;font-size:18px;font-family:FangSong_GB2312}td{border:1px solid #000;font-size:18px;font-family:FangSong_GB2312}h1{font-size:80px;font-family:STZhongsong;letter-spacing:20px;text-align:center}h2{font-weight:700;font-family:FangSong_GB2312;font-size:30px;border:none!important}.subtitle h2{font-size:30px;color:red;font-family:STZhongsong;border:none!important}.title_bg h2{border:none!important}.report_moudle_header_title{border:none!important;font-size:18px!important;font-family:FangSong_GB2312}h3{font-weight:700;font-family:FangSong_GB2312;font-size:18px;text-indent:2em}p{font-size:18px;text-indent:2em;font-family:FangSong_GB2312}.module_text{font-family:FangSong_GB2312;line-height:38px}.article-analyis—title{font-size:16px;font-weight:700}
      </style>
    </head><body>
'

2.普通樣式文字
直接拼接即可

template += "<div><h3>測試標題</h3></div>"

3.表格資料
也是直接拼接

template += <table class="table"
                style="width: 100%; border-collapse: collapse; border-spacing: 0; border: 1px solid #e9e9e9;">
                <thead>
                  <tr>
                    <th style="border: 1px solid #e9e9e9; background: #f7f7f7;">序號</th>
                    <th style="border: 1px solid #e9e9e9; background: #f7f7f7;">名稱1</th>
                    <th style="border: 1px solid #e9e9e9; background: #f7f7f7;">名稱2</th>
                  </tr>
                </thead>
                <tbody>
                  ${p.data
                    .map(
                      (q, index) => `
                      <tr>
                          <td style="border: 1px solid #e9e9e9;">${
                            index + 1
                          }</td>
                          <td style="border: 1px solid #e9e9e9;">${
                            q.name
                          }</td>
                          <td style="border: 1px solid #e9e9e9;">${
                            q.value
                          }</td>
                      </tr>
                    `
                    )
                    .join('')}
                </tbody>
              </table>"

4.echarts圖表
使用的lime-echart,地址:https://ext.dcloud.net.cn/plugin?id=4899 / https://gitee.com/liangei/lime-echart
修改元件方法,增加echarts圖表匯出圖片功能

//位置 myapp\src\uni_modules\lime-echart\components\l-echart\l-echart.vue
透過provider和inject實現匯出圖片命名。cacheBase64存放圖片地址,cId圖片需要的命名
inject: ['cacheBase64', 'cId'],
...
async init()方法最後加入:

setTimeout(() => {
    const canvasId = config.canvas.canvasId;
    uni
      .createSelectorQuery()
      .in(this)
      .select('#' + canvasId)
      .fields(
        {
          size: true,
          node: true,
        },
        (res) => {
          const canvas = res.node;
		  //canvas轉圖片
          uni.canvasToTempFilePath(
            {
              canvasId: canvasId,
              canvas: canvas,
              success: (res) => {
			    //圖片轉base64
                uni.getFileSystemManager().readFile({
                  filePath: res.tempFilePath,
                  encoding: 'base64', // 編碼格式
                  success: (res) => {
                    if (this.cId) {
					  //資料存入base64中
                      this.$set(
                        this.cacheBase64,
                        this.cId,
                        'data:image/png;base64,' + res.data
                      );
                    }
                  },
                  fail: function (err) {
                    console.error(err);
                  },
                });
              },
            },
            this
          );
        }
      )
      .exec();
  }, 2000);

將所有需要的圖片base64拼接入模板中:

template += `<div style="text-align: center;width: 100%">
              <img
                src="${this.cacheBase64[c.type]}"
                style="width:100%;max-width: 800px">
              <br>
            </div>`

5.帶有複雜樣式效果的div
由於這個div的樣式有很多before,after等樣式效果。最後決定將效果渲染出來後再截圖。使用puppeteer在node伺服器上實現
安裝puppeteer:使用puppeteer+獨立安裝liunx-chrome的方式

npm i puppeteer --ignore-scripts
下載linux-chrome,並將解壓後的資料夾放入伺服器上

# linux: https://cdn.npmmirror.com/binaries/chromium-browser-snapshots/Linux_x64/1271307/chrome-linux.zip

安裝puppeteer伺服器需要用到的其他包:

sudo apt-get install ca-certificates fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils -y

將安裝包解壓到伺服器上後,在該目錄下使用操作:
1.將SIMSUN.TTC(window系統自帶的宋體字型C:windows/fonts)檔案複製到/usr/local/share/fonts目錄下(linux匯出存在亂碼,這個是中文字型);
2.修改chrome許可權:chmod -R 777 chrome-linux/chrome
		 chmod -R 777 /usr/node/htmldocxjs/chrome-linux/chrome_crashpad_handler

nodejs服務程式碼

// 測試時間線圖
app.post('/transform/image', function (req, response) {
  const data = req.body.data;
  getColumnTImeLine(data).then((res) => {
  //讀取轉為base64,後返回到前端
    fs.readFile(res, 'base64', (err, data) => {
      if (err) {
        console.error(err);
        response.send({ data: err });
        return;
      }
      response.send({ data: data });
    });
  });
});



const puppeteer = require('puppeteer');
async function getColumnTImeLine(data) {
  return new Promise(async (resolve, reject) => {
    const brower = await puppeteer.launch({
      executablePath: '**pathtochrom**/chrome-linux/chrome',
      args: [
        '--font-family=simsun',
        '--disable-gpu',
        '--disable-dev-shm-usage',
        '--disable-setuid-sandbox',
        '--no-first-run',
        '--no-sandbox',
        '--no-zygote',
        '--single-process',
      ],
    });
    const page = await brower.newPage();
    await page.setViewport({
      width: 800,
      height: 1500,
    });
    if (!data) {
    }
//根據data生成html模板
    const htmlContent = ``;
    await page.setContent(htmlContent);
    await page.screenshot({
      path: path.join(__dirname, 'public') + '/test.png',
    });
//返回生成圖片的地址
    resolve(path.join(__dirname, 'public') + '/test.png');
    await brower.close();
  });
}


//將返回的base64存入記憶體中

self.$set(
            self.cacheBase64,
            `${cId}`,
            'data:image/png;base64,' + res.data.data
          );

最後頁面匯出的時候將該元件替換img+base64程式碼放入模板中
6.新增結尾 ,後將檔案透過步驟1進行html轉doc檔案。

template += </body></html>

相關文章