<template>
|
<div v-if="billDataList.length" ref="container" :print-name="printName" class="print-list-container">
|
<template v-for="(item, index) in billDataList">
|
<div v-if="!item.vueData" :key="index" class="margin-20">
|
<el-alert type="error">快递公司未设置出库单打印模板</el-alert>
|
</div>
|
<div v-else :key="index" :style="canvasStyle(item.vueData)" class="canvas-container page-next">
|
<div class="canvas-content">
|
<draggable v-model="item.vueData.fields" :options="{group:'group', ghostClass: 'ghost', filter:'.detail-table'}" class="drag-list">
|
<template v-for="(element, index) in item.vueData.fields">
|
<DraggableResizable :style="'z-index:' + element.options.z" :resizable="false" :draggable="false" :key="index" v-bind.sync="element.options" class="resable-item">
|
<template v-if="element.type=='table'">
|
<table class="detail-table">
|
<thead v-if="element.showHeader">
|
<draggable v-model="element.fields" :class="{'drag-th':true}" :options="{group:'detail', ghostClass: 'ghost', animation:300, chosenClass:'chosen-item'}" tag="tr">
|
<th v-for="(th, thIndex) in element.fields" :key="thIndex" :style="{'border-color':element.borderColor, 'width':th.width+'mm'}">
|
<div :style="{'font-size':th.header.fontSize+'mm', 'line-height':th.header.lineHeight+'mm', 'text-align':th.header.textAlign}" class="cell">{{ th.label }}</div>
|
</th>
|
</draggable>
|
</thead>
|
<tbody>
|
<template v-if="!item.detailList">
|
<tr>
|
<td :colspan="element.fields.length" :style="{'border-color':element.borderColor}" class="no-detail-row">
|
<div class="cell">没有明细数据</div>
|
</td>
|
</tr>
|
</template>
|
<template v-else>
|
<tr v-for="(detailRow, index) in item.detailList" :key="'tr'+index">
|
<td v-for="(field, thIndex) in element.fields" :key="thIndex" :style="{'border-color':element.borderColor}">
|
<div :style="{'font-size':field.body.fontSize+'mm', 'line-height':field.body.lineHeight+'mm', 'text-align':field.body.textAlign}" class="cell">{{ detailRow[common.caseStyle(field.prop)] }}</div>
|
</td>
|
</tr>
|
</template>
|
</tbody>
|
</table>
|
</template>
|
<template v-else-if="element.type=='field'">
|
<div :style="element.styles" class="text-box">
|
<div :style="element.label.styles" class="label">{{ element.label.title }}
|
<template v-if="element.label.showColon">:</template>
|
</div>
|
<div :style="element.text.styles" class="text">{{ item[item.vueData.dataOptions.tableView][common.caseStyle(element.text.prop)] }}</div>
|
</div>
|
</template>
|
<!--一维码-->
|
<template v-else-if="element.type=='barcode'">
|
<div :style="element.styles" class="text-box">
|
<vue-barcode :value="item[item.vueData.dataOptions.tableView][common.caseStyle(element.barcode.value)]" :options="element.barcode.options" />
|
</div>
|
</template>
|
<!--二维码-->
|
<template v-else-if="element.type=='qrcode'">
|
<div :style="element.styles" class="text-box">
|
<vue-qrcode :value="item[item.vueData.dataOptions.tableView][common.caseStyle(element.qrOptions.value)]" :options="element.qrOptions" />
|
</div>
|
</template>
|
<template v-else-if="element.type=='image'">
|
<div :style="element.styles" class="text-box">
|
<img v-if="element.image.imageUrl" :src="element.image.imageUrl" :style="{width:element.image.width+'mm', height:element.image.height+'mm'}" class="img">
|
</div>
|
</template>
|
<template v-else-if="['line-horizontal', 'line-vertical'].indexOf(element.type)>=0">
|
<div :style="element.styles" class="text-box" />
|
</template>
|
<template v-else>
|
<div :style="element.styles" class="text-box">
|
<div :style="element.label.styles" class="label">{{ element.label.title }}</div>
|
</div>
|
</template>
|
</DraggableResizable>
|
</template>
|
</draggable>
|
</div>
|
</div>
|
</template>
|
<el-form class="form padding-20 no-print">
|
<el-form-item>
|
<el-button v-if="!ids" type="primary" @click="printBill">普通打印</el-button>
|
<el-button type="primary" @click="lodopPrint()">LODOP直接打印</el-button>
|
<el-button type="primary" @click="lodopPrint('preview')">LODOP打印预览</el-button>
|
<el-button type="primary" @click="exportPDF">导出PDF</el-button>
|
</el-form-item>
|
</el-form>
|
</div>
|
</template>
|
|
<script>
|
import Draggable from "vuedraggable";
|
import DraggableResizable from "@/components/base/draggable-resizable";
|
import VueBarcode from "@xkeshi/vue-barcode";
|
import VueQrcode from "@chenfengyuan/vue-qrcode";
|
import { getLodop } from "@/utils/LodopFuncs";
|
import html2canvas from "html2canvas";
|
import JsPDF from "jspdf";
|
import $ from "jquery";
|
|
export default {
|
name: "PrintBase",
|
components: {
|
Draggable,
|
DraggableResizable,
|
VueBarcode,
|
VueQrcode
|
},
|
props: {
|
// 单据类型
|
type: {
|
type: String,
|
required: false,
|
default: ""
|
},
|
// 打印单据ID List
|
ids: {
|
type: String,
|
required: false,
|
default: ""
|
},
|
// 打印机名称
|
printName: {
|
type: String,
|
required: false,
|
default: ""
|
},
|
// 自动打印
|
isAutoPrint: {
|
type: Boolean,
|
default: false
|
},
|
// 自动加载数据
|
isAutoLoad: {
|
type: Boolean,
|
default: true
|
}
|
},
|
data() {
|
return {
|
// 打印布局数据结构
|
vueData: {},
|
// 打印参数
|
printInfo: {},
|
// 单据数据列表
|
billDataList: [],
|
BASE_API: process.env.BASE_API
|
};
|
},
|
computed: {
|
// 画布样式设置
|
canvasStyle: function() {
|
return vueData => {
|
var opts = vueData.dataOptions;
|
if (!opts) return {};
|
|
return {
|
width: opts.width + "mm",
|
height: opts.height + "mm",
|
"padding-top": opts.paddingTop + "mm",
|
"padding-bottom": opts.paddingBottom + "mm",
|
"padding-left": opts.paddingLeft + "mm",
|
"padding-right": opts.paddingRight + "mm"
|
};
|
};
|
}
|
},
|
watch: {
|
ids: function() {
|
this.loadModuleData();
|
}
|
},
|
created() {
|
if (this.isAutoLoad) {
|
this.loadModuleData();
|
}
|
},
|
methods: {
|
// 加载模板信息
|
loadModuleData() {
|
var type = this.type || this.$route.params.type;
|
var ids = this.ids || this.$route.params.ids;
|
var url = "/api/sys/print/orderList";
|
// 入库标签打印
|
if (type === "purchase-order") {
|
url = "/api/sys/print/purchaseOrderList";
|
}
|
// 入库标签打印
|
if (type === "eupurchase-order") {
|
url = "/api/sys/print/eupurchaseOrderList";
|
}
|
var params = {
|
ids: ids,
|
type: type
|
};
|
this.common.ajax(
|
url,
|
params,
|
res => {
|
this.common.showMsg(res);
|
if (res.result) {
|
debugger;
|
this.billDataList = res.data;
|
this.billDataList.forEach(item => {
|
if (item.vueData) {
|
item.vueData = JSON.parse(item.vueData);
|
this.vueData = item.vueData;
|
}
|
});
|
// 自动打印
|
if (this.isAutoPrint) {
|
this.$nextTick(() => {
|
this.lodopPrint();
|
});
|
}
|
}
|
},
|
true
|
);
|
},
|
// 打印单据
|
printBill() {
|
window.print();
|
},
|
// lodop打印
|
lodopPrint(type) {
|
var the = this;
|
var width = the.vueData.dataOptions.width + "mm";
|
var height = the.vueData.dataOptions.height + "mm";
|
const LODOP = getLodop();
|
LODOP.SET_LICENSES("达令心潮(北京)商贸有限公司", "B95259F540E2C256132EC7CE6AB55737", "", "");
|
LODOP.PRINT_INITA(0, 0, width, height, "面单打印");
|
LODOP.SET_PRINT_PAGESIZE(1, width, height, "面单打印");
|
|
if ($("div[print-name='" + this.printName + "'].print-list-container .canvas-container").length === 0) {
|
this.$message.error("没有可打印的内容!!!!");
|
return;
|
}
|
$("div[print-name='" + this.printName + "'].print-list-container .canvas-container").each(function(i) {
|
LODOP.NEWPAGE();
|
var content = "<style>" + _style + "</style>" + this.outerHTML;
|
LODOP.ADD_PRINT_HTM("0mm", "0mm", width, height, content);
|
|
var billInfo = the.billDataList[i][the.vueData.dataOptions.tableView];
|
if (!the.vueData.fields) return true;
|
|
the.vueData.fields.forEach(item => {
|
if (item.type === "barcode") {
|
const val = billInfo[the.common.caseStyle(item.barcode.value)];
|
const lodopBarcode = item.barcode.options.lodopBarcode || "128A";
|
if (item.styles["-webkit-transform"] === "rotate(270deg)") {
|
if (the.vueData.dataOptions.title.indexOf("百世") >= 0) {
|
LODOP.ADD_PRINT_BARCODE(
|
item.options.y + item.barcode.options.textMargin - 105,
|
item.options.x + 100,
|
item.barcode.options.height + 10,
|
item.options.w - item.barcode.options.textMargin - 10,
|
lodopBarcode,
|
val
|
);
|
LODOP.SET_PRINT_STYLEA(0, "Angle", 90);
|
} else {
|
// 中通
|
LODOP.ADD_PRINT_BARCODE(item.options.y + item.barcode.options.textMargin - 63, item.options.x + 70, 40, 200, "128A", val);
|
LODOP.SET_PRINT_STYLEA(0, "Angle", 90);
|
}
|
} else {
|
LODOP.ADD_PRINT_BARCODE(
|
item.options.y + item.barcode.options.textMargin + 10,
|
item.options.x,
|
item.options.w - item.barcode.options.textMargin - 10,
|
item.barcode.options.height + 10,
|
lodopBarcode,
|
val
|
);
|
}
|
} else if (item.type === "qrcode") {
|
let val = billInfo[the.common.caseStyle(item.qrOptions.value)];
|
if (!val) {
|
val = item.qrOptions.value;
|
}
|
LODOP.ADD_PRINT_BARCODE(
|
item.options.y + item.qrOptions.margin,
|
item.options.x + item.qrOptions.margin,
|
item.qrOptions.width,
|
item.qrOptions.width,
|
"QRCode",
|
val
|
);
|
}
|
});
|
});
|
// 导出PDF
|
if (type === "pdf") {
|
LODOP.SET_PRINTER_INDEXA("Microsoft print to PDF");
|
LODOP.PRINT();
|
} else {
|
// 指定打印机
|
if (this.printName) {
|
LODOP.SET_PRINTER_INDEXA(this.printName);
|
}
|
if (type === "preview") {
|
LODOP.PREVIEW();
|
} else {
|
LODOP.PRINT();
|
}
|
}
|
},
|
// 导出PDF
|
exportPDF() {
|
// var pdf = new JsPDF();
|
var pdf = new JsPDF();
|
var c = $(".print-list-container").find(".canvas-container");
|
insert(0);
|
|
function insert(index) {
|
html2canvas(c[index]).then(canvas => {
|
var imgData = canvas.toDataURL("image/jpeg", 1.0);
|
pdf.addImage(imgData, "JPEG", 0, 0, canvas.width / 2, canvas.height / 2);
|
if (index < c.length - 1) {
|
pdf.addPage();
|
insert(++index);
|
} else {
|
pdf.save("物流单导出.pdf");
|
}
|
});
|
}
|
}
|
}
|
};
|
|
var _style = `
|
.vdr {
|
position: absolute;
|
-webkit-box-sizing: border-box;
|
box-sizing: border-box;
|
}
|
.handle {
|
-webkit-box-sizing: border-box;
|
box-sizing: border-box;
|
display: none;
|
position: absolute;
|
width: 10px;
|
height: 10px;
|
font-size: 1px;
|
background: #eee;
|
border: 1px solid #333;
|
z-index: 999;
|
}
|
.handle-tl {
|
top: -5px;
|
left: -5px;
|
cursor: nw-resize;
|
}
|
.handle-tm {
|
top: -5px;
|
left: 50%;
|
margin-left: -5px;
|
cursor: n-resize;
|
}
|
.handle-tr {
|
top: -5px;
|
right: -5px;
|
cursor: ne-resize;
|
}
|
.handle-ml {
|
top: 50%;
|
margin-top: -5px;
|
left: -5px;
|
cursor: w-resize;
|
}
|
.handle-mr {
|
top: 50%;
|
margin-top: -5px;
|
right: -5px;
|
cursor: e-resize;
|
}
|
.handle-bl {
|
bottom: -5px;
|
left: -5px;
|
cursor: sw-resize;
|
}
|
.handle-bm {
|
bottom: -5px;
|
left: 50%;
|
margin-left: -5px;
|
cursor: s-resize;
|
}
|
.handle-br {
|
bottom: -5px;
|
right: -5px;
|
cursor: se-resize;
|
}
|
@media only screen and (max-width: 768px) {
|
/* For mobile phones: */
|
[class*="handle-"]:before {
|
content: "";
|
left: -10px;
|
right: -10px;
|
bottom: -10px;
|
top: -10px;
|
position: absolute;
|
}
|
}
|
|
.canvas-container {
|
background-color: white;
|
-webkit-box-shadow: 0px 0px 10px #3d3d3d;
|
box-shadow: 0px 0px 10px #3d3d3d;
|
margin: 0px;
|
}
|
.canvas-container .canvas-content {
|
height: 100%;
|
padding-bottom: 0px;
|
border: 1px none #000000;
|
}
|
.canvas-container .canvas-content .drag-list {
|
height: 100%;
|
position: relative;
|
}
|
.canvas-container .canvas-content .tool {
|
display: none;
|
position: absolute;
|
right: 0;
|
bottom: -30px;
|
}
|
.canvas-container .canvas-content .text-box {
|
overflow: hidden;
|
white-space: nowrap;
|
}
|
.canvas-container .canvas-content .text-box .label {
|
width: 120px;
|
display: inline-block;
|
text-align: right;
|
}
|
.canvas-container .canvas-content .text-box .text {
|
display: inline-block;
|
}
|
.canvas-container .ghost {
|
background: #fff;
|
border: 1px dashed #409eff;
|
}
|
.canvas-container .ghost::after {
|
background: #fff;
|
}
|
.canvas-container li.ghost {
|
height: 30px;
|
list-style: none;
|
font-size: 0;
|
}
|
.canvas-container .detail-table {
|
border-collapse: collapse;
|
border: 1px solid #dcdfe6;
|
background-color: white;
|
width: 100%;
|
}
|
.canvas-container .detail-table th,
|
.canvas-container .detail-table td {
|
font-weight: normal;
|
position: relative;
|
border-collapse: collapse;
|
border: 1px solid #dcdfe6;
|
padding: 3px 10px;
|
}
|
.canvas-container .detail-table th .cell,
|
.canvas-container .detail-table td .cell {
|
min-height: 20px;
|
}
|
.canvas-container .detail-table th.drag-active,
|
.canvas-container .detail-table td.drag-active {
|
background-color: #d5eaff;
|
}
|
.canvas-container .detail-table th.drag-active .tool,
|
.canvas-container .detail-table td.drag-active .tool {
|
display: block;
|
}
|
.canvas-container .detail-table .no-detail-row {
|
text-align: center;
|
color: #888;
|
padding: 20px;
|
}
|
@media print {
|
.canvas-container {
|
-webkit-box-shadow: none;
|
box-shadow: none;
|
margin: 0px;
|
}
|
.canvas-container .canvas-content {
|
border: 0px dotted #000000;
|
}
|
.no-print {
|
display: none;
|
}
|
.page-next {
|
page-break-after: always;
|
}
|
}`;
|
</script>
|
|
<style id="print2" lang="scss" scoped>
|
.canvas-container {
|
background-color: white;
|
-moz-box-shadow: 0px 0px 10px #3d3d3d;
|
-webkit-box-shadow: 0px 0px 10px #3d3d3d;
|
box-shadow: 0px 0px 10px #3d3d3d;
|
margin: 20px;
|
.canvas-content {
|
.drag-list {
|
height: 100%;
|
position: relative;
|
}
|
height: 100%;
|
padding-bottom: 0px;
|
border: 1px none #000000;
|
|
// .resable-item {
|
// &.active {
|
// border: 1px dashed #888;
|
// }
|
// }
|
.tool {
|
display: none;
|
position: absolute;
|
right: 0;
|
bottom: -30px;
|
}
|
// .active {
|
// background-color: rgb(213, 234, 255);
|
// > .tool {
|
// display: block;
|
// }
|
// }
|
.text-box {
|
overflow: hidden;
|
white-space: nowrap;
|
.label {
|
width: 120px;
|
display: inline-block;
|
text-align: right;
|
}
|
.text {
|
display: inline-block;
|
}
|
}
|
}
|
|
.ghost {
|
background: #fff;
|
border: 1px dashed #409eff;
|
|
&::after {
|
background: #fff;
|
}
|
}
|
|
li.ghost {
|
height: 30px;
|
list-style: none;
|
font-size: 0;
|
}
|
|
.detail-table {
|
border-collapse: collapse;
|
border: 1px solid #dcdfe6;
|
background-color: white;
|
width: 100%;
|
th,
|
td {
|
font-weight: normal;
|
position: relative;
|
border-collapse: collapse;
|
border: 1px solid #dcdfe6;
|
padding: 3px 10px;
|
.cell {
|
min-height: 20px;
|
}
|
&.drag-active {
|
background-color: rgb(213, 234, 255);
|
.tool {
|
display: block;
|
}
|
}
|
}
|
.no-detail-row {
|
text-align: center;
|
color: #888;
|
padding: 20px;
|
}
|
}
|
}
|
|
@media print {
|
.canvas-container {
|
-moz-box-shadow: none;
|
-webkit-box-shadow: none;
|
box-shadow: none;
|
margin: 0px;
|
.canvas-content {
|
border: 0px dotted #000000;
|
}
|
}
|
.no-print {
|
display: none;
|
}
|
.page-next {
|
page-break-after: always;
|
}
|
}
|
</style>
|