You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sign.vue 16 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
2 years ago
2 years ago
2 years ago
2 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. <template>
  2. <view>
  3. <view>
  4. <view class="cu-bar bg-white cu-title solid-bottom">
  5. <view class="action" style="vertical-align: middle;">
  6. <text class="margin-right-xs text-blue cuIcon-title">
  7. <span></span>
  8. </text>
  9. <text class="text-xl">
  10. <span>经费开支申报单</span>
  11. </text>
  12. </view>
  13. </view>
  14. </view>
  15. <l-label title="流程名称:">{{ taskParam.taskName }}</l-label>
  16. <l-label title="当前操作:">{{ typeText }}{{ type === 'sign' ? '' : ` [${taskParam.name}]` }}</l-label>
  17. <l-organize-picker
  18. v-if="type === 'sign'"
  19. v-model="staff"
  20. title="加签人员"
  21. placeholder="请选择加签人员"
  22. type="user"
  23. required
  24. arrow
  25. />
  26. <!-- 手写签名 canvas 区 -->
  27. <template v-if="Number(taskParam.isSign) === 1">
  28. <view class="cu-form-group"><view class="title">手写签名:</view></view>
  29. <view class="sign-area bg-white">
  30. <canvas
  31. v-if="canvas"
  32. @touchmove="signMove"
  33. @touchstart="signStart($event)"
  34. @touchend="signEnd"
  35. @touchcancel="signEnd"
  36. disable-scroll="true"
  37. canvas-id="sign-canvas"
  38. id="sign-canvas"
  39. class="sign-canvas"
  40. ></canvas>
  41. </view>
  42. <view class="sign-action padding-bottom text-right">
  43. <l-button @click="clearSign" color="red" style="margin-right: 15px;">清空签名板</l-button>
  44. </view>
  45. <view class="cu-form-group"><view class="title">签章:</view></view>
  46. <view v-if="" class="Signwrap">
  47. <image style="width: 50px;height: 50px;margin: 15px;" :src="Signimg"></image>
  48. </view>
  49. <view class="sign-action padding-bottom text-right">
  50. <!-- <l-button @click="Signature" color="" style="color: #FFFFFF; background: #0081ff; margin-right: 15px;">签章</l-button> -->
  51. <picker class="cu-btn df" style="background-color: #0081ff;color: #ffffff;margin-right: 15px;border-radius: 5px;" @change="Signature" :range="Signaitem" range-key="F_StampName">个人签章</picker>
  52. <picker v-if="ComStatus" class="cu-btn df" style="background-color: #fe955c;color: #ffffff;margin-right: 15px;border-radius: 5px;" @change="Commonture" :range="Commonseal" range-key="F_StampName">学校签章</picker>
  53. <l-button @click="clearcom" color="red" style="margin-right: 15px;">清空签章</l-button>
  54. </view>
  55. </template>
  56. <l-textarea v-model="remark" :placeholder="`输入${typeText}意见`" title="审核意见:" required />
  57. <!-- 弹层 -->
  58. <l-modal v-model="modal" @close="reviewer = ''" title="指派审核人">
  59. <l-checkbox-picker @input="(e)=>{reviewer = e}" :value="reviewer" :readonly="false" :range="reviewerList"
  60. required title="审核人" />
  61. <l-button @click="userAssign" :disabled="userAssignDisable" color="blue" class="block" block>确定指派</l-button>
  62. <l-button @click="()=>{modal = false;reviewer = ''}" line="blue" class="block margin-top-sm" block>取消</l-button>
  63. </l-modal>
  64. <view class="padding margin-top bg-white">
  65. <l-button @click="submit" :disabled="submitDisable" class="block" size="lg" color="green" block>提交流程{{ typeText }}</l-button>
  66. </view>
  67. <view class="pswd-uni-mask" v-if="passshow">
  68. <view class="pswd-uni-modal">
  69. <view class="pswd-uni-modal__hd">
  70. <view class="pswd-strong">输入密码</view>
  71. </view>
  72. <view class="pswd-uni-modal__hd" style="margin-bottom: 1em;">
  73. <input class="pswdinp" type="password" v-model="password" placeholder="密码">
  74. </view>
  75. <view class="pswd-uni-modal__ft">
  76. <view class="pswd-uni-modal__btn" style="color:rgb(57, 181, 74)" @click="cancelpswd">
  77. 取消
  78. </view>
  79. <view class="pswd-uni-modal__btn pswd__btn_primary" style="color: rgb(245, 88, 80);" @click="confirmpswd">
  80. 确认
  81. </view>
  82. </view>
  83. </view>
  84. </view>
  85. </view>
  86. </template>
  87. <script>
  88. import get from 'lodash/get'
  89. let context = null
  90. let touchs = []
  91. export default {
  92. data() {
  93. return {
  94. type: 'sign',
  95. typeText: '',
  96. staff: '',
  97. remark: '',
  98. taskParam: {},
  99. canvas: true,
  100. modal: false,
  101. reviewer: [],
  102. reviewerList: [],
  103. reviewerListId:'',
  104. submitPostData:null,
  105. // 列表与分页信息
  106. page: 1,
  107. total: 2,
  108. // 选择个人签章
  109. Signaitem:[],
  110. // 公章
  111. Commonseal:[],
  112. // 是否显示公章
  113. ComStatus:false,
  114. pitchon:'',
  115. Signimg: '',
  116. // 填写密码
  117. passshow:false,
  118. password:'',
  119. PitchonSign:'',
  120. canvasW:'',
  121. canvasH:'',
  122. signshow:false,
  123. ConfirmSign:'',
  124. submitDisable:false,
  125. userAssignDisable:false,
  126. }
  127. },
  128. async onLoad() {
  129. await this.init()
  130. // this.Signimg = this.API + `/learun/adms/user/img?data=${'System'}`
  131. // this.Signimg = this.API + `/learun/adms/StampPersonal/img?data=${'47201f98-7611-4052-b271-bb99bd7b1d3c'}`
  132. },
  133. methods: {
  134. // 页面初始化
  135. async init() {
  136. this.taskParam = this.GET_PARAM()
  137. this.type = this.taskParam.type
  138. this.typeText = this.taskParam.type === 'sign' ? '加签' : '审核'
  139. await this.getComstatus()
  140. await this.getSigna()
  141. await this.getCommonseal()
  142. if (Number(this.taskParam.isSign) === 1) {
  143. this.canvasInit()
  144. }
  145. },
  146. // 获取个人签章
  147. getSigna(){
  148. return this.HTTP_GET("learun/adms/StampPersonal/list",{},"加载数据时出错").then(async res=>{
  149. if(res){
  150. this.Signaitem = res
  151. }
  152. })
  153. },
  154. // 获取是否展示公章
  155. getComstatus(){
  156. return this.HTTP_GET("learun/adms/StampPersonal/ishaveright",{},"加载数据时出错").then(async res=>{
  157. if(res){
  158. this.ComStatus = res.result
  159. }
  160. })
  161. },
  162. // 获取学校签章
  163. getCommonseal(){
  164. return this.HTTP_GET("learun/adms/StampPersonal/schoollist",{},"加载数据时出错").then(async res=>{
  165. if(res){
  166. this.Commonseal = res
  167. }
  168. })
  169. },
  170. // 初始化签名区 canvas
  171. canvasInit() {
  172. this.canvas = true
  173. context = uni.createCanvasContext('sign-canvas')
  174. context.setStrokeStyle('#000')
  175. context.setLineWidth(5)
  176. context.setLineCap('round')
  177. context.setLineJoin('round')
  178. touchs = []
  179. },
  180. // 点击个人签章
  181. Signature(e){
  182. let signindex = e.detail.value;
  183. this.pitchon = this.Signaitem[signindex]
  184. this.PitchonSign = this.Signaitem[signindex].F_StampId
  185. this.passshow = true
  186. },
  187. // 选择学校签章
  188. Commonture(e){
  189. let signindex = e.detail.value;
  190. this.pitchon = this.Commonseal[signindex]
  191. this.PitchonSign = this.Commonseal[signindex].F_StampId
  192. this.passshow = true
  193. },
  194. // 取消
  195. cancelpswd(){
  196. this.passshow = false
  197. this.password = ''
  198. },
  199. // 确认
  200. confirmpswd(){
  201. let _this = this
  202. this.HTTP_POST('learun/adms/StampPersonal/equalPassword', {
  203. F_StampId:this.pitchon.F_StampId,
  204. F_Password: this.MD5(this.password)
  205. }).then(async res=>{
  206. if(res.result){
  207. _this.ConfirmSign = _this.PitchonSign
  208. let imgurl = _this.API + `/learun/adms/StampPersonal/img?data=${_this.pitchon.F_StampId}`
  209. _this.Signimg = imgurl
  210. _this.signshow = true
  211. _this.passshow = false
  212. _this.password = ''
  213. _this.pitchon = ''
  214. }else{
  215. this.TOAST(`${res.msg}`, 'error')
  216. }
  217. })
  218. },
  219. // 绘制签章图片到canvas
  220. SignaApply(goodsImg){
  221. let _this = this
  222. uni.getImageInfo({
  223. src:goodsImg,
  224. success:function(image){
  225. _this.canvasW = image.width / 5;//因为需要所以缩放了8倍
  226. _this.canvasH = image.height / 5;
  227. }
  228. })
  229. setTimeout(()=>{
  230. context.drawImage(goodsImg,250,150,100,100)
  231. context.draw(true,(res)=>{
  232. })
  233. })
  234. },
  235. // 指派审核人
  236. async userAssign() {
  237. if(!this.reviewer.length){
  238. this.modal = false
  239. this.TOAST('请选择审核人')
  240. return
  241. }
  242. // let formdata = new FormData()
  243. // formdata.append("data",this.taskParam.formreq)
  244. this.userAssignDisable = true
  245. uni.showLoading({
  246. title: '提交中...'
  247. });
  248. const res = await this.HTTP_POST('learun/adms/newwf/instance', this.taskParam.formreq, `指派审核人时发生错误`)
  249. if(!res){
  250. this.userAssignDisable = false
  251. uni.hideLoading();
  252. return
  253. }
  254. this.submitPostData.auditors = JSON.stringify({[this.reviewerListId]:this.reviewer.toString()})
  255. delete this.submitPostData.formreq
  256. // this.submitPostData.auditors[this.reviewerListId] = this.reviewer
  257. const success = await this.HTTP_POST(
  258. this.type === 'sign' ? 'learun/adms/newwf/sign' : 'learun/adms/newwf/auditors',
  259. this.submitPostData,
  260. `提交[${this.typeText}]时发生错误`
  261. )
  262. if (!success) {
  263. this.userAssignDisable = false
  264. uni.hideLoading();
  265. return
  266. }
  267. uni.hideLoading();
  268. this.EMIT('task-list-change')
  269. this.NAV_BACK(2)
  270. this.TOAST(`已成功提交${this.typeText}`, 'success')
  271. },
  272. newguid() {
  273. return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  274. var r = Math.random() * 16 | 0,
  275. v = c == 'x' ? r : (r & 0x3 | 0x8);
  276. return v.toString(16);
  277. });
  278. },
  279. // 点击「提交」按钮
  280. async submit() {
  281. if (this.type === 'sign' && !this.staff) {
  282. this.CONFIRM('请补全必填项', '必须指定一个加签用户')
  283. return
  284. }
  285. if(!this.remark){
  286. this.TOAST(`请签署审批意见`)
  287. return false;
  288. }
  289. this.submitDisable = true
  290. uni.showLoading({
  291. title: '提交中...'
  292. });
  293. const postData = {
  294. operationCode: this.taskParam.code,
  295. operationName: this.taskParam.name,
  296. processId: this.taskParam.processId,
  297. taskId: this.taskParam.taskId,
  298. des: this.remark,
  299. formreq: this.taskParam.formreq,
  300. stamp: this.ConfirmSign
  301. }
  302. if (this.type === 'sign') {
  303. postData.userId = this.staff
  304. } else {
  305. postData.auditors = this.taskParam.auditors
  306. }
  307. // 需要手写签名时,将 canvas 导出为 base64 格式
  308. // 各个平台写法均不相同,需要注意
  309. if (Number(this.taskParam.isSign) === 1) {
  310. // H5 平台,canvasToTempFilePath 的结果直接为画布的 base64
  311. // #ifdef H5
  312. const [err, { tempFilePath }] = await uni.canvasToTempFilePath({ canvasId: 'sign-canvas' })
  313. postData.signUrl = tempFilePath
  314. // #endif
  315. // App 平台,canvasToTempFilePath 输出文件,上传后台转为 base64 格式
  316. // #ifdef APP-VUE
  317. const [err, { tempFilePath }] = await uni.canvasToTempFilePath({ canvasId: 'sign-canvas' })
  318. const signBase64 = await this.HTTP_UPLOAD2('/learun/adms/annexes/wxbase64',tempFilePath,{folderId:this.newguid()})
  319. postData.signUrl = 'data:image/png;base64,' + signBase64
  320. // #endif
  321. // 微信小程序,canvasToTempFilePath 输出文件,使用文件管理器以 base64 格式读取文件即可
  322. // #ifdef MP-WEIXIN
  323. const [err, { tempFilePath }] = await uni.canvasToTempFilePath({ canvasId: 'sign-canvas' })
  324. postData.signUrl = 'data:image/png;base64,' + uni.getFileSystemManager().readFileSync(tempFilePath, 'base64')
  325. // #endif
  326. // #ifdef MP-ALIPAY
  327. // 钉钉小程序,context.toTempFilePath 输出文件,上传后台转为 base64 格式
  328. // #ifdef MP-DINGTALK
  329. const filePath = await new Promise((res, rej) => {
  330. context.toTempFilePath({
  331. success: ({ filePath }) => {
  332. res(filePath)
  333. },
  334. fail: () => {
  335. rej()
  336. }
  337. })
  338. })
  339. const signBase64 = await this.HTTP_UPLOAD('/annexes/wxtobase64', filePath)
  340. postData.signUrl = 'data:image/png;base64,' + signBase64
  341. // #endif
  342. // 支付宝小程序,context.toDataURL 直接输出 base64 字符串
  343. // #ifndef MP-DINGTALK
  344. postData.signUrl = await context.toDataURL('image/png', 1)
  345. // #endif
  346. // #endif
  347. }
  348. // 是否需要指派审核人
  349. let isNext = this.taskParam.currentNode.isNext
  350. if (this.taskParam.next == '2') {
  351. isNext = '1';
  352. }
  353. if (isNext == '1') {
  354. let params = {
  355. code: this.taskParam.schemeCode,
  356. processId: this.taskParam.processId,
  357. taskId: this.taskParam.taskId,
  358. nodeId: this.taskParam.currentNode.id,
  359. operationCode: this.taskParam.code,
  360. }
  361. const userList = await this.HTTP_GET('learun/adms/newwf/auditer', params, `获取审核人时发生错误`)
  362. // if (!userList) {
  363. // return
  364. // }
  365. if(Object.keys(userList).length==0){
  366. const success = await this.HTTP_POST(
  367. this.type === 'sign' ? 'learun/adms/newwf/sign' : 'learun/adms/newwf/audit',
  368. postData,
  369. `提交[${this.typeText}]时发生错误`
  370. )
  371. if (!success) {
  372. this.submitDisable = false
  373. uni.hideLoading();
  374. return
  375. }
  376. this.EMIT('task-list-change')
  377. this.NAV_BACK(2)
  378. this.TOAST(`已成功提交${this.typeText}`, 'success')
  379. }
  380. let arr = Object.entries(userList)
  381. this.reviewerListId = arr[0][0]
  382. this.reviewerList = arr[0][1].map(item => {
  383. return {
  384. text: item.Name,
  385. value: item.Id
  386. }
  387. })
  388. this.submitPostData = postData
  389. if(this.reviewerList.length>1){
  390. this.submitDisable = false
  391. uni.hideLoading();
  392. this.modal = true
  393. return
  394. }
  395. }
  396. const success = await this.HTTP_POST(
  397. this.type === 'sign' ? 'learun/adms/newwf/sign' : 'learun/adms/newwf/audit',
  398. postData,
  399. `提交[${this.typeText}]时发生错误`
  400. )
  401. if (!success) {
  402. this.submitDisable = false
  403. uni.hideLoading();
  404. return
  405. }
  406. this.EMIT('task-list-change')
  407. this.NAV_BACK(2)
  408. uni.hideLoading();
  409. this.TOAST(`已成功提交${this.typeText}`, 'success')
  410. },
  411. // 手写板事件(开始拖动)
  412. signStart(e) {
  413. touchs.push({
  414. x: e.changedTouches[0].x,
  415. y: e.changedTouches[0].y
  416. })
  417. },
  418. // 手写板事件(拖动签名)
  419. signMove(e) {
  420. touchs.push({
  421. x: e.touches[0].x,
  422. y: e.touches[0].y
  423. })
  424. this.drawLine()
  425. },
  426. // 手写板事件(签名结束)
  427. signEnd(e) {
  428. touchs = []
  429. },
  430. // 手写板事件(绘出线型)
  431. drawLine() {
  432. if (touchs.length < 2) {
  433. return
  434. }
  435. const [p1, p2] = touchs
  436. touchs.shift()
  437. context.moveTo(p1.x, p1.y)
  438. context.lineTo(p2.x, p2.y)
  439. context.stroke()
  440. context.draw(true)
  441. },
  442. // 清除手写板
  443. // 阿里小程序无法使用 clearRect 来清空,因此直接重新渲染 canvas
  444. clearSign() {
  445. // #ifndef MP-ALIPAY
  446. context.clearRect(0, 0, 9999, 9999)
  447. context.draw()
  448. context.setStrokeStyle('#000')
  449. context.setLineWidth(5)
  450. context.setLineCap('round')
  451. context.setLineJoin('round')
  452. // #endif
  453. // #ifdef MP-ALIPAY
  454. // 阿里系小程序无法 clearRect 清空画布,必须重新渲染 canvas
  455. this.canvas = false
  456. this.$nextTick(() => {
  457. this.canvasInit()
  458. })
  459. // #endif
  460. },
  461. clearcom(){
  462. this.Signimg = ''
  463. this.ConfirmSign = ''
  464. }
  465. }
  466. }
  467. </script>
  468. <style lang="less" scoped>
  469. .sign-area {
  470. min-height: 500rpx;
  471. margin: 23rpx;
  472. border: 2rpx dashed #444444;
  473. .sign-canvas {
  474. width: 700rpx;
  475. height: 500rpx;
  476. }
  477. .sign-action {
  478. text-align: right;
  479. }
  480. }
  481. .pswd-uni-mask{
  482. position: fixed;
  483. z-index: 999;
  484. top: 0;
  485. right: 0;
  486. left: 0;
  487. bottom: 0;
  488. background: rgba(0,0,0,.5);
  489. }
  490. .pswd-uni-modal{
  491. position: fixed;
  492. z-index: 999;
  493. width: 80%;
  494. max-width: 300px;
  495. top: 50%;
  496. left: 50%;
  497. -webkit-transform: translate(-50%,-50%);
  498. transform: translate(-50%,-50%);
  499. background-color: #fff;
  500. text-align: center;
  501. border-radius: 3px;
  502. overflow: hidden;
  503. }
  504. .pswd-uni-modal__hd{
  505. padding: 1em 1.6em 0.3em;
  506. }
  507. .pswd-strong{
  508. font-weight: 400;
  509. font-size: 18px;
  510. }
  511. .pswd-uni-modal__ft{
  512. position: relative;
  513. line-height: 48px;
  514. font-size: 18px;
  515. display: -webkit-box;
  516. display: -webkit-flex;
  517. display: flex;
  518. }
  519. .pswd-uni-modal__ft::after {
  520. content: " ";
  521. position: absolute;
  522. left: 0;
  523. top: 0;
  524. right: 0;
  525. height: 1px;
  526. border-top: 1px solid #d5d5d6;
  527. color: #d5d5d6;
  528. -webkit-transform-origin: 0 0;
  529. transform-origin: 0 0;
  530. -webkit-transform: scaleY(.5);
  531. transform: scaleY(.5);
  532. }
  533. .pswd-uni-modal__btn{
  534. display: block;
  535. -webkit-box-flex: 1;
  536. /* -webkit-flex: 1; */
  537. flex: 1;
  538. color: #3cc51f;
  539. text-decoration: none;
  540. -webkit-tap-highlight-color: rgba(0,0,0,0);
  541. position: relative;
  542. cursor: pointer;
  543. }
  544. .pswd__btn_primary::after {
  545. content: " ";
  546. position: absolute;
  547. left: 0;
  548. top: 0;
  549. width: 1px;
  550. bottom: 0;
  551. border-left: 1px solid #d5d5d6;
  552. color: #d5d5d6;
  553. -webkit-transform-origin: 0 0;
  554. transform-origin: 0 0;
  555. -webkit-transform: scaleX(.5);
  556. transform: scaleX(.5);
  557. }
  558. .pswdinp{
  559. background: #f5f5f5;
  560. }
  561. .Signwrap{
  562. margin: 11px;
  563. border: 1px dashed #444444;
  564. background: #ffffff;
  565. }
  566. </style>