info.ejs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  6. <meta http-equiv="x-ua-compatible" content="ie=edge">
  7. <title>签字-计量支付</title>
  8. <style>
  9. #app {
  10. position: absolute;
  11. top: 0;
  12. left: 0;
  13. right: 0;
  14. bottom: 0;
  15. font-family: 'Avenir', Helvetica, Arial, sans-serif;
  16. -webkit-font-smoothing: antialiased;
  17. -moz-osx-font-smoothing: grayscale;
  18. text-align: center;
  19. color: #2c3e50;
  20. }
  21. .container {
  22. width: 100%;
  23. height: 100%;
  24. }
  25. #canvasBox {
  26. display: flex;
  27. flex-direction: column;
  28. height: 100%;
  29. align-items: center; /*定义body的元素垂直居中*/
  30. justify-content: center; /*定义body的里的元素水平居中*/
  31. }
  32. .greet {
  33. padding: 20px;
  34. font-size: 20px;
  35. user-select: none;
  36. }
  37. input {
  38. font-size: 20px;
  39. }
  40. .greet select {
  41. font-size: 18px;
  42. }
  43. canvas {
  44. flex: 1;
  45. cursor: crosshair;
  46. border:2px dashed #007bff;
  47. }
  48. .image-box {
  49. width: 100%;
  50. height: 100%;
  51. }
  52. .image-box header{
  53. font-size: 18px;
  54. }
  55. .image-box img {
  56. max-width: 80%;
  57. max-height: 80%;
  58. margin-top: 50px;
  59. border: 1px solid gray;
  60. }
  61. </style>
  62. </head>
  63. <body>
  64. <div id="app">
  65. <% if (error !== undefined && error) { %>
  66. <div>token已更新,请重新登录并再次扫码。</div>
  67. <% } else { %>
  68. <div class="container">
  69. <div id="canvasBox" :style="getHorizontalStyle" v-show="!showBox">
  70. <div class="greet">
  71. <span>{{msg}}</span>
  72. <input type="button" value="清空" @touchstart="clear" @mousedown="clear"/>
  73. <input type="button" value="下一步 生成签名图" @touchstart="savePNG" @mousedown="savePNG"/>
  74. </div>
  75. <canvas></canvas>
  76. </div>
  77. <div class="image-box" v-show="showBox">
  78. <header>
  79. <input type="button" value="返回" @click="showBox = false"/>
  80. <input type="button" value="上传" @click="upload" :disabled="showSuccess"/>
  81. <span v-show="showSuccess">已成功上传,请刷新电脑页面查看签名效果吧。</span>
  82. </header>
  83. <img :src="signImage">
  84. </div>
  85. </div>
  86. <% } %>
  87. </div>
  88. <% if (error === false) { %>
  89. <script type="text/javascript" src="/public/js/vue/vue.js"></script>
  90. <script type="text/javascript" src="/public/js/draw.js"></script>
  91. <script>
  92. const id = '<%- id %>';
  93. const name = '<%- name %>';
  94. const from = '<%- from %>';
  95. const csrf = '<%= ctx.csrf %>';
  96. new Vue({
  97. el: '#app',
  98. data() {
  99. return {
  100. msg: '您好,' + name + ',请在虚线框内手写您的签名。',
  101. degree: 90, // 屏幕整体旋转的角度, 可取 -90,90,180等值
  102. signImage: null,
  103. showBox: false,
  104. showSuccess: false
  105. };
  106. },
  107. components: {
  108. Draw,
  109. },
  110. beforeCreate() {
  111. // document.title = '手写签名';
  112. },
  113. mounted() {
  114. this.canvasBox = document.getElementById('canvasBox');
  115. this.initCanvas();
  116. },
  117. computed: {
  118. getHorizontalStyle() {
  119. const d = document;
  120. const w = window.innerWidth || d.documentElement.clientWidth || d.body.clientWidth;
  121. const h = window.innerHeight || d.documentElement.clientHeight || d.body.clientHeight;
  122. let length = (h - w) / 2;
  123. let width = w;
  124. let height = h;
  125. switch (this.degree) {
  126. case -90:
  127. length = -length;
  128. case 90:
  129. width = h;
  130. height = w;
  131. break;
  132. default:
  133. length = 0;
  134. }
  135. if (this.canvasBox) {
  136. this.canvasBox.removeChild(document.querySelector('canvas'));
  137. this.canvasBox.appendChild(document.createElement('canvas'));
  138. setTimeout(() => {
  139. this.initCanvas();
  140. }, 200);
  141. }
  142. return {
  143. transform: `rotate(${this.degree}deg) translate(${length}px,${length}px)`,
  144. width: `${width}px`,
  145. height: `${height}px`,
  146. transformOrigin: 'center center',
  147. };
  148. },
  149. },
  150. methods: {
  151. initCanvas() {
  152. const canvas = document.querySelector('canvas');
  153. this.draw = new Draw(canvas, -this.degree);
  154. },
  155. clear() {
  156. this.draw.clear();
  157. },
  158. download() {
  159. this.draw.downloadPNGImage(this.draw.getPNGImage());
  160. },
  161. savePNG() {
  162. this.signImage = this.draw.getPNGImage();
  163. this.showBox = true;
  164. this.showSuccess = false;
  165. },
  166. upload() {
  167. if (!this.showSuccess) {
  168. const image = this.draw.getPNGImage();
  169. const blob = this.draw.dataURLtoBlob(image);
  170. const successCallback = (response) => {
  171. // console.log(response);
  172. if (JSON.parse(response).err === 0) {
  173. this.showSuccess = true;
  174. } else {
  175. this.showSuccess = false;
  176. }
  177. };
  178. const failureCallback = (error) => {
  179. // console.log(error);
  180. this.showSuccess = false;
  181. };
  182. const url = '/sign/save';
  183. if (from === 'netcasign') {
  184. this.blobToBase64(blob).then(res => {
  185. this.draw.uploadBase64(res.split(',')[1], url, successCallback, failureCallback);
  186. });
  187. } else {
  188. this.draw.upload(blob, url, successCallback, failureCallback);
  189. }
  190. }
  191. },
  192. blobToBase64(blob) {
  193. return new Promise((resolve, reject) => {
  194. const fileReader = new FileReader();
  195. fileReader.onload = (e) => {
  196. resolve(e.target.result);
  197. };
  198. // readAsDataURL
  199. fileReader.readAsDataURL(blob);
  200. fileReader.onerror = () => {
  201. reject(new Error('文件流异常'));
  202. };
  203. });
  204. }
  205. },
  206. });
  207. </script>
  208. <% } %>
  209. </body>
  210. </html>