y-gtmi-ui
Version:
y-gtmi-ui 基于Element-plus二次封装基础组件文档
998 lines (930 loc) • 25 kB
text/typescript
/** 打印插件 */
export interface PrintThisOptions {
hide?: Array<string> | string;
horizontal?: boolean | string | null | undefined;
iePreview?: boolean;
blank?: boolean;
close?: boolean;
margin?: string | number | null | undefined;
title?: string;
isHideNone?: boolean;
}
export interface PrintHtmlOptions {
html?: string;
print?: boolean;
loading?: boolean;
header?: string;
footer?: string;
before?: Function;
done?: Function;
style?: string;
}
export interface PrintHtmlOptionsExtends extends PrintThisOptions {
html?: string;
print?: boolean;
loading?: boolean;
header?: string;
footer?: string;
before?: Function;
done?: Function;
style?: string;
}
export interface PrintPageOptionsExtends extends PrintHtmlOptionsExtends {
htmls?: Array<string>;
pages?: Array<string>;
style?: string;
padding?: string;
isDebug?: boolean;
width?: string;
height?: string;
}
export interface makeTableCols {
field?: string;
title?: string;
width?: number | string;
align?: string;
thAlign?: string;
colspan?: number;
rowspan?: number;
style?: string;
thStyle?: string;
templet?: Function;
[key : string]: any;
}
/**
* ie 打印预览控件
*/
const ieWebBrowser =
'<object id="WebBrowser" classid="clsid:8856F961-340A-11D0-A96B-00C04FD705A2" width="0" height="0"></object>';
/**
* iframe 打印窗口的 id
*/
const printFrameId = 'y-printer-frame';
/**
* 打印全局样式的 style 的 id
*/
const printStyleId = 'y-printer-style';
/**
* 打印方向设置的 style 的 id
*/
const printOptionId = 'y-printer-set';
/**
* 加载层 id
*/
const loadingElId = 'y-printer-loading';
/**
* 正在打印标识的 class
*/
const printingClass = 'y-printer-printing';
/**
* 打印时隐藏的 class
*/
const hideClass = 'y-printer-hide';
/**
* 打印时隐藏不占位置的 class
*/
const hideNoneClass = 'y-printer-hide-none';
/**
* 生成随机 id
* @param length 长度
*/
function uuid(length = 8) {
const num = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
let str = 'p_';
for (let i = 0; i < length; i++) {
str += num.charAt(Math.floor(Math.random() * num.length));
}
return str;
}
/**
* 是否是 ie
*/
function isIE() {
return !!window['ActiveXObject'] || 'ActiveXObject' in window;
}
/**
* 获取的打印 iframe
*/
function getPrintFrame() {
const pFrame = document.getElementById(printFrameId);
if (pFrame && pFrame.parentNode) {
pFrame.parentNode.removeChild(pFrame);
}
const elem = document.createElement('iframe');
elem.id = printFrameId;
elem.style.width = '0px';
elem.style.height = '0px';
elem.style.position = 'fixed';
elem.style.visibility = 'hidden';
document.body.appendChild(elem);
elem.focus();
return elem;
}
/**
* 获取打印全局样式
* @param isPrinting 是否已开始打印
*/
function getCommonCss(isPrinting = false) {
return `
@media print {
html, body {
padding: 0;
margin: 0;
}
::-webkit-scrollbar {
width: 0 !important;
height: 0!important;
}
.el-scrollbar__wrap {
margin: 0 !important;
}
}
/* 打印时不显示的元素 */
.${hideClass}.${printingClass} {
visibility: hidden !important;
}
.${hideClass} {
${isPrinting ? 'visibility: hidden !important;' : ''}
}
.${hideClass}.${printingClass}.${hideNoneClass},
.${hideClass}.${hideNoneClass}${isPrinting ? '' : '-no'} {
display: none !important;
}
/* 表格样式 */
.y-printer-table {
width: 100%;
border-collapse: collapse;
border: none;
}
.y-printer-table td, .y-printer-table th {
color: #333;
padding: 9px 15px;
border: 1px solid #333;
word-break: break-all;
}
/* loading样式 */
#${loadingElId} {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: hsla(0, 0%, 100%, .9);
z-index: 19000000;
}
#${loadingElId}:after {
content: "";
width: 40px;
height: 40px;
position: absolute;
top: 50%;
left: 50%;
margin: -20px auto auto -20px;
border: 2px solid #3296FA;
border-right-color: transparent;
border-bottom-color: transparent;
border-radius: 50%;
animation: ele-printer-loading-anim .8s linear infinite;
}
@keyframes ele-printer-loading-anim {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 带页眉页脚页面样式 */
.y-printer-table-page {
width: 100%;
border-collapse: collapse;
border: none;
}
.y-printer-table-page td {
padding: 0;
border: none;
}
`;
}
/**
* 获取分页打印的样式
* @param padding 每一页边距
* @param width 每一页宽度
* @param height 每一页高度
*/
function getPageStyleHtml(padding: any, width: any, height: any) {
return `
<style>
body {
margin: 0 !important;
}
/* 自定义边距及宽高样式 */
.ele-printer-page .ele-printer-page-item {
width: ${width ?? 'auto'};
height: ${height ?? 'auto'};
padding: ${padding ?? '0'};
page-break-after: always !important;
box-sizing: border-box !important;
border: none !important;
position: relative;
}
/* 调试模式样式 */
.ele-printer-page.ele-printer-debug .ele-printer-page-item {
border: 1px solid red !important;
}
/* 全局样式 */
${getCommonCss(true)}
</style>
`;
}
/**
* 生成控制打印的 html
* @param option PrintHtmlOption
*/
function getOptionHtml(option: PrintPageOptionsExtends) {
const { beforeJs, doneJs } = addCallback(option.before, option.done);
const blank = option.blank || (option.iePreview !== false && isIE());
const closeJs = blank && option.close !== false ? 'window.close();' : '';
const hideLoadJs =
'parent.hideElePrinterLoading&&parent.hideElePrinterLoading();';
const optHtml = [];
// 增加打印设置的 css
optHtml.push(`<style type="text/css" media="print" id="${printOptionId}">`);
optHtml.push('@page {');
// 打印方向
if (typeof option.horizontal !== 'undefined') {
optHtml.push(`size: ${option.horizontal ? 'landscape' : 'portrait'};`);
}
// 页间距
if (option.margin != null) {
optHtml.push(`margin: ${option.margin};`);
}
optHtml.push(`}`);
optHtml.push(`</style>`);
// 增加打印和回调的 js
if (option.iePreview !== false && isIE()) {
// 兼容 ie 打印预览
optHtml.push(ieWebBrowser);
if (option.print !== false) {
optHtml.push(`
<script>
window.onload = function() {
${beforeJs}
try {
window.WebBrowser.ExecWB(7, 1);
} catch(e) {
console.error(e);
window.print();
}
${hideLoadJs}
${doneJs}
${closeJs}
}
</script>
`);
}
} else if (option.print !== false) {
optHtml.push(`
<script>
window.onload = function() {
${beforeJs}
window.print();
${hideLoadJs}
${doneJs}
${closeJs}
}
</script>
`);
}
return optHtml.join('');
}
/**
* 加入核心样式
*/
function addCommonCss() {
if (!document.getElementById(printStyleId)) {
const elem = document.createElement('style');
elem.id = printStyleId;
elem.setAttribute('type', 'text/css');
elem.innerHTML = getCommonCss();
document.body.appendChild(elem);
}
}
/**
* 增加页眉页脚
* @param html 页面内容
* @param header 页眉
* @param footer 页脚
*/
function addHeaderFooter(html: string, header: string | undefined, footer: string | undefined) {
if (!header && !footer) {
return html ?? '';
}
let result = '<table class="y-printer-table-page">';
if (header) {
result += `<thead><tr><td>${header}</td></tr></thead>`;
}
result += `<tbody><tr><td>${html ?? ''}</td></tr></tbody>`;
if (footer) {
result += `<tfoot><tr><td>${footer}</td></tr></tfoot>`;
}
return result + '</table>';
}
/**
* 添加回调监听
* @param before 打印前回调
* @param done 打印后回调
*/
function addCallback(before: any, done: any) {
const taskId = 'p' + uuid();
if (!window['yPrinterBefore']) {
window['yPrinterBefore'] = {};
}
if (!window['yPrinterDone']) {
window['yPrinterDone'] = {};
}
if (before) {
window['yPrinterBefore'][taskId] = before;
}
if (done) {
window['yPrinterDone'][taskId] = done;
}
const beforeJs = `;parent.elePrinterBefore&&parent.elePrinterBefore.${taskId}&&parent.elePrinterBefore.${taskId}();`;
const doneJs = `;parent.elePrinterDone&&parent.elePrinterDone.${taskId}&&parent.elePrinterDone.${taskId}();`;
return { taskId, beforeJs, doneJs };
}
/**
* 隐藏元素
* @param elems 需要隐藏的元素
* @param isNone 是否是隐藏不占位置
*/
function hideElem(elems: Array<string> | string, isNone: boolean) {
Array.prototype.forEach.call(
document.getElementsByClassName(hideClass),
(elem) => {
if (elem?.classList) {
elem.classList.add(printingClass);
}
}
);
if (!elems) {
return;
}
const isArray =
Array?.isArray(elems) ||
// eslint-disable-next-line no-prototype-builtins
NodeList?.prototype?.isPrototypeOf(elems) ||
// eslint-disable-next-line no-prototype-builtins
HTMLCollection?.prototype?.isPrototypeOf(elems);
Array.prototype.forEach.call(isArray ? elems : [elems], (elem) => {
if (typeof elem === 'string') {
Array.prototype.forEach.call(document.querySelectorAll(elem), (el) => {
if (el?.classList) {
el.classList.add(hideClass);
el.classList.add(printingClass);
if (isNone) {
el.classList.add(hideNoneClass);
}
}
});
} else if (elem?.classList) {
elem.classList.add(hideClass);
elem.classList.add(printingClass);
if (isNone) {
elem.classList.add(hideNoneClass);
}
}
});
}
/**
* 取消隐藏元素
* @param elems 需要取消隐藏的元素
*/
function showElem(elems: string | Object) {
Array.prototype.forEach.call(
document.getElementsByClassName(hideClass),
(elem) => {
if (elem?.classList) {
elem.classList.remove(printingClass);
}
}
);
if (!elems) {
return;
}
const isArray =
Array?.isArray(elems) ||
// eslint-disable-next-line no-prototype-builtins
NodeList?.prototype?.isPrototypeOf(elems) ||
// eslint-disable-next-line no-prototype-builtins
HTMLCollection?.prototype?.isPrototypeOf(elems);
Array.prototype.forEach.call(isArray ? elems : [elems], (elem) => {
if (typeof elem === 'string') {
Array.prototype.forEach.call(document.querySelectorAll(elem), (el) => {
if (el?.classList) {
el.classList.remove(hideClass);
el.classList.remove(printingClass);
el.classList.remove(hideNoneClass);
}
});
} else if (elem?.classList) {
elem.classList.remove(hideClass);
elem.classList.remove(printingClass);
elem.classList.remove(hideNoneClass);
}
});
}
/**
* 显示加载层
*/
export function showPrintLoading() {
addCommonCss();
let elem = document.getElementById(loadingElId);
if (!elem) {
elem = document.createElement('div');
elem.id = loadingElId;
document.body.appendChild(elem);
}
elem.style.display = 'block';
window['hideElePrinterLoading'] = () => {
hidePrintLoading();
};
return elem;
}
/**
* 关闭加载层
*/
export function hidePrintLoading() {
const elem = document.getElementById(loadingElId);
if (elem) {
elem.style.display = 'none';
}
}
/**
* 打印当前页面
* @param hide 需要隐藏的元素
* @param horizontal 是否横向打印
* @param iePreview 是否支持ie打印预览
* @param blank 是否在新窗口打印
* @param close 如果在新窗口打印,打印完是否关闭新窗口
* @param margin 页间距
* @param title 页面标题
*
*/
export function printThis(
{
hide = '',
horizontal = false,
iePreview = true,
blank = false,
close = true,
margin = 0,
title,
isHideNone
}: PrintThisOptions
) {
// 让当前窗口获取焦点
window.focus();
// 加入打印全局样式
addCommonCss();
// 增加打印设置的 css
const optElem = document.getElementById(printOptionId);
if (optElem && optElem.parentNode) {
optElem.parentNode.removeChild(optElem);
}
const optStr = [];
// 打印方向设置
if (typeof horizontal === 'boolean') {
optStr.push(`size: ${horizontal ? 'landscape' : 'portrait'};`);
}
// 页间距设置
if (margin != null) {
optStr.push(`margin: ${margin};`);
}
if (optStr) {
const elem = document.createElement('style');
elem.id = printOptionId;
elem.setAttribute('type', 'text/css');
elem.setAttribute('media', 'print');
elem.innerHTML = `@page { ${optStr.join('')} }`;
document.body.appendChild(elem);
}
// 隐藏打印时需要隐藏的内容
hideElem(hide, isHideNone);
// 设置窗口标题
const oldTitle = document.title;
if (title) {
document.title = title;
}
let pWin;
if (!blank) {
// 当前窗口打印
pWin = window;
if (iePreview !== false && isIE()) {
if (!document.getElementById('WebBrowser')) {
const elem = document.createElement('object');
elem.id = 'WebBrowser';
elem.setAttribute(
'classid',
'clsid:8856F961-340A-11D0-A96B-00C04FD705A2'
);
elem.style.display = 'none';
document.body.appendChild(elem);
}
try {
window['WebBrowser'].ExecWB(7, 1);
} catch (e) {
pWin.print();
}
} else {
pWin.print();
}
} else {
// 新窗口打印
pWin = window.open('', '_blank');
if (pWin) {
pWin.focus();
// 写入内容到打印窗口
const pDoc = pWin.document;
if (pDoc) {
pDoc.open();
let html =
'<!DOCTYPE html>' +
document.getElementsByTagName('html')[0]?.outerHTML;
// 去除js
html = html
.replace(/<script/g, '<div style="display:none;" ')
.replace(/<\/script>/g, '</div>');
const addHtml = function (str: string) {
html = html.replace(/<\/html>/, `${str}</html>`);
};
if (iePreview !== false && isIE()) {
if (!document.getElementById('WebBrowser')) {
addHtml(ieWebBrowser);
}
addHtml(`
<script>
window.onload = function() {
try {
window.WebBrowser.ExecWB(7,1);
} catch(e) {
console.error(e);
window.print();
}
${close !== false ? 'window.close();' : ''}
}
</script>
`);
} else {
addHtml(`
<script>
window.onload = function() {
window.print();
${close !== false ? 'window.close();' : ''}
}
</script>
`);
}
pDoc.write(html);
pDoc.close();
}
}
}
// 恢复窗口标题
if (title) {
document.title = oldTitle;
}
// 恢复隐藏的内容
showElem(hide);
return pWin;
}
/**
* 打印任意内容
* @param option PrintHtmlOption
* @param 参数说明,都是可选参数,此外还支持 printThis 的所有参数
* @param html 要打印内容 String
* @param print 是否立即打印 Boolean
* @param loading 是否显示加载层 Boolean
* @param header 页眉 String
* @param footer 页脚 String
* @param before Function 打印开始的回调
* @param done Function 打印完成的回调
* @param style String 打印的自定义样式
*/
export function printHtml(option: PrintHtmlOptionsExtends) {
if (option.loading !== false && option.blank !== true) {
showPrintLoading();
}
// 创建打印窗口
let pWin, pDoc;
if (option.blank || (option.iePreview !== false && isIE())) {
// 新窗口打印
pWin = window.open('', '_blank');
pDoc = pWin?.document;
} else {
// 当前窗口打印
const pFrame = getPrintFrame();
pWin = pFrame.contentWindow;
pDoc = pFrame.contentDocument || pWin?.document;
}
if (pWin) {
// 让打印窗口获取焦点
pWin.focus();
// 写入内容到打印窗口
if (pDoc && option.html) {
pDoc.open();
pDoc.write(`
<!DOCTYPE html>
<head>
<meta charset="UTF-8"/>
<title>${option.title ?? ''}</title>
<style>
${getCommonCss(true)}
</style>
${option.style ?? ''}
</head>
<html>
<body>
${addHeaderFooter(option.html, option.header, option.footer)}
${getOptionHtml(option)}
</body>
</html>
`);
pDoc.close();
}
}
return pWin;
}
/**
* 分页打印
* @param option PrintPageOption
*/
export function printPage(option: PrintPageOptionsExtends) {
if (option.loading !== false && option.blank !== true) {
showPrintLoading();
}
// 创建打印窗口
let pWin, pDoc;
if (option.blank || (option.iePreview !== false && isIE())) {
// 新窗口打印
pWin = window.open('', '_blank');
pDoc = pWin?.document;
} else {
// 当前窗口打印
const pFrame = getPrintFrame();
pWin = pFrame.contentWindow;
pDoc = pFrame.contentDocument || pWin?.document;
}
if (pWin && pDoc) {
// 让打印窗口获取焦点
pWin.focus();
// 写入内容到打印窗口
const content =
(option.pages ?? option.htmls)
?.map((h) => `<div class="ele-printer-page-item">${h}</div>`)
.join('') ?? '';
const pageClass =
'ele-printer-page' + (option.isDebug ? ' ele-printer-debug' : '');
const contentHtml = `<div class="${pageClass}">${content}</div>`;
pDoc.open();
pDoc.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>${option.title ?? ''}</title>
${getPageStyleHtml(option.padding, option.width, option.height)}
${option.style ?? ''}
</head>
<body>
${addHeaderFooter(contentHtml, option.header, option.footer)}
${getOptionHtml(option)}
</body>
</html>
`);
pDoc.close();
}
return pWin;
}
/**
* 打印 pdf
* @param option PrintPdfOption
*/
export function printPdf(option: any) {
if (option.loading !== false) {
showPrintLoading();
}
const pFrame = getPrintFrame();
const pWin = pFrame.contentWindow;
pFrame.onload = () => {
if (!pFrame.getAttribute('src')) {
return;
}
pFrame.focus();
option.before && option.before();
pWin?.print();
hidePrintLoading();
option.done && option.done();
};
// 开始打印
function doPrint(arraybuffer: BlobPart) {
const localPdf = new window.Blob([arraybuffer], {
type: 'application/pdf'
});
// 兼容 IE
if (window.navigator && window.navigator['msSaveOrOpenBlob']) {
window.navigator['msSaveOrOpenBlob'](localPdf, 'print.pdf');
hidePrintLoading();
} else {
pFrame.setAttribute('src', window.URL.createObjectURL(localPdf));
}
}
// 请求 pdf 数据
if (option.arraybuffer) {
doPrint(option.arraybuffer);
} else if (option.url) {
const req = new window.XMLHttpRequest();
req.open('GET', option.url, true);
req.responseType = 'arraybuffer';
req.onload = () => {
if ([200, 201].indexOf(req.status) === -1) {
return option.error && option.error(req.status, req.statusText);
}
doPrint(req.response);
};
req.send();
}
return pWin;
}
/**
* 生成表格 html
* @param data 数据
* @param cols 列配置
* @param thead 是否需要表头
*/
export function makeTable(data: Array<any>, cols: makeTableCols, thead = true) {
// 恢复 cols 参数初始状态
cols.forEach((col: any[]) => {
col.forEach((c) => {
c.INIT_OK = void 0;
c.key = void 0;
c.colGroup = void 0;
c.HAS_PARENT = void 0;
c.parentKey = void 0;
c.PARENT_COL_INDEX = void 0;
});
});
// cols 转为嵌套结构
const colArrays: any[] = [];
let colIndex = 0;
for (let i1 = 0; i1 < cols.length; i1++) {
const item1 = cols[i1];
for (let i2 = 0; i2 < item1.length; i2++) {
const item2 = item1[i2];
if (!item2) {
item1.splice(i2, 1);
continue;
}
// 合并单元格处理
item2.key = i1 + '-' + i2;
let CHILD_COLS = null;
if (item2.colGroup || (item2.colspan && item2.colspan > 1)) {
item2.colGroup = true;
CHILD_COLS = [];
colIndex++;
let childIndex = 0;
for (let i22 = 0; i22 < cols[i1 + 1].length; i22++) {
const item22 = { ...cols[i1 + 1][i22] };
if (
item22.HAS_PARENT ||
(childIndex > 1 && childIndex == item2.colspan)
) {
cols[i1 + 1][i22] = item22;
continue;
}
item22.HAS_PARENT = true;
item22.parentKey = i1 + '-' + i2;
item22.key = i1 + 1 + '-' + i22;
item22.PARENT_COL_INDEX = colIndex;
CHILD_COLS.push(item22);
childIndex =
childIndex +
Number(item22.colspan && item22.colspan > 1 ? item22.colspan : 1);
cols[i1 + 1][i22] = item22;
}
}
item2.CHILD_COLS = CHILD_COLS;
if (!item2.PARENT_COL_INDEX) {
colArrays.push(item2);
}
cols[i1][i2] = item2;
}
}
// 遍历嵌套结构 cols 的方法
const eachCols = function (callback: Function, arr: Array<any>) {
if (!arr) {
arr = colArrays;
}
for (let i = 0; i < arr.length; i++) {
const item = arr[i];
callback && callback(i, item);
if (item.CHILD_COLS) {
eachCols(callback, item.CHILD_COLS);
}
}
};
// 计算表格宽度
let maxWidth = 1;
let needSetWidth = true;
const colgroupHtml = [] as any;
eachCols((_i: any, c: any) => {
if (!c.colGroup) {
colgroupHtml.push('<col');
if (c.width) {
colgroupHtml.push(` width="${c.width}"`);
}
colgroupHtml.push('/>');
if (c.width && !/\d+%$/.test(String(c.width))) {
maxWidth += c.width + 1;
} else {
needSetWidth = false;
}
}
});
// 表头
const thHtml = cols
.map((cs) => {
const th = cs
.map((c) => {
return `<th
colspan="${c.colspan || 1}"
rowspan="${c.rowspan || 1}"
align="${c.thAlign || c.align || 'left'}"
style="${c.thStyle}">${c.title || ''}
</th>`;
})
.join('');
return '<tr>' + th + '</tr>';
})
.join('');
const headHtml = '<thead>' + thHtml + '</thead>';
// 主体
const trHtml = data
.map((d: any, index: any) => {
const tr = ['<tr>'];
let colIndex = 0;
eachCols((_i: any, c: any) => {
if (!c.colGroup) {
const value = c.field ? d[c.field] : '';
const content = c.templet ? c.templet(d, index, colIndex) : value;
const align = c.align || 'left';
tr.push(`<td align="${align}" style="${c.style}">${content}</td>`);
colIndex++;
}
});
tr.push('</tr>');
return tr.join('');
})
.join('');
const bodyHtml = '<tbody>' + trHtml + '</tbody>';
return `<table
style="width: ${needSetWidth ? maxWidth + 'px' : '100%'};"
class="y-printer-table">
<colgroup>${colgroupHtml.join('')}</colgroup>
${thead ? headHtml : ''} ${bodyHtml}
</table>`;
}
/**
* 打印指定区域
* @param elem HTMLElement
* @param option 参数配置 支持 printThis 所有参数
*/
export function printElement(elem: HTMLElement, option: PrintThisOptions) {
if (!elem) {
return null;
}
const bodyChilds = Array.prototype.filter.call(
document.body.children,
(el) => {
if (
typeof el?.tagName === 'string' &&
['style', 'script', 'link'].includes(el.tagName.toLowerCase())
) {
return false;
}
return true;
}
);
hideElem(bodyChilds, true);
const parntEl = elem.parentNode;
const nextEl = elem.nextElementSibling;
document.body.appendChild(elem);
const pWin = printThis(option);
if (nextEl) {
parntEl?.insertBefore(elem, nextEl);
} else {
parntEl?.appendChild(elem);
}
showElem(bodyChilds);
return pWin;
}