Files
temu-plugin/src/components/print/Print.vue
2024-10-30 17:29:34 +08:00

851 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="print">
<div class="print-wrapper" v-if="!isPrint">
<div class="left">
<div class="left-wrapper">
<div class="title">基础元素</div>
<div class="left-item__wrapper">
<div class="ep-draggable-item item" tid="defaultModule.text">
<i class="iconfont">&#xe649;</i>
<span>文本</span>
</div>
<div class="ep-draggable-item item" tid="defaultModule.longText">
<i class="iconfont">&#xe7dc;</i>
<span>长文</span>
</div>
<div class="ep-draggable-item item" tid="defaultModule.table">
<i class="iconfont">&#xea3f;</i>
<span>表格</span>
</div>
</div>
<div class="title">辅助元素</div>
<div class="left-item__wrapper">
<div class="ep-draggable-item item" tid="defaultModule.hline">
<i class="iconfont">&#xe7dd;</i>
<span>横线</span>
</div>
<div class="ep-draggable-item item" tid="defaultModule.vline">
<i class="iconfont">&#xe70f;</i>
<span>竖线</span>
</div>
<div class="ep-draggable-item item" tid="defaultModule.rect">
<i class="iconfont">&#xe620;</i>
<span>矩形</span>
</div>
<div class="ep-draggable-item item" tid="defaultModule.oval">
<i class="iconfont">&#xe76a;</i>
<span>圆形</span>
</div>
</div>
<div class="title">常用元素</div>
<div class="left-item__wrapper" id="custom-provider">
</div>
<div class="title">
<span>素材</span>
</div>
<div class="left-item__wrapper">
<div
class="item"
style="cursor: pointer;"
@click="search.type = 1, search.current = 1, isShowImage = true, getConfig()">
<div>图片素材</div>
</div>
<div
class="item"
style="cursor: pointer;"
@click="search.type = 0, search.current = 1, isShowImage = true, getConfig()">
<div>文字素材</div>
</div>
</div>
<div class="title">
<span>动态数据如日期</span>
<el-button type="primary" size="mini" @click="addField">添加</el-button>
</div>
<div class="left-item__wrapper">
<div
class="item"
v-for="(item, index) in dynamicFromList"
:key="index"
style="cursor: pointer;"
@click="addItemToCanvas(item.fieldName)">
<div>{{ item.fieldName }}</div>
<span class="el-icon-error" @click.stop="removeField(index)"></span>
</div>
</div>
</div>
</div>
<div class="center">
<div class="center-header">
<div class="paper">
<el-button-group size="small">
<template v-for="(value, type) in paperTypes">
<el-button size="small" :type="curPaperType === type ? 'primary' : ''" @click="setPaper(type,value)" :key="type">
{{ type }}
</el-button>
</template>
<el-popover v-model="paperPopVisible" placement="top" :width="260" trigger="click">
<div>
<div style="font-size: 16px; font-weight: bold">设置纸张宽高(mm)</div>
<div style="margin-top: 10px">
<el-input size="small" style="margin-bottom: 10px" v-model="paperWidth" type="number" placeholder="宽(mm)" />
<el-input size="small" v-model="paperHeight" type="number" placeholder="高(mm)" />
</div>
<el-button style="margin-top: 12px" size="small" @click.stop="setPaperOther">确定</el-button>
</div>
<el-button slot="reference" size="small" :type="'other' == curPaperType ? 'primary' : ''">自定义纸张</el-button>
</el-popover>
</el-button-group>
<el-button @click="isShowTemplate = true" type="warning" size="small" style="margin-left: 10px;">模板库</el-button>
</div>
</div>
<div class="center-wrapper">
<div id="hiprint-printTemplate"></div>
</div>
</div>
<div class="right">
<div id="PrintElementOptionSetting"></div>
</div>
</div>
<ai-dialog :visible.sync="isShowPreview" title="预览" width="1200" customFooter>
<div class="print-viewer" v-html="html"></div>
<div class="dialog-footer" slot="footer">
<el-button @click="isShowPreview = false">取消</el-button>
</div>
</ai-dialog>
<ai-dialog :visible.sync="isShowImage" title="图片/文字素材" width="960" customFooter>
<el-select v-model="search.type" placeholder="请选择图片/文字素材" size="small" @change="search.current = 1, getConfig()">
<el-option label="图片素材" :value="1"></el-option>
<el-option label="文字素材" :value="0"></el-option>
</el-select>
<ai-table
v-if="search.type === 1"
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
:current.sync="search.current"
:size.sync="search.size"
style="margin-top: 8px;"
height="400"
@getList="getConfig">
<el-table-column slot="image" label="图片" align="left">
<template v-slot="{ row }">
<el-image
v-if="search.type === 1"
style="width: 80px; height: 80px"
:src="row.imgUrl"
:preview-src-list="[row.imgUrl]">
</el-image>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" align="center">
<template v-slot="{ row }">
<div class="table-options">
<el-button type="text" @click="row.type === '1' ? addImage(row.imgUrl) : addText(row.contents), isShowImage = false">添加</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-table
v-if="search.type === 0"
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
:current.sync="search.current"
:size.sync="search.size"
style="margin-top: 8px;"
height="400"
@getList="getConfig">
<el-table-column slot="options" label="操作" align="center">
<template v-slot="{ row }">
<div class="table-options">
<el-button type="text" @click="row.type === '1' ? addImage(row.imgUrl) : addText(row.contents), isShowImage = false">添加</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<div class="dialog-footer" slot="footer">
<el-button @click="isShowImage = false">取消</el-button>
</div>
</ai-dialog>
<ai-dialog
:visible.sync="isShowDynamicForm"
title="动态数据"
width="590px"
@confirm="onConfirm">
<el-form :model="dynamicFrom" ref="form" label-width="100px">
<el-form-item
label="数据名称:"
:prop="`field_${dynamicFromList.length > 9 ? dynamicFromList.length + 1 : '0' + (dynamicFromList.length + 1)}`"
:rules="[{ required: true, message: '请输入数据名称', trigger: 'blur' }]">
<el-input placeholder="请输入数据名称" type="text" v-model="dynamicFrom[`field_${dynamicFromList.length > 9 ? dynamicFromList.length + 1 : '0' + (dynamicFromList.length + 1)}`]"></el-input>
</el-form-item>
</el-form>
</ai-dialog>
<ai-dialog :visible.sync="isShowTemplate" title="模板库" width="960" customFooter>
<ai-table
:tableData="templateList"
:col-configs="templateColConfigs"
:total="templateTotal"
:current.sync="searchTemplate.current"
:size.sync="searchTemplate.size"
style="margin-top: 8px;"
height="400"
@getList="getTemplateList"
v-loading="templateLoading">
<el-table-column slot="options" label="操作" align="center" width="200">
<template v-slot="{ row }">
<div class="table-options">
<el-button type="text" @click="showTemplate(row)">模板预览</el-button>
<el-button type="text" @click="updateTempate(row)">使用</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<div class="dialog-footer" slot="footer">
<el-button @click="isShowImage = false">取消</el-button>
</div>
</ai-dialog>
</div>
</template>
<script>
import { hiprint, defaultElementTypeProvider, disAutoConnect } from 'vue-plugin-hiprint'
import { newHiprintPrintTemplate } from '@/utils/template-helper'
import { customProvider } from './customProvider'
disAutoConnect()
export default {
props: {
list: {
type: Array,
default: () => {
return []
}
},
params: {
type: Array,
default: () => {
return []
}
},
printData: {
type: Object,
default: () => {
return {
}
}
},
template: {
type: Object,
default: () => {
return {
"panels": [{
"index": 0,
"name": 1,
"height": 200,
"width": 200,
"printElements": [],
"paperNumberDisabled": true,
"paperNumberContinue": true,
"fontFamily": "Microsoft YaHei",
"watermarkOptions": {}
}]
}
}
},
isPrint: {
type: Boolean,
default: false
}
},
data () {
return {
html: '',
isShowPreview: false,
hiprintTemplate: null,
isShowTemplate: false,
templateList: [],
curPaper: {
type: 'other',
width: 200,
height: 200
},
paperTypes: {
'100 * 100': {
width: 200,
height: 200
},
'100 * 80': {
width: 200,
height: 160
},
'80 * 60': {
width: 160,
height: 120
},
'60 * 40': {
width: 120,
height: 80
}
},
paperPopVisible: false,
paperWidth: 200,
paperHeight: 200,
isShowDynamicForm: false,
dynamicFrom: {
},
dynamicFromList: [],
panel: null,
contents: [],
images: [],
search: {
current: 1,
size: 10,
type: 1
},
searchTemplate: {
current: 1,
size: 10,
type: 1
},
templateTotal: 0,
isShowImage: false,
tableData: [],
total: 0,
templateColConfigs: [
{ prop: 'name', label: '模板名称', align: 'left' }
],
templateLoading: false
}
},
watch: {
template: {
handler(value) {
if (value && this.hiprintTemplate && !this.isPrint) {
const config = value.panels[0]
this.hiprintTemplate.update(value)
this.curPaper = {type: 'other', width: config.width, height: config.height}
this.hiprintTemplate.setPaper(config.width, config.height)
if (this.params) {
this.dynamicFromList = this.params
}
}
},
deep: true
}
},
computed: {
curPaperType() {
let type = 'other'
let types = this.paperTypes
for (const key in types) {
let item = types[key]
let {width, height} = this.curPaper
if (item.width === width && item.height === height) {
type = key
}
}
return type
},
colConfigs () {
if (this.search.type === 1) {
return [
{ slot: 'image' },
{ prop: 'remark', label: '描述', align: 'center' }
]
}
return [
{ prop: 'contents', label: '文本', align: 'center' },
{ prop: 'remark', label: '描述', align: 'center' }
]
}
},
mounted() {
if (this.isPrint) {
this.hiprintTemplate = newHiprintPrintTemplate('temulables')
} else {
hiprint.init({
providers: [defaultElementTypeProvider(), customProvider({})]
})
this.buildLeftElement()
this.buildDesigner()
this.getConfig()
this.getTemplateList()
}
},
methods: {
addField() {
const num = this.dynamicFromList.length > 9 ? this.dynamicFromList.length + 1 : `0${this.dynamicFromList.length + 1}`
// eslint-disable-next-line no-empty
if (this.dynamicFromList.length && !this.dynamicFromList.at(-1)[`field_${num}`]) {
} else {
this.$set(this.dynamicFrom, `field_${num}`, '')
}
this.isShowDynamicForm = true
},
removeField(index) {
this.dynamicFromList.splice(index, 1)
},
updateTempate(row) {
this.templateLoading = true
this.$http.post(`/api/templateRecommend/detail?id=${row.id}`).then(res => {
if (res.code === 0) {
const config = JSON.parse(res.data.content)
this.hiprintTemplate.update(config)
this.hiprintTemplate.setPaper(config.panels[0].width, config.panels[0].height)
this.dynamicFromList = JSON.parse(res.data.params)
this.isShowTemplate = false
}
this.templateLoading = false
})
},
getTemplateList() {
this.$http.post(`/api/templateRecommend/getRecommendPage`, null, {
params: this.searchTemplate
}).then(res => {
if (res.code === 0) {
this.templateList = res.data.records
this.templateTotal = res.data.total
}
})
},
showTemplate (row) {
this.templateLoading = true
this.$http.post(`/api/templateRecommend/detail?id=${row.id}`).then(res => {
this.templateLoading = false
if (res.code === 0) {
this.html = res.data.codes
this.isShowPreview = true
}
})
},
getConfig() {
this.$http.post(`/api/material/getPage?current=${this.search.current}&size=${this.search.size}&type=${this.search.type}`).then(res => {
if (res.code === 0) {
this.tableData = res.data.records
this.total = res.data.total
}
})
},
addImage(src) {
this.panel.addPrintImage({
options: {
title: '',
left: 70.5,
top: 58.5,
src: src,
width: 100,
height: 100
}
})
const el = this.hiprintTemplate.printPanels[0].printElements.at(-1)
const designPaper = this.hiprintTemplate.printPanels[0].designPaper
this.hiprintTemplate.printPanels[0].appendDesignPrintElement(designPaper, el, true)
el.design(void 0, designPaper)
},
addText(text, isSetField = false) {
this.panel.addPrintText({
options: {
field: isSetField ? text : '',
testData: isSetField ? text : '',
title: isSetField ? '' : text,
left: 70.5,
top: 58.5,
width: 140,
height: 20,
coordinateSync: true,
contentPaddingLeft: 5.25,
textContentVerticalAlign: 'middle',
widthHeightSync: true,
hideTitle: true,
fontFamily: 'Microsoft YaHei',
fontWeight: '700'
}
})
const el = this.hiprintTemplate.printPanels[0].printElements.at(-1)
const designPaper = this.hiprintTemplate.printPanels[0].designPaper
this.hiprintTemplate.printPanels[0].appendDesignPrintElement(designPaper, el, true)
el.design(void 0, designPaper)
},
addItemToCanvas(name) {
this.addText(name, true)
},
onConfirm() {
this.$refs.form.validate((valid) => {
if (valid) {
const num = this.dynamicFromList.length > 9 ? this.dynamicFromList.length + 1 : `0${this.dynamicFromList.length + 1}`
this.dynamicFromList.push({
fieldValue: `field_${num}`,
fieldName: this.dynamicFrom[`field_${num}`]
})
this.isShowDynamicForm = false
}
})
},
buildLeftElement() {
// eslint-disable-next-line no-undef
hiprint.PrintElementTypeManager.buildByHtml($('.ep-draggable-item'))
// eslint-disable-next-line no-undef
$('#custom-provider').empty()
// eslint-disable-next-line no-undef
hiprint.PrintElementTypeManager.build($('#custom-provider'), 'customProvider')
},
buildDesigner() {
// eslint-disable-next-line no-undef
$('#hiprint-printTemplate').empty()
this.hiprintTemplate = newHiprintPrintTemplate('temulables', {
template: this.template,
settingContainer: '#PrintElementOptionSetting',
onImageChooseClick: (target) => {
let input = document.createElement('input')
input.setAttribute('type', 'file')
input.click()
input.onchange = function () {
var file = this.files[0]
if (file) {
var reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = function () {
target.refresh(reader.result)
}
}
}
input.remove()
}
})
this.$nextTick(() => {
this.hiprintTemplate.design('#hiprint-printTemplate', {
grid: true
})
this.panel = this.hiprintTemplate.printPanels[0]
})
},
setPaperOther () {
let value = {}
value.width = this.paperWidth
value.height = this.paperHeight
this.setPaper('other', value)
this.paperPopVisible = false
},
setPaper(type, value) {
try {
if (Object.keys(this.paperTypes).includes(type)) {
this.curPaper = {type: type, width: value.width, height: value.height}
this.hiprintTemplate.setPaper(value.width, value.height)
} else {
this.curPaper = {type: 'other', width: value.width, height: value.height}
this.hiprintTemplate.setPaper(value.width, value.height)
}
} catch (error) {
this.$message.error(`操作失败: ${error}`)
}
},
print() {
this.hiprintTemplate.print(this.printData)
},
// js打印不显示设计界面
toPrint (template, printData) {
this.hiprintTemplate = newHiprintPrintTemplate('temulables')
setTimeout(() => {
this.hiprintTemplate.update(template)
this.hiprintTemplate.print(printData)
}, 100)
},
elementToString(el) {
const node = document.createElement('div')
node.innerHTML = el.html()
document.querySelector('body').appendChild(node)
const html = node.innerHTML
document.querySelector('body').removeChild(node)
return html
},
savePdf() {
this.hiprintTemplate.toPdf(this.printData, '测试导出pdf',{scale:2 }).then(v => {
console.log(v)
})
},
save() {
const html = this.elementToString(this.hiprintTemplate.getHtml(this.printData))
const json = this.hiprintTemplate.getJson()
return {
html,
json,
params: JSON.stringify(this.dynamicFromList)
}
},
preview() {
this.html = this.elementToString(this.hiprintTemplate.getHtml(this.printData))
this.isShowPreview = true
},
getHtml() {
return this.elementToString(this.hiprintTemplate.getHtml(this.printData))
},
clearPaper() {
this.hiprintTemplate.clear()
},
exportJson() {
return this.hiprintTemplate.getJson()
}
}
}
</script>
<style lang="scss" scoped>
.print {
height: 100%;
.temuBarCode {
display: flex;
flex-direction: column;
padding: 1pt 3pt;
}
.print-wrapper {
display: flex;
height: calc(100vh - 180px);
::v-deep(.prop-tab-items) {
background-color: transparent !important;
.prop-tab-item {
background-color: transparent !important;
.tab-title {
font-size: 14px;
}
}
}
::v-deep(.hiprint-option-items .hiprint-option-item-label) {
width: 100%;
margin-bottom: 14px;
text-align: left;
font-size: 14px;
}
::v-deep(.hiprint-printPanel) {
display: block;
.dynamicField {
background-color: #bfc2e9;
border-color: #bfc2e9;
}
}
::v-deep(.minicolors) {
flex: 1;
width: inherit;
input {
width: 100% !important;
}
}
::v-deep(.hiprint-option-item-field) {
width: 100%;
font-size: 14px;
input {
height: 24px;
}
}
::v-deep(.hiprint-option-item-row) {
display: block;
}
::v-deep(.prop-tab-items) {
margin-bottom: 10px;
}
::v-deep(.hiprint-option-items),
::v-deep(.prop-tabs) {
background-color: transparent !important;
}
.left {
width: 350px;
height: 100%;
overflow-y: auto;
.title {
display: flex;
align-items: center;
justify-content: space-between;
width: 320px;
margin: 14px 0;
}
.left-item__wrapper {
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
.item {
display: flex;
position: relative;
flex-direction: column;
align-items: center;
justify-content: center;
left: 0!important;
top: 0!important;
width: 100px;
margin-bottom: 10px;
margin-right: 10px;
padding: 10px 0;
background-color: #eee;
border-radius: 4px;
&:nth-of-type(3n) {
margin-right: 0;
}
i {
margin-bottom: 10px;
}
.el-icon-error {
position: absolute;
top: -6px;
right: -6px;
color: red;
font-size: 18px;
cursor: pointer;
}
}
::v-deep(ul) {
margin: 0;
padding: 0;
list-style: none;
.title {
display: none;
}
ul {
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
li {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100px;
margin-bottom: 10px;
margin-right: 10px;
background-color: #eee;
border-radius: 4px;
&:nth-of-type(3n) {
margin-right: 0;
}
a {
margin-top: 10px;
padding: 10px 0;
}
}
}
}
}
}
.center {
display: flex;
position: relative;
flex-direction: column;
flex: 1;
overflow: hidden;
padding: 0 10px;
color: #000;
.center-wrapper {
flex: 1;
overflow-x: auto;
overflow-y: auto;
width: 100%;
padding: 20px 20px 10px;
}
.center-header {
width: 100%;
padding-bottom: 10px;
.paper {
display: flex;
position: relative;
align-items: center;
justify-content: center;
}
.scale {
display: flex;
align-items: center;
margin: 0 10px;
}
}
}
.right {
width: 300px;
overflow-y: auto;
overflow-x: hidden;
}
}
.print-viewer {
color: #000;
}
}
</style>