Update
This commit is contained in:
parent
292f516e33
commit
082be76c51
@ -32,8 +32,8 @@
|
||||
</div>
|
||||
<div class="modal-body d-flex justify-content-center align-items-center">
|
||||
<div class="container">
|
||||
<div class="row" >
|
||||
<div class="col-6 text-center">
|
||||
<div class="row" ref="qrInfo" id="qrInfo">
|
||||
<div class="col-5 text-center">
|
||||
<div class="row">
|
||||
<div id="QrContainer"></div>
|
||||
</div>
|
||||
@ -43,19 +43,24 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 text-center">
|
||||
<div class="col-12 text-center">
|
||||
Department: {{thisQRInfo.departmentName}}
|
||||
<div class="col-7 d-flex align-items-center justify-content-center">
|
||||
<div class="text-center fs-4 text">
|
||||
<div class="col-12 my-3">
|
||||
{{thisQRInfo.departmentName}}
|
||||
</div>
|
||||
<div class="col-12 text-center">
|
||||
Item: {{thisQRInfo.productName}}
|
||||
<div class="col-12 my-3">
|
||||
{{thisQRInfo.productName}}
|
||||
</div>
|
||||
<div class="col-12 text-center">
|
||||
Warranty: {{thisQRInfo.endWDate}}
|
||||
<div class="col-12 my-3">
|
||||
{{thisQRInfo.endWDate}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button v-on:click="printQRInfo">Print QR Info</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -622,11 +627,27 @@
|
||||
});
|
||||
|
||||
$('#itemDatatable tbody').on('click', '.print-btn', function () {
|
||||
const itemId = $(this).data('id');
|
||||
var $row = $(this).closest('tr'); // get the row containing the button
|
||||
var imageSrc = $row.find('img').attr('src'); // find the img element in the row and get its src
|
||||
// console.log(imageSrc);
|
||||
self.printItem(itemId, imageSrc);
|
||||
const $button = $(this); // The clicked button
|
||||
const $row = $button.closest('tr'); // The parent row of the button
|
||||
const itemId = $button.data('id'); // Get the item ID from the button's data attribute
|
||||
|
||||
let imageSrc;
|
||||
|
||||
// Check if the table is collapsed
|
||||
if ($row.hasClass('child')) {
|
||||
// For collapsed view: Look for the closest `.dtr-data` that contains the img
|
||||
imageSrc = $row.prev('tr').find('td:first-child img').attr('src');
|
||||
} else {
|
||||
// For expanded view: Find the img in the first column of the current row
|
||||
imageSrc = $row.find('td:first-child img').attr('src');
|
||||
}
|
||||
|
||||
if (imageSrc) {
|
||||
console.log(imageSrc);
|
||||
self.printItem(itemId, imageSrc); // Call the print function with the itemId and imageSrc
|
||||
} else {
|
||||
console.error("Image source not found.");
|
||||
}
|
||||
});
|
||||
|
||||
this.loading = false;
|
||||
@ -898,6 +919,120 @@
|
||||
}
|
||||
return this.items.find(item => item.uniqueID === uniqueID);
|
||||
},
|
||||
// printQRInfo() {
|
||||
// const qrElement = this.$refs.qrInfo;
|
||||
|
||||
// if (qrElement) {
|
||||
// const qrElement = this.$refs.qrInfo;
|
||||
// if (qrElement) {
|
||||
// domtoimage.toPng(qrElement,{
|
||||
// quality: 1,
|
||||
// })
|
||||
// .then(function (dataUrl) {
|
||||
// // Print the image using printJS
|
||||
// printJS({
|
||||
// printable: dataUrl, // Image data URL
|
||||
// type: 'image',
|
||||
// css: '/../lib/bootstrap/dist/css/bootstrap.css',
|
||||
// style: `
|
||||
// @@media print {
|
||||
// @@page { margin-top: 15px; margin-bottom: 15px; }
|
||||
// body { margin: 0; }
|
||||
// img {
|
||||
// display: block;
|
||||
// margin: auto;
|
||||
// width: auto;
|
||||
// max-width: 100%;
|
||||
// height: auto;
|
||||
// max-height: 100vh;
|
||||
// }
|
||||
// }
|
||||
// `
|
||||
// });
|
||||
// })
|
||||
// .catch(function (error) {
|
||||
// console.error("Error generating image:", error);
|
||||
// });
|
||||
// }
|
||||
// else {
|
||||
// console.error("QR Info element not found.");
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
printQRInfo() {
|
||||
// Create a virtual DOM element
|
||||
const virtualElement = document.createElement('div');
|
||||
virtualElement.style.width = '500px'; // Set dimensions
|
||||
virtualElement.style.height = 'auto';
|
||||
virtualElement.style.position = 'absolute';
|
||||
virtualElement.style.left = '-9999px'; // Position offscreen
|
||||
|
||||
// Populate the virtual DOM with content
|
||||
virtualElement.innerHTML = `
|
||||
<div class="row" id="qrInfo">
|
||||
<div class="col-5 text-center">
|
||||
<div id="QrContainer">
|
||||
<!-- QR Code can be dynamically inserted here -->
|
||||
</div>
|
||||
<div>
|
||||
<p>${this.thisQRInfo.uniqueID}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-7 d-flex align-items-center justify-content-center">
|
||||
<div class="text-center fs-4 text">
|
||||
<div>${this.thisQRInfo.departmentName}</div>
|
||||
<div>${this.thisQRInfo.productName}</div>
|
||||
<div>${this.thisQRInfo.endWDate}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Append the virtual DOM to the body (temporarily)
|
||||
document.body.appendChild(virtualElement);
|
||||
|
||||
// Use html2canvas to convert the virtual DOM to an image
|
||||
html2canvas(virtualElement, {
|
||||
scale: 10, // Increase scale for sharper image
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
}).then((canvas) => {
|
||||
// Convert the canvas to an image
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
|
||||
// Use printJS to print the image
|
||||
printJS({
|
||||
printable: imgData,
|
||||
type: 'image',
|
||||
css: '/../lib/bootstrap/dist/css/bootstrap.css',
|
||||
style: `
|
||||
@@media print {
|
||||
@@page {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
body { margin: 0; }
|
||||
img {
|
||||
display: block;
|
||||
margin: auto;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
max-height: 100vh;
|
||||
}
|
||||
}
|
||||
`
|
||||
});
|
||||
|
||||
// Remove the virtual DOM from the body after use
|
||||
document.body.removeChild(virtualElement);
|
||||
}).catch((error) => {
|
||||
console.error("Error generating image:", error);
|
||||
// Remove the virtual DOM if an error occurs
|
||||
document.body.removeChild(virtualElement);
|
||||
});
|
||||
},
|
||||
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Mono.TextTemplating;
|
||||
@ -19,11 +20,13 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
{
|
||||
private readonly ILogger<InvMainAPI> _logger;
|
||||
private readonly CentralSystemContext _centralDbContext;
|
||||
private readonly UserManager<UserModel> _userManager;
|
||||
|
||||
public InvMainAPI(ILogger<InvMainAPI> logger, CentralSystemContext centralDbContext)
|
||||
public InvMainAPI(ILogger<InvMainAPI> logger, CentralSystemContext centralDbContext, UserManager<UserModel> userManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_centralDbContext = centralDbContext;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public class DepartmentCompany
|
||||
@ -209,8 +212,30 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
[HttpPost("ItemList")]
|
||||
public async Task<IActionResult> ItemList()
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return BadRequest("User not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
user.departmentId = user.departmentId != null ? user.departmentId : 0;
|
||||
}
|
||||
|
||||
var userRole = await _userManager.GetRolesAsync(user);
|
||||
var isAdmin = userRole.Contains("SystemAdmin") || userRole.Contains("SuperAdmin");
|
||||
List<ItemModel> itemList = new List<ItemModel>();
|
||||
// Get the item list
|
||||
var itemList = await _centralDbContext.Items.Include("CreatedBy").Include("Department").Include("Product").ToListAsync();
|
||||
if (isAdmin)
|
||||
{
|
||||
itemList = await _centralDbContext.Items.Include("CreatedBy").Include("Department").Include("Product").ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
itemList = await _centralDbContext.Items.Include("CreatedBy").Include("Department").Include("Product").Where(i => i.DepartmentId == user.departmentId).ToListAsync();
|
||||
}
|
||||
|
||||
// Get the departments list (DepartmentId references Departments)
|
||||
var departments = await _centralDbContext.Departments.ToListAsync();
|
||||
@ -226,7 +251,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
item.SerialNumber,
|
||||
item.Quantity,
|
||||
item.Supplier,
|
||||
item.PurchaseDate,
|
||||
PurchaseDate = item.PurchaseDate.ToString("dd/MM/yyyy"),
|
||||
item.PONo,
|
||||
item.Currency,
|
||||
item.DefaultPrice,
|
||||
@ -234,7 +259,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
item.ConvertPrice,
|
||||
item.DODate,
|
||||
item.Warranty,
|
||||
item.EndWDate,
|
||||
EndWDate = item.EndWDate.ToString("dd/MM/yyyy"),
|
||||
item.InvoiceDate,
|
||||
item.Department?.DepartmentName,
|
||||
item.CreatedBy!.UserName,
|
||||
@ -244,8 +269,11 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
|
||||
return Json(itemListWithDetails);
|
||||
}
|
||||
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("GenerateItemQr/{id}")]
|
||||
public IActionResult GenerateItemQr(string id)
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
<link rel="stylesheet" href="~/assets/libs/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css" />
|
||||
<link rel="stylesheet" href="~/assets/libs/quill/dist/quill.snow.css" />
|
||||
<link href="~/dist/css/style.min.css" rel="stylesheet" />
|
||||
|
||||
<link href="~/lib/printjs/print.min.css" rel="stylesheet" />
|
||||
<!-- DataTables CSS-->
|
||||
<link href="~/lib/datatables/datatables.css" rel="stylesheet" />
|
||||
<!-- Vue Js -->
|
||||
@ -740,6 +740,9 @@
|
||||
<script src="~/assets/libs/jquery-minicolors/jquery.minicolors.min.js"></script>
|
||||
<script src="~/assets/libs/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js"></script>
|
||||
<script src="~/assets/libs/quill/dist/quill.min.js"></script>
|
||||
<script src="~/lib/printjs/print.min.js"></script>
|
||||
<script src="~/lib/html2canvas/html2canvas.min.js"></script>
|
||||
<script src="~/lib/html2canvas/dom-to-image.min.js"></script>
|
||||
<!-- Datatables JS-->
|
||||
<script src="~/lib/datatables/datatables.js"></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
|
||||
29
wwwroot/lib/PrintJs/browser.js
Normal file
29
wwwroot/lib/PrintJs/browser.js
Normal file
@ -0,0 +1,29 @@
|
||||
const Browser = {
|
||||
// Firefox 1.0+
|
||||
isFirefox: () => {
|
||||
return typeof InstallTrigger !== 'undefined'
|
||||
},
|
||||
// Internet Explorer 6-11
|
||||
isIE: () => {
|
||||
return navigator.userAgent.indexOf('MSIE') !== -1 || !!document.documentMode
|
||||
},
|
||||
// Edge 20+
|
||||
isEdge: () => {
|
||||
return !Browser.isIE() && !!window.StyleMedia
|
||||
},
|
||||
// Chrome 1+
|
||||
isChrome: (context = window) => {
|
||||
return !!context.chrome
|
||||
},
|
||||
// At least Safari 3+: "[object HTMLElementConstructor]"
|
||||
isSafari: () => {
|
||||
return Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0 ||
|
||||
navigator.userAgent.toLowerCase().indexOf('safari') !== -1
|
||||
},
|
||||
// IOS Chrome
|
||||
isIOSChrome: () => {
|
||||
return navigator.userAgent.toLowerCase().indexOf('crios') !== -1
|
||||
}
|
||||
}
|
||||
|
||||
export default Browser
|
||||
103
wwwroot/lib/PrintJs/functions.js
Normal file
103
wwwroot/lib/PrintJs/functions.js
Normal file
@ -0,0 +1,103 @@
|
||||
import Modal from './modal'
|
||||
import Browser from './browser'
|
||||
|
||||
export function addWrapper (htmlData, params) {
|
||||
const bodyStyle = 'font-family:' + params.font + ' !important; font-size: ' + params.font_size + ' !important; width:100%;'
|
||||
return '<div style="' + bodyStyle + '">' + htmlData + '</div>'
|
||||
}
|
||||
|
||||
export function capitalizePrint (obj) {
|
||||
return obj.charAt(0).toUpperCase() + obj.slice(1)
|
||||
}
|
||||
|
||||
export function collectStyles (element, params) {
|
||||
const win = document.defaultView || window
|
||||
|
||||
// String variable to hold styling for each element
|
||||
let elementStyle = ''
|
||||
|
||||
// Loop over computed styles
|
||||
const styles = win.getComputedStyle(element, '')
|
||||
|
||||
for (let key = 0; key < styles.length; key++) {
|
||||
// Check if style should be processed
|
||||
if (params.targetStyles.indexOf('*') !== -1 || params.targetStyle.indexOf(styles[key]) !== -1 || targetStylesMatch(params.targetStyles, styles[key])) {
|
||||
if (styles.getPropertyValue(styles[key])) elementStyle += styles[key] + ':' + styles.getPropertyValue(styles[key]) + ';'
|
||||
}
|
||||
}
|
||||
|
||||
// Print friendly defaults (deprecated)
|
||||
elementStyle += 'max-width: ' + params.maxWidth + 'px !important; font-size: ' + params.font_size + ' !important;'
|
||||
|
||||
return elementStyle
|
||||
}
|
||||
|
||||
function targetStylesMatch (styles, value) {
|
||||
for (let i = 0; i < styles.length; i++) {
|
||||
if (typeof value === 'object' && value.indexOf(styles[i]) !== -1) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function addHeader (printElement, params) {
|
||||
// Create the header container div
|
||||
const headerContainer = document.createElement('div')
|
||||
|
||||
// Check if the header is text or raw html
|
||||
if (isRawHTML(params.header)) {
|
||||
headerContainer.innerHTML = params.header
|
||||
} else {
|
||||
// Create header element
|
||||
const headerElement = document.createElement('h1')
|
||||
|
||||
// Create header text node
|
||||
const headerNode = document.createTextNode(params.header)
|
||||
|
||||
// Build and style
|
||||
headerElement.appendChild(headerNode)
|
||||
headerElement.setAttribute('style', params.headerStyle)
|
||||
headerContainer.appendChild(headerElement)
|
||||
}
|
||||
|
||||
printElement.insertBefore(headerContainer, printElement.childNodes[0])
|
||||
}
|
||||
|
||||
export function cleanUp (params) {
|
||||
// If we are showing a feedback message to user, remove it
|
||||
if (params.showModal) Modal.close()
|
||||
|
||||
// Check for a finished loading hook function
|
||||
if (params.onLoadingEnd) params.onLoadingEnd()
|
||||
|
||||
// If preloading pdf files, clean blob url
|
||||
if (params.showModal || params.onLoadingStart) window.URL.revokeObjectURL(params.printable)
|
||||
|
||||
// Run onPrintDialogClose callback
|
||||
let event = 'mouseover'
|
||||
|
||||
if (Browser.isChrome() || Browser.isFirefox()) {
|
||||
// Ps.: Firefox will require an extra click in the document to fire the focus event.
|
||||
event = 'focus'
|
||||
}
|
||||
|
||||
const handler = () => {
|
||||
// Make sure the event only happens once.
|
||||
window.removeEventListener(event, handler)
|
||||
|
||||
params.onPrintDialogClose()
|
||||
|
||||
// Remove iframe from the DOM
|
||||
const iframe = document.getElementById(params.frameId)
|
||||
|
||||
if (iframe) {
|
||||
iframe.remove()
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener(event, handler)
|
||||
}
|
||||
|
||||
export function isRawHTML (raw) {
|
||||
const regexHtml = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*>(.*?)</\\1>')
|
||||
return regexHtml.test(raw)
|
||||
}
|
||||
65
wwwroot/lib/PrintJs/html.js
Normal file
65
wwwroot/lib/PrintJs/html.js
Normal file
@ -0,0 +1,65 @@
|
||||
import { collectStyles, addHeader } from './functions'
|
||||
import Print from './print'
|
||||
|
||||
export default {
|
||||
print: (params, printFrame) => {
|
||||
// Get the DOM printable element
|
||||
const printElement = document.getElementById(params.printable)
|
||||
|
||||
// Check if the element exists
|
||||
if (!printElement) {
|
||||
window.console.error('Invalid HTML element id: ' + params.printable)
|
||||
return
|
||||
}
|
||||
|
||||
// Clone the target element including its children (if available)
|
||||
params.printableElement = cloneElement(printElement, params)
|
||||
|
||||
// Add header
|
||||
if (params.header) {
|
||||
addHeader(params.printableElement, params)
|
||||
}
|
||||
|
||||
// Print html element contents
|
||||
Print.send(params, printFrame)
|
||||
}
|
||||
}
|
||||
|
||||
function cloneElement (element, params) {
|
||||
// Clone the main node (if not already inside the recursion process)
|
||||
const clone = element.cloneNode()
|
||||
|
||||
// Loop over and process the children elements / nodes (including text nodes)
|
||||
const childNodesArray = Array.prototype.slice.call(element.childNodes)
|
||||
for (let i = 0; i < childNodesArray.length; i++) {
|
||||
// Check if we are skiping the current element
|
||||
if (params.ignoreElements.indexOf(childNodesArray[i].id) !== -1) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Clone the child element
|
||||
const clonedChild = cloneElement(childNodesArray[i], params)
|
||||
|
||||
// Attach the cloned child to the cloned parent node
|
||||
clone.appendChild(clonedChild)
|
||||
}
|
||||
|
||||
// Get all styling for print element (for nodes of type element only)
|
||||
if (params.scanStyles && element.nodeType === 1) {
|
||||
clone.setAttribute('style', collectStyles(element, params))
|
||||
}
|
||||
|
||||
// Check if the element needs any state processing (copy user input data)
|
||||
switch (element.tagName) {
|
||||
case 'SELECT':
|
||||
// Copy the current selection value to its clone
|
||||
clone.value = element.value
|
||||
break
|
||||
case 'CANVAS':
|
||||
// Copy the canvas content to its clone
|
||||
clone.getContext('2d').drawImage(element, 0, 0)
|
||||
break
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
48
wwwroot/lib/PrintJs/image.js
Normal file
48
wwwroot/lib/PrintJs/image.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { addHeader } from './functions'
|
||||
import Print from './print'
|
||||
import Browser from './browser'
|
||||
|
||||
export default {
|
||||
print: (params, printFrame) => {
|
||||
// Check if we are printing one image or multiple images
|
||||
if (params.printable.constructor !== Array) {
|
||||
// Create array with one image
|
||||
params.printable = [params.printable]
|
||||
}
|
||||
|
||||
// Create printable element (container)
|
||||
params.printableElement = document.createElement('div')
|
||||
|
||||
// Create all image elements and append them to the printable container
|
||||
params.printable.forEach(src => {
|
||||
// Create the image element
|
||||
const img = document.createElement('img')
|
||||
img.setAttribute('style', params.imageStyle)
|
||||
|
||||
// Set image src with the file url
|
||||
img.src = src
|
||||
|
||||
// The following block is for Firefox, which for some reason requires the image's src to be fully qualified in
|
||||
// order to print it
|
||||
if (Browser.isFirefox()) {
|
||||
const fullyQualifiedSrc = img.src
|
||||
img.src = fullyQualifiedSrc
|
||||
}
|
||||
|
||||
// Create the image wrapper
|
||||
const imageWrapper = document.createElement('div')
|
||||
|
||||
// Append image to the wrapper element
|
||||
imageWrapper.appendChild(img)
|
||||
|
||||
// Append wrapper to the printable element
|
||||
params.printableElement.appendChild(imageWrapper)
|
||||
})
|
||||
|
||||
// Check if we are adding a print header
|
||||
if (params.header) addHeader(params.printableElement, params)
|
||||
|
||||
// Print image
|
||||
Print.send(params, printFrame)
|
||||
}
|
||||
}
|
||||
168
wwwroot/lib/PrintJs/init.js
Normal file
168
wwwroot/lib/PrintJs/init.js
Normal file
@ -0,0 +1,168 @@
|
||||
'use strict'
|
||||
|
||||
import Browser from './browser'
|
||||
import Modal from './modal'
|
||||
import Pdf from './pdf'
|
||||
import Html from './html'
|
||||
import RawHtml from './raw-html'
|
||||
import Image from './image'
|
||||
import Json from './json'
|
||||
|
||||
const printTypes = ['pdf', 'html', 'image', 'json', 'raw-html']
|
||||
|
||||
export default {
|
||||
init () {
|
||||
const params = {
|
||||
printable: null,
|
||||
fallbackPrintable: null,
|
||||
type: 'pdf',
|
||||
header: null,
|
||||
headerStyle: 'font-weight: 300;',
|
||||
maxWidth: 800,
|
||||
properties: null,
|
||||
gridHeaderStyle: 'font-weight: bold; padding: 5px; border: 1px solid #dddddd;',
|
||||
gridStyle: 'border: 1px solid lightgray; margin-bottom: -1px;',
|
||||
showModal: false,
|
||||
onError: (error) => { throw error },
|
||||
onLoadingStart: null,
|
||||
onLoadingEnd: null,
|
||||
onPrintDialogClose: () => {},
|
||||
onIncompatibleBrowser: () => {},
|
||||
modalMessage: 'Retrieving Document...',
|
||||
frameId: 'printJS',
|
||||
printableElement: null,
|
||||
documentTitle: 'Document',
|
||||
targetStyle: ['clear', 'display', 'width', 'min-width', 'height', 'min-height', 'max-height'],
|
||||
targetStyles: ['border', 'box', 'break', 'text-decoration'],
|
||||
ignoreElements: [],
|
||||
repeatTableHeader: true,
|
||||
css: null,
|
||||
style: null,
|
||||
scanStyles: true,
|
||||
base64: false,
|
||||
|
||||
// Deprecated
|
||||
onPdfOpen: null,
|
||||
font: 'TimesNewRoman',
|
||||
font_size: '12pt',
|
||||
honorMarginPadding: true,
|
||||
honorColor: false,
|
||||
imageStyle: 'max-width: 100%;'
|
||||
}
|
||||
|
||||
// Check if a printable document or object was supplied
|
||||
const args = arguments[0]
|
||||
if (args === undefined) {
|
||||
throw new Error('printJS expects at least 1 attribute.')
|
||||
}
|
||||
|
||||
// Process parameters
|
||||
switch (typeof args) {
|
||||
case 'string':
|
||||
params.printable = encodeURI(args)
|
||||
params.fallbackPrintable = params.printable
|
||||
params.type = arguments[1] || params.type
|
||||
break
|
||||
case 'object':
|
||||
params.printable = args.printable
|
||||
params.fallbackPrintable = typeof args.fallbackPrintable !== 'undefined' ? args.fallbackPrintable : params.printable
|
||||
params.fallbackPrintable = params.base64 ? `data:application/pdf;base64,${params.fallbackPrintable}` : params.fallbackPrintable
|
||||
for (var k in params) {
|
||||
if (k === 'printable' || k === 'fallbackPrintable') continue
|
||||
|
||||
params[k] = typeof args[k] !== 'undefined' ? args[k] : params[k]
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error('Unexpected argument type! Expected "string" or "object", got ' + typeof args)
|
||||
}
|
||||
|
||||
// Validate printable
|
||||
if (!params.printable) throw new Error('Missing printable information.')
|
||||
|
||||
// Validate type
|
||||
if (!params.type || typeof params.type !== 'string' || printTypes.indexOf(params.type.toLowerCase()) === -1) {
|
||||
throw new Error('Invalid print type. Available types are: pdf, html, image and json.')
|
||||
}
|
||||
|
||||
// Check if we are showing a feedback message to the user (useful for large files)
|
||||
if (params.showModal) Modal.show(params)
|
||||
|
||||
// Check for a print start hook function
|
||||
if (params.onLoadingStart) params.onLoadingStart()
|
||||
|
||||
// To prevent duplication and issues, remove any used printFrame from the DOM
|
||||
const usedFrame = document.getElementById(params.frameId)
|
||||
|
||||
if (usedFrame) usedFrame.parentNode.removeChild(usedFrame)
|
||||
|
||||
// Create a new iframe for the print job
|
||||
const printFrame = document.createElement('iframe')
|
||||
|
||||
if (Browser.isFirefox()) {
|
||||
// Set the iframe to be is visible on the page (guaranteed by fixed position) but hidden using opacity 0, because
|
||||
// this works in Firefox. The height needs to be sufficient for some part of the document other than the PDF
|
||||
// viewer's toolbar to be visible in the page
|
||||
printFrame.setAttribute('style', 'width: 1px; height: 100px; position: fixed; left: 0; top: 0; opacity: 0; border-width: 0; margin: 0; padding: 0')
|
||||
} else {
|
||||
// Hide the iframe in other browsers
|
||||
printFrame.setAttribute('style', 'visibility: hidden; height: 0; width: 0; position: absolute; border: 0')
|
||||
}
|
||||
|
||||
// Set iframe element id
|
||||
printFrame.setAttribute('id', params.frameId)
|
||||
|
||||
// For non pdf printing, pass an html document string to srcdoc (force onload callback)
|
||||
if (params.type !== 'pdf') {
|
||||
printFrame.srcdoc = '<html><head><title>' + params.documentTitle + '</title>'
|
||||
|
||||
// Attach css files
|
||||
if (params.css) {
|
||||
// Add support for single file
|
||||
if (!Array.isArray(params.css)) params.css = [params.css]
|
||||
|
||||
// Create link tags for each css file
|
||||
params.css.forEach(file => {
|
||||
printFrame.srcdoc += '<link rel="stylesheet" href="' + file + '">'
|
||||
})
|
||||
}
|
||||
|
||||
printFrame.srcdoc += '</head><body></body></html>'
|
||||
}
|
||||
|
||||
// Check printable type
|
||||
switch (params.type) {
|
||||
case 'pdf':
|
||||
// Check browser support for pdf and if not supported we will just open the pdf file instead
|
||||
if (Browser.isIE()) {
|
||||
try {
|
||||
console.info('Print.js doesn\'t support PDF printing in Internet Explorer.')
|
||||
const win = window.open(params.fallbackPrintable, '_blank')
|
||||
win.focus()
|
||||
params.onIncompatibleBrowser()
|
||||
} catch (error) {
|
||||
params.onError(error)
|
||||
} finally {
|
||||
// Make sure there is no loading modal opened
|
||||
if (params.showModal) Modal.close()
|
||||
if (params.onLoadingEnd) params.onLoadingEnd()
|
||||
}
|
||||
} else {
|
||||
Pdf.print(params, printFrame)
|
||||
}
|
||||
break
|
||||
case 'image':
|
||||
Image.print(params, printFrame)
|
||||
break
|
||||
case 'html':
|
||||
Html.print(params, printFrame)
|
||||
break
|
||||
case 'raw-html':
|
||||
RawHtml.print(params, printFrame)
|
||||
break
|
||||
case 'json':
|
||||
Json.print(params, printFrame)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
109
wwwroot/lib/PrintJs/json.js
Normal file
109
wwwroot/lib/PrintJs/json.js
Normal file
@ -0,0 +1,109 @@
|
||||
import { capitalizePrint, addHeader } from './functions'
|
||||
import Print from './print'
|
||||
|
||||
export default {
|
||||
print: (params, printFrame) => {
|
||||
// Check if we received proper data
|
||||
if (typeof params.printable !== 'object') {
|
||||
throw new Error('Invalid javascript data object (JSON).')
|
||||
}
|
||||
|
||||
// Validate repeatTableHeader
|
||||
if (typeof params.repeatTableHeader !== 'boolean') {
|
||||
throw new Error('Invalid value for repeatTableHeader attribute (JSON).')
|
||||
}
|
||||
|
||||
// Validate properties
|
||||
if (!params.properties || !Array.isArray(params.properties)) {
|
||||
throw new Error('Invalid properties array for your JSON data.')
|
||||
}
|
||||
|
||||
// We will format the property objects to keep the JSON api compatible with older releases
|
||||
params.properties = params.properties.map(property => {
|
||||
return {
|
||||
field: typeof property === 'object' ? property.field : property,
|
||||
displayName: typeof property === 'object' ? property.displayName : property,
|
||||
columnSize: typeof property === 'object' && property.columnSize ? property.columnSize + ';' : 100 / params.properties.length + '%;'
|
||||
}
|
||||
})
|
||||
|
||||
// Create a print container element
|
||||
params.printableElement = document.createElement('div')
|
||||
|
||||
// Check if we are adding a print header
|
||||
if (params.header) {
|
||||
addHeader(params.printableElement, params)
|
||||
}
|
||||
|
||||
// Build the printable html data
|
||||
params.printableElement.innerHTML += jsonToHTML(params)
|
||||
|
||||
// Print the json data
|
||||
Print.send(params, printFrame)
|
||||
}
|
||||
}
|
||||
|
||||
function jsonToHTML (params) {
|
||||
// Get the row and column data
|
||||
const data = params.printable
|
||||
const properties = params.properties
|
||||
|
||||
// Create a html table
|
||||
let htmlData = '<table style="border-collapse: collapse; width: 100%;">'
|
||||
|
||||
// Check if the header should be repeated
|
||||
if (params.repeatTableHeader) {
|
||||
htmlData += '<thead>'
|
||||
}
|
||||
|
||||
// Add the table header row
|
||||
htmlData += '<tr>'
|
||||
|
||||
// Add the table header columns
|
||||
for (let a = 0; a < properties.length; a++) {
|
||||
htmlData += '<th style="width:' + properties[a].columnSize + ';' + params.gridHeaderStyle + '">' + capitalizePrint(properties[a].displayName) + '</th>'
|
||||
}
|
||||
|
||||
// Add the closing tag for the table header row
|
||||
htmlData += '</tr>'
|
||||
|
||||
// If the table header is marked as repeated, add the closing tag
|
||||
if (params.repeatTableHeader) {
|
||||
htmlData += '</thead>'
|
||||
}
|
||||
|
||||
// Create the table body
|
||||
htmlData += '<tbody>'
|
||||
|
||||
// Add the table data rows
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
// Add the row starting tag
|
||||
htmlData += '<tr>'
|
||||
|
||||
// Print selected properties only
|
||||
for (let n = 0; n < properties.length; n++) {
|
||||
let stringData = data[i]
|
||||
|
||||
// Support nested objects
|
||||
const property = properties[n].field.split('.')
|
||||
if (property.length > 1) {
|
||||
for (let p = 0; p < property.length; p++) {
|
||||
stringData = stringData[property[p]]
|
||||
}
|
||||
} else {
|
||||
stringData = stringData[properties[n].field]
|
||||
}
|
||||
|
||||
// Add the row contents and styles
|
||||
htmlData += '<td style="width:' + properties[n].columnSize + params.gridStyle + '">' + stringData + '</td>'
|
||||
}
|
||||
|
||||
// Add the row closing tag
|
||||
htmlData += '</tr>'
|
||||
}
|
||||
|
||||
// Add the table and body closing tags
|
||||
htmlData += '</tbody></table>'
|
||||
|
||||
return htmlData
|
||||
}
|
||||
62
wwwroot/lib/PrintJs/modal.js
Normal file
62
wwwroot/lib/PrintJs/modal.js
Normal file
@ -0,0 +1,62 @@
|
||||
const Modal = {
|
||||
show (params) {
|
||||
// Build modal
|
||||
const modalStyle = 'font-family:sans-serif; ' +
|
||||
'display:table; ' +
|
||||
'text-align:center; ' +
|
||||
'font-weight:300; ' +
|
||||
'font-size:30px; ' +
|
||||
'left:0; top:0;' +
|
||||
'position:fixed; ' +
|
||||
'z-index: 9990;' +
|
||||
'color: #0460B5; ' +
|
||||
'width: 100%; ' +
|
||||
'height: 100%; ' +
|
||||
'background-color:rgba(255,255,255,.9);' +
|
||||
'transition: opacity .3s ease;'
|
||||
|
||||
// Create wrapper
|
||||
const printModal = document.createElement('div')
|
||||
printModal.setAttribute('style', modalStyle)
|
||||
printModal.setAttribute('id', 'printJS-Modal')
|
||||
|
||||
// Create content div
|
||||
const contentDiv = document.createElement('div')
|
||||
contentDiv.setAttribute('style', 'display:table-cell; vertical-align:middle; padding-bottom:100px;')
|
||||
|
||||
// Add close button (requires print.css)
|
||||
const closeButton = document.createElement('div')
|
||||
closeButton.setAttribute('class', 'printClose')
|
||||
closeButton.setAttribute('id', 'printClose')
|
||||
contentDiv.appendChild(closeButton)
|
||||
|
||||
// Add spinner (requires print.css)
|
||||
const spinner = document.createElement('span')
|
||||
spinner.setAttribute('class', 'printSpinner')
|
||||
contentDiv.appendChild(spinner)
|
||||
|
||||
// Add message
|
||||
const messageNode = document.createTextNode(params.modalMessage)
|
||||
contentDiv.appendChild(messageNode)
|
||||
|
||||
// Add contentDiv to printModal
|
||||
printModal.appendChild(contentDiv)
|
||||
|
||||
// Append print modal element to document body
|
||||
document.getElementsByTagName('body')[0].appendChild(printModal)
|
||||
|
||||
// Add event listener to close button
|
||||
document.getElementById('printClose').addEventListener('click', function () {
|
||||
Modal.close()
|
||||
})
|
||||
},
|
||||
close () {
|
||||
const printModal = document.getElementById('printJS-Modal')
|
||||
|
||||
if (printModal) {
|
||||
printModal.parentNode.removeChild(printModal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Modal
|
||||
57
wwwroot/lib/PrintJs/pdf.js
Normal file
57
wwwroot/lib/PrintJs/pdf.js
Normal file
@ -0,0 +1,57 @@
|
||||
import Print from './print'
|
||||
import { cleanUp } from './functions'
|
||||
|
||||
export default {
|
||||
print: (params, printFrame) => {
|
||||
// Check if we have base64 data
|
||||
if (params.base64) {
|
||||
const bytesArray = Uint8Array.from(atob(params.printable), c => c.charCodeAt(0))
|
||||
createBlobAndPrint(params, printFrame, bytesArray)
|
||||
return
|
||||
}
|
||||
|
||||
// Format pdf url
|
||||
params.printable = /^(blob|http|\/\/)/i.test(params.printable)
|
||||
? params.printable
|
||||
: window.location.origin + (params.printable.charAt(0) !== '/' ? '/' + params.printable : params.printable)
|
||||
|
||||
// Get the file through a http request (Preload)
|
||||
const req = new window.XMLHttpRequest()
|
||||
req.responseType = 'arraybuffer'
|
||||
|
||||
req.addEventListener('error', () => {
|
||||
cleanUp(params)
|
||||
params.onError(req.statusText)
|
||||
|
||||
// Since we don't have a pdf document available, we will stop the print job
|
||||
})
|
||||
|
||||
req.addEventListener('load', () => {
|
||||
// Check for errors
|
||||
if ([200, 201].indexOf(req.status) === -1) {
|
||||
cleanUp(params)
|
||||
params.onError(req.statusText)
|
||||
|
||||
// Since we don't have a pdf document available, we will stop the print job
|
||||
return
|
||||
}
|
||||
|
||||
// Print requested document
|
||||
createBlobAndPrint(params, printFrame, req.response)
|
||||
})
|
||||
|
||||
req.open('GET', params.printable, true)
|
||||
req.send()
|
||||
}
|
||||
}
|
||||
|
||||
function createBlobAndPrint (params, printFrame, data) {
|
||||
// Pass response or base64 data to a blob and create a local object url
|
||||
let localPdf = new window.Blob([data], { type: 'application/pdf' })
|
||||
localPdf = window.URL.createObjectURL(localPdf)
|
||||
|
||||
// Set iframe src with pdf document url
|
||||
printFrame.setAttribute('src', localPdf)
|
||||
|
||||
Print.send(params, printFrame)
|
||||
}
|
||||
102
wwwroot/lib/PrintJs/print.js
Normal file
102
wwwroot/lib/PrintJs/print.js
Normal file
@ -0,0 +1,102 @@
|
||||
import Browser from './browser'
|
||||
import { cleanUp } from './functions'
|
||||
|
||||
const Print = {
|
||||
send: (params, printFrame) => {
|
||||
// Append iframe element to document body
|
||||
document.getElementsByTagName('body')[0].appendChild(printFrame)
|
||||
|
||||
// Get iframe element
|
||||
const iframeElement = document.getElementById(params.frameId)
|
||||
|
||||
// Wait for iframe to load all content
|
||||
iframeElement.onload = () => {
|
||||
if (params.type === 'pdf') {
|
||||
// Add a delay for Firefox. In my tests, 1000ms was sufficient but 100ms was not
|
||||
if (Browser.isFirefox()) {
|
||||
setTimeout(() => performPrint(iframeElement, params), 1000)
|
||||
} else {
|
||||
performPrint(iframeElement, params)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get iframe element document
|
||||
let printDocument = (iframeElement.contentWindow || iframeElement.contentDocument)
|
||||
if (printDocument.document) printDocument = printDocument.document
|
||||
|
||||
// Append printable element to the iframe body
|
||||
printDocument.body.appendChild(params.printableElement)
|
||||
|
||||
// Add custom style
|
||||
if (params.type !== 'pdf' && params.style) {
|
||||
// Create style element
|
||||
const style = document.createElement('style')
|
||||
style.innerHTML = params.style
|
||||
|
||||
// Append style element to iframe's head
|
||||
printDocument.head.appendChild(style)
|
||||
}
|
||||
|
||||
// If printing images, wait for them to load inside the iframe
|
||||
const images = printDocument.getElementsByTagName('img')
|
||||
|
||||
if (images.length > 0) {
|
||||
loadIframeImages(Array.from(images)).then(() => performPrint(iframeElement, params))
|
||||
} else {
|
||||
performPrint(iframeElement, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function performPrint (iframeElement, params) {
|
||||
try {
|
||||
iframeElement.focus()
|
||||
|
||||
// If Edge or IE, try catch with execCommand
|
||||
if (Browser.isEdge() || Browser.isIE()) {
|
||||
try {
|
||||
iframeElement.contentWindow.document.execCommand('print', false, null)
|
||||
} catch (e) {
|
||||
iframeElement.contentWindow.print()
|
||||
}
|
||||
} else {
|
||||
// Other browsers
|
||||
iframeElement.contentWindow.print()
|
||||
}
|
||||
} catch (error) {
|
||||
params.onError(error)
|
||||
} finally {
|
||||
if (Browser.isFirefox()) {
|
||||
// Move the iframe element off-screen and make it invisible
|
||||
iframeElement.style.visibility = 'hidden'
|
||||
iframeElement.style.left = '-1px'
|
||||
}
|
||||
|
||||
cleanUp(params)
|
||||
}
|
||||
}
|
||||
|
||||
function loadIframeImages (images) {
|
||||
const promises = images.map(image => {
|
||||
if (image.src && image.src !== window.location.href) {
|
||||
return loadIframeImage(image)
|
||||
}
|
||||
})
|
||||
|
||||
return Promise.all(promises)
|
||||
}
|
||||
|
||||
function loadIframeImage (image) {
|
||||
return new Promise(resolve => {
|
||||
const pollImage = () => {
|
||||
!image || typeof image.naturalWidth === 'undefined' || image.naturalWidth === 0 || !image.complete
|
||||
? setTimeout(pollImage, 500)
|
||||
: resolve()
|
||||
}
|
||||
pollImage()
|
||||
})
|
||||
}
|
||||
|
||||
export default Print
|
||||
1
wwwroot/lib/PrintJs/print.min.css
vendored
Normal file
1
wwwroot/lib/PrintJs/print.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.printModal{font-family:sans-serif;display:flex;text-align:center;font-weight:300;font-size:30px;left:0;top:0;position:absolute;color:#045fb4;width:100%;height:100%;background-color:hsla(0,0%,100%,.9)}.printClose{position:absolute;right:10px;top:10px}.printClose:before{content:"\00D7";font-family:Helvetica Neue,sans-serif;font-weight:100;line-height:1px;padding-top:.5em;display:block;font-size:2em;text-indent:1px;overflow:hidden;height:1.25em;width:1.25em;text-align:center;cursor:pointer}.printSpinner{margin-top:3px;margin-left:-40px;position:absolute;display:inline-block;width:25px;height:25px;border:2px solid #045fb4;border-radius:50%;animation:spin .75s linear infinite}.printSpinner:after,.printSpinner:before{left:-2px;top:-2px;display:none;position:absolute;content:"";width:inherit;height:inherit;border:inherit;border-radius:inherit}.printSpinner,.printSpinner:after,.printSpinner:before{display:inline-block;border-color:#045fb4 transparent transparent;animation-duration:1.2s}.printSpinner:before{transform:rotate(120deg)}.printSpinner:after{transform:rotate(240deg)}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}
|
||||
1
wwwroot/lib/PrintJs/print.min.js
vendored
Normal file
1
wwwroot/lib/PrintJs/print.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
15
wwwroot/lib/PrintJs/raw-html.js
Normal file
15
wwwroot/lib/PrintJs/raw-html.js
Normal file
@ -0,0 +1,15 @@
|
||||
import Print from './print'
|
||||
|
||||
export default {
|
||||
print: (params, printFrame) => {
|
||||
// Create printable element (container)
|
||||
params.printableElement = document.createElement('div')
|
||||
params.printableElement.setAttribute('style', 'width:100%')
|
||||
|
||||
// Set our raw html as the printable element inner html content
|
||||
params.printableElement.innerHTML = params.printable
|
||||
|
||||
// Print html contents
|
||||
Print.send(params, printFrame)
|
||||
}
|
||||
}
|
||||
2
wwwroot/lib/html2canvas/dom-to-image.min.js
vendored
Normal file
2
wwwroot/lib/html2canvas/dom-to-image.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7830
wwwroot/lib/html2canvas/html2canvas.js
Normal file
7830
wwwroot/lib/html2canvas/html2canvas.js
Normal file
File diff suppressed because one or more lines are too long
20
wwwroot/lib/html2canvas/html2canvas.min.js
vendored
Normal file
20
wwwroot/lib/html2canvas/html2canvas.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user