wk-popup-sign.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <template>
  2. <u-popup
  3. v-model="show"
  4. safe-area-inset-bottom
  5. border-radius="20"
  6. closeable
  7. mode="bottom">
  8. <view class="container">
  9. <view class="title">
  10. 签名
  11. </view>
  12. <view class="handCenter" :style="getStyle">
  13. <!-- v-if="show" -->
  14. <canvas
  15. class="hand-writing"
  16. disable-scroll
  17. canvas-id="__signature__canvas"
  18. @touchstart="uploadScaleStart"
  19. @touchmove="uploadScaleMove"
  20. @touchend="uploadScaleEnd"
  21. />
  22. </view>
  23. <view class="buttons">
  24. <text class="button button_rewrite" @click="rewrite">
  25. 重签
  26. </text>
  27. <text class="button button_submit linear-gradient" @click="submit">
  28. 提交
  29. </text>
  30. </view>
  31. <view class="safe-area" />
  32. </view>
  33. </u-popup>
  34. </template>
  35. <script>
  36. /**
  37. * this code copy from github
  38. * Modify by yxk
  39. * @see https://github.com/aoobao/signature/tree/master/components/SignaturePad
  40. */
  41. import { FileQueryOneByBatch, FileDeleteById } from 'API/file.js'
  42. import UPopup from './u-popup.vue'
  43. import Handwriting from './signature.js'
  44. import { BASE_URL_FN } from '@/config.js'
  45. const CANVAS_ID = '__signature__canvas'
  46. const WIDTH = 710
  47. export default {
  48. name: 'WkPopupSign',
  49. components: {UPopup},
  50. props: {
  51. value: {
  52. type: String,
  53. default: null
  54. }
  55. },
  56. data() {
  57. return {
  58. canvasId: CANVAS_ID,
  59. show: false,
  60. width: '100%',
  61. height: '500rpx',
  62. fileData: null,
  63. tempFilePath: null
  64. };
  65. },
  66. computed: {
  67. getStyle() {
  68. return `width:${this.width};height:${this.height};`;
  69. }
  70. },
  71. watch: {
  72. show(val) {
  73. if (!val) {
  74. // 关闭
  75. if (this.reject) {
  76. this.reject();
  77. }
  78. this.fileData = null
  79. this.tempFilePath = null
  80. }
  81. }
  82. },
  83. methods: {
  84. sign() {
  85. return new Promise((resolve, reject) => {
  86. this.resolve = resolve;
  87. this.reject = reject;
  88. if (this.value) {
  89. this.getImage()
  90. } else {
  91. this.initDraw()
  92. }
  93. })
  94. },
  95. initDraw(scale = 1) {
  96. const width = uni.upx2px(WIDTH)
  97. const color = '#000'
  98. this.width = width + 'px';
  99. this.height = (width / 2.6) + 'px';
  100. this.show = true;
  101. setTimeout(() => {
  102. let query = uni.createSelectorQuery().in(this);
  103. let ctx = uni.createCanvasContext(CANVAS_ID, this);
  104. this.handwriting = new Handwriting({
  105. lineColor: color,
  106. slideValue: scale,
  107. canvasName: CANVAS_ID,
  108. ctx: ctx
  109. });
  110. query
  111. .select('.handCenter')
  112. .boundingClientRect(rect => {
  113. this.handwriting.setSize(rect);
  114. this.handwriting.setBgColor('#FFFFFF')
  115. if (this.tempFilePath) {
  116. this.$nextTick(() => {
  117. ctx.drawImage(this.tempFilePath, 0, 0, rect.width, rect.height)
  118. ctx.draw() // 绘制
  119. this.handwriting.linePrack.push('')
  120. })
  121. }
  122. })
  123. .exec();
  124. }, 500);
  125. },
  126. getUrl(path) {
  127. let url = BASE_URL_FN() + (path.startsWith('/') ? path.replace('/', '') : path)
  128. const token = uni.getStorageSync('token') || ''
  129. const appid = uni.getStorageSync('appid') || ''
  130. const arr = []
  131. if (token) {
  132. arr.push(`c=${token}`)
  133. }
  134. if (appid) {
  135. arr.push(`k=${appid}`)
  136. }
  137. if (arr.length > 0) {
  138. return url + '?' + arr.join('&')
  139. } else {
  140. return url
  141. }
  142. },
  143. /**
  144. * 通过请求挂载图片
  145. */
  146. getImage() {
  147. const appid = uni.getStorageSync('appid') || ''
  148. const header = {
  149. 'Admin-Token': uni.getStorageSync('token') || ''
  150. }
  151. if (appid) {
  152. header.k = appid
  153. }
  154. const that = this
  155. FileQueryOneByBatch({
  156. batchId: this.value
  157. }).then(res => {
  158. if (res) {
  159. let path = that.getUrl(res.url)
  160. that.fileData = res
  161. uni.downloadFile({
  162. url: path,
  163. header,
  164. success: fileRes => {
  165. that.tempFilePath = fileRes.tempFilePath
  166. uni.getImageInfo({
  167. src: that.tempFilePath,
  168. success: data => {
  169. // console.log('imageInfo: ', data)
  170. uni.getSystemInfo({
  171. success: info => {
  172. const scale = uni.upx2px(WIDTH) * info.pixelRatio / data.width
  173. that.initDraw(scale)
  174. }
  175. })
  176. },
  177. fail: () => {
  178. that.initDraw()
  179. }
  180. })
  181. },
  182. fail: () => {
  183. that.tempFilePath = null
  184. that.initDraw()
  185. }
  186. })
  187. } else {
  188. that.tempFilePath = null
  189. that.initSign()
  190. }
  191. }).catch(() => {})
  192. },
  193. /**
  194. * 删除文件
  195. */
  196. deleteImgFile() {
  197. if (!this.fileData || !this.fileData.fileId) return
  198. const id = this.fileData.fileId
  199. this.fileData = null
  200. FileDeleteById({
  201. id: id
  202. })
  203. },
  204. /**
  205. * 上传签名图
  206. */
  207. uploadSignature(path) {
  208. const header = {
  209. 'Admin-Token': uni.getStorageSync('token') || ''
  210. }
  211. const appid = uni.getStorageSync('appid') || ''
  212. if (appid) {
  213. header.k = appid
  214. }
  215. const formData = {
  216. type: 'img'
  217. }
  218. if (this.value) {
  219. formData.batchId = this.value
  220. }
  221. const that = this
  222. const requestConfig = {
  223. url: BASE_URL_FN() + 'adminFile/uploadBySingle',
  224. fileType: 'image',
  225. name: 'file',
  226. header,
  227. formData,
  228. success: res => {
  229. // console.log('ddd', res)
  230. uni.hideLoading()
  231. let data = res.data
  232. if (typeof res.data === 'string') {
  233. try {
  234. data = JSON.parse(res.data)
  235. } catch (e) {
  236. that.$toast('网络异常,请稍后重试')
  237. that.reject('网络异常,请稍后重试')
  238. return
  239. }
  240. }
  241. that.fileData = data.data
  242. that.$emit('input', data.data.batchId)
  243. that.resolve(data.data.batchId)
  244. },
  245. fail: () => {
  246. uni.hideLoading()
  247. that.$toast('上传失败')
  248. that.reject('上传失败')
  249. }
  250. }
  251. requestConfig.filePath = path
  252. uni.uploadFile(requestConfig)
  253. },
  254. close() {
  255. this.show = false;
  256. },
  257. rewrite() {
  258. this.handwriting.clear();
  259. },
  260. submit() {
  261. let self = this;
  262. if (this.handwriting.isEmpty()) {
  263. // 未签字
  264. self.deleteImgFile()
  265. self.$emit('input', null)
  266. self.resolve(null)
  267. self.reject = null
  268. self.show = false
  269. return
  270. }
  271. uni.getSystemInfo({
  272. success: info => {
  273. self.canvasToTempPath(info.pixelRatio)
  274. },
  275. fail: () => {
  276. self.canvasToTempPath(3)
  277. }
  278. })
  279. },
  280. canvasToTempPath(pixelRatio = 3) {
  281. let self = this;
  282. const w = Number(self.width.replace('px', ''))
  283. const h = Number(self.height.replace('px', ''))
  284. uni.canvasToTempFilePath(
  285. {
  286. canvasId: CANVAS_ID,
  287. quality: 1.0,
  288. fileType: 'png',
  289. x: 0,
  290. y: 0,
  291. width: w,
  292. height: h,
  293. destWidth: w * pixelRatio,
  294. destHeight: h * pixelRatio,
  295. success(res) {
  296. console.log(res.tempFilePath)
  297. let path = res.tempFilePath;
  298. self.reject = null;
  299. self.uploadSignature(path);
  300. },
  301. fail(err) {
  302. let reject = self.reject;
  303. self.reject = null;
  304. reject({ type: 'err', err: err });
  305. },
  306. complete() {
  307. // 失败关闭
  308. self.show = false;
  309. }
  310. },
  311. this
  312. );
  313. },
  314. uploadScaleStart(event) {
  315. this.handwriting.uploadScaleStart(event);
  316. },
  317. uploadScaleMove(event) {
  318. this.handwriting.uploadScaleMove(event);
  319. },
  320. uploadScaleEnd(event) {
  321. this.handwriting.uploadScaleEnd(event);
  322. }
  323. }
  324. };
  325. </script>
  326. <style scoped lang="scss">
  327. .container {
  328. width: 100%;
  329. /* height: 822rpx; */
  330. position: relative;
  331. background-color: #fff;
  332. overflow: hidden;
  333. }
  334. .title {
  335. width: 100%;
  336. display: flex;
  337. justify-content: center;
  338. color: $dark;
  339. font-size: $wk-font-large;
  340. font-weight: bold;
  341. margin-top: 25rpx;
  342. }
  343. .handCenter {
  344. margin: 25rpx auto 0;
  345. border: 1px dashed #979797;
  346. }
  347. .hand-writing {
  348. width: 100%;
  349. height: 100%;
  350. }
  351. .safe-area {
  352. width: 100%;
  353. height: 0;
  354. height: constant(safe-area-inset-bottom);
  355. height: env(safe-area-inset-bottom);
  356. }
  357. .buttons {
  358. width: 80%;
  359. margin: 25rpx auto;
  360. display: flex;
  361. justify-content: space-between;
  362. }
  363. .buttons .button {
  364. width: 200rpx;
  365. height: 60rpx;
  366. display: flex;
  367. justify-content: center;
  368. align-items: center;
  369. border-radius: 14rpx;
  370. font-size: $wk-font-base;
  371. }
  372. .buttons .button.button_rewrite {
  373. border: 1px solid $theme-color;
  374. background-color: #ffffff;
  375. color: $theme-color;
  376. }
  377. .buttons .button.button_submit {
  378. color: #fff;
  379. border: 0 none;
  380. }
  381. </style>