需求:一键打印页面上的内容
思路:只需要传入一个选择器获取就可以获取打印的内容,并呼出打印选择界面
实现:主要利用window.print()方法实现打印功能,只需要处理打印的内容以及格式问题
使用:可直接通过v-print="'#printData'"使用,也指定纸张宽度(默认采用A4纸宽度),例如:v-print.A5="'#printData'"
主要实现代码:
import type { Directive, DirectiveBinding } from 'vue';
/**
*
* @param el
* @param binding
*
* v-print="#printData" 打印指令,绑定一个选择器 , 默认A4大小
* v-print.A4="#printData" 可选纸张(A3、A4、A5、A6)
*/
// 纸张尺寸
const sizes = [
{ size: 'A3', width: '297mm' },
{ size: 'A4', width: '210mm' },
{ size: 'A5', width: '148mm' },
{ size: 'A6', width: '105mm' },
];
const vPrint: Directive = (el: HTMLElement, binding: DirectiveBinding) => {
if (!binding.value) throw new Error('请绑定选择器');
const innerHtml = document.querySelector(binding.value).innerHTML;
el.addEventListener('click', () => {
if (innerHtml) {
let div: HTMLDivElement = document.createElement('div');
div.style.width = sizes.find(s => s.size === Object.keys(binding.modifiers)[0]?.toUpperCase())?.width ?? '210mm';
div.innerHTML = innerHtml;
printHtml(div.outerHTML);
}
});
};
// 打印内容
const printHtml = (html: string) => {
const style = setStyle();
const container = setContainer(html);
document.body.appendChild(style);
document.body.appendChild(container);
loadPromise(container).then(() => {
window.print();
document.body.removeChild(style);
document.body.removeChild(container);
});
};
// 设置样式
const setStyle = () => {
const styleContent = `
#print-container {
display: none;
}
@media print {
@page {
margin: 5mm 0 0;
}
body > :not(.print-container) {
display: none;
}
html,
body {
display: block;
margin: 0 2mm;
}
#print-container {
display: block;
}
}`;
const style = document.createElement('style');
style.innerHTML = styleContent;
return style;
};
// 清空打印内容
const clearPrint = () => {
const div = document.getElementById('print-container');
div && document.querySelector('body')?.removeChild(div);
};
// 创建打印内容
const setContainer = (html: string) => {
clearPrint();
const container = document.createElement('div');
container.setAttribute('id', 'print-container');
container.innerHTML = html;
return container;
};
// 处理图片加载
const loadPromise = (el: HTMLElement) => {
const imgs: NodeListOf<HTMLImageElement> = el.querySelectorAll('img');
if (imgs.length == 0) return Promise.resolve();
let finishedCount: number = 0;
return new Promise((resolve: Function, reject: Function) => {
function check() {
finishedCount++;
if (finishedCount === imgs.length) resolve();
}
imgs.forEach(img => {
// 不管加载成功还是失败,都表示加载完成
img.addEventListener('load', check);
img.addEventListener('error', check);
});
});
};
export default vPrint;
全局指令注册:
// 在 directives目录下 建立index.ts文件
import type { App, Directive } from 'vue';
import print from './vPrint';
interface Directives {
print: Directive;
}
// 注意:使用时需要(v-)开头
const directives: Directives = { print };
export default {
install(app: App) {
Object.keys(directives).forEach(key => {
app.directive(key, directives[key as keyof Directives]);
});
},
};
// 在main.ts中的操作如下
import { createApp } from 'vue';
import App from './App.vue';
import directives from './directives/index';
const app = createApp(App);
app.use(directives);
使用示例:
<el-button v-print="'#printData'">打印文章</el-button>
<div id="printData">
<article>
曲曲折折的荷塘上面,弥望旳是田田的叶子。叶子出水很高,像亭亭旳舞女旳裙。层层的叶子中间,零星地点缀着些白花,有袅娜(niǎo,nuó)地开着旳,有羞涩地打着朵儿旳;正如一粒粒的明珠,又如碧天里的星星,又如刚出浴的美人。微风过处,送来缕缕清香,仿佛远处高楼上渺茫的歌声似的。这时候叶子与花也有一丝的颤动,像闪电般,霎时传过荷塘的那边去了。叶子本是肩并肩密密地挨着,这便宛然有了一道凝碧的波痕。叶子底下是脉脉(mò)的流水,遮住了,不能见一些颜色;而叶子却更见风致了
</article>
<img
width="500"
height="auto"
style="object-fit: scale-down"
src="https://ts1.cn.mm.bing.net/th/id/R-C.22cdb21060f95297035a380467123768?rik=ZZ5YeI3loyMu0w&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fwallpaper%2f1307%2f10%2fc0%2f23166997_1373443405186.jpg&ehk=UuR5odrdPxwmlbjY0C5WGXyxSJdyN%2b1aYi%2bnwiG6DPQ%3d&risl=&pid=ImgRaw&r=0"
alt=""
/>
</div>
效果展示:
对自定义指令不了解的小伙伴可前往官网查看,如有其它问题可在评论区留言