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.
 
 
 
 
 
 

614 lines
16 KiB

  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" 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" 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. }
  125. },
  126. async onLoad() {
  127. await this.init()
  128. // this.Signimg = this.API + `/learun/adms/user/img?data=${'System'}`
  129. // this.Signimg = this.API + `/learun/adms/StampPersonal/img?data=${'47201f98-7611-4052-b271-bb99bd7b1d3c'}`
  130. },
  131. methods: {
  132. // 页面初始化
  133. async init() {
  134. this.taskParam = this.GET_PARAM()
  135. this.type = this.taskParam.type
  136. this.typeText = this.taskParam.type === 'sign' ? '加签' : '审核'
  137. await this.getComstatus()
  138. await this.getSigna()
  139. await this.getCommonseal()
  140. if (Number(this.taskParam.isSign) === 1) {
  141. this.canvasInit()
  142. }
  143. },
  144. // 获取个人签章
  145. getSigna(){
  146. return this.HTTP_GET("learun/adms/StampPersonal/list",{},"加载数据时出错").then(async res=>{
  147. if(res){
  148. this.Signaitem = res
  149. }
  150. })
  151. },
  152. // 获取是否展示公章
  153. getComstatus(){
  154. return this.HTTP_GET("learun/adms/StampPersonal/ishaveright",{},"加载数据时出错").then(async res=>{
  155. if(res){
  156. this.ComStatus = res.result
  157. }
  158. })
  159. },
  160. // 获取学校签章
  161. getCommonseal(){
  162. return this.HTTP_GET("learun/adms/StampPersonal/schoollist",{},"加载数据时出错").then(async res=>{
  163. if(res){
  164. this.Commonseal = res
  165. }
  166. })
  167. },
  168. // 初始化签名区 canvas
  169. canvasInit() {
  170. this.canvas = true
  171. context = uni.createCanvasContext('sign-canvas')
  172. context.setStrokeStyle('#000')
  173. context.setLineWidth(5)
  174. context.setLineCap('round')
  175. context.setLineJoin('round')
  176. touchs = []
  177. },
  178. // 点击个人签章
  179. Signature(e){
  180. let signindex = e.detail.value;
  181. this.pitchon = this.Signaitem[signindex]
  182. this.PitchonSign = this.Signaitem[signindex].F_StampId
  183. this.passshow = true
  184. },
  185. // 选择学校签章
  186. Commonture(e){
  187. let signindex = e.detail.value;
  188. this.pitchon = this.Commonseal[signindex]
  189. this.PitchonSign = this.Commonseal[signindex].F_StampId
  190. this.passshow = true
  191. },
  192. // 取消
  193. cancelpswd(){
  194. this.passshow = false
  195. this.password = ''
  196. },
  197. // 确认
  198. confirmpswd(){
  199. let _this = this
  200. this.HTTP_POST('learun/adms/StampPersonal/equalPassword', {
  201. F_StampId:this.pitchon.F_StampId,
  202. F_Password: this.MD5(this.password)
  203. }).then(async res=>{
  204. if(res.result){
  205. _this.ConfirmSign = _this.PitchonSign
  206. let imgurl = _this.API + `/learun/adms/StampPersonal/img?data=${_this.pitchon.F_StampId}`
  207. _this.Signimg = imgurl
  208. _this.signshow = true
  209. _this.passshow = false
  210. _this.password = ''
  211. _this.pitchon = ''
  212. }else{
  213. this.TOAST(`${res.msg}`, 'error')
  214. }
  215. })
  216. },
  217. // 绘制签章图片到canvas
  218. SignaApply(goodsImg){
  219. let _this = this
  220. uni.getImageInfo({
  221. src:goodsImg,
  222. success:function(image){
  223. _this.canvasW = image.width / 5;//因为需要所以缩放了8倍
  224. _this.canvasH = image.height / 5;
  225. }
  226. })
  227. setTimeout(()=>{
  228. context.drawImage(goodsImg,250,150,100,100)
  229. context.draw(true,(res)=>{
  230. })
  231. })
  232. },
  233. // 指派审核人
  234. async userAssign() {
  235. if(!this.reviewer.length){
  236. this.modal = false
  237. this.TOAST('请选择审核人')
  238. return
  239. }
  240. // let formdata = new FormData()
  241. // formdata.append("data",this.taskParam.formreq)
  242. const res = await this.HTTP_POST('learun/adms/newwf/instance', this.taskParam.formreq, `指派审核人时发生错误`)
  243. if(!res){
  244. return
  245. }
  246. this.submitPostData.auditors = JSON.stringify({[this.reviewerListId]:this.reviewer.toString()})
  247. delete this.submitPostData.formreq
  248. // this.submitPostData.auditors[this.reviewerListId] = this.reviewer
  249. const success = await this.HTTP_POST(
  250. this.type === 'sign' ? 'learun/adms/newwf/sign' : 'learun/adms/newwf/auditors',
  251. this.submitPostData,
  252. `提交[${this.typeText}]时发生错误`
  253. )
  254. if (!success) {
  255. return
  256. }
  257. this.EMIT('task-list-change')
  258. this.NAV_BACK(2)
  259. this.TOAST(`已成功提交${this.typeText}`, 'success')
  260. },
  261. // 点击「提交」按钮
  262. async submit() {
  263. if (this.type === 'sign' && !this.staff) {
  264. this.CONFIRM('请补全必填项', '必须指定一个加签用户')
  265. return
  266. }
  267. if(!this.remark){
  268. this.TOAST(`请签署审批意见`)
  269. return false;
  270. }
  271. const postData = {
  272. operationCode: this.taskParam.code,
  273. operationName: this.taskParam.name,
  274. processId: this.taskParam.processId,
  275. taskId: this.taskParam.taskId,
  276. des: this.remark,
  277. formreq: this.taskParam.formreq,
  278. stamp: this.ConfirmSign
  279. }
  280. if (this.type === 'sign') {
  281. postData.userId = this.staff
  282. } else {
  283. postData.auditors = this.taskParam.auditors
  284. }
  285. // 需要手写签名时,将 canvas 导出为 base64 格式
  286. // 各个平台写法均不相同,需要注意
  287. if (Number(this.taskParam.isSign) === 1) {
  288. // H5 平台,canvasToTempFilePath 的结果直接为画布的 base64
  289. // #ifdef H5
  290. const [err, { tempFilePath }] = await uni.canvasToTempFilePath({ canvasId: 'sign-canvas' })
  291. postData.signUrl = tempFilePath
  292. // #endif
  293. // App 平台,canvasToTempFilePath 输出文件,上传后台转为 base64 格式
  294. // #ifdef APP-VUE
  295. const [err, { tempFilePath }] = await uni.canvasToTempFilePath({ canvasId: 'sign-canvas' })
  296. const signBase64 = await this.HTTP_UPLOAD('/annexes/wxtobase64', tempFilePath)
  297. postData.signUrl = 'data:image/png;base64,' + signBase64
  298. // #endif
  299. // 微信小程序,canvasToTempFilePath 输出文件,使用文件管理器以 base64 格式读取文件即可
  300. // #ifdef MP-WEIXIN
  301. const [err, { tempFilePath }] = await uni.canvasToTempFilePath({ canvasId: 'sign-canvas' })
  302. postData.signUrl = 'data:image/png;base64,' + uni.getFileSystemManager().readFileSync(tempFilePath, 'base64')
  303. // #endif
  304. // #ifdef MP-ALIPAY
  305. // 钉钉小程序,context.toTempFilePath 输出文件,上传后台转为 base64 格式
  306. // #ifdef MP-DINGTALK
  307. const filePath = await new Promise((res, rej) => {
  308. context.toTempFilePath({
  309. success: ({ filePath }) => {
  310. res(filePath)
  311. },
  312. fail: () => {
  313. rej()
  314. }
  315. })
  316. })
  317. const signBase64 = await this.HTTP_UPLOAD('/annexes/wxtobase64', filePath)
  318. postData.signUrl = 'data:image/png;base64,' + signBase64
  319. // #endif
  320. // 支付宝小程序,context.toDataURL 直接输出 base64 字符串
  321. // #ifndef MP-DINGTALK
  322. postData.signUrl = await context.toDataURL('image/png', 1)
  323. // #endif
  324. // #endif
  325. }
  326. // 是否需要指派审核人
  327. let isNext = this.taskParam.currentNode.isNext
  328. if (this.taskParam.next == '2') {
  329. isNext = '1';
  330. }
  331. if (isNext == '1') {
  332. let params = {
  333. code: this.taskParam.schemeCode,
  334. processId: this.taskParam.processId,
  335. taskId: this.taskParam.taskId,
  336. nodeId: this.taskParam.currentNode.id,
  337. operationCode: this.taskParam.code,
  338. }
  339. const userList = await this.HTTP_GET('learun/adms/newwf/auditer', params, `获取审核人时发生错误`)
  340. // if (!userList) {
  341. // return
  342. // }
  343. if(Object.keys(userList).length==0){
  344. const success = await this.HTTP_POST(
  345. this.type === 'sign' ? 'learun/adms/newwf/sign' : 'learun/adms/newwf/audit',
  346. postData,
  347. `提交[${this.typeText}]时发生错误`
  348. )
  349. if (!success) {
  350. return
  351. }
  352. this.EMIT('task-list-change')
  353. this.NAV_BACK(2)
  354. this.TOAST(`已成功提交${this.typeText}`, 'success')
  355. }
  356. let arr = Object.entries(userList)
  357. this.reviewerListId = arr[0][0]
  358. this.reviewerList = arr[0][1].map(item => {
  359. return {
  360. text: item.Name,
  361. value: item.Id
  362. }
  363. })
  364. this.submitPostData = postData
  365. if(this.reviewerList.length>1){
  366. this.modal = true
  367. return
  368. }
  369. }
  370. uni.showLoading({
  371. title: '提交中...'
  372. });
  373. const success = await this.HTTP_POST(
  374. this.type === 'sign' ? 'learun/adms/newwf/sign' : 'learun/adms/newwf/audit',
  375. postData,
  376. `提交[${this.typeText}]时发生错误`
  377. )
  378. if (!success) {
  379. uni.hideLoading();
  380. return
  381. }
  382. this.EMIT('task-list-change')
  383. this.NAV_BACK(2)
  384. uni.hideLoading();
  385. this.TOAST(`已成功提交${this.typeText}`, 'success')
  386. },
  387. // 手写板事件(开始拖动)
  388. signStart(e) {
  389. touchs.push({
  390. x: e.changedTouches[0].x,
  391. y: e.changedTouches[0].y
  392. })
  393. },
  394. // 手写板事件(拖动签名)
  395. signMove(e) {
  396. touchs.push({
  397. x: e.touches[0].x,
  398. y: e.touches[0].y
  399. })
  400. this.drawLine()
  401. },
  402. // 手写板事件(签名结束)
  403. signEnd(e) {
  404. touchs = []
  405. },
  406. // 手写板事件(绘出线型)
  407. drawLine() {
  408. if (touchs.length < 2) {
  409. return
  410. }
  411. const [p1, p2] = touchs
  412. touchs.shift()
  413. context.moveTo(p1.x, p1.y)
  414. context.lineTo(p2.x, p2.y)
  415. context.stroke()
  416. context.draw(true)
  417. },
  418. // 清除手写板
  419. // 阿里小程序无法使用 clearRect 来清空,因此直接重新渲染 canvas
  420. clearSign() {
  421. // #ifndef MP-ALIPAY
  422. context.clearRect(0, 0, 9999, 9999)
  423. context.draw()
  424. context.setStrokeStyle('#000')
  425. context.setLineWidth(5)
  426. context.setLineCap('round')
  427. context.setLineJoin('round')
  428. // #endif
  429. // #ifdef MP-ALIPAY
  430. // 阿里系小程序无法 clearRect 清空画布,必须重新渲染 canvas
  431. this.canvas = false
  432. this.$nextTick(() => {
  433. this.canvasInit()
  434. })
  435. // #endif
  436. },
  437. clearcom(){
  438. this.Signimg = ''
  439. this.ConfirmSign = ''
  440. }
  441. }
  442. }
  443. </script>
  444. <style lang="less" scoped>
  445. .sign-area {
  446. min-height: 500rpx;
  447. margin: 23rpx;
  448. border: 2rpx dashed #444444;
  449. .sign-canvas {
  450. width: 700rpx;
  451. height: 500rpx;
  452. }
  453. .sign-action {
  454. text-align: right;
  455. }
  456. }
  457. .pswd-uni-mask{
  458. position: fixed;
  459. z-index: 999;
  460. top: 0;
  461. right: 0;
  462. left: 0;
  463. bottom: 0;
  464. background: rgba(0,0,0,.5);
  465. }
  466. .pswd-uni-modal{
  467. position: fixed;
  468. z-index: 999;
  469. width: 80%;
  470. max-width: 300px;
  471. top: 50%;
  472. left: 50%;
  473. -webkit-transform: translate(-50%,-50%);
  474. transform: translate(-50%,-50%);
  475. background-color: #fff;
  476. text-align: center;
  477. border-radius: 3px;
  478. overflow: hidden;
  479. }
  480. .pswd-uni-modal__hd{
  481. padding: 1em 1.6em 0.3em;
  482. }
  483. .pswd-strong{
  484. font-weight: 400;
  485. font-size: 18px;
  486. }
  487. .pswd-uni-modal__ft{
  488. position: relative;
  489. line-height: 48px;
  490. font-size: 18px;
  491. display: -webkit-box;
  492. display: -webkit-flex;
  493. display: flex;
  494. }
  495. .pswd-uni-modal__ft::after {
  496. content: " ";
  497. position: absolute;
  498. left: 0;
  499. top: 0;
  500. right: 0;
  501. height: 1px;
  502. border-top: 1px solid #d5d5d6;
  503. color: #d5d5d6;
  504. -webkit-transform-origin: 0 0;
  505. transform-origin: 0 0;
  506. -webkit-transform: scaleY(.5);
  507. transform: scaleY(.5);
  508. }
  509. .pswd-uni-modal__btn{
  510. display: block;
  511. -webkit-box-flex: 1;
  512. /* -webkit-flex: 1; */
  513. flex: 1;
  514. color: #3cc51f;
  515. text-decoration: none;
  516. -webkit-tap-highlight-color: rgba(0,0,0,0);
  517. position: relative;
  518. cursor: pointer;
  519. }
  520. .pswd__btn_primary::after {
  521. content: " ";
  522. position: absolute;
  523. left: 0;
  524. top: 0;
  525. width: 1px;
  526. bottom: 0;
  527. border-left: 1px solid #d5d5d6;
  528. color: #d5d5d6;
  529. -webkit-transform-origin: 0 0;
  530. transform-origin: 0 0;
  531. -webkit-transform: scaleX(.5);
  532. transform: scaleX(.5);
  533. }
  534. .pswdinp{
  535. background: #f5f5f5;
  536. }
  537. .Signwrap{
  538. margin: 11px;
  539. border: 1px dashed #444444;
  540. background: #ffffff;
  541. }
  542. </style>