效果
1. 全局样式种添加下面css
/* 设置打印页面时的外边距 START */
/* 代码来自 npm react-to-print */
@page {
size: auto;
margin: 0mm;
}
@media print {
body {
-webkit-print-color-adjust: exact;
}
}
/* 设置打印页面时的外边距 END */
2.编写组件,并解决 element ui table 显示不全
TheElDialog 组件
<template>
<el-dialog
:close-on-press-escape="closeOnPressEscape"
:close-on-click-modal="closeOnClickModal"
:destroy-on-close="destroyOnClose"
:width="width || (isMobile ? '92%' : '75%')"
:title="title"
v-bind="$attrs"
v-on="$listeners"
class="minWidth"
:fullscreen="isFullscreen"
>
<el-button class="fullscreen-button">
<i class="el-dialog__close el-icon el-icon-full-screen " @click="isFullscreen = !isFullscreen"></i>
</el-button>
<slot></slot>
<template slot="footer">
<slot name="footer"></slot>
</template>
</el-dialog>
</template>
<script>
export default {
name: 'TheElDialog',
props: {
width: {
type: String,
default: '75%'
},
minWidth: {
type: Boolean,
default: false
},
fullscreen: {
type: Boolean,
default: false
},
title: {
type: String
},
closeOnPressEscape: {type: Boolean, default: false},
closeOnClickModal: {type: Boolean, default: false},
destroyOnClose: {type: Boolean, default: true}
},
data() {
return {
isFullscreen: false
};
},
computed: {
isMobile() {
return this.$store.getters['app/isMobile'];
}
},
created() {
this.isFullscreen = this.fullscreen;
},
methods: {
setFullscreen(isFullscreen) {
this.isFullscreen = isFullscreen;
}
}
};
</script>
<style lang="scss">
.minWidth {
.el-dialog {
min-width: 600px;
}
.fullscreen-button {
position: absolute;
top: 22px;
right: 44px;
font-size: 14px;
padding: 0;
border: none;
background-color: transparent;
}
}
</style>
// dialogToPrint.js
export default async function dialogToPrint(dialogRef, hiddens = [], A4width) {
try {
dialogRef = dialogRef || this.$refs.dialog;
const dialogEl = dialogRef.$el;
dialogRef.setFullscreen(true);
const width = dialogEl.style.width;
// A4 纸的宽度
dialogEl.style.width = A4width || '210mm';
// 隐藏footer
hiddens = hiddens.length === 0 ? [this.$el.querySelector('.dialog-footer')] : hiddens;
// 缓存style.display
const hiddenNodeDisplays = hiddens.map(item => item.style.display);
// 隐藏dom
hiddens.forEach(item => (item.style.display = 'none'));
new Promise((resolve, reject) =>
setTimeout(() => {
// 同步任务
window.print();
resolve();
}, 500)
);
await new Promise((resolve, reject) =>
setTimeout(() => {
// 恢复原来的宽度
dialogEl.style.width = width;
// 取消全屏
dialogRef.setFullscreen(false);
// 恢复footer
hiddens.forEach((item, index) => (item.style.display = hiddenNodeDisplays[index]));
resolve();
}, 1000)
);
dialogRef = null;
hiddenNodeDisplays.length = 0;
hiddens.length = 0;
} catch (error) {
console.log('toprint error :>> ', error);
throw new Error(error);
}
}
需要打印的弹窗组件
<template>
<TheElDialog
ref="dialog"
:title="title"
:visible.sync="centerDialogVisible"
@close="handleCenterDialogClose"
v-bind="$attrs"
>
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="10" :xl="8">
<el-form
@submit.native.prevent
ref="form_documentNo_"
:model="form"
:rules="rules"
:label-width="isMobile ? '80px' : '120px'"
>
<el-form-item prop="documentNo" label="单据编号">
<template v-if="isDetail">{{ form.documentNo }}</template>
<el-input
v-else
class="textvalue"
:disabled="true"
v-model="form.documentNo"
placeholder=""
></el-input>
</el-form-item>
</el-form>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="10" :xl="8">
<el-form
@submit.native.prevent
ref="form_documentDate_"
:model="form"
:rules="rules"
:label-width="isMobile ? '80px' : '120px'"
>
<el-form-item prop="documentDate" label="单据日期">
<template v-if="isDetail">{{ form.documentDate | formatDateFillStr }}</template>
<template v-else>
<TheDatePicker v-model="form.documentDate"></TheDatePicker>
</template>
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="10" :xl="8">
<el-form
@submit.native.prevent
ref="form_customer_"
:model="form"
:rules="rules"
:label-width="isMobile ? '80px' : '120px'"
>
<el-form-item prop="customer" label="客户">
<template v-if="isDetail">{{
{id: form.customer, arr: optionsCustomers} | findValue
}}</template>
<el-select
v-else
v-model="form.customer"
filterable
clearable
placeholder="请选择"
style="width: 100%"
>
<el-option
v-for="item in optionsCustomers"
:key="item._id"
:label="item.name"
:value="item._id"
>
</el-option>
</el-select>
</el-form-item>
</el-form>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="10" :xl="8">
<el-form
@submit.native.prevent
ref="form_person_"
:model="form"
:rules="rules"
:label-width="isMobile ? '80px' : '120px'"
>
<el-form-item prop="person" label="财务人员">
<template v-if="isDetail">{{ {id: form.person, arr: optionsPersons} | findValue }}</template>
<el-select
v-else
v-model="form.person"
filterable
clearable
placeholder="请选择"
style="width: 100%"
>
<el-option
v-for="item in optionsPersons"
:key="item._id"
:label="item.name"
:value="item._id"
>
</el-option>
</el-select>
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="10" :xl="8">
<el-form
@submit.native.prevent
ref="form_account_"
:model="form"
:rules="rules"
:label-width="isMobile ? '80px' : '120px'"
>
<el-form-item prop="account" label="账户名称">
<template v-if="isDetail">{{ {id: form.account, arr: optionsAccounts} | findValue }}</template>
<el-select
v-else
v-model="form.account"
filterable
clearable
placeholder="请选择"
style="width: 100%"
>
<el-option
v-for="item in optionsAccounts"
:key="item._id"
:label="item.name"
:value="item._id"
>
</el-option>
</el-select>
</el-form-item>
</el-form>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="10" :xl="8">
<el-form
@submit.native.prevent
ref="form_total_"
:model="form"
:rules="rules"
:label-width="isMobile ? '80px' : '120px'"
>
<el-form-item prop="total" label="单据金额">
<template v-if="isDetail">{{ form.total }}</template>
<el-input
v-else
class="textvalue"
type="number"
:disabled="true"
@mousewheel.native.prevent
@DOMMouseScroll.native.prevent
v-model="form.total"
></el-input>
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="10" :xl="8">
<el-form
@submit.native.prevent
ref="form_depot_"
:model="form"
:rules="rules"
:label-width="isMobile ? '80px' : '120px'"
>
<el-form-item prop="depot" label="仓库">
<template v-if="isDetail">{{ {id: form.depot, arr: optionsDepots} | findValue }}</template>
<el-select
v-else
v-model="form.depot"
filterable
clearable
placeholder="请选择"
style="width: 100%"
>
<el-option
v-for="item in optionsDepots"
:key="item._id"
:label="item.name"
:value="item._id"
>
</el-option>
</el-select>
</el-form-item>
</el-form>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="10" :xl="8">
<el-form
@submit.native.prevent
ref="form_remark_"
:model="form"
:rules="rules"
:label-width="isMobile ? '80px' : '120px'"
>
<el-form-item prop="remark" label="备注">
<template v-if="isDetail">{{ form.remark }}</template>
<el-input
v-else
:show-word-limit="form.remark ? form.remark && form.remark.length > 190 : false"
:maxlength="200"
type="textarea"
v-model="form.remark"
:rows="1"
placeholder="请填写备注"
></el-input>
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-button
:style="{visibility: isDetail ? 'hidden' : 'visible'}"
style="width: 100%;margin-bottom: 10px;"
type="primary"
@click="onPushSelection"
>
+1 行数据
</el-button>
<el-table :data="form.itemOutIns" style="width: 100%;" :summary-method="getSummaries" show-summary border>
<el-table-column type="index" width="50" label="序号"> </el-table-column>
<el-table-column prop="date" label="日期">
<template v-slot:header><span style="color:#f00;padding-right: 4px;">*</span>日期</template>
<template v-slot="{row}">
<el-form
@submit.native.prevent
:key="`form_itemOutIns_date_${row.uuid}`"
:ref="`form_itemOutIns_date_${row.uuid}`"
:model="row"
:rules="rules"
>
<el-form-item prop="date" label="">
<template v-if="isDetail">{{ row.date | formatShortDate }}</template>
<el-date-picker
v-else
type="date"
placeholder="选择日期"
format="yyyy/MM/dd"
prefix-icon="false"
v-model="row.date"
:clearable="true"
class="form-the-datepicker"
style="width: 100%;"
></el-date-picker>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column prop="itemOutInId" label="收支项目">
<template v-slot:header><span style="color:#f00;padding-right: 4px;">*</span>收支项目</template>
<template v-slot="{row}">
<el-form
@submit.native.prevent
:key="`form_itemOutIns_itemOutInId_${row.uuid}`"
:ref="`form_itemOutIns_itemOutInId_${row.uuid}`"
:model="row"
:rules="rules"
>
<el-form-item prop="itemOutInId" label="">
<template v-if="isDetail">{{
{id: row.itemOutInId, arr: optionsProjects} | findValue
}}</template>
<el-select
v-else
v-model="row.itemOutInId"
filterable
clearable
placeholder="请选择"
style="width: 100%"
>
<el-option
v-for="item in optionsProjects"
:key="item._id"
:label="item.name"
:value="item._id"
>
</el-option>
</el-select>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column prop="money" label="金额">
<template v-slot:header><span style="color:#f00;padding-right: 4px;">*</span>金额</template>
<template v-slot="{row}">
<el-form
@submit.native.prevent
:key="`form_itemOutIns_money_${row.uuid}`"
:ref="`form_itemOutIns_money_${row.uuid}`"
:model="row"
:rules="rules"
>
<el-form-item prop="money" label="">
<template v-if="isDetail">{{ row.money }}</template>
<el-input
v-else
type="number"
@mousewheel.native.prevent
@DOMMouseScroll.native.prevent
v-model="row.money"
placeholder=""
></el-input>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column prop="abstract" label="摘要">
<template v-slot:header><span style="color:#f00;padding-right: 4px;">*</span>摘要</template>
<template v-slot="{row}">
<el-form
@submit.native.prevent
:key="`form_itemOutIns_abstract_${row.uuid}`"
:ref="`form_itemOutIns_abstract_${row.uuid}`"
:model="row"
:rules="rules"
>
<el-form-item prop="abstract" label="">
<template v-if="isDetail">{{ row.abstract }}</template>
<el-input
v-else
type="textarea"
rows="1"
:maxlength="200"
v-model="row.abstract"
placeholder="填写摘要备注信息"
></el-input>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column v-if="!isDetail" align="center" label="操作" :width="50">
<template v-slot="{row}">
<el-button size="small" @click="onRemoveAddSelection(row)" type="text">
移除
</el-button>
</template>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button :disabled="centerDialogLoading" @click="centerDialogVisible = false">取 消</el-button>
<el-button v-if="isDetail" type="primary" @click="onClickPrint('dialog')">打 印</el-button>
<el-button
v-else
:disabled="isDetail"
:loading="centerDialogLoading"
type="primary"
@click="handleSubmit('formRule')"
>确 定</el-button
>
</span>
</TheElDialog>
</template>
<script>
import {postApiList, putApiList} from '@/api/itemIn';
import getRules from '@/views/financial/getRules';
import {v4 as uuidv4} from 'uuid';
import moment from 'moment';
import VueToPrint from '@/components/VueToPrint';
import dialogToPrint from '@/utils/dialogToPrint';
export default {
name: 'ItemInDialog',
props: {
title: {
type: String,
default: ''
},
visible: {
type: Boolean,
default: false
},
dataSource: {
type: Object,
required: true
},
optionsPersons: {
type: Array,
default: () => [],
required: true
},
optionsCustomers: {
type: Array,
default: () => [],
required: true
},
optionsProjects: {
type: Array,
default: () => [],
required: true
},
optionsAccounts: {
type: Array,
default: () => [],
required: true
},
optionsDepots: {
type: Array,
default: () => [],
required: true
}
},
components: {VueToPrint},
data() {
return {
// 新建弹窗 提交表单中
centerDialogLoading: false,
inputStyle: {width: '320px'},
// 新建表单
form: {
id: undefined,
documentNo: `SR${moment().format('YYYYMMDDHHmmssSSS')}`,
documentDate: undefined,
customer: undefined,
person: undefined,
remark: undefined,
account: undefined,
total: undefined,
depot: undefined,
itemOutIns: [
{
uuid: uuidv4(),
itemOutInId: undefined,
date: new Date(),
money: undefined,
abstract: ''
}
]
},
rules: getRules()
};
},
computed: {
isMobile() {
return this.$store.getters['app/isMobile'];
},
isDetail() {
return typeof this.title === 'string' && this.title.includes('查看');
},
centerDialogVisible: {
get() {
return this.visible;
},
set(val) {
this.$emit('update:visible', val);
}
}
},
watch: {
dataSource: {
handler(val) {
const form = {
...this.form,
...val,
itemOutIns: (val.itemOutIns || []).map(item => ({uuid: uuidv4(), ...item}))
};
if (this.title && this.title.includes('添加')) {
form.documentNo = `SR${moment().format('YYYYMMDDHHmmssSSS')}`;
}
this.form = form;
},
immediate: true
}
},
methods: {
getSummaries(param) {
const {columns, data} = param;
const sums = [];
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '合计';
return;
}
const values = data.map(item => Number(item[column.property]));
if (column.property === 'money') {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0);
sums[index] = `¥ ${sums[index].toFixed(2)}`;
} else {
// sums[index] = 'N/A';
sums[index] = '';
}
});
return sums;
},
onPushSelection() {
this.form.itemOutIns.push({
uuid: uuidv4(),
itemOutInId: undefined,
date: new Date(),
money: undefined,
remark: ''
});
},
onRemoveAddSelection(row) {
this.form.itemOutIns = this.form.itemOutIns.filter(({uuid}) => uuid !== row.uuid);
},
async formValidates(values) {
const handers = [];
values.forEach(item => {
const uuid = item.uuid || '';
const formNames = [
`form_documentNo_${uuid}`,
`form_documentDate_${uuid}`,
`form_customer_${uuid}`,
`form_person_${uuid}`,
`form_remark_${uuid}`,
`form_account_${uuid}`,
`form_depot_${uuid}`,
`form_total_${uuid}`,
`form_itemOutIns_date_${uuid}`,
`form_itemOutIns_itemOutInId_${uuid}`,
`form_itemOutIns_money_${uuid}`,
`form_itemOutIns_abstract_${uuid}`
];
formNames.forEach(formName => {
if (!this.$refs[formName]) {
// console.warn('formName', formName);
return;
}
const hander = new Promise((resolve, reject) => {
this.$refs[formName].validate((valid, fields) => {
if (valid) {
resolve(valid);
return;
}
reject(valid);
});
});
handers.push(hander);
});
});
await Promise.all(handers);
},
onClickPrint() {
dialogToPrint.call(this, this.$refs.dialog, [
this.$el.querySelector('.dialog-footer'),
...Array.from(this.$el.querySelectorAll('.el-dialog__close'))
]);
},
// 提交表单
async handleSubmit() {
const form = this.form;
const newItem = {
_id: form._id,
documentNo: form.documentNo,
documentDate: form.documentDate,
customer: form.customer,
person: form.person,
remark: form.remark,
account: form.account,
depot: form.depot,
total: form.total,
itemOutIns: form.itemOutIns
};
try {
if (form.itemOutIns.length === 0) {
throw new Error('表单校验失败');
}
await this.formValidates([newItem, ...form.itemOutIns]);
if (this.title && this.title.includes('添加')) {
this.onCreate(newItem);
}
if (this.title && this.title.includes('编辑')) {
this.onPutData(newItem);
}
} catch (error) {
console.log('error :>> ', error);
this.$message({showClose: true, type: 'error', message: '表单校验失败'});
}
},
// 创建角色
onCreate(newItem) {
this.centerDialogLoading = true;
postApiList(newItem)
.then(({data: res}) => {
if (res.status === 201) {
this.$message({showClose: true, type: 'success', message: '创建成功'});
this.centerDialogVisible = false;
// this.getList();
// 重新更新数据
this.$emit('on-ok');
} else {
throw new Error(res);
}
})
.catch(error => {
const data = error.response && error.response.data;
if (data && data.error) {
this.$message({showClose: true, type: 'error', message: data.error});
} else {
this.$message({showClose: true, type: 'error', message: '创建失败'});
}
})
.finally(() => {
this.centerDialogLoading = false;
});
},
// 更新
onPutData(newItem) {
this.centerDialogLoading = true;
putApiList(newItem._id, newItem)
.then(({data: res}) => {
if (res.status === 204) {
this.$message({showClose: true, type: 'success', message: '更新成功'});
this.centerDialogVisible = false;
// this.getList();
// 重新更新数据
this.$emit('on-ok');
} else {
throw new Error(res);
}
})
.catch(error => {
this.$message({showClose: true, type: 'error', message: error});
})
.finally(() => {
this.centerDialogLoading = false;
});
},
// dialog 关闭
handleCenterDialogClose() {
// 关闭
this.$emit('close');
this.form = {
...this.$options.data().form,
documentNo: `SR${moment().format('YYYYMMDDHHmmssSSS')}`,
itemOutIns: [
{
uuid: uuidv4(),
itemOutInId: undefined,
date: new Date(),
money: undefined,
remark: ''
}
]
};
}
}
};
</script>
<style lang="scss" scoped>
.textvalue {
::v-deep {
.el-input__inner {
cursor: text;
color: #606266;
}
}
}
</style>
3.点击打印 效果如下
点击打印前
点击打印按钮
更新-element ui 分页打印页面
关键点:
1. 打印的元素必须设置 overflow: unset; position:unset; height: auto;
2. 打印的内容要与body的高度一致
3. display: flex; 不生效(具体替代方法还在找,有知道的可以评论区告诉我哈)
// 打印页面设置
@page {
size: auto;
margin: 0mm; /* 上下左右的外边距,这么设置之后可以用css 控制空白区域 */
}
// 打印时生效的css
@media print {
/* 设置打印页面时的外边距 END */
body {
-webkit-print-color-adjust: exact;
}
/* 滚动条样式, 隐藏掉 */
.scrollbar-style::-webkit-scrollbar {
width: 8px;
}
.scrollbar-style::-webkit-scrollbar-track {
border-radius: 10px;
background-color: transparent;
}
.scrollbar-style::-webkit-scrollbar-thumb {
border-radius: 5px;
background-color: rgba(150, 150, 150, 0.66);
border: 4px solid rgba(150, 150, 150, 0.66);
background-clip: content-box;
}
/* element-ui 的半透明窗口隐藏掉,把#app,整个应用的内容隐藏掉 */
.v-modal,
#app {
display: none !important;
}
/* 不可以设置成position: fixed;, 高度必须 auto,不然不会分页,*/
.el-dialog__wrapper {
position: unset !important;
height: auto !important;
overflow: unset !important;
}
}
分页效果如下