399 lines
14 KiB
Vue
399 lines
14 KiB
Vue
|
<template>
|
||
|
<div class="_fd-print">
|
||
|
<el-tooltip
|
||
|
effect="dark"
|
||
|
:content="t('designer.print.title')"
|
||
|
placement="top"
|
||
|
:hide-after="0"
|
||
|
>
|
||
|
<i class="fc-icon icon-print" @click="open"></i>
|
||
|
</el-tooltip>
|
||
|
<el-dialog class="_fd-print-dialog _fd-config-dialog" v-model="visible" destroy-on-close
|
||
|
:close-on-click-modal="false"
|
||
|
:title="t('designer.print.title')"
|
||
|
append-to-body
|
||
|
width="1080px">
|
||
|
<el-container class="_fd-print-con" style="height: 600px">
|
||
|
<el-aside style="width:255px;">
|
||
|
<el-container class="_fd-print-l">
|
||
|
<el-header class="_fd-print-head" height="40px">
|
||
|
{{ t('designer.print.config') }}
|
||
|
</el-header>
|
||
|
<el-main>
|
||
|
<el-form label-position="top" size="small">
|
||
|
<el-form-item :label="t('props.mode')">
|
||
|
<el-radio-group v-model="formData.type">
|
||
|
<el-radio-button value="form">{{ t('form.formMode') }}</el-radio-button>
|
||
|
<el-radio-button value="read">{{ t('form.previewMode') }}</el-radio-button>
|
||
|
</el-radio-group>
|
||
|
</el-form-item>
|
||
|
<el-form-item :label="t('props.style')">
|
||
|
<el-radio-group v-model="formData.style">
|
||
|
<el-radio-button value="default">{{
|
||
|
t('designer.print.defaultStyle')
|
||
|
}}
|
||
|
</el-radio-button>
|
||
|
<el-radio-button value="word">{{
|
||
|
t('designer.print.wordStyle')
|
||
|
}}
|
||
|
</el-radio-button>
|
||
|
</el-radio-group>
|
||
|
</el-form-item>
|
||
|
<el-form-item :label="t('style.width')">
|
||
|
<el-input-number :min="300" controls-position="right"
|
||
|
v-model="formData.width"></el-input-number>
|
||
|
</el-form-item>
|
||
|
<template v-for="item in padding" :key="item">
|
||
|
<el-form-item :label="t('designer.print.' + item)">
|
||
|
<el-input-number :min="0" controls-position="right"
|
||
|
v-model="formData[item]"></el-input-number>
|
||
|
</el-form-item>
|
||
|
</template>
|
||
|
</el-form>
|
||
|
</el-main>
|
||
|
</el-container>
|
||
|
</el-aside>
|
||
|
<el-main>
|
||
|
<el-container class="_fd-print-r">
|
||
|
<el-main>
|
||
|
<ViewForm class="_fd-print-form" :class="{'_fd-print-form-word': formData.style === 'word'}"
|
||
|
ref="form" :rule="rule" :option="options"
|
||
|
v-if="visible"
|
||
|
:style="{width: formData.width > 0 ? (formData.width + 'px') : '100%'}">
|
||
|
<template v-for="(_, name) in $slots" #[name]="scope">
|
||
|
<slot :name="name" v-bind="scope ?? {}"/>
|
||
|
</template>
|
||
|
</ViewForm>
|
||
|
</el-main>
|
||
|
</el-container>
|
||
|
</el-main>
|
||
|
</el-container>
|
||
|
<template #footer>
|
||
|
<div>
|
||
|
<el-button size="default" @click="visible=false">{{ t('props.cancel') }}</el-button>
|
||
|
<el-button size="default" @click="print(true)">{{
|
||
|
t('designer.print.export')
|
||
|
}}</el-button>
|
||
|
<el-button type="primary" size="default" @click="print(false)" :loading="printing">{{ t('props.print') }}
|
||
|
</el-button>
|
||
|
</div>
|
||
|
</template>
|
||
|
</el-dialog>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
import {defineComponent, markRaw} from 'vue';
|
||
|
import Warning from './Warning.vue';
|
||
|
import viewForm from '../utils/form';
|
||
|
import loadjs from '../utils/loadjs/loadjs';
|
||
|
import SizeInput from './style/SizeInput.vue';
|
||
|
|
||
|
export default defineComponent({
|
||
|
name: 'PrintForm',
|
||
|
components: {
|
||
|
SizeInput,
|
||
|
Warning,
|
||
|
ViewForm: viewForm.$form(),
|
||
|
},
|
||
|
inject: ['designer'],
|
||
|
data() {
|
||
|
return {
|
||
|
visible: false,
|
||
|
printing: false,
|
||
|
frame: null,
|
||
|
rule: [],
|
||
|
options: {},
|
||
|
padding: ['top', 'bottom', 'left', 'right'],
|
||
|
formData: {
|
||
|
type: 'form',
|
||
|
style: 'default',
|
||
|
left: 20,
|
||
|
right: 20,
|
||
|
top: 20,
|
||
|
bottom: 20,
|
||
|
width: 780,
|
||
|
},
|
||
|
};
|
||
|
},
|
||
|
computed: {
|
||
|
t() {
|
||
|
return this.designer.setupState.t;
|
||
|
},
|
||
|
},
|
||
|
watch: {
|
||
|
visible(v) {
|
||
|
if (v) {
|
||
|
this.rule = viewForm.parseJson(this.designer.setupState.getPreviewRule());
|
||
|
this.options = viewForm.parseJson(this.designer.setupState.getOptionsJson());
|
||
|
this.options.submitBtn = false;
|
||
|
this.options.resetBtn = false;
|
||
|
} else {
|
||
|
this.printing = false;
|
||
|
if (this.frame) {
|
||
|
document.body.removeChild(this.frame);
|
||
|
this.frame = null;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
'formData.type': function (n) {
|
||
|
this.options.preview = n === 'read';
|
||
|
}
|
||
|
},
|
||
|
methods: {
|
||
|
open() {
|
||
|
this.visible = true;
|
||
|
},
|
||
|
disableImageSmoothing(ctx) {
|
||
|
ctx.imageSmoothingEnabled = false;
|
||
|
ctx.mozImageSmoothingEnabled = false;
|
||
|
ctx.webkitImageSmoothingEnabled = false;
|
||
|
ctx.msImageSmoothingEnabled = false;
|
||
|
},
|
||
|
print(flag) {
|
||
|
this.printing = true;
|
||
|
loadjs.ready(['html2canvas', 'jspdf'], () => {
|
||
|
window.html2canvas(this.$refs.form.$el, {
|
||
|
allowTaint: true,
|
||
|
useCORS: true,
|
||
|
}).then((canvas) => {
|
||
|
const pdf = new window.jspdf.jsPDF({
|
||
|
orientation: 'p',
|
||
|
unit: 'pt',
|
||
|
format: 'a4',
|
||
|
});
|
||
|
|
||
|
this.disableImageSmoothing(canvas.getContext('2d'));
|
||
|
|
||
|
const {
|
||
|
left: marginLeft,
|
||
|
right: marginRight,
|
||
|
top: marginTop,
|
||
|
bottom: marginBottom,
|
||
|
} = this.formData;
|
||
|
|
||
|
const pageWidth = pdf.internal.pageSize.getWidth();
|
||
|
const pageHeight = pdf.internal.pageSize.getHeight();
|
||
|
const contentWidth = pageWidth - marginLeft - marginRight;
|
||
|
const contentHeight = pageHeight - marginTop - marginBottom;
|
||
|
|
||
|
const scaledHeight = (canvas.height * contentWidth) / canvas.width;
|
||
|
|
||
|
if (scaledHeight <= contentHeight) {
|
||
|
pdf.addImage(
|
||
|
canvas.toDataURL('image/jpeg'),
|
||
|
'JPEG',
|
||
|
marginLeft,
|
||
|
marginTop,
|
||
|
contentWidth,
|
||
|
scaledHeight
|
||
|
);
|
||
|
} else {
|
||
|
let remainingHeight = scaledHeight;
|
||
|
let page = 0;
|
||
|
const clipHeight = canvas.width * contentHeight / contentWidth;
|
||
|
|
||
|
while (remainingHeight > 0) {
|
||
|
const tempCanvas = document.createElement('canvas');
|
||
|
const tempCtx = tempCanvas.getContext('2d');
|
||
|
this.disableImageSmoothing(tempCtx);
|
||
|
|
||
|
const offsetY = page * clipHeight;
|
||
|
const actualClipHeight = Math.min(clipHeight, canvas.height - offsetY);
|
||
|
|
||
|
tempCanvas.width = canvas.width;
|
||
|
tempCanvas.height = actualClipHeight;
|
||
|
|
||
|
tempCtx.drawImage(
|
||
|
canvas,
|
||
|
0, offsetY, canvas.width, actualClipHeight,
|
||
|
0, 0, canvas.width, actualClipHeight
|
||
|
);
|
||
|
|
||
|
const imageHeight = (actualClipHeight / canvas.height) * scaledHeight;
|
||
|
|
||
|
pdf.addImage(
|
||
|
tempCanvas.toDataURL('image/jpeg'),
|
||
|
'JPEG',
|
||
|
marginLeft,
|
||
|
marginTop,
|
||
|
contentWidth,
|
||
|
imageHeight
|
||
|
);
|
||
|
|
||
|
remainingHeight -= contentHeight;
|
||
|
if (remainingHeight > 0) {
|
||
|
pdf.addPage();
|
||
|
page++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (flag) {
|
||
|
this.printing = false;
|
||
|
window.open(URL.createObjectURL(pdf.output('blob')));
|
||
|
} else {
|
||
|
this.printPdf(pdf);
|
||
|
}
|
||
|
}).catch((e) => {
|
||
|
this.printing = false;
|
||
|
});
|
||
|
});
|
||
|
},
|
||
|
printPdf(pdf) {
|
||
|
if (!this.frame) {
|
||
|
const frame = markRaw(document.createElement('iframe'));
|
||
|
frame.style.width = '0';
|
||
|
frame.style.position = 'absolute';
|
||
|
frame.style.height = '0';
|
||
|
frame.style.border = 'none';
|
||
|
frame.onload = function () {
|
||
|
setTimeout(() => {
|
||
|
frame.contentWindow.print();
|
||
|
}, 100);
|
||
|
}
|
||
|
document.body.appendChild(frame);
|
||
|
this.frame = frame;
|
||
|
}
|
||
|
this.frame.src = URL.createObjectURL(pdf.output('blob'));
|
||
|
this.printing = false;
|
||
|
},
|
||
|
},
|
||
|
created() {
|
||
|
if (window.html2canvas) {
|
||
|
loadjs.done('html2canvas');
|
||
|
} else if (!loadjs.isDefined('html2canvas')) {
|
||
|
loadjs.loadNpm('html2canvas@1.4.1/dist/html2canvas.min.js', 'html2canvas');
|
||
|
}
|
||
|
if (window.jspdf) {
|
||
|
loadjs.done('jspdf');
|
||
|
} else if (!loadjs.isDefined('jspdf')) {
|
||
|
loadjs.loadNpm('jspdf@3.0.1/dist/jspdf.umd.js', 'jspdf');
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
</script>
|
||
|
|
||
|
<style>
|
||
|
._fd-print .el-button {
|
||
|
font-weight: 400;
|
||
|
width: 100%;
|
||
|
}
|
||
|
|
||
|
._fd-print-con .el-main {
|
||
|
padding: 0;
|
||
|
}
|
||
|
|
||
|
._fd-print-l, ._fd-print-r {
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
flex: 1;
|
||
|
height: 100%;
|
||
|
border: 1px solid var(--fc-line-color-3);
|
||
|
}
|
||
|
|
||
|
._fd-print-head {
|
||
|
display: flex;
|
||
|
padding: 5px 15px;
|
||
|
border-bottom: 1px solid var(--fc-line-color-3);
|
||
|
background: var(--fc-bg-color-3);
|
||
|
align-items: center;
|
||
|
}
|
||
|
|
||
|
._fd-print-head .el-button.is-link {
|
||
|
color: var(--fc-style-color-1);
|
||
|
}
|
||
|
|
||
|
._fd-print-r {
|
||
|
border-left: 0 none;
|
||
|
}
|
||
|
|
||
|
._fd-print-r ._fd-print-head {
|
||
|
justify-content: flex-end;
|
||
|
}
|
||
|
|
||
|
._fd-print-l > .el-main, ._fd-print-r > .el-main {
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
flex: 1;
|
||
|
flex-basis: auto;
|
||
|
box-sizing: border-box;
|
||
|
min-width: 0;
|
||
|
width: 100%;
|
||
|
padding: 10px;
|
||
|
}
|
||
|
|
||
|
._fd-print-l .el-form .el-radio-group, ._fd-print-l .el-form .el-radio-button__inner {
|
||
|
width: 100%;
|
||
|
}
|
||
|
|
||
|
._fd-print-l .el-form .el-radio-button {
|
||
|
flex: 1;
|
||
|
}
|
||
|
|
||
|
._fd-print-r > .el-main {
|
||
|
flex-direction: column;
|
||
|
padding: 20px;
|
||
|
position: relative;
|
||
|
}
|
||
|
|
||
|
._fd-print-form {
|
||
|
padding: 2px;
|
||
|
box-sizing: border-box;
|
||
|
}
|
||
|
|
||
|
._fd-print-form .el-input__wrapper, ._fd-print-form .el-textarea__inner, ._fd-print-form .el-select__wrapper {
|
||
|
box-shadow: none !important;
|
||
|
border: 1px solid var(--el-input-border-color, var(--el-border-color));
|
||
|
}
|
||
|
|
||
|
._fd-print-form .el-select__placeholder {
|
||
|
position: unset !important;
|
||
|
top: unset !important;
|
||
|
transform: unset !important;
|
||
|
}
|
||
|
|
||
|
._fd-print-form .is-disabled .el-input__wrapper {
|
||
|
background-color: unset !important;
|
||
|
}
|
||
|
|
||
|
._fd-print-form .is-disabled .el-input__inner {
|
||
|
color: unset !important;
|
||
|
}
|
||
|
|
||
|
._fd-print-form-word .el-input__wrapper, ._fd-print-form-word .el-textarea__inner, ._fd-print-form-word .el-select__wrapper {
|
||
|
border: none !important;
|
||
|
border-bottom: 1px solid var(--el-input-border-color, var(--el-border-color)) !important;
|
||
|
border-color: inherit !important;
|
||
|
border-radius: 0 !important;
|
||
|
}
|
||
|
|
||
|
._fd-print-form-word .el-input-number__decrease, ._fd-print-form-word .el-input-number__increase {
|
||
|
display: none !important;
|
||
|
}
|
||
|
|
||
|
._fd-print-form-word ._fc-read-view {
|
||
|
display: block;
|
||
|
width: 100%;
|
||
|
height: 1.5em;
|
||
|
padding: 0 4px;
|
||
|
line-height: 1.5em;
|
||
|
border-bottom: 1px solid var(--el-input-border-color, var(--el-border-color)) !important;
|
||
|
border-color: inherit !important;
|
||
|
}
|
||
|
|
||
|
._fd-print-page-line {
|
||
|
position: absolute;
|
||
|
border-bottom: 1px dashed var(--fc-line-color-3);
|
||
|
left: 0;
|
||
|
right: 0;
|
||
|
height: 1px;
|
||
|
color: var(--fc-text-color-3);
|
||
|
padding-left: 4px;
|
||
|
box-sizing: border-box;
|
||
|
font-size: 12px;
|
||
|
line-height: 2em;
|
||
|
z-index: 1;
|
||
|
}
|
||
|
|
||
|
</style>
|