前端打印插件TS版(支持vue,react)

  • 内容
  • 评论
  • 相关

把之前的插件转成了ts版本,这里感谢ai的帮忙,让我省了很多事情,不多说,上代码:

interface Options {
    noPrint?: string;
    title?:string;
    printComplete?:Function;
}
export default class Print {
    private options: Options;
    private dom: HTMLElement;
    private delay:number = 500;
    private iframe!:HTMLIFrameElement;

    constructor(dom: string | HTMLElement, options: Options={noPrint:".no-print",title:"打印"}) {
        this.options = options

        // if ((typeof dom) === "string") {
        //     this.dom = document.getElementById(dom as string) as HTMLElement;
        // } else {
        //     this.isDOM(dom)
        //     this.dom = this.isDOM(dom) ? dom as HTMLElement : (dom as any).$el;
        // }
        this.dom = this.getDom(dom);
        this.init();
    }
    private getDom(dom: string | HTMLElement):HTMLElement
    {
        let _dom;
        if ((typeof dom) === "string") {
            _dom = document.getElementById(dom as string) as HTMLElement;
        } else {
            this.isDOM(dom)
            _dom = this.isDOM(dom) ? dom as HTMLElement : (dom as any).$el;
        }
        return _dom;
    }
    public static start(dom: string | HTMLElement, options?: Options)
    {
        return new Print(dom,options);
    }
    private init(): void {
        let content = this.getStyle() + this.getHtml();
        this.writeIframe(content);
    }
    private getStyle(): string {
        let str = "",
            styles = document.querySelectorAll('style,link');
        for (let i = 0; i < styles.length; i++) {
            str += styles[i].outerHTML;
        }
        str += "<style>" + (this.options.noPrint ? this.options.noPrint : '.no-print') + "{display:none!important;}</style>";

        return str;
    }
    private getHtml(): string {
        let inputs = document.querySelectorAll('input');
        let textareas = document.querySelectorAll('textarea');
        let selects = document.querySelectorAll('select');

        for (let k = 0; k < inputs.length; k++) {
            if (inputs[k].type == "checkbox" || inputs[k].type == "radio") {
                if (inputs[k].checked == true) {
                    inputs[k].setAttribute('checked', "checked")
                } else {
                    inputs[k].removeAttribute('checked')
                }
            } else if (inputs[k].type == "text") {
                inputs[k].setAttribute('value', inputs[k].value)
            } else {
                inputs[k].setAttribute('value', inputs[k].value)
            }
        }
        for (let k2 = 0; k2 < textareas.length; k2++) {
            if (textareas[k2].type == 'textarea') {
                textareas[k2].innerHTML = textareas[k2].value
            }
        }

        for (let k3 = 0; k3 < selects.length; k3++) {
            if (selects[k3].type == 'select-one') {
                let child = selects[k3].children;
                for (let i in child) {
                    if (child[i].tagName == 'OPTION') {
                        if (child[i]['selected'] == true) {
                            child[i].setAttribute('selected', "selected")
                        } else {
                            child[i].removeAttribute('selected')
                        }
                    }
                }
            }
        }
        let _dom = this.wrapperRefDom(this.dom);
        if(_dom)
        {
            let outerHTML = _dom.outerHTML;
            return outerHTML;
        }
       else
        {
            return ''
        }

    }
    // 向父级元素循环,包裹当前需要打印的元素
    // 防止根级别开头的 css 选择器不生效
    private wrapperRefDom(refDom: HTMLElement): HTMLElement | null{
        let prevDom: HTMLElement | null = null
        let currDom: HTMLElement | null = refDom
        // 判断当前元素是否在 body 中,不在文档中则直接返回该节点
        if (!this.isInBody(currDom)) return currDom

        while (currDom) {
            if (prevDom) {
                let element = currDom.cloneNode(false) as HTMLElement
                element.appendChild(prevDom)
                prevDom = element
            } else {
                prevDom = currDom.cloneNode(true) as HTMLElement
            }

            let list;
            // @ts-ignore
            if(currDom.parentElement.classList)
            {
                // @ts-ignore
                list= currDom.parentElement.classList.value.split(' ')
            }
            if (list&&list.indexOf('no-print-root') !== -1) {
                currDom = null;
            }
            else
            {
                currDom = currDom.parentElement
            }

        }

        return prevDom
    }
    private writeIframe(content: string): void {
        let w: Window, doc: Document, iframe:HTMLIFrameElement = document.createElement('iframe'),
            f = document.body.appendChild(iframe);
        iframe.id = "myIframe";
        iframe.setAttribute('style', 'position:absolute;width:0;height:0;top:-10px;left:-10px;z-index:1000');
        // iframe.setAttribute('style', 'position:absolute;width:1920px;height:1080px;top:0;left:0;z-index:1000');
        w = f.contentWindow || f.contentDocument as any;
        doc = f.contentDocument || f.contentWindow!.document;
        doc.open();
        doc.write(content);
        doc.close();
        iframe.onload = () =>{
            this.toPrint(w);
            setTimeout(function () {
                document.body.removeChild(iframe)
            }, this.delay)
        }
        this.iframe = iframe;
    }
    private toPrint(frameWindow: Window): void {
        try {
            let _title = document.title;
            document.title = this.options.title as string;
            setTimeout( () =>{
                frameWindow.focus();
                try {
                    if (!frameWindow.document.execCommand('print', false, undefined)) {
                        frameWindow.print();
                    }
                } catch (e) {
                    frameWindow.print();
                }
                frameWindow.close();
                document.title = _title;
                if(this.options.printComplete)
                {
                    this.options.printComplete(this.iframe)
                }
            }, 10);
        } catch (err) {
            console.log('err', err);
        }
    }
    private isInBody(node: Node): boolean {
        return (node === document.body) ? false : document.body.contains(node);
    }
    private isDOM(obj: any): boolean {
        return (typeof HTMLElement === 'object') ?
            obj instanceof HTMLElement :
            obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';
    }
}

使用:Print.start('domId');

注意:

1.不要打印的加no-print的class

2.如果不想往上冒泡的加no-print-root的class

(有时候打印的东西太长,你需要打印滚动容器内的内容,但是一般的打印插件会把整个父给copy下来,导致把滚动容器也打印出来,最后打印的是你看到的页面,滚动容器遮住的部分打印不出来,用no-print-root可以有效的防止这点,而且比我js写的那版需要穿父类参数的简单的多)

5e12e6ec9970d.jpg

评论

2条评论
  1. Gravatar 头像

    匿名 回复

    123

  2. Gravatar 头像

    匿名 回复

    1

发表评论

电子邮件地址不会被公开。