目前有个uniapp封装app里展示合同的需求,数据都在页面拼接展示,合同是写到页面里的,数据是活的。
用到了html2canvas,renderjshtml2canvas-renderjs - DCloud 插件市场
后面可能还有一系列优化需求,持续更新
<template>
<view>
<image v-for="(item,index) in img":src="item" mode="widthFix" style="width: 100%;height: auto;"></image>
<view style="position: fixed;left: 750rpx;top: 0;">
<view class="page" id="contractimage1">第1页内容</view>
<view class="page" id="contractimage2">第2页内容</view>
<view class="page" id="contractimage3">第3页内容</view>
<view class="page" id="contractimage4">第4页内容</view>
<view class="page" id="contractimage5">第5页内容
<view class="signature" style="display: flex;align-items: center;">
<text>甲方:(盖章)</text>
<image :src="form.onePicture" style="width: 200rpx;height: 200rpx;" @load="html2canvas.load"></image>
</view>
</view>
</view>
</view>
</template>
<!-- 逻辑层 -->
<script>
import html2canvas from 'html2canvas';
export default {
data() {
return {
img:[],
pages: 5,
form:'',
days:''
}
},
//此处数据是上一页面传过来的-也可以自行在onload里请求获取
onLoad({info}) {
this.showLoading()
this.form=JSON.parse(decodeURIComponent(info))
},
methods: {
showLoading() {
uni.showLoading({
title: '加载中……',
mask: true
})
},
hideLoading() {
uni.hideLoading()
},
//接受视图层传的数据
renderFinish(opt) {
this.img.push(opt)
if(this.img.length==this.pages)this.hideLoading()
},
}
}
</script>
<!-- 动态生成 script -->
<!-- renderjs 视图(渲染层)层可以操作dom和window -->
<!-- 扩展:相当于使用 mixin 方式,可以直接访问逻辑层数据 -->
<!-- 扩展:f2、threejs等库都可以这么用 -->
<script module="html2canvas" lang="renderjs">
import html2canvas from 'html2canvas'
export default {
//create()html完成前就开始了
//mounted()html渲染完成 执行
mounted() {
//因为页面中有网络图片,加载时间和html渲染时间不能同步完成,直接截图会导致图片空白,故使用load去监听图片加载完成,完成后再截图
//this.create()
},
//生命周期不支持 beforeDestroy、destroyed、beforeUnmount、unmounted),不可以使用 App、Page 的生命周期
methods: {
//@load="html2canvas.load"
//逻辑层 绑定了事件 到渲染层
load(e){//图片加载完成
console.log('load2',e)
//图片加载完-再执行html2canvas
this.create();
},
async create() {
for(let i=0;i<5;i++){
html2canvas(document.querySelector('#contractimage'+Number(i+1)), {
scale:3,
dpi:window.devicePixelRatio * 1,
useCORS:true,
}).then(canvas => {
//向逻辑层的renderFinish传值
this.$ownerInstance.callMethod('renderFinish',canvas.toDataURL('image/png'))
});
}
}
}
}
</script>
<style lang="scss" scoped>
.page {
margin: 0 auto;
width: 592.28pt;//592.28
height: 840pt;// 841.89
padding: 70pt 80pt;
box-sizing: border-box;
position: relative;
color: #000000;
}
.signature {
width: 50%;
margin-top: 50px;
flex: 1;
font-size: 12pt;
&:last-child {
padding-bottom: 30px;
}
}
</style>
PS(懒得再开文章)
附上另一个展示合同需求:
1.合同内容是docx形式展示,2.前端实现签名操作,3.后台合成签名图片和docx后在小程序展示。
小程序签名,DCloud 插件市场有现成的插件canves 手写 签名 画板(开箱即用) - DCloud 插件市场
因为后台使用的插件不支持https,但小程序必须https,故后台设计了多步文件转换(这里前端先不考虑)
方法思路如下:(有点绕)
第一步,在小程序里展示docx
(找了好多方法都直接展示不出来签名按钮和docx,或者只能直接打开文件)
如直接打开满足需求,强烈建议优先使用!
uni.downloadFile({
url: 'https://example.com/somefile.pdf',
success: function (res) {
var filePath = res.tempFilePath;
uni.openDocument({
filePath: filePath,
showMenu: true,
success: function (res) {
console.log('打开文档成功');
}
});
}
});
另辟蹊径:用webview嵌套web页面,在web页面中显示docx和签名按钮
- 小程序新建页面webview.vue
<template>
<view>
<web-view :src="url"></web-view>
<!-- <cover-view class="public-btn" @click="navback">业主签字</cover-view> -->
</view>
</template>
<script>
export default {
data() {
return {
url:'',//这个页面是自己写的
}
},
onLoad() {
if(uni.getStorageSync('token')){
this.url='https://***/h5/#/pages/contract?token='+uni.getStorageSync('token')
}
}
}
</script>
有钱嫌麻烦的客户,可以直接找三方链接
https://view.xdocin.com/xdoc?img=true&_xdoc=前10天还是15天免费,后面就会停服收费,但是也要嵌套在web里,(设置成img类型可以不用在小程序后台填xdocxin的webview域名,但是长期服务收三方费)
免费的 Microsoft 365 Online | Word、Excel、PowerPoint
微软的虽然免费,但是webview域名没法填,样式也有限,web会自动重定向到微软里,还没有办法解决。但是非小程序的可以使用
<iframe v-if="filesrc" ref="iframe" id="iframe" width="100%" height="100%" frameborder="0" :src="filesrc" scrolling="no" security="restricted" sandbox=""></iframe>
2.web页面
用到了mammoth,但是图片无法正常显示,果断换docx-preview,如下:
<template>
<view>
<view :style="'width: 100%;height:'+height+'px;overflow:hidden;'">
<view id="file"></view>
</view>
<!-- 判断签过名不显示按钮 -->
<view v-if="!sign&&status==4" class="public-btn" @click="navback">业主签字</view>
</view>
</template>
用户端 访问的合同H5
<script>
import wx from 'weixin-js-sdk';
import { renderAsync } from 'docx-preview'
// const docx2html=require("docx2html")
// import mammoth from "mammoth";//--m
export default {
data() {
return {
sign:false,//签字板
file:'',
img:'',
filesrc:'',
status:'',
wordText:'',
docxOptions:{
className: "filestyle", // string:默认和文档样式类的类名/前缀
inWrapper: true, // boolean:启用围绕文档内容的包装器渲染
ignoreWidth: false, // boolean:禁用页面的渲染宽度
ignoreHeight: false, // boolean:禁止渲染页面高度
ignoreFonts: false, // boolean:禁用字体渲染
breakPages: true, // boolean:在分页符上启用分页
ignoreLastRenderedPageBreak: true, // boolean:在 lastRenderedPageBreak 元素上禁用分页
experimental: false, // boolean:启用实验功能(制表符停止计算)
trimXmlDeclaration: true, // boolean:如果为true,解析前会从 xml 文档中移除 xml 声明
useBase64URL: false, // boolean:如果为true,图片、字体等会转为base 64 URL,否则使用URL.createObjectURL
useMathMLPolyfill: true, // boolean:包括用于 chrome、edge 等的 MathML polyfill。
showChanges: false, // boolean:启用文档更改的实验性渲染(插入/删除)
debug: false, // boolean:启用额外的日志记录
},
height:700
};
},
onLoad({token}) {
if(token)uni.setStorageSync('token',token)
},
onShow() {
this.getinfo()
},
methods:{
//回到小程序指定页面
navback(){
wx.miniProgram.navigateTo({
url: '/pages/order/sign' //小程序必须有该目录--此处是签名页
})
},
//合同模板
getinfo(){
uni.showLoading({
mask:true
})
setTimeout(()=>{
uni.hideLoading()
},5000)
this.$Fly.post('api/order/kanChaList').then(res=>{
if(res.code==1){
this.status=res.data.status
}
})
this.$Fly.post('api/Order/contract').then(res=>{
if(res.code==1){
this.file=res.data.file
uni.request({
url:this.file,
responseType:'arraybuffer',
header:{
Authorization:"Bearer e2589fc8-8748-481b-8ec4-c63df33e4371",
token:uni.getStorageSync('token')
},
method:'GET',
success: (rres) => {
renderAsync(rres.data,document.getElementById('file'), null,this.docxOptions).then(rr=>{
uni.hideLoading()
this.height=document.getElementById('file').clientHeight*55/100+200
})
}
})
}
})
},
}
}
</script>
文档持续更新。。。