vue+ts在线文档编辑(类腾讯文档)多人在线编辑-自定义页眉和分页打印(三)
随着在线办公场景越来越多,同时需要各式各样办公软件,在开发时就用得到在线文档来内容指定某人填写、审批、传阅文件,文件导出、打印成纸盖章等。所以需要功能较为强大、复杂的文档编辑器,前两篇文章我们已经教大家如何制作分页的文档编辑器,这篇文章我们把功能补全,教大家做好的文档怎么添加自定义页眉、打印。
前言
随着在线办公场景越来越多,同时需要各式各样办公软件,在开发时就用得到在线文档来内容指定某人填写、审批、传阅文件,文件导出、打印成纸盖章等。所以需要功能较为强大、复杂的文档编辑器,前两篇文章我们已经教大家如何制作分页的文档编辑器,这篇文章我们把功能补全,教大家做好的文档怎么添加自定义页眉、打印。
老样子在教程开始前先上demo,有图有真相,在线demo让你看了是否是您想要的东西,不用浪费您半天时间,看了一推无用东西。
一、自定义页眉内容
在vue+ts在线文档编辑(腾讯文档)多人在线编辑-实现假分页(二) 基础上加入页眉内页。
首先添加组件 pageHeader.vue 文件,代码如下:
<template>
<div class="editor-headerbox" >
<div class="left">
<table class="headertable">
<tr>
<td>单位</td>
<td>天文台检查站</td>
<td>监管单位</td>
<td>中国</td>
</tr>
<tr>
<td>文件编号</td>
<td>488 </td>
<td>页码</td>
<td>
<div style="display: flex; justify-content: center; align-items: center;">
第{{page}}页 / 共{{total}}页
</div>
</td>
</tr>
</table>
</div>
<div class="right">
<img src="@/assets/logo.jpg" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed, unref } from 'vue';
export default defineComponent({
name: 'pageHeader',
components: {
},
props:{
page:{
type: Number,
default:1
},
total:{
type: Number,
default:1
},
},
emits: ['success'],
setup() {
return {
};
},
});
</script>
<style lang="scss" scoped>
//页眉
.editor-headerbox{
display: flex;
padding: 0px 40px;
height: 100px;
user-select: none;
.left{
flex:1;
text-align: left;
display: flex;
align-items: center;
.headertable{
border-collapse: separate;
border-spacing: 0;
border-top: 1px solid #000000;
border-left: 1px solid #000000;
td{
padding: 5px 10px;
border: 1px solid #000000;
border-top: none;
border-left: none;
}
}
}
.right{
text-align: right;
img{
height: 100%;
cursor: pointer;
pointer-events: auto;
}
}
}
</style>
在编辑器直接引入:
import pageHeader from './component/pageHeader.vue'
二、分页文档打印
1.加入分页符
由于文档按word的A4纸尺寸分页,在打印的时候也是按照A4大小分页打印,在预览的页符加打印分页属性, 下图处的分隔符添加css属性(page-break-after:always;)
完整代码 如下:
<div class="editor-page-list" >
<template v-for="item in pageCount">
<div class="editor-logic-page logc-page-box" >
<!--页眉-->
<div class="header-page page_wrapper" >
<pageHeader :page="item" :total="pageCount"></pageHeader>
</div>
<!--页脚-->
<div class="footer-page page_wrapper">
<div class="pagetext">-第{{item}}页-</div>
</div>
</div>
<div class="melo-page-gap" v-if="item!=pageCount" style="page-break-after:always; width: 100%; height: 23px; pointer-events: auto;"></div>
</template>
</div>
2.打印方法
打印直接选用浏览器内置的打印方法(建议使用google和微软Microsoft Edge浏览器),代码如下:
const doPrint=()=>{
window.print(); //打印刚才新建的网页
return false;
}
3.打印模块完整代码
vue 、ts代码如下:
<!--填写质量文件-->
<template>
<!-- <div class=" form-widget-main"> -->
<div class="form-widget-container" :style="{height:'100%'}">
<el-button type="primary" class="savebtn" @click="printFile" circle >
印
</el-button>
<div class="editor-container-box" :style="{height:pageRaelheight+'px'}">
<div class="editor-content" id="editor-content" :style="{height:pageRaelheight+'px'}" >
<div class="editor-page-list" >
<template v-for="item in pageCount">
<div class="editor-logic-page logc-page-box" >
<!--页眉-->
<div class="header-page page_wrapper" >
<pageHeader :page="item" :total="pageCount"></pageHeader>
</div>
<!--页脚-->
<div class="footer-page page_wrapper">
<div class="pagetext">-第{{item}}页-</div>
</div>
</div>
<div class="melo-page-gap" v-if="item!=pageCount" style="page-break-after:always; width: 100%; height: 23px; pointer-events: auto;"></div>
</template>
</div>
<!--编辑器-->
<div ref="containerRef" style="padding-top:100px;padding-bottom: 40px;" ></div>
<div class="editor-pagebg-list" >
<template v-for="item in pageCount">
<div class="editor-bg-page" >
</div>
</template>
</div>
</div>
</div>
</div>
<!-- </div> -->
</template>
<script lang="ts" >
import { defineComponent, onMounted, reactive,ref,nextTick, toRefs } from 'vue';
import { getDocValue, getPluginValue,getDemoData} from "../pageEditor/script/index";//数据存储本地
import { cards, plugins, pluginConfig } from "../FileEditor/script/config";
//数据
import { inputItem} from '../FileEditor/script/data';
//打印
import { pageHeader } from '../pageEditor/component'
//编辑器
import Engine, {
$,
EngineInterface,
} from "@aomao/engine";
//方法
import { WaterMark } from '../pageEditor/script/commom';
export default defineComponent({
name: 'pagePrint',
// 注册组件
components: {
pageHeader,
},
setup() {
const winHeight=ref(document.documentElement.clientHeight-0)
//获取组件数据
const plugindata=getPluginValue()||''
const pluginList=ref<inputItem[]>([])
// console.log('获取组件:', plugindata)
if(plugindata&&plugindata!="undefined"){
pluginList.value=JSON.parse(plugindata)
}
// 编辑器容器
const containerRef = ref<HTMLElement | null>(null);
// 编辑器引擎
const engine = ref<EngineInterface | null>(null);
// 默认设置为当前在加载中
const loading = ref(true);
onMounted(() => {
document.title="质量文件编辑器-分页打印测试"
// 容器加载后实例化编辑器引擎
if (containerRef.value) {
const engineInstance = new Engine(containerRef.value, {
// 启用的插件
plugins,
// 启用的卡片
cards,
readonly:true,
lazyRender:false,
// 所有的卡片配置
config: pluginConfig,
});
const value = getDocValue() || getDemoData();
engineInstance.setValue(value, () => {
loading.value = false;
});
//监听渲染完成
nextTick(()=>{
setTimeout(() => {
countPage()//计算的分页
pluginList.value.forEach((item:inputItem,index:number)=>{
const pluginDom=<HTMLInputElement>document.getElementById(item.domid)
if(pluginDom){
if(item.type=="input"){
if(pluginDom.parentElement&&pluginDom.parentElement.parentElement)
pluginDom.parentElement.parentElement.innerHTML=`<span style="width:${item.style.width}px;min-height: 26px;display: inline-block;border-bottom: 1px solid #d9d9d9;margin-bottom: -6px;"> ${item.value||'<span style="opacity:0;">-</span>'}</span>`
}else if(item.type=="textarea"){
if(pluginDom.parentElement)
pluginDom.parentElement.innerHTML=item.value
}else if(item.type=="checkbox"){
let htmlstr=""
if(item.value&&parseInt(item.value)==1){
htmlstr=`<span style="width:${item.style.width}px;height:${item.style.height}px;position: relative;display: inline-block;vertical-align: text-bottom;border: 1px solid #000000;border-radius: 3px;" >
<i style=" content: ''; width: ${item.style.height/2}px;height: ${item.style.height/3}px; border-left: 1px solid #000000; border-bottom: 1px solid #000000; position: absolute; top: 3px;left: 3px;transform: rotate(-45deg);"></i>
</span>`
}else{
htmlstr=`<span style="width:${item.style.width}px;height:${item.style.height}px;position: relative;display: inline-block;vertical-align: text-bottom;border: 1px solid #000000;border-radius: 3px;" ></span>`
}
if(pluginDom.parentElement&&pluginDom.parentElement.parentElement)
pluginDom.parentElement.parentElement.innerHTML=htmlstr
}else if(item.type=="image"){
if(pluginDom.parentElement)
pluginDom.parentElement.innerHTML=`<img id="image_${item.uuid}" style="width:${item.style.width}px;margin-bottom: ${item.style.marginBottom}px;" src="https://tuwen.hulingyun.cn/common/uploadfile/getimage?url=resource/uploads/20230303/4c0fe297b181e4b509cedacf9918aa61.jpg"/>`
}else if(item.type=="textsync"){//动态数据
if(pluginDom.parentElement&&pluginDom.parentElement.parentElement)
pluginDom.parentElement.parentElement.innerText=item.value
}
//事件
if(item.type=="image"){//放大图片
$(`#${item.domid}`).on("click",(event)=>{
// alert("放大图片")
});
}
}
});
}, 500);
})
}
});
//打印预览
const easyPrint = ref()
const printFile=()=>{
doPrint();
// if(easyPrint.value)
// easyPrint.value.print()
}
const doPrint=()=>{
// var head_str = "<html><head><title></title></head><body>"; //先生成头部
// var foot_str = "</body></html>"; //生成尾部
// var new_str = document.getElementsByClassName('form-widget-container')[0].innerHTML; //获取指定打印区域
// document.body.innerHTML = head_str + new_str + foot_str; //构建新网页
window.print(); //打印刚才新建的网页
return false;
}
//分页
//计算有多少页面
const pageRaelheight=ref(1100)
const pageCount=ref(1)//页面数
const countPage=()=>{
let editorheight=containerRef.value?.clientHeight||0
editorheight=editorheight-40
let pagenum=Math.ceil(editorheight/1100)
if(pagenum>1){
const midileheight=(pagenum-1)*25//中间分割线高之和
const allheight=editorheight+midileheight
//重新介绍高度
pagenum=Math.ceil(allheight/(1100))
pageRaelheight.value=pagenum*1100+midileheight
}else{
pageRaelheight.value=pagenum*1100
}
pageCount.value=pagenum
nextTick(()=>{
// 添加水印
var watermark_txt = "<div>仅供GoFly专用 请注意保护隐私</div>" ;//水印内容
WaterMark({ "watermarl_element": ".editor-bg-page", "watermark_txt": watermark_txt });
})
}
return{
winHeight,
containerRef,engine,loading,
printFile,
easyPrint,
//分页
pageRaelheight,pageCount,
}
}
});
</script>
<style lang="scss" scoped>
@import "./style/index.scss";
</style>
less样式 (样式布局很重要,这直接影响打印效果)如下:
.form-widget-container{
background: transparent;
overflow: auto;
width: 100%;
height: 100%;
display: inline-flex;
background: #f3f5f7;
.savebtn{
position:fixed;
top: 60px;
right: 120px;
z-index: 99;
}
}
//编辑器
.editor-container-box {
width: 793.667px;
margin: 0 auto;
margin: 12px auto;
.editor-content {
overflow: hidden;
position: relative;
background: #fff;
.editor-page-list{
position: absolute;
top: 0;
z-index: 33;
pointer-events: none;
.editor-logic-page{
width: 793.667px;
height: 1100px;
display: block;
position: relative;
.header-page{
width: 100%;
position: absolute;
top: 0px;
height: 100px;
background: #fff;
}
.footer-page{
width: 100%;
position: absolute;
bottom: 0px;
height: 40px;
background: #fff;
overflow: hidden;
.pagetext{
text-align: center;
padding-top: 10px;
height: 100%;
}
}
}
.melo-page-gap {
background: #f3f5f7;
z-index:99999999;
cursor: url("data:image/svg+xml,%3c%3fxml version='1.0' encoding='UTF-8'%3f%3e %3csvg width='24px' height='24px' viewBox='0 0 24 24' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3e %3ctitle%3eICON/bottombar/paging%3c/title%3e %3cg id='ICON/bottombar/paging' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3e %3cg id='Word-%e5%ba%95%e9%83%a8%e6%a0%8f-%e5%88%86%e9%a1%b5'%3e %3cg id='Group%e5%a4%8d%e5%88%b6-3' opacity='0' fill='black' fill-rule='nonzero'%3e %3crect id='Rectangle' opacity='0.0599999987' x='0' y='0' width='24' height='24'%3e%3c/rect%3e %3crect id='Rectangle' opacity='0.100000001' x='3' y='3' width='18' height='18'%3e%3c/rect%3e %3crect id='Rectangle' opacity='0.119999997' x='4' y='4' width='16' height='16'%3e%3c/rect%3e %3c/g%3e %3cpath d='M6.25%2c14 L6.25%2c16 L6.25%2c17.75 L17.75%2c17.75 L17.75%2c16 L17.75%2c14 L19%2c14 L19%2c18.5 C19%2c18.7761424 18.7761424%2c19 18.5%2c19 L5.5%2c19 C5.22385763%2c19 5%2c18.7761424 5%2c18.5 L5%2c14 L6.25%2c14 Z' id='%e5%bd%a2%e7%8a%b6%e7%bb%93%e5%90%88%e5%a4%8d%e5%88%b6' fill='%23464D5A' transform='translate(12.000000%2c 16.500000) scale(-1%2c 1) rotate(-180.000000) translate(-12.000000%2c -16.500000) '%3e%3c/path%3e %3crect id='%e7%9f%a9%e5%bd%a2' fill='%23464D5A' x='3' y='11.25' width='18' height='1.25'%3e%3c/rect%3e %3cpath d='M6.25%2c5 L6.25%2c7 L6.25%2c8.75 L17.75%2c8.75 L17.75%2c7 L17.75%2c5 L19%2c5 L19%2c9.5 C19%2c9.77614237 18.7761424%2c10 18.5%2c10 L5.5%2c10 C5.22385763%2c10 5%2c9.77614237 5%2c9.5 L5%2c5 L6.25%2c5 Z' id='%e5%bd%a2%e7%8a%b6%e7%bb%93%e5%90%88' fill='%23464D5A'%3e%3c/path%3e %3c/g%3e %3c/g%3e %3c/svg%3e") 15 15,auto;
}
}
.am-engine-view{
position: absolute;
top: 0;
width: 793.667px;
background: transparent;
padding: 0px 40px 0px 40px;
z-index: 19;
}
.am-engine{
position: absolute;
top: 0;
width: 793.667px;
background: transparent;
padding: 0px 40px 0px 40px;
z-index: 19;
}
//下一层 加水印
.editor-pagebg-list{
position: absolute;
top: 0;
z-index: 2;
.editor-bg-page{
width: 794px;
height: 1120px;
position: relative;
overflow: hidden;
background: transparent;
pointer-events: none;
}
}
}
:deep(.data-table){
background-color: transparent;
max-width: 100%;
}
:deep(.data-table-reader .data-table tr td){
border: 1px solid #000000;
padding: 4px 4px;
}
//修改线颜色
:deep(.table-wrapper .table-main-bg .table-main-border-top){
border-top: 1px solid #000000;
}
:deep(.table-wrapper .table-main-bg .table-main-border-right){
border-right: 1px solid #000000;
}
:deep(.table-wrapper .table-main-bg .table-main-border-bottom){
border-bottom: 1px solid #000000;
}
:deep(.table-wrapper .table-main-bg .table-main-border-left){
border-left: 1px solid #000000;
}
}
@media print {
@page{
size:auto;
margin: 0;
width: 793.667px;
height: 1100px;
}
.melo-page-gap{
background: #fff;
}
.savebtn{
display: none;
}
.editor-container-box{
margin: 0px;
}
}
4.实际效果如下:在线预览打印效果 建议用谷歌浏览器打开
总结
到此整个文档分页,自定义页眉页脚,文档打印的完整业务流程讲完了,希望看完让你也能直接写出腾讯文档一样的在线文档,需要源码在这里免费下载,如果您还有问题直接到GoFly全栈开发者社区找我讨论,作者也希望更多人分享出自己开发过程遇到难点项目,让更多人一起进步,助力新人成长。
更多推荐
所有评论(0)