记录超简单实现打印,window.print() 分页打印,vue element-ui window.print()打印

1,832 阅读1分钟

效果

image.png

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.点击打印 效果如下

点击打印前

image.png

点击打印按钮

image.png

更新-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;
    }
}

分页效果如下

image.png

image.png