vue3 微信小程序,uni-app 签字版插件sp-sign-board。上传图片 电脑版发表于:2025/2/10 16:57 插件地址:https://ext.dcloud.net.cn/plugin?id=14966 文档:https://sonvee.github.io/sv-app-docs/docs-github/src/plugins/sp-sign-board/sp-sign-board.html [TOC] ### 组件的引入 直接下载下来放到这个组件文件夹下就可以了  ### 基础使用 ``` <template> <view class="upLoadSigns-container"> <view class="sign-area"> <sp-sign-board ref="signBoardRef" sid="sign-board" :horizontal="false" bgColor="#ffffff" :mark-text="markText" :needBack="false" @reset="reset" @firstTouchStart="firstTouchStart" @confirm = "signConfirm" ></sp-sign-board> </view> </view> </template> <script setup lang="ts"> import { ref, reactive, onMounted } from 'vue' import { onLoad,onUnload } from '@dcloudio/uni-app' const markText = ref([] as any) const signBoardRef = ref() onLoad(() => { // 图片签字完成后执行的方法 uni.$on('getSignImg', (e) => { console.log("签字完成了",e) }) refreshMark() }) // 页面关闭的时候把监听的事件注销掉,不然下次在进入页面的时候会多次触发 onUnload(()=>{ console.log("页面关闭了....") uni.$off('getSignImg') }) function refreshMark() { const currentDate = new Date() const year = currentDate.getFullYear() const month = String(currentDate.getMonth() + 1).padStart(2, '0') const day = String(currentDate.getDate()).padStart(2, '0') const hours = String(currentDate.getHours()).padStart(2, '0') const minutes = String(currentDate.getMinutes()).padStart(2, '0') const seconds = String(currentDate.getSeconds()).padStart(2, '0') // markText.value = [`${year}-${month}-${day}`, `${hours}:${minutes}:${seconds}`] markText.value = ["tnblog"] } const signConfirm = ()=>{ console.log("点击了确认....") } function firstTouchStart() { // 在第一次开始触碰时,更新一下时间水印,防止滞留时间太长造成时间误差(非必要) refreshMark() // 手动调用组件内绘制水印方法重新绘制 signBoardRef.value.drawMark(markText.value) } function reset() { refreshMark() } </script> <style lang="scss"> /* 为页面根元素设置背景颜色 page { background-color: #f7f7f8; }*/ </style> <style lang="scss" scoped> .sign-area { width: 100%; height: calc(100vh - 44px); } </style> ``` ### 包含上传图片的 ``` <template> <view class="upLoadSigns-container"> <view class="sign-area"> <sp-sign-board ref="signBoardRef" sid="sign-board" :horizontal="false" bgColor="#ffffff" :mark-text="markText" :needBack="false" @reset="reset" @firstTouchStart="firstTouchStart" @confirm="signConfirm" ></sp-sign-board> </view> </view> </template> <script setup lang="ts"> import { ref, reactive, onMounted } from 'vue' import { onLoad, onUnload } from '@dcloudio/uni-app' import { ApiBaseUrl } from '@/common/sysconfig' import request from '@/common/Http' const markText = ref([] as any) const signBoardRef = ref() onLoad((_params) => { state.subTrainingId = _params?.subTrainingId // 图片签字完成后执行的方法 uni.$on('getSignImg', (e) => { // console.log('签字完成了', e) // e.base64可以拿到base64的格式。e.path可以拿到临时路径,这里使用临时路径直接上传图片更加方便一些 uploadFile(e.path) }) refreshMark() }) // 页面关闭的时候把监听的事件注销掉,不然下次在进入页面的时候会多次触发 onUnload(() => { uni.$off('getSignImg') }) const state = reactive({ subTrainingId:"", UpLoadData: {} as any }) const getToken = () => { let info = uni.getStorageSync('loginInfo') let token = info?.resData?.access_token return token } // 上传签字图片 const uploadFile = (_filePath:any)=>{ uni.uploadFile({ // 请求接口地址 url: ApiBaseUrl + '/oss/api/SmartFiles/UpLoadFormXXX', filePath: _filePath, name: 'file', header: { 'Authorization': `Bearer ${getToken()}`, }, formData: { 'bucketName': 'teacher-certification', 'filePath': 'teacherTraining', 'fileType': '1' }, success: (uploadFileRes:any) => { let result = JSON.parse(uploadFileRes.data) // 下面是图片上传成功之后配合其他接口的操作 // state.UpLoadData.Sign = result.data.id // submitUpload() } }); } const submitUpload = async () => { // 提交签字 state.UpLoadData.SubProgramId = state.subTrainingId const result: any = await request.post('/teacherCertifiApi/api/ProjectMembersResult/XXX', state.UpLoadData) } function refreshMark() { const currentDate = new Date() const year = currentDate.getFullYear() const month = String(currentDate.getMonth() + 1).padStart(2, '0') const day = String(currentDate.getDate()).padStart(2, '0') const hours = String(currentDate.getHours()).padStart(2, '0') const minutes = String(currentDate.getMinutes()).padStart(2, '0') const seconds = String(currentDate.getSeconds()).padStart(2, '0') // markText.value = [`${year}-${month}-${day}`, `${hours}:${minutes}:${seconds}`] markText.value = ['足下科教集团'] } const signConfirm = () => { console.log('点击了确认....') } function firstTouchStart() { // 在第一次开始触碰时,更新一下时间水印,防止滞留时间太长造成时间误差(非必要) refreshMark() // 手动调用组件内绘制水印方法重新绘制 signBoardRef.value.drawMark(markText.value) } function reset() { refreshMark() } </script> <style lang="scss"> /* 为页面根元素设置背景颜色 page { background-color: #f7f7f8; }*/ </style> <style lang="scss" scoped> .sign-area { width: 100%; height: calc(100vh - 44px); } </style> ``` 可以参考水厂项目小程序里边的维修签字确认 ### 和表单放到一起的布局(签字组件的按钮放出来的形式有点丑) 布局效果如下(其实按钮太多了看着有点丑): <img src="https://img.tnblog.net/arcimg/notebook/a759d7bac3fd435f8a2c11fb67f40d7f.png" style="width:399px;height:auto;"> 可以参考水厂项目小程序里边的开户申请 ### 和表单放到一起的布局(去掉签字组件的默认按钮,好看点) 布局效果如下(可以参考水厂项目小程序里边的物品领用): <img src="https://img.tnblog.net/arcimg/notebook/001d2e7013e2474b896aecf309d845ea.png" style="width:399px;height:auto;"> 这样重写编写按钮直接放到右边,然后确定签字的按钮就和那个业务的提交按钮放到一起 **把popupMode属性设置成true就不会显示出来默认的组件按钮了** ``` <sp-sign-board ref="signBoardRef" sid="sign-board" :horizontal="false" bgColor="#fff" :mark-text="markText" :needBack="false" :popupMode="true" @reset="reset" @firstTouchStart="firstTouchStart" @confirm="signConfirm" ></sp-sign-board> ``` 把needBack设置成false就不触发默认的回退 **重写的方法以及提交签字的方法:** 自己实现按钮去调用的方法 ``` const signBoardRef = ref() // 重写的方 signBoardRef.value.reset() // 提交签字的方法 signBoardRef.value.confirm() ``` **由于提交签字的方法中要自动去提交业务所以业务的一些参数验证需要加到图片提交之前更好** 不然会出现签字图片上传了,但是业务逻辑的验证没有通过就尴尬了 ``` // 提交签字 const toSubmitSign = ()=>{ let itemsUsage = getItemUsageData() console.log("看看物品使用情况",itemsUsage) if(!itemsUsage||itemsUsage.length===0){ uni.showToast({ title: '请领取物品!', icon: 'error' }); return } // 提交签字图片 signBoardRef.value.confirm() } ``` **图片上传成功的回调里边去触发以前的业务提交逻辑** ``` const signPhotoRef = ref() onLoad(() => { // 图片签字完成后执行的方法 uni.$on('getSignImg', (e) => { uploadImg(e.path, (result: any) => { let resultObj = JSON.parse(result); // 用于传递到后台存储 signPhotoRef.value = resultObj.data.fileNameAndFolderName // 签字完成了直接提交正常的业务逻辑就行 handleSubmit() }); }) refreshMark() }) onUnload(()=>{ // 页面关闭的时候把监听的事件注销掉,不然下次在进入页面的时候会多次触发 uni.$off('getSignImg') }) ``` **添加成功回退前也可以手动关闭一下事件监听,保险点,不然有多次触发图片上传成功的回调函数的风险** ``` const handleSubmit = async () => { let itemsUsage = getItemUsageData() const submitData = { ItemsUsage: getItemUsageData(), SignPhoto:signPhotoRef.value }; const result: any = await Http.post('/watertap/api/YY/XX', submitData); if (result.success) { uni.showToast({ title: '物品领用成功!', icon: 'success' }); // 添加成功回退前也可以手动关闭一下事件监听,保险点,不然有多次触发图片上传成功的回调函数的风险 uni.$off('getSignImg') uni.navigateBack(); } else { uni.showToast({ title: result.message || '提交失败', icon: 'none' }); } }; ```