项目-02-数学学院后台项目开发过程中的问题总结

发布于:2024-12-06 ⋅ 阅读:(23) ⋅ 点赞:(0)

一、后台(pc端,vue2)

1. dialog对话框被黑色蒙层盖住

问题: dialog被黑色蒙层盖住(如下图),我希望dialog背景是黑色蒙层,但是dialog对话框不被盖住
在这里插入图片描述
解决办法: 给el-dialog添加如下两个属性

    <el-dialog 
      :modal-append-to-body="false" 
      :append-to-body="true">

添加后效果如下:
在这里插入图片描述

2. 将前端表格导出为word文档

需求:

  1. 前端独立完成
  2. 将审批通过的数据,按照班级分类打印出通过人员名单
  3. word文档的名字可以指定(前端人员传参决定),例如传参为“入团积极分子审查表”
  4. word文档的内容标题为动态的,是各期活动名字,例如“入团申请第十一期”
  5. word文档的后缀为.docx

效果图如下:

在这里插入图片描述

解决方法:
一. 参考文档

二. 具体方法

  1. 安装第三方包:
  • vue3下载包参考版本
"dependencies": {
    "angular-expressions": "^1.2.1",
    "docx-preview": "^0.3.2",
    "docxtemplater": "^3.49.1",
    "docxtemplater-image-module-free": "^1.1.1",
    "file-saver": "^2.0.5",
    "lodash": "^4.17.21",
    "pizzip": "^3.1.7",
  },

  • vue2下载包参考版本(docx-preview版本过高可能会报错)
"dependencies": {
    "angular-expressions": "^1.2.1",
    "docx-preview": "^0.1.20",
    "docxtemplater": "^3.49.1",
    "docxtemplater-image-module-free": "^1.1.1",
    "file-saver": "^2.0.5",
    "lodash": "^4.17.21",
    "pizzip": "^3.1.7",
  },
  1. 根据自己的需求,创建word文档,我创建的word文档如下:(具体书写规则参考vue3实现包含表格的Word文件导出里的解释)

在这里插入图片描述

  1. 编写导出Word的工具函数(我在utils文件夹下创建了exportFile.js文件)

注意:

  • 下面代码是完整版代码(包括word的 导出预览,导出功能包括支持图片导出和不支持图片导出)
  • 由于我只需要 “导出word,不支持图片” 的功能,所以最后只使用了下列代码中的exportWord函数,大家用的时候可以根据需要填写
// 编写导出word的工具函数

// 引入基本模块
import Docxtemplater from "docxtemplater";
import PizZip from "pizzip";
import PizZipUtils from "pizzip/utils/index.js";
import { saveAs } from "file-saver";
// 图片模块
import ImageModule from "docxtemplater-image-module-free";
// 解析语法模块
import expressions from "angular-expressions";
import assign from "lodash/assign";
// 文档预览模块
import { renderAsync } from "docx-preview";

expressions.filters.lower = function (input) {
  if (!input) return input;
  return input.toLowerCase();
};

function angularParser(tag) {
  tag = tag
    .replace(/^\.$/, "this")
    .replace(/('|')/g, "'")
    .replace(/("|")/g, '"');
  const expr = expressions.compile(tag);
  return {
    get: function (scope, context) {
      let obj = {};
      const scopeList = context.scopeList;
      const num = context.num;
      for (let i = 0, len = num + 1; i < len; i++) {
        obj = assign(obj, scopeList[i]);
      }
      return expr(scope, obj);
    },
  };
}

// 加载文件
function loadFile(url, callback) {
  PizZipUtils.getBinaryContent(url, callback);
}

// 配置空值替换函数 作为配置参数可配置在setOptions中
function nullGetter(part, scopeManager) {
  if (!part.module) {
    return "-null-";
  }
  if (part.module === "rawxml") {
    return "";
  }
  return "--";
}

/**
 * 预览word,支持图片
 * @param {Object} tempDocxPath 模板文件路径
 * @param {Object} wordData 导出数据
 * @param {Object} fileName 导出文件名
 * @param {Arrsy} imgSize 自定义图片尺寸
 */
export const getWordImage = (tempDocxPath, wordData, imgSize, file) => {
  loadFile(tempDocxPath, (error, content) => {
    if (error) {
      throw error;
    }

    // 图片配置
    const imageOpts = {
      getImage: function (tagValue, tagName) {
        return new Promise(function (resolve, reject) {
          PizZipUtils.getBinaryContent(tagValue, function (error, content) {
            if (error) {
              return reject(error);
            }
            return resolve(content);
          });
        });
      },
      getSize: function (img, tagValue, tagName) {
        const size = imgSize[tagName] ? imgSize[tagName] : [150, 150];
        return size;
      },
    };

    let imageModule = new ImageModule(imageOpts);

    const zip = new PizZip(content);

    // 实例化有两种方式 这里是链式
    const doc = new Docxtemplater()
      .loadZip(zip)
      .setOptions({
        // delimiters: { start: "[[", end: "]]" },
        paragraphLoop: true,
        linebreaks: true,
        nullGetter: nullGetter,
        parser: angularParser,
      })
      .attachModule(imageModule)
      .compile();

    doc.renderAsync(wordData).then(() => {
      const out = doc.getZip().generate({
        type: "blob",
        mimeType:
          "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      });
      renderAsync(out, file);
    });
  });
};



/**
 * 导出word,不支持图片
 * @param {Object} tempDocxPath 模板文件路径
 * @param {Object} wordData 导出数据
 * @param {Object} fileName 导出文件名
 */
export const exportWord = (tempDocxPath, wordData, fileName) => {
  loadFile(tempDocxPath, (error, content) => {
    if (error) {
      throw error;
    }
    const zip = new PizZip(content);
    const doc = new Docxtemplater().loadZip(zip)
    doc.setData({
      ...wordData.form,
      user_list: wordData.user_list,
      outsideList: wordData.outsideList
    })

    try {
      doc.render()
    } catch (error) {
      const e = {
        message: error.message,
        name: error.name,
        stack: error.stack,
        properties: error.properties
      }
      throw error
    }

    const out = doc.getZip().generate({
      type: "blob",
      mimeType:
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    });
    // Output the document using Data-URI
    saveAs(out, `${fileName}.docx`);
  });
}

/**
 * 导出word,支持图片
 * @param {Object} tempDocxPath 模板文件路径
 * @param {Object} wordData 导出数据
 * @param {Object} fileName 导出文件名
 * @param {Arrsy} imgSize 自定义图片尺寸
 */
export const exportWordImage = (tempDocxPath, wordData, fileName, imgSize) => {
  loadFile(tempDocxPath, (error, content) => {
    if (error) {
      throw error;
    }

    // 图片配置
    const imageOpts = {
      getImage: function (tagValue, tagName) {
        return new Promise(function (resolve, reject) {
          PizZipUtils.getBinaryContent(tagValue, function (error, content) {
            if (error) {
              return reject(error);
            }
            return resolve(content);
          });
        });
      },
      getSize: function (img, tagValue, tagName) {
        const size = imgSize[tagName] ? imgSize[tagName] : [150, 150]
        return size;
      },
    };

    let imageModule = new ImageModule(imageOpts);

    const zip = new PizZip(content);

    // 实例化有两种方式 这里是链式
    const doc = new Docxtemplater()
      .loadZip(zip)
      .setOptions({
        // delimiters: { start: "[[", end: "]]" },
        paragraphLoop: true,
        linebreaks: true,
        nullGetter: nullGetter,
        parser: angularParser,
      })
      .attachModule(imageModule)
      .compile();

    doc.renderAsync(wordData).then(function () {
      const out = doc.getZip().generate({
        type: "blob",
        mimeType:
          "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      });
      saveAs(out, `${fileName}.docx`);
    });
  });
}


  1. vue页面使用
import { exportWord } from '../../utils/exportFile';

export default {
	data() {
		return {
			activity_name: '入团积极分子第十一期',
			filename: '入团积极分子审查表'
		}
	},
	methods: {
		exportTable() {
			// 需要导出的所有数据都需要写在wordData里面
			// 因为我们写的exportWord工具函数只接受三个参数,第一个是模版文件路径,第二个是导出数据,第三个是表的名称
			// 当然,大家也可以根据需要修改../../utils/exportFile里面的函数
			let wordData = { 
				form: {
					activity_name: this.activity_name // 活动名称
				},
				outsideList: [ // 要导出的班级人员名单数据
			      {
			        user_class: '计科211',
			        user_list: [
			          { name: '袁云熙'},
			          { name: '莫睿'}
			        ]
			      },
			      {
			        user_class: '数本221',
			        user_list: [
			          { name: '向致远'}
			        ]
			      }
			    ]
			}
			exportWord("../../../static/template.docx", wordData, this.filename)
		}
	}
}

3. 在线查看、下载 .docx、.doc、.pdf文档

需求:

  1. 前端独立完成
  2. 后端返回的是文件地址,例如:
  3. 点击“查看”按钮,在新窗口打开查看文档内容
  4. 点击“下载”按钮,可以下载对应的文档
  5. 点击“一键导出”,可以下载全部的文档

参考文档:

解决方案:

<div class="top">
  <el-button @click="downAllfile" style="padding: 3px 0" type="text">一键导出</el-button>
</div>

<el-table :data="wordPdfData" border>
    <el-table-column label="操作" align="center" width="250">
      <template #default="{ row }">
        <span class="check">
          <a :href="getFileExtension(row.file_name) === 'pdf' ? `${row.file_path}` : `https://view.officeapps.live.com/op/view.aspx?src=${row.file_path}`" target="_blank">查看</a>
        </span>
        <span class="download">
          <a @click="downFile(row.file_path, row.file_name)">下载</a>
        </span>
      </template>
    </el-table-column>
</el-table>
export default {
	data() {
		return {
			wordPdfData: []
		}
	},
	methods: {
		// 获取文件后缀名
	    getFileExtension(filename) {
	      return filename.split('.').pop().toLowerCase()
	    },
	    // 下载文件
	    downFile(url, filename) { // url:文件地址,filename:文件名称
	      return new Promise((resolve, reject) => {
	        // 创建一个隐藏的<a>元素用于触发下载
	        const a = document.createElement("a");
	        
	        // 使用fetch API下载文件
	        fetch(url)
	          .then(res => {
	            // 检查网络响应是否成功
	            if (!res.ok) {
	              // 如果响应不成功,则抛出一个错误
	              throw new Error(`网络响应失败,状态码:${res.status}`);
	            }
	            // 如果响应成功,则返回Blob对象
	            return res.blob();
	          })
	          .then(blob => {
	            // 创建一个对象URL用于下载
	            const objectURL = URL.createObjectURL(blob);
	            a.href = objectURL;
	            a.download = filename; // 设置下载的文件名
	            a.style.display = 'none'; // 隐藏<a>元素
	            document.body.appendChild(a); // 将<a>元素添加到文档中
	            a.click(); // 触发下载
	            
	            // 使用setTimeout在下一轮事件循环中释放对象URL并移除<a>元素
	            // 同时解析Promise表示下载成功
	            setTimeout(() => {
	              URL.revokeObjectURL(objectURL);
	              document.body.removeChild(a);
	              resolve(); // 下载成功,解析Promise
	            }, 0);
	          })
	          .catch(error => {
	            // 如果在下载过程中发生错误,则拒绝Promise
	            reject(error); // 下载失败,拒绝Promise
	          });
	      });
	    },
	    // 批量下载文件
	    downAllfile() {
	      let downList = this.wordPdfData
	
	      const downloadPromises = downList.map(item => {
	        const downloadUrl = item.file_path; // 构造下载URL
	        return new Promise((resolve, reject) => {
	          // 调用downImage方法下载图片,并在下载完成后解析或拒绝Promise
	          this.downFile(downloadUrl, item.file_name)
	            .then(() => resolve()) 
	            .catch(error => reject(error)); 
	        });
	      })
	 
	      // 使用Promise.all等待所有下载任务完成
	      Promise.all(downloadPromises)
	        .then(() => {
	          // 所有文件都已成功下载
	          this.$message({
	            type: 'success',
	            message: '所有导出成功!',
	            showClose: true,
	            duration: 3000
	          });
	        })
	        .catch(error => {
	          // 至少有一个文件下载失败
	          console.error('至少有一个文件下载失败:', error);
	          this.$message({
	            type: 'error',
	            message: '导出失败,请重试。',
	            showClose: true,
	            duration: 3000
	          });
	        });
	    }	    
	}
}

网站公告

今日签到

点亮在社区的每一天
去签到