tmsSign.html 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta content="yes" name="apple-mobile-web-app-capable">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  7. <title>Hello APP</title>
  8. <link rel="stylesheet" type="text/css" href="../../../../../css/api.css" />
  9. <link rel="stylesheet" type="text/css" href="../../../../../css/aui-win.css" />
  10. <link rel="stylesheet" type="text/css" href="../../../../../css/css/vant-ui.css" />
  11. <link rel="stylesheet" type="text/css" href="../../../../../css/css/iconfont.css">
  12. <script src="../../../../../script/js/vue.js"></script>
  13. <script src="../../../../../script/js/vant-ui.js"></script>
  14. <script src="../../../../../script/js/auto-size.js"></script>
  15. <script src="../../../../../script/js/pdfjs-2.5.207-es5-dist/build/pdf.js"></script>
  16. <script src="../../../../../script/js/pdf_plugins.js"></script>
  17. <style>
  18. .flex{
  19. display: flex;
  20. }
  21. #app{}
  22. #aui-header{
  23. background: linear-gradient(to right,#1474E4,#14A7E4);
  24. justify-content: space-between;
  25. align-items: center;
  26. }
  27. #scrollcontaier{
  28. overflow-y: auto;
  29. }
  30. .aui-btn-info{
  31. border: 0
  32. }
  33. .title{
  34. padding: 10px 12px;
  35. color: #ffffff;
  36. }
  37. .image-icon{
  38. width: .66rem;
  39. height: .66rem;
  40. margin-right: 19px;
  41. }
  42. img{
  43. width: 100%;
  44. height: 100%;
  45. }
  46. .nav{
  47. width: 10.1rem;
  48. height: 1.44rem;
  49. margin: .28rem auto 0;
  50. justify-content: space-between;
  51. align-items: center;
  52. background: url("../../../../../image/icon/hedui.png") no-repeat 0 0;
  53. background-size: 100% auto;
  54. }
  55. .details-btn{
  56. height: .96rem;
  57. line-height: .96rem;
  58. padding: 0 .38rem;
  59. border-radius: 0.08rem;
  60. background: #1474E4;
  61. color: #ffffff;
  62. margin-right: .24rem;
  63. }
  64. .details-wrap{
  65. background: #ffffff;
  66. box-sizing: border-box;
  67. width: 10.1rem;
  68. margin: 0.36rem auto 0;
  69. border-radius: 0.08rem;
  70. padding: .4rem;
  71. }
  72. .canvas-container{
  73. position: relative;
  74. }
  75. #canvas{
  76. width: 9.45rem;
  77. height: 2.39rem;
  78. background: blue;
  79. }
  80. .scale-btn{
  81. position: absolute;
  82. top: 0;
  83. right: -.13rem;
  84. width: 0;
  85. height: 0;
  86. border-bottom: .8rem solid transparent;
  87. border-right: .8rem solid #1474E4;
  88. border-left: .8rem solid transparent;
  89. }
  90. .scale-btn span{
  91. display: inline-block;
  92. width: .32rem;
  93. height: .32rem;
  94. position: absolute;
  95. top: 0;
  96. right: -.7rem;
  97. }
  98. .scale-btn span img{
  99. width: 100%;
  100. height: 100%;
  101. }
  102. .text-msg{
  103. color: #595959;
  104. }
  105. .field-container{
  106. background: #F5F5F5;
  107. }
  108. .signature{
  109. /*height: 3.2rem;*/
  110. width: 100%;
  111. }
  112. .signature p{
  113. color: #595959;
  114. }
  115. .esign-box{
  116. background: rgba(236,236,236,.5) url(../../../../../image/icon/qianming.png) no-repeat 50%;
  117. width: 9.42rem;
  118. height: 4.54rem;
  119. }
  120. .esigh-btns{
  121. margin-top: 10px;
  122. }
  123. .confirm-btn-container{
  124. justify-content: center;
  125. }
  126. .confirm-btn{
  127. background: linear-gradient(to right, #F09623, #FFCF4C);
  128. /*padding: 0 1.18rem;*/
  129. height: .96rem;
  130. width: 10.1rem;
  131. line-height: .96rem;
  132. border-radius: .48rem;
  133. color: #ffffff;
  134. font-size: .42rem;
  135. text-align: center;
  136. margin: .42rem auto;
  137. }
  138. </style>
  139. </head>
  140. <style>
  141. </style>
  142. <body>
  143. <div id="app">
  144. <header class="flex flex-between" id="aui-header">
  145. <a class="aui-btn aui-btn-info aui-pull-left" tapmode onclick="closeWin()"> <span class="aui-iconfont aui-icon-left"></span> </a>
  146. <div class="title">签收</div>
  147. <div class="image-icon"><img src="../../../../../image/icon/zhiyinshuoming.png" alt=""></div>
  148. </header>
  149. <div id="scrollcontaier" ref="scrollcontaier">
  150. <div class="nav flex"><span></span><span class="details-btn" @click="handleGo">核对明细<van-icon name="arrow" /></span></div>
  151. <div class="details-wrap">
  152. <div class="canvas-container">
  153. <div id="canvas" ref="canvas"></div>
  154. <div class="scale-btn"><span @click='handleScale'><img src="../../../../../image/icon/fangda.png" alt="放大"></span></div>
  155. </div>
  156. <van-divider ></van-divider>
  157. <p class="text-msg">若您收到的货物与订单存在差异,可上传相关图片及描述,我们会及时处理。</p>
  158. <van-row class="image-container" gutter="20">
  159. <van-col span="8" v-for="(item,index) in imageList" class="image-item" :key="index">
  160. <van-image :src="item.url" fit="contain" width="2.82rem" height="2.82rem" />
  161. </van-col>
  162. <van-col span="8" class="image-item">
  163. <van-image src="../../../../../image/icon/shangchuan.png" fit="contain" width="2.82rem" height="2.62rem" @click="handleSelectedPhoto"/>
  164. </van-col>
  165. </van-row>
  166. <van-field
  167. class="field-container"
  168. v-model="message"
  169. rows="2"
  170. autosize
  171. type="textarea"
  172. maxlength="50"
  173. placeholder="请输入备注~"
  174. show-word-limit
  175. @focus.stop.prevent="handleFocus"
  176. ></van-field>
  177. <van-divider ></van-divider>
  178. <div class="signature">
  179. <p>请在空白处签名~</p>
  180. <div class="esign-box" ref="esignbox">
  181. <canvas ref="signcanvas" id="signcanvas" width="300" height="300"
  182. @mousedown="mouseDown"
  183. @mousemove="mouseMove"
  184. @mouseup="mouseUp"
  185. @touchstart="touchStart"
  186. @touchmove="touchMove"
  187. @touchend="touchEnd"></canvas>
  188. </div>
  189. <div class="esigh-btns">
  190. <button @click="reset">清空画板</button>
  191. <button @click="generate">生成图片</button>
  192. <button @click="save">保存图片</button>
  193. </div>
  194. <div class="esigh-result">
  195. <img v-if="resultImg" :src="resultImg" alt="">
  196. </div>
  197. </div>
  198. </div>
  199. <div class="list-item-row confirm-btn-container flex"><div><span class="confirm-btn" @click="handleSubmit">提交</span></div></div>
  200. </div>
  201. </div>
  202. </body>
  203. <script type="text/javascript" src="../../../../../script/api.js"></script>
  204. <link rel="stylesheet" href="../../../../../script/mescroll/mescroll.min.css">
  205. <script type="text/javascript" src="../../../../../script/mescroll/mescroll.min.js"></script>
  206. <script type="text/javascript" src="../../../../../script/httpRequest.js"></script>
  207. <script type="text/javascript" src="../../../../../script/vue_plugins.js"></script>
  208. <script type="text/javascript">
  209. function closeWin() {
  210. api.closeWin({
  211. });
  212. }
  213. apiready = function() {
  214. api.parseTapmode();
  215. var header = $api.byId('aui-header');
  216. $api.fixStatusBar(header);
  217. var headerPos = $api.offset(header);
  218. var body_h = $api.offset($api.dom('body')).h;
  219. document.getElementById("scrollcontaier").style.height = $api.offset($api.dom('body')).h - headerPos.h + "px";
  220. console.log(headerPos.h);
  221. new Vue({
  222. el: '#app',
  223. data: {
  224. pdfReader: "",
  225. imageList: [
  226. {
  227. name: "cat",
  228. url: "https://img.yzcdn.cn/vant/cat.jpeg"
  229. },{
  230. name: "cat",
  231. url: "https://img.yzcdn.cn/vant/cat.jpeg"
  232. },{
  233. name: "cat",
  234. url: "https://img.yzcdn.cn/vant/cat.jpeg"
  235. },{
  236. name: "cat",
  237. url: "https://img.yzcdn.cn/vant/cat.jpeg"
  238. }],
  239. message: "",
  240. width:300,
  241. height: 300,
  242. lineWidth: 6,
  243. lineColor: '#000000',
  244. bgColor: '',
  245. isCrop: false,
  246. hasDrew: false,
  247. resultImg: '',
  248. points: [],
  249. canvasTxt: null,
  250. startX: 0,
  251. startY: 0,
  252. isDrawing: false,
  253. sratio: 1
  254. },
  255. computed: {
  256. ratio () {
  257. return this.height / this.width
  258. },
  259. stageInfo () {
  260. return this.$refs.signcanvas.getBoundingClientRect()
  261. },
  262. myBg () {
  263. return this.bgColor ? this.bgColor : 'rgba(255, 255, 255, 0)'
  264. }
  265. },
  266. watch: {
  267. 'myBg': function (newVal) {
  268. this.$refs.signcanvas.style.background = newVal
  269. }
  270. },
  271. beforeMount () {
  272. window.addEventListener('resize', this.$_resizeHandler)
  273. },
  274. beforeDestroy () {
  275. window.removeEventListener('resize', this.$_resizeHandler)
  276. },
  277. filters:{
  278. },
  279. mounted: function() {
  280. //月度pdf文件
  281. this.pdfReader = api.require('pdfReader');
  282. // pdf展示成图片
  283. var canvasWidth=this.$refs.canvas.offsetWidth,canvasHeight=this.$refs.canvas.offsetHeight;
  284. $canvasPdf.loadPDF("https://srmapi.sailuntire.com/Files/20201221/20200000000186-3000100-8379-Out.pdf",canvasWidth,canvasHeight);
  285. // 电子签名
  286. this.width = this.$refs.esignbox.offsetWidth;
  287. this.height = this.$refs.esignbox.offsetHeight;
  288. var canvas = this.$refs.signcanvas;
  289. canvas.height = this.height;
  290. canvas.width = this.width;
  291. canvas.style.background = this.myBg;
  292. this.$_resizeHandler();
  293. // 在画板以外松开鼠标后冻结画笔
  294. document.onmouseup = function() {
  295. this.isDrawing = false;
  296. }
  297. },
  298. methods:{
  299. handleScale: function(){
  300. this.pdfReader.open({
  301. path: "https://srmapi.sailuntire.com/Files/20201221/20200000000186-3000100-8379-Out.pdf",
  302. hidden: {
  303. print: true,
  304. export: true,
  305. bookmark: true,
  306. email: true
  307. },
  308. backBtn: {
  309. size: { //JSON对象;左上角按钮的大小配置
  310. w: 20, //数字类型;左上角按钮的宽;默认:60
  311. h: 20 //数字类型;左上角按钮的高;默认:40
  312. },
  313. title: { //JSON对象;按钮标题配置
  314. text: "返回", //字符串类型;标题文本;默认:‘’
  315. },
  316. corner: 5 //数字类型;左上角按钮圆角大小;默认值:5.0
  317. }
  318. });
  319. },
  320. handleSelectedPhoto: function(){
  321. var _this = this;
  322. this.ifHasPrme('storage', function(ret) {
  323. if (ret == true) {
  324. _this.ifHasPrme('camera', function(res) {
  325. if (res == true) {
  326. _this.chooseImage(function(ret) {
  327. var obj = {
  328. name: "cat",
  329. url: "https://img.yzcdn.cn/vant/cat.jpeg"
  330. };
  331. _this.imageList.push(obj);
  332. // uploadFile(ret, type);
  333. // if (type == 1) {
  334. // vm.urldata1 = _this.localImage;
  335. // } else {
  336. // vm.urldata2 = _this.localImage;
  337. // }
  338. })
  339. }
  340. })
  341. }
  342. })
  343. },
  344. handleSubmit: function(){
  345. },
  346. handleFocus: function(){
  347. console.log($api.offset($api.dom('body')).w)
  348. },
  349. handleGo: function(){
  350. this.goWin("tmsdetails","./tmsDetails.html");
  351. },
  352. $_resizeHandler () {
  353. var canvas = this.$refs.signcanvas;
  354. canvas.style.width = this.width + "px"
  355. var realw = parseFloat(window.getComputedStyle(canvas).width)
  356. canvas.style.height = this.ratio * realw + "px";
  357. this.canvasTxt = canvas.getContext('2d')
  358. this.canvasTxt.scale(1 * this.sratio, 1 * this.sratio)
  359. this.sratio = realw / this.width
  360. this.canvasTxt.scale(1 / this.sratio, 1 / this.sratio)
  361. },
  362. // pc
  363. mouseDown (e) {
  364. e = e || event
  365. e.preventDefault()
  366. this.isDrawing = true
  367. this.hasDrew = true
  368. var obj = {
  369. x: e.offsetX,
  370. y: e.offsetY
  371. }
  372. this.drawStart(obj)
  373. },
  374. mouseMove (e) {
  375. e = e || event
  376. e.preventDefault()
  377. if (this.isDrawing) {
  378. var obj = {
  379. x: e.offsetX,
  380. y: e.offsetY
  381. }
  382. this.drawMove(obj)
  383. }
  384. },
  385. mouseUp (e) {
  386. e = e || event
  387. e.preventDefault()
  388. var obj = {
  389. x: e.offsetX,
  390. y: e.offsetY
  391. }
  392. this.drawEnd(obj)
  393. this.isDrawing = false
  394. },
  395. // mobile
  396. touchStart (e) {
  397. e = e || event
  398. e.preventDefault()
  399. this.hasDrew = true
  400. if (e.touches.length === 1) {
  401. var obj = {
  402. x: e.targetTouches[0].clientX - this.$refs.signcanvas.getBoundingClientRect().left,
  403. y: e.targetTouches[0].clientY - this.$refs.signcanvas.getBoundingClientRect().top
  404. }
  405. this.drawStart(obj)
  406. }
  407. },
  408. touchMove (e) {
  409. e = e || event
  410. e.preventDefault()
  411. if (e.touches.length === 1) {
  412. var obj = {
  413. x: e.targetTouches[0].clientX - this.$refs.signcanvas.getBoundingClientRect().left,
  414. y: e.targetTouches[0].clientY - this.$refs.signcanvas.getBoundingClientRect().top
  415. }
  416. this.drawMove(obj)
  417. }
  418. },
  419. touchEnd (e) {
  420. e = e || event
  421. e.preventDefault()
  422. if (e.touches.length === 1) {
  423. var obj = {
  424. x: e.targetTouches[0].clientX - this.$refs.signcanvas.getBoundingClientRect().left,
  425. y: e.targetTouches[0].clientY - this.$refs.signcanvas.getBoundingClientRect().top
  426. }
  427. this.drawEnd(obj)
  428. }
  429. },
  430. // 绘制
  431. drawStart (obj) {
  432. this.startX = obj.x
  433. this.startY = obj.y
  434. this.canvasTxt.beginPath()
  435. this.canvasTxt.moveTo(this.startX, this.startY)
  436. this.canvasTxt.lineTo(obj.x, obj.y)
  437. this.canvasTxt.lineCap = 'round'
  438. this.canvasTxt.lineJoin = 'round'
  439. this.canvasTxt.lineWidth = this.lineWidth * this.sratio;
  440. console.log(this.lineWidth * this.sratio);
  441. this.canvasTxt.stroke()
  442. this.canvasTxt.closePath()
  443. this.points.push(obj)
  444. },
  445. drawMove (obj) {
  446. this.canvasTxt.beginPath()
  447. this.canvasTxt.moveTo(this.startX, this.startY)
  448. this.canvasTxt.lineTo(obj.x, obj.y)
  449. this.canvasTxt.strokeStyle = this.lineColor
  450. this.canvasTxt.lineWidth = this.lineWidth * this.sratio
  451. this.canvasTxt.lineCap = 'round'
  452. this.canvasTxt.lineJoin = 'round'
  453. this.canvasTxt.stroke()
  454. this.canvasTxt.closePath()
  455. this.startY = obj.y
  456. this.startX = obj.x
  457. this.points.push(obj)
  458. },
  459. drawEnd (obj) {
  460. this.canvasTxt.beginPath()
  461. this.canvasTxt.moveTo(this.startX, this.startY)
  462. this.canvasTxt.lineTo(obj.x, obj.y)
  463. this.canvasTxt.lineCap = 'round'
  464. this.canvasTxt.lineJoin = 'round'
  465. this.canvasTxt.stroke()
  466. this.canvasTxt.closePath()
  467. this.points.push(obj)
  468. this.points.push({x: -1, y: -1})
  469. },
  470. // 操作
  471. generate () {
  472. var pm = new Promise((resolve, reject) => {
  473. if (!this.hasDrew) {
  474. reject(`Warning: Not Signned!`)
  475. return
  476. }
  477. var resImgData = this.canvasTxt.getImageData(0, 0, this.$refs.signcanvas.width, this.$refs.signcanvas.height)
  478. this.canvasTxt.globalCompositeOperation = "destination-over"
  479. this.canvasTxt.fillStyle = this.myBg
  480. this.canvasTxt.fillRect(0,0,this.$refs.signcanvas.width ,this.$refs.signcanvas.height)
  481. this.resultImg = this.$refs.signcanvas.toDataURL()
  482. var resultImg = this.resultImg
  483. this.canvasTxt.clearRect(0, 0, this.$refs.signcanvas.width ,this.$refs.signcanvas.height)
  484. this.canvasTxt.putImageData(resImgData, 0, 0)
  485. this.canvasTxt.globalCompositeOperation = "source-over"
  486. if (this.isCrop) {
  487. var crop_area = this.getCropArea(resImgData.data)
  488. var crop_canvas = document.createElement('canvas')
  489. var crop_ctx = crop_canvas.getContext('2d')
  490. crop_canvas.width = crop_area[2] - crop_area[0]
  491. crop_canvas.height = crop_area[3] - crop_area[1]
  492. var crop_imgData = this.canvasTxt.getImageData(...crop_area)
  493. crop_ctx.globalCompositeOperation = "destination-over"
  494. crop_ctx.putImageData(crop_imgData, 0, 0)
  495. crop_ctx.fillStyle = this.myBg
  496. crop_ctx.fillRect(0, 0, crop_canvas.width , crop_canvas.height)
  497. resultImg = crop_canvas.toDataURL()
  498. crop_canvas = null
  499. }
  500. resolve(resultImg)
  501. })
  502. return pm
  503. },
  504. reset () {
  505. this.canvasTxt.clearRect(
  506. 0,
  507. 0,
  508. this.$refs.signcanvas.width,
  509. this.$refs.signcanvas.height
  510. )
  511. this.$emit('update:bgColor', '')
  512. this.$refs.signcanvas.style.background = 'rgba(255, 255, 255, 0)'
  513. this.points = []
  514. this.hasDrew = false
  515. this.resultImg = ''
  516. },
  517. getCropArea (imgData) {
  518. var topX = this.$refs.signcanvas.width; var btmX = 0; var topY = this.$refs.signcanvas.height; var btnY = 0
  519. for (var i = 0; i < this.$refs.signcanvas.width; i++) {
  520. for (var j = 0; j < this.$refs.signcanvas.height; j++) {
  521. var pos = (i + this.$refs.signcanvas.width * j) * 4
  522. if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
  523. btnY = Math.max(j, btnY)
  524. btmX = Math.max(i, btmX)
  525. topY = Math.min(j, topY)
  526. topX = Math.min(i, topX)
  527. }
  528. }
  529. }
  530. topX++
  531. btmX++
  532. topY++
  533. btnY++
  534. var data = [topX, topY, btmX, btnY]
  535. return data
  536. },
  537. save: function(){
  538. console.log(this.resultImg)
  539. }
  540. }
  541. })
  542. };
  543. </script>
  544. </html>