# 自定义打印
# 背景介绍
通常情况下,纷享销客中打印模板功能可以满足大多数的打印需求,但是像一些特殊的报表打印,打印模板便无能为力。
这篇文章教您如何开发自定义组件来定制打印页面。
# 书写template
大体内容就是用html去构建表格。
<template>
{ <div class="cus-demo" v-if="dData">
<h1 class="text-center">资金使用审批表</h1>
<div class="flex bd">
<div class="flex flex-1 flex-col">
<div class="flex">
<div class="w-120 text-center bd">收款单位</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_uhcib__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">开户银行</div>
<div class="flex-1">
<div class="flex">
<div class="w-60 text-center bd">开户行</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_utckZ__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">账户</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_oQgRl__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">地址</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_v82bd__c }}}</div>
</div>
</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">本次付款金额</div>
<div class="flex-1">
<div class="flex">
<div class="w-60 text-center bd">大写</div>
<div class="flex-1 padding-5 bd">{{{ digitUppercase(dData.field_8P18m__c) }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">小写</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_8P18m__c }}}</div>
</div>
</div>
</div>
<div class="flex">
<div class="w-120 text-center bd">付款方式</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_fJY1p__c }}}</div>
</div>
<div class="flex">
<div class="flex-1 bd">
<div class="text-center">付款事由</div>
<div class="text-center padding-5">{{{ dData.field_fwjy1__c }}}</div>
</div>
<div class="flex-1 bd">
<div class="text-center">经办部门意见</div>
<div class="text-center padding-5">{{{ dData.field_36nKK__c }}}</div>
</div>
<div class="flex-1 bd">
<div class="text-center">项目负责人意见</div>
<div class="text-center padding-5">{{{ dData.field_J3N26__c }}}</div>
</div>
</div>
</div>
</div>
<div class="text-right no-print">点击浏览器的打印按钮直接进行打印(该文字不会被打印)</div>
</div>
</template>
# 书写script
我们先来理一下思路:
获取当前详情的完整数据。这里依赖uiaction中的userData
获取当前详情所属业务对象的字段描述。这里依赖FxUI提供的api
有了数据和描述,我们可以解析出能够被用户看的懂的数据。这里依赖FxUI提供的api
根据上述的步骤,我们组织逻辑如下:
<script>
export default {
props: ['userData'], //上述提到的第一步。自定义组件是通过userData来接手uiaction中的数据
data() {
return {
dData: null
}
},
created() {
this.getData();
},
mounted() {
//触发弹窗位置的重新计算
this.$emit('action', {type: 'resize'});
},
methods: {
getData() {
const userData = this.userData;
//上述提到的第二步。获取对象描述
FxUI.objectApi.fetch_describe(userData.object_describe_api_name).then((data) => {
const {
fields
} = data.objectDescribe;
//上述提到的第三步。解析数据
let result = {};
Object.keys(fields).forEach(fApiName => {
result[fApiName] = (FxUI.objectApi.format_field_value || PAAS.format_field_value)(fields[fApiName], userData[fApiName], userData);
})
//最后一步。赋值渲染
this.dData = result;
this.$nextTick(() => {
//数据渲染到页面后,会导致高度变化。此时我们需要让弹窗重新计算位置
this.$emit('action', {type: 'resize'});
})
})
}
}
}
</script>
另外,需求中有涉及金额大小写转换的场景,于是我们优化代码,在组件中提供相应的方法 digitUppercase
,示例如下:
<script>
export default {
props: ['userData', 'data'],
data() {
return {
dData: null
}
},
created() {
this.getData();
},
mounted() {
this.$emit('action', {type: 'resize'});
},
methods: {
//金额小写转大写
digitUppercase(n) {
var fraction = ['角', '分'];
var digit = [
'零', '壹', '贰', '叁', '肆',
'伍', '陆', '柒', '捌', '玖'
];
var unit = [
['元', '万', '亿'],
['', '拾', '佰', '仟']
];
var head = n < 0 ? '欠' : '';
n = Math.abs(n);
var s = '';
for (var i = 0; i < fraction.length; i++) {
s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '');
}
s = s || '整';
n = Math.floor(n);
for (var i = 0; i < unit[0].length && n > 0; i++) {
var p = '';
for (var j = 0; j < unit[1].length && n > 0; j++) {
p = digit[n % 10] + unit[1][j] + p;
n = Math.floor(n / 10);
}
s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s;
}
return head + s.replace(/(零.)*零元/, '元')
.replace(/(零.)+/g, '零')
.replace(/^整$/, '零元整');
},
getData() {
const userData = this.userData;
//获取描述然后解析数据
FxUI.objectApi.fetch_describe(userData.object_describe_api_name).then((data) => {
const {
fields
} = data.objectDescribe;
let result = {};
Object.keys(fields).forEach(fApiName => {
result[fApiName] = (FxUI.objectApi.format_field_value || PAAS.format_field_value)(fields[fApiName], userData[fApiName], userData);
})
this.dData = result;
this.$nextTick(() => {
this.$emit('action', {type: 'resize'});
})
})
}
}
}
</script>
# 书写style
<style lang="less">
.cus-demo {
.demo {
max-width: 1200px;
}
h1 {
font-size: 28px;
font-weight: bold;
margin-bottom: 15px;
}
div {
box-sizing: border-box;
line-height: 34px;
}
.flex {
display: flex;
}
.flex-1 {
flex: 1;
}
.flex-col {
flex-direction: column;
}
.w-120 {
width: 120px;
}
.w-60 {
width: 60px;
}
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.padding-5 {
padding: 0 10px;
}
.bd {
border: 1px solid #ccc;
}
&--rotate {
transform: rotate(90deg);
}
}
</style>
因为涉及打印,我们希望打印自定义组件中的内容,这里我们通过媒体查询的方式去屏蔽整个页面不需要打印的部分,代码优化如下:
注意:这种方式是个hack,@吴敬 会提供其他健康的方式来达到此目的,这里就不多阐述了。
<style lang="less">
@media print {
body {
height: auto!important;
}
#crm-layout {
display: none;
}
#app-portal {
display: none;
}
.detail-dialog-v3 {
display: none;
}
.el-dialog__header {
display: none!important;
}
.f-qx-container {
display: none;
}
.fx-dialog {
margin-top: 0!important;
}
.no-print {
display: none!important;
}
}
</style>
# 最终全部代码如下
<template>
<div class="cus-demo" v-if="dData">
<h1 class="text-center">资金使用审批表</h1>
<div class="flex bd">
<div class="flex flex-1 flex-col">
<div class="flex">
<div class="w-120 text-center bd">收款单位</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_uhcib__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">开户银行</div>
<div class="flex-1">
<div class="flex">
<div class="w-60 text-center bd">开户行</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_utckZ__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">账户</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_oQgRl__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">地址</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_v82bd__c }}}</div>
</div>
</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">本次付款金额</div>
<div class="flex-1">
<div class="flex">
<div class="w-60 text-center bd">大写</div>
<div class="flex-1 padding-5 bd">{{{ digitUppercase(dData.field_8P18m__c) }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">小写</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_8P18m__c }}}</div>
</div>
</div>
</div>
<div class="flex">
<div class="w-120 text-center bd">付款方式</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_fJY1p__c }}}</div>
</div>
<div class="flex">
<div class="flex-1 bd">
<div class="text-center">付款事由</div>
<div class="text-center padding-5">{{{ dData.field_fwjy1__c }}}</div>
</div>
<div class="flex-1 bd">
<div class="text-center">经办部门意见</div>
<div class="text-center padding-5">{{{ dData.field_36nKK__c }}}</div>
</div>
<div class="flex-1 bd">
<div class="text-center">项目负责人意见</div>
<div class="text-center padding-5">{{{ dData.field_J3N26__c }}}</div>
</div>
</div>
</div>
</div>
<div class="text-right no-print">点击浏览器的打印按钮直接进行打印(该文字不会被打印)</div>
</div>
</template>
<script>
export default {
props: ['userData', 'data'],
data() {
return {
dData: null
}
},
created() {
this.getData();
},
mounted() {
this.$emit('action', {type: 'resize'});
},
methods: {
//金额小写转大写
digitUppercase(n) {
var fraction = ['角', '分'];
var digit = [
'零', '壹', '贰', '叁', '肆',
'伍', '陆', '柒', '捌', '玖'
];
var unit = [
['元', '万', '亿'],
['', '拾', '佰', '仟']
];
var head = n < 0 ? '欠' : '';
n = Math.abs(n);
var s = '';
for (var i = 0; i < fraction.length; i++) {
s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '');
}
s = s || '整';
n = Math.floor(n);
for (var i = 0; i < unit[0].length && n > 0; i++) {
var p = '';
for (var j = 0; j < unit[1].length && n > 0; j++) {
p = digit[n % 10] + unit[1][j] + p;
n = Math.floor(n / 10);
}
s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s;
}
return head + s.replace(/(零.)*零元/, '元')
.replace(/(零.)+/g, '零')
.replace(/^整$/, '零元整');
},
getData() {
const userData = this.userData;
//获取描述然后解析数据
FxUI.objectApi.fetch_describe(userData.object_describe_api_name).then((data) => {
const {
fields
} = data.objectDescribe;
let result = {};
Object.keys(fields).forEach(fApiName => {
result[fApiName] = (FxUI.objectApi.format_field_value || PAAS.format_field_value)(fields[fApiName], userData[fApiName], userData);
})
this.dData = result;
this.$nextTick(() => {
this.$emit('action', {type: 'resize'});
})
})
}
}
}
</script>
<style lang="less">
.cus-demo {
.demo {
max-width: 1200px;
}
h1 {
font-size: 28px;
font-weight: bold;
margin-bottom: 15px;
}
div {
box-sizing: border-box;
line-height: 34px;
}
.flex {
display: flex;
}
.flex-1 {
flex: 1;
}
.flex-col {
flex-direction: column;
}
.w-120 {
width: 120px;
}
.w-60 {
width: 60px;
}
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.padding-5 {
padding: 0 10px;
}
.bd {
border: 1px solid #ccc;
}
&--rotate {
transform: rotate(90deg);
}
}
//该样式仅作demo使用,具体更合理的方式,请咨询@吴敬
@media print {
body {
height: auto!important;
}
#crm-layout {
display: none;
}
#app-portal {
display: none;
}
.detail-dialog-v3 {
display: none;
}
.el-dialog__header {
display: none!important;
}
.f-qx-container {
display: none;
}
.fx-dialog {
margin-top: 0!important;
}
.no-print {
display: none!important;
}
}
</style>
# 配置UI按钮
假设上述自定义组件的API Name是fund_approval_form__c,UI按钮绑定的自定义函数代码如下:
Map objectData = context.data as Map
UIAction uiaction = OpenDialogAction.build(){
title = "资金使用审批表"
width = "750px"
component {
apiName = "fund_approval_form__c"
}
userData = objectData
}
return uiaction
# 实际的展示效果
复制代码