English
Custom Printing
About 1726 wordsAbout 6 min
2025-12-16
In most cases, the print template feature in Fxiaoke can satisfy general printing needs. However, it is not enough for special report-printing scenarios. This article shows you how to build a custom component to create a tailored print page.
Write the Template
The core idea is to build the table layout with HTML.
<template>
{ <div class="cus-demo" v-if="dData">
<h1 class="text-center">Fund Usage Approval Form</h1>
<div class="flex bd">
<div class="flex flex-1 flex-col">
<div class="flex">
<div class="w-120 text-center bd">Payee</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_uhcib__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">Bank</div>
<div class="flex-1">
<div class="flex">
<div class="w-60 text-center bd">Branch</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_utckZ__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">Account</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_oQgRl__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">Address</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">Payment Amount</div>
<div class="flex-1">
<div class="flex">
<div class="w-60 text-center bd">Uppercase</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">Numeric</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">Payment Method</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">Reason for Payment</div>
<div class="text-center padding-5">{{{ dData.field_fwjy1__c }}}</div>
</div>
<div class="flex-1 bd">
<div class="text-center">Handling Department Opinion</div>
<div class="text-center padding-5">{{{ dData.field_36nKK__c }}}</div>
</div>
<div class="flex-1 bd">
<div class="text-center">Project Owner Opinion</div>
<div class="text-center padding-5">{{{ dData.field_J3N26__c }}}</div>
</div>
</div>
</div>
</div>
<div class="text-right no-print">Click the browser's print button to print directly (this text will not be printed)</div>
</div>
</template>Write the Script
Let's first sort out the approach:
Get the full detail data of the current record. This depends on
userDatain the UI action.Get the field metadata of the business object for the current detail record. This depends on the API provided by
FxUI.With both data and metadata, parse the values into a format users can understand. This also depends on the API provided by
FxUI.
Based on these steps, we can organize the logic as follows:
<script>
export default {
props: ['userData'], // Step 1 mentioned above: the custom component receives UI action data through userData
data() {
return {
dData: null
}
},
created() {
this.getData();
},
mounted() {
// Trigger recalculation of the dialog position
this.$emit('action', {type: 'resize'});
},
methods: {
getData() {
const userData = this.userData;
// Step 2 mentioned above: get object metadata
FxUI.objectApi.fetch_describe(userData.object_describe_api_name).then((data) => {
const {
fields
} = data.objectDescribe;
// Step 3 mentioned above: parse data
let result = {};
Object.keys(fields).forEach(fApiName => {
result[fApiName] = (FxUI.objectApi.format_field_value || PAAS.format_field_value)(fields[fApiName], userData[fApiName], userData);
})
// Final step: assign values for rendering
this.dData = result;
this.$nextTick(() => {
// After data is rendered to the page, the height may change. Recalculate the dialog position at this point.
this.$emit('action', {type: 'resize'});
})
})
}
}
}
</script>In addition, the requirement includes converting numeric amounts into uppercase Chinese financial text, so we optimize the code by providing a corresponding method digitUppercase in the component, as shown below:
<script>
export default {
props: ['userData', 'data'],
data() {
return {
dData: null
}
},
created() {
this.getData();
},
mounted() {
this.$emit('action', {type: 'resize'});
},
methods: {
// Convert numeric amount to uppercase Chinese financial text
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;
// Get metadata and then parse data
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>Write the 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>Because printing is involved, we want only the content inside the custom component to be printed. Here we use a media query to hide parts of the page that should not be printed, and optimize the code as follows:
Note: This approach is a hack. @Wu Jing will provide another healthier way to achieve this goal, so it is not discussed in detail here.
<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>Complete Code
<template>
<div class="cus-demo" v-if="dData">
<h1 class="text-center">Fund Usage Approval Form</h1>
<div class="flex bd">
<div class="flex flex-1 flex-col">
<div class="flex">
<div class="w-120 text-center bd">Payee</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_uhcib__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">Bank</div>
<div class="flex-1">
<div class="flex">
<div class="w-60 text-center bd">Branch</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_utckZ__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">Account</div>
<div class="flex-1 padding-5 bd">{{{ dData.field_oQgRl__c }}}</div>
</div>
<div class="flex">
<div class="w-60 text-center bd">Address</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">Payment Amount</div>
<div class="flex-1">
<div class="flex">
<div class="w-60 text-center bd">Uppercase</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">Numeric</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">Payment Method</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">Reason for Payment</div>
<div class="text-center padding-5">{{{ dData.field_fwjy1__c }}}</div>
</div>
<div class="flex-1 bd">
<div class="text-center">Handling Department Opinion</div>
<div class="text-center padding-5">{{{ dData.field_36nKK__c }}}</div>
</div>
<div class="flex-1 bd">
<div class="text-center">Project Owner Opinion</div>
<div class="text-center padding-5">{{{ dData.field_J3N26__c }}}</div>
</div>
</div>
</div>
</div>
<div class="text-right no-print">Click the browser's print button to print directly (this text will not be printed)</div>
</div>
</template>
<script>
export default {
props: ['userData', 'data'],
data() {
return {
dData: null
}
},
created() {
this.getData();
},
mounted() {
this.$emit('action', {type: 'resize'});
},
methods: {
// Convert numeric amount to uppercase Chinese financial text
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;
// Get metadata and then parse data
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);
}
}
// This style is only for demo purposes. For a more reasonable approach, please consult @Wu Jing
@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>Configure the UI Button
Assume the API Name of the custom component above is fund_approval_form__c. The custom function code bound to the UI button is as follows:
Map objectData = context.data as Map
UIAction uiaction = OpenDialogAction.build(){
title = "Fund Usage Approval Form"
width = "750px"
component {
apiName = "fund_approval_form__c"
}
userData = objectData
}
return uiactionActual Display Effect
<video src="https://a9.fspage.com/FSR/uipaas/demo-print.mov" style="width: 100%;" controls></video>