u-qrcode.vue 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. <!-- ---------------------------------------------------------------------
  2. // uQRCode二维码生成插件 v3.6.5
  3. //
  4. // uQRCode是一款基于Javascript环境开发的二维码生成插件,适用所有Javascript运行环境的前端应用和Node.js。
  5. //
  6. // Copyright (c) Sansnn uQRCode All rights reserved.
  7. //
  8. // Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  9. //
  10. // github地址:
  11. // https://github.com/Sansnn/uQRCode
  12. //
  13. // npm地址:
  14. // https://www.npmjs.com/package/@uqrcode/uni-app
  15. //
  16. // uni-app插件市场地址:
  17. // https://ext.dcloud.net.cn/plugin?id=1287
  18. //
  19. // 复制使用请保留本段注释,感谢支持开源!
  20. //
  21. --------------------------------------------------------------------- -->
  22. <template>
  23. <view class="uqrcode" :class="{ 'uqrcode-hide': hide }" :style="{ width: `${templateOptions.width}px`, height: `${templateOptions.height}px` }">
  24. <!-- 画布 -->
  25. <!-- #ifndef APP-NVUE -->
  26. <canvas
  27. class="uqrcode-canvas"
  28. :id="canvasId"
  29. :canvas-id="canvasId"
  30. :type="type"
  31. :style="{
  32. width: `${templateOptions.canvasWidth}px`,
  33. height: `${templateOptions.canvasHeight}px`,
  34. transform: templateOptions.canvasTransform
  35. }"
  36. @click="onClick"
  37. v-if="templateOptions.canvasDisplay"
  38. ></canvas>
  39. <!-- #endif -->
  40. <!-- nvue用gcanvas -->
  41. <!-- #ifdef APP-NVUE -->
  42. <gcanvas
  43. class="uqrcode-canvas"
  44. ref="gcanvas"
  45. :style="{
  46. width: `${templateOptions.canvasWidth}px`,
  47. height: `${templateOptions.canvasHeight}px`
  48. }"
  49. @click="onClick"
  50. v-if="templateOptions.canvasDisplay"
  51. ></gcanvas>
  52. <!-- #endif -->
  53. <!-- H5保存提示,可在此替换。后续版本做成插槽 -->
  54. <!-- #ifdef H5 -->
  55. <view class="uqrcode-h5-save" v-if="isH5Save">
  56. <image class="uqrcode-h5-save-image" :src="tempFilePath"></image>
  57. <text class="uqrcode-h5-save-text">若保存失败,请长按二维码进行保存</text>
  58. <view class="uqrcode-h5-save-close" @click="isH5Save = false">
  59. <view class="uqrcode-h5-save-close-before"></view>
  60. <view class="uqrcode-h5-save-close-after"></view>
  61. </view>
  62. </view>
  63. <!-- #endif -->
  64. <!-- 加载效果,可在此替换。后续版本做成插槽 -->
  65. <view class="uqrcode-makeing" v-if="makeing">
  66. <image
  67. class="uqrcode-makeing-image"
  68. :style="{ width: `${templateOptions.size / 4}px`, height: `${templateOptions.size / 4}px` }"
  69. src="data:image/gif;base64,R0lGODlhAAEAAfIEAOHh4SSsWuDg4N3d3f///wAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDYuMC1jMDAyIDc5LjE2NDQ4OCwgMjAyMC8wNy8xMC0yMjowNjo1MyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIyLjAgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODhGMzM4RDEwMTExRUM4MDhCRkVBQkE2QUZDQzkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjAyODhGMzM5RDEwMTExRUM4MDhCRkVBQkE2QUZDQzkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDI4OEYzMzZEMTAxMTFFQzgwOEJGRUFCQTZBRkNDOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDI4OEYzMzdEMTAxMTFFQzgwOEJGRUFCQTZBRkNDOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQ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"
  70. ></image>
  71. </view>
  72. <!-- 错误处理,可在此替换 -->
  73. <view class="uqrcode-error" v-if="inError"><text class="uqrcode-error-message">Error, see console.</text></view>
  74. </view>
  75. </template>
  76. <script>
  77. // #ifdef VUE3
  78. import { toRaw } from 'vue';
  79. // #endif
  80. /* 引入uQRCode核心js */
  81. import UQRCode from '../../js_sdk/uqrcode';
  82. /* 引入nvue所需模块 */
  83. // #ifdef APP-NVUE
  84. import { enable, WeexBridge } from '../../js_sdk/gcanvas';
  85. const modal = weex.requireModule('modal');
  86. // #endif
  87. export default {
  88. name: 'uqrcode',
  89. props: {
  90. /**
  91. * canvas组件id
  92. */
  93. canvasId: {
  94. type: String,
  95. required: true // canvasId在微信小程序初始值不能为空,created中赋值也不行,必须给一个值,否则挂载组件后无法绘制
  96. },
  97. /**
  98. * 二维码内容
  99. */
  100. value: {
  101. type: [String, Number]
  102. },
  103. /**
  104. * 二维码大小
  105. */
  106. size: {
  107. type: [String, Number],
  108. default: 200
  109. },
  110. /**
  111. * 二维码尺寸单位
  112. */
  113. sizeUnit: {
  114. type: String,
  115. default: 'px'
  116. },
  117. /**
  118. * 选项
  119. */
  120. options: {
  121. type: Object,
  122. default: () => {
  123. return {};
  124. }
  125. },
  126. /**
  127. * 导出的文件类型
  128. */
  129. fileType: {
  130. type: String,
  131. default: 'png'
  132. },
  133. /**
  134. * 是否初始化组件后就开始生成
  135. */
  136. start: {
  137. type: Boolean,
  138. default: true
  139. },
  140. /**
  141. * 是否数据发生改变自动重绘
  142. */
  143. auto: {
  144. type: Boolean,
  145. default: false
  146. },
  147. /**
  148. * 隐藏组件
  149. */
  150. hide: {
  151. type: Boolean,
  152. default: false
  153. },
  154. /**
  155. * canvas 类型
  156. * 注意:微信小程序type2d手机上正常,PC上微信内打开小程序toDataURL报错
  157. */
  158. type: {
  159. type: String,
  160. default: undefined
  161. },
  162. /**
  163. * 是否队列加载图片
  164. */
  165. isQueueLoadImage: {
  166. type: Boolean,
  167. default: false
  168. }
  169. },
  170. data() {
  171. return {
  172. canvas: undefined,
  173. canvasContext: undefined,
  174. makeDelegate: undefined,
  175. drawDelegate: undefined,
  176. toTempFilePathDelegate: undefined,
  177. makeing: false,
  178. drawing: false,
  179. inError: false,
  180. isH5Save: false,
  181. tempFilePath: '',
  182. templateOptions: {
  183. size: 0,
  184. width: 0, // 组件宽度
  185. height: 0,
  186. canvasWidth: 0, // canvas宽度
  187. canvasHeight: 0,
  188. canvasTransform: '',
  189. canvasDisplay: false
  190. },
  191. uqrcodeOptions: {
  192. data: ''
  193. },
  194. makeingPattern: [
  195. [
  196. [true, true, true, false, false, false, false, true, true, true],
  197. [true, true, true, false, false, false, false, true, true, true],
  198. [true, true, true, false, false, false, false, true, true, true],
  199. [true, true, true, false, false, false, false, true, true, true],
  200. [true, true, true, false, false, false, false, true, true, true],
  201. [true, true, true, false, false, false, false, true, true, true],
  202. [true, true, true, false, false, false, false, true, true, true],
  203. [true, true, true, true, true, true, true, true, true, true],
  204. [true, true, true, true, true, true, true, true, true, true],
  205. [true, true, true, true, true, true, true, true, true, true]
  206. ],
  207. [
  208. [true, true, true, true, true, true, true, true, true, true],
  209. [true, true, true, true, true, true, true, true, true, true],
  210. [true, true, true, true, true, true, true, true, true, true],
  211. [true, true, true, false, false, false, false, true, true, true],
  212. [true, true, true, false, false, false, false, true, true, true],
  213. [true, true, true, false, false, false, false, true, true, true],
  214. [true, true, true, false, false, false, false, false, false, false],
  215. [true, true, true, true, true, true, false, true, true, true],
  216. [true, true, true, true, true, true, false, true, true, true],
  217. [true, true, true, true, true, true, false, true, true, true]
  218. ],
  219. [
  220. [true, true, true, true, true, true, true, true, true, true],
  221. [true, true, true, true, true, true, true, true, true, true],
  222. [true, true, true, true, true, true, true, true, true, true],
  223. [true, true, true, false, false, false, false, true, true, true],
  224. [true, true, true, false, false, false, false, true, true, true],
  225. [true, true, true, true, true, true, true, false, false, false],
  226. [true, true, true, true, true, true, true, false, false, false],
  227. [true, true, true, true, true, true, true, false, false, false],
  228. [true, true, true, false, false, false, false, true, true, true],
  229. [true, true, true, false, false, false, false, true, true, true]
  230. ],
  231. [
  232. [true, true, true, true, true, true, true, true, true, true],
  233. [true, true, true, true, true, true, true, true, true, true],
  234. [true, true, true, true, true, true, true, true, true, true],
  235. [true, true, true, false, false, false, false, false, false, false],
  236. [true, true, true, false, false, false, false, false, false, false],
  237. [true, true, true, false, false, false, false, false, false, false],
  238. [true, true, true, false, false, false, false, false, false, false],
  239. [true, true, true, true, true, true, true, true, true, true],
  240. [true, true, true, true, true, true, true, true, true, true],
  241. [true, true, true, true, true, true, true, true, true, true]
  242. ]
  243. ]
  244. };
  245. },
  246. watch: {
  247. value: {
  248. handler() {
  249. if (this.auto) {
  250. this.remake();
  251. }
  252. }
  253. },
  254. size: {
  255. handler() {
  256. if (this.auto) {
  257. this.remake();
  258. }
  259. }
  260. },
  261. options: {
  262. handler() {
  263. if (this.auto) {
  264. this.remake();
  265. }
  266. },
  267. deep: true
  268. },
  269. makeing: {
  270. handler(val) {
  271. if (!val) {
  272. if (typeof this.toTempFilePathDelegate === 'function') {
  273. this.toTempFilePathDelegate();
  274. }
  275. }
  276. }
  277. },
  278. isQueueLoadImage: {
  279. handler(val) {
  280. UQRCode.isQueueLoadImage = val;
  281. },
  282. immediate: true
  283. }
  284. },
  285. mounted() {
  286. this.templateOptions.size = this.sizeUnit == 'rpx' ? uni.upx2px(this.size) : this.size;
  287. this.templateOptions.canvasWidth = this.templateOptions.size;
  288. this.templateOptions.canvasHeight = this.templateOptions.size;
  289. if (this.type == '2d') {
  290. // #ifndef MP-WEIXIN
  291. this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
  292. this.templateOptions.canvasHeight})`;
  293. // #endif
  294. } else {
  295. this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
  296. this.templateOptions.canvasHeight})`;
  297. }
  298. if (this.start) {
  299. this.make();
  300. }
  301. },
  302. methods: {
  303. /**
  304. * 获取模板选项
  305. */
  306. getTemplateOptions() {
  307. var size = this.sizeUnit == 'rpx' ? uni.upx2px(this.size) : this.size;
  308. return UQRCode.deepReplace(this.templateOptions, {
  309. size,
  310. width: size,
  311. height: size
  312. });
  313. },
  314. /**
  315. * 获取插件选项
  316. */
  317. getUqrcodeOptions() {
  318. return UQRCode.deepReplace(this.options, {
  319. data: String(this.value),
  320. size: Number(this.templateOptions.size)
  321. });
  322. },
  323. /**
  324. * 重置画布
  325. */
  326. resetCanvas(callback) {
  327. this.templateOptions.canvasDisplay = false;
  328. this.$nextTick(() => {
  329. this.templateOptions.canvasDisplay = true;
  330. this.$nextTick(() => {
  331. callback && callback();
  332. });
  333. });
  334. },
  335. /**
  336. * 绘制二维码
  337. */
  338. async draw(callback = {}, isDrawDelegate = false) {
  339. if (typeof callback.success != 'function') {
  340. callback.success = () => {};
  341. }
  342. if (typeof callback.fail != 'function') {
  343. callback.fail = () => {};
  344. }
  345. if (typeof callback.complete != 'function') {
  346. callback.complete = () => {};
  347. }
  348. if (this.drawing) {
  349. if (!isDrawDelegate) {
  350. this.drawDelegate = () => {
  351. this.draw(callback, true);
  352. };
  353. return;
  354. }
  355. } else {
  356. this.drawing = true;
  357. }
  358. this.inError = false;
  359. if (!this.canvasId) {
  360. console.error('[uQRCode]: canvasId must be set!');
  361. this.inError = true;
  362. callback.fail({
  363. errMsg: '[uQRCode]: canvasId must be set!'
  364. });
  365. return;
  366. }
  367. if (!this.value) {
  368. console.error('[uQRCode]: value must be set!');
  369. this.inError = true;
  370. callback.fail({
  371. errMsg: '[uQRCode]: value must be set!'
  372. });
  373. return;
  374. }
  375. /* 组件数据 */
  376. this.templateOptions = this.getTemplateOptions();
  377. /* uQRCode选项 */
  378. this.uqrcodeOptions = this.getUqrcodeOptions();
  379. /* 纠错等级兼容字母写法 */
  380. if (typeof this.uqrcodeOptions.errorCorrectLevel === 'string') {
  381. this.uqrcodeOptions.errorCorrectLevel = UQRCode.errorCorrectLevel[this.uqrcodeOptions.errorCorrectLevel];
  382. }
  383. /* nvue不支持动态修改gcanvas尺寸,除nvue外,默认使用useDynamicSize */
  384. // #ifndef APP-NVUE
  385. if (typeof this.options.useDynamicSize === 'undefined') {
  386. this.uqrcodeOptions.useDynamicSize = true;
  387. }
  388. // #endif
  389. // #ifdef APP-NVUE
  390. this.uqrcodeOptions.useDynamicSize = false;
  391. // #endif
  392. /* 获取uQRCode实例 */
  393. const qr = new UQRCode();
  394. /* 设置uQRCode选项 */
  395. qr.setOptions(this.uqrcodeOptions);
  396. /* 调用制作二维码方法 */
  397. qr.make();
  398. /* 获取canvas上下文 */
  399. let canvasContext = null;
  400. // #ifndef APP-NVUE
  401. if (this.type === '2d') {
  402. // #ifdef MP-WEIXIN
  403. /* 微信小程序获取canvas2d上下文方式 */
  404. const canvas = (this.canvas = await new Promise(resolve => {
  405. uni
  406. .createSelectorQuery()
  407. .in(this) // 在组件内使用需要
  408. .select(`#${this.canvasId}`)
  409. .fields({
  410. node: true,
  411. size: true
  412. })
  413. .exec(res => {
  414. resolve(res[0].node);
  415. });
  416. }));
  417. canvasContext = this.canvasContext = canvas.getContext('2d');
  418. /* 2d的组件设置宽高与实际canvas绘制宽高不是一个,打个比方,组件size=200,canvas.width设置为100,那么绘制出来就是100=200,组件size=400,canvas.width设置为800,绘制大小还是800=400,所以无需理会下方返回的dynamicSize是多少,按dpr重新赋值给canvas即可 */
  419. this.templateOptions.canvasWidth = qr.size;
  420. this.templateOptions.canvasHeight = qr.size;
  421. this.templateOptions.canvasTransform = '';
  422. /* 使用dynamicSize+scale,可以解决小块间出现白线问题,dpr可以解决模糊问题 */
  423. const dpr = uni.getSystemInfoSync().pixelRatio;
  424. canvas.width = qr.dynamicSize * dpr;
  425. canvas.height = qr.dynamicSize * dpr;
  426. canvasContext.scale(dpr, dpr);
  427. /* 微信小程序获取图像方式,多个组件type默认和2d混用导致loadImage被替换,从而获取图像失败,导致报错:Unhandled promise rejection RangeError: Maximum call stack size exceeded,后续优化一下,现在只能统一所有组件的type */
  428. UQRCode.loadImage = function(src) {
  429. /* 小程序下获取网络图片信息需先配置download域名白名单才能生效 */
  430. return new Promise((resolve, reject) => {
  431. const img = canvas.createImage();
  432. img.src = src;
  433. img.onload = () => {
  434. resolve(img);
  435. };
  436. img.onerror = err => {
  437. reject(err);
  438. };
  439. });
  440. };
  441. // #endif
  442. // #ifndef MP-WEIXIN
  443. /* 非微信小程序不支持2d,切换回uniapp获取canvas上下文方式 */
  444. canvasContext = this.canvasContext = uni.createCanvasContext(this.canvasId, this);
  445. /* 使用dynamicSize,可以解决小块间出现白线问题,再通过scale缩放至size,使其达到所设尺寸 */
  446. this.templateOptions.canvasWidth = qr.dynamicSize;
  447. this.templateOptions.canvasHeight = qr.dynamicSize;
  448. this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
  449. this.templateOptions.canvasHeight})`;
  450. /* uniapp获取图像方式 */
  451. UQRCode.loadImage = function(src) {
  452. return new Promise((resolve, reject) => {
  453. if (src.startsWith('http')) {
  454. uni.getImageInfo({
  455. src,
  456. success: res => {
  457. resolve(res.path);
  458. },
  459. fail: err => {
  460. reject(err);
  461. }
  462. });
  463. } else {
  464. if (src.startsWith('.')) {
  465. console.error('[uQRCode]: 本地图片路径仅支持绝对路径!');
  466. throw new Error('[uQRCode]: local image path only supports absolute path!');
  467. } else {
  468. resolve(src);
  469. }
  470. }
  471. });
  472. };
  473. // #endif
  474. } else {
  475. /* uniapp获取canvas上下文方式 */
  476. canvasContext = this.canvasContext = uni.createCanvasContext(this.canvasId, this);
  477. /* 使用dynamicSize,可以解决小块间出现白线问题,再通过scale缩放至size,使其达到所设尺寸 */
  478. this.templateOptions.canvasWidth = qr.dynamicSize;
  479. this.templateOptions.canvasHeight = qr.dynamicSize;
  480. this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
  481. this.templateOptions.canvasHeight})`;
  482. /* uniapp获取图像方式 */
  483. UQRCode.loadImage = function(src) {
  484. return new Promise((resolve, reject) => {
  485. /* getImageInfo在微信小程序的bug:本地路径返回路径会把开头的/或../移除,导致路径错误,解决方法:限制只能使用绝对路径 */
  486. if (src.startsWith('http')) {
  487. uni.getImageInfo({
  488. src,
  489. success: res => {
  490. resolve(res.path);
  491. },
  492. fail: err => {
  493. reject(err);
  494. }
  495. });
  496. } else {
  497. if (src.startsWith('.')) {
  498. console.error('[uQRCode]: 本地图片路径仅支持绝对路径!');
  499. throw new Error('[uQRCode]: local image path only supports absolute path!');
  500. } else {
  501. resolve(src);
  502. }
  503. }
  504. });
  505. };
  506. }
  507. // #endif
  508. // #ifdef APP-NVUE
  509. /* NVue获取canvas上下文方式 */
  510. const gcanvas = this.$refs['gcanvas'];
  511. const canvas = enable(gcanvas, {
  512. bridge: WeexBridge
  513. });
  514. canvasContext = this.canvasContext = canvas.getContext('2d');
  515. /* NVue获取图像方式 */
  516. UQRCode.loadImage = function(src) {
  517. return new Promise((resolve, reject) => {
  518. /* getImageInfo在nvue的bug:获取同一个路径的图片信息,同一时间第一次获取成功,后续失败,猜测是写入本地时产生文件写入冲突,所以没有返回,特别是对于网络资源 --- js部分已实现队列绘制,已解决此问题 */
  519. if (src.startsWith('http')) {
  520. uni.getImageInfo({
  521. src,
  522. success: res => {
  523. resolve(res.path);
  524. },
  525. fail: err => {
  526. reject(err);
  527. }
  528. });
  529. } else {
  530. if (src.startsWith('.')) {
  531. console.error('[uQRCode]: 本地图片路径仅支持绝对路径!');
  532. throw new Error('[uQRCode]: local image path only supports absolute path!');
  533. } else {
  534. resolve(src);
  535. }
  536. }
  537. });
  538. };
  539. // #endif
  540. /* 设置uQRCode实例的canvas上下文 */
  541. qr.canvasContext = canvasContext;
  542. /* 延时等待页面重新绘制完毕 */
  543. setTimeout(() => {
  544. /* 调用绘制方法将二维码图案绘制到canvas上 */
  545. qr.drawCanvas()
  546. .then(() => {
  547. if (this.drawDelegate) {
  548. /* 高频重绘纠正 */
  549. let delegate = this.drawDelegate;
  550. this.drawDelegate = undefined;
  551. delegate();
  552. } else {
  553. this.drawing = false;
  554. callback.success();
  555. }
  556. })
  557. .catch(err => {
  558. if (this.drawDelegate) {
  559. /* 高频重绘纠正 */
  560. let delegate = this.drawDelegate;
  561. this.drawDelegate = undefined;
  562. delegate();
  563. } else {
  564. this.drawing = false;
  565. this.inError = true;
  566. callback.fail(err);
  567. }
  568. })
  569. .finally(() => {
  570. callback.complete();
  571. });
  572. }, 300);
  573. },
  574. /**
  575. * 生成二维码
  576. */
  577. make(callback = {}) {
  578. this.makeing = true;
  579. if (typeof callback.success != 'function') {
  580. callback.success = () => {};
  581. }
  582. if (typeof callback.fail != 'function') {
  583. callback.fail = () => {};
  584. }
  585. if (typeof callback.complete != 'function') {
  586. callback.complete = () => {};
  587. }
  588. this.resetCanvas(() => {
  589. clearTimeout(this.makeDelegate);
  590. this.makeDelegate = setTimeout(() => {
  591. this.draw({
  592. success: () => {
  593. setTimeout(() => {
  594. callback.success();
  595. this.complete(true);
  596. }, 300);
  597. },
  598. fail: err => {
  599. callback.fail(err);
  600. this.complete(false, err.errMsg);
  601. },
  602. complete: () => {
  603. callback.complete();
  604. this.makeing = false;
  605. }
  606. });
  607. }, 300);
  608. });
  609. },
  610. /**
  611. * 重新生成
  612. */
  613. remake(callback) {
  614. this.$emit('change');
  615. this.make(callback);
  616. },
  617. /**
  618. * 生成完成
  619. */
  620. complete(success = true, errMsg = '') {
  621. if (success) {
  622. this.$emit('complete', {
  623. success
  624. });
  625. } else {
  626. this.$emit('complete', {
  627. success,
  628. errMsg
  629. });
  630. }
  631. },
  632. /**
  633. * 导出临时路径
  634. */
  635. toTempFilePath(callback = {}) {
  636. if (typeof callback.success != 'function') {
  637. callback.success = () => {};
  638. }
  639. if (typeof callback.fail != 'function') {
  640. callback.fail = () => {};
  641. }
  642. if (typeof callback.complete != 'function') {
  643. callback.complete = () => {};
  644. }
  645. if (this.makeing) {
  646. /* 如果还在生成状态,那当前操作将托管到委托,监听生成完成后再通过委托复调当前方法 */
  647. this.toTempFilePathDelegate = () => {
  648. this.toTempFilePath(callback);
  649. };
  650. return;
  651. } else {
  652. this.toTempFilePathDelegate = null;
  653. }
  654. // #ifndef APP-NVUE
  655. if (this.type === '2d') {
  656. // #ifdef MP-WEIXIN
  657. /* 需要将 data:image/png;base64, 这段去除 writeFile 才能正常打开文件,否则是损坏文件,无法打开*/
  658. const reg = new RegExp('^data:image/png;base64,', 'g');
  659. // #ifdef VUE3
  660. const dataURL = toRaw(this.canvas)
  661. .toDataURL()
  662. .replace(reg, '');
  663. // #endif
  664. // #ifndef VUE3
  665. const dataURL = this.canvas.toDataURL().replace(reg, '');
  666. // #endif
  667. const fs = wx.getFileSystemManager();
  668. const tempFilePath = `${wx.env.USER_DATA_PATH}/${new Date().getTime()}${
  669. Math.random()
  670. .toString()
  671. .split('.')[1]
  672. }.png`;
  673. fs.writeFile({
  674. filePath: tempFilePath, // 要写入的文件路径 (本地路径)
  675. data: dataURL, // base64图片
  676. encoding: 'base64',
  677. success: res => {
  678. callback.success({
  679. tempFilePath
  680. });
  681. },
  682. fail: err => {
  683. callback.fail(err);
  684. },
  685. complete: () => {
  686. callback.complete();
  687. }
  688. });
  689. // #endif
  690. } else {
  691. uni.canvasToTempFilePath(
  692. {
  693. canvasId: this.canvasId,
  694. fileType: this.fileType,
  695. width: Number(this.templateOptions.canvasWidth),
  696. height: Number(this.templateOptions.canvasHeight),
  697. destWidth: Number(this.templateOptions.size),
  698. destHeight: Number(this.templateOptions.size),
  699. success: res => {
  700. callback.success(res);
  701. },
  702. fail: err => {
  703. callback.fail(err);
  704. },
  705. complete: () => {
  706. callback.complete();
  707. }
  708. },
  709. this
  710. );
  711. }
  712. // #endif
  713. // #ifdef APP-NVUE
  714. /* 测试过程中,size需要乘以3才能保存完整,3又对应dpr,猜测是与像素比有关,故乘以3。第一次运行无法保存,后续正常,待排查。 */
  715. const dpr = uni.getSystemInfoSync().pixelRatio;
  716. this.canvasContext.toTempFilePath(
  717. 0,
  718. 0,
  719. this.templateOptions.canvasWidth * dpr,
  720. this.templateOptions.canvasHeight * dpr,
  721. this.templateOptions.size * dpr,
  722. this.templateOptions.size * dpr,
  723. '',
  724. 1,
  725. res => {
  726. callback.success(res);
  727. callback.complete(res);
  728. }
  729. );
  730. // #endif
  731. },
  732. /**
  733. * 保存
  734. */
  735. save(callback = {}) {
  736. if (typeof callback.success != 'function') {
  737. callback.success = () => {};
  738. }
  739. if (typeof callback.fail != 'function') {
  740. callback.fail = () => {};
  741. }
  742. if (typeof callback.complete != 'function') {
  743. callback.complete = () => {};
  744. }
  745. this.toTempFilePath({
  746. success: res => {
  747. // #ifndef H5
  748. uni.saveImageToPhotosAlbum({
  749. filePath: res.tempFilePath,
  750. success: res1 => {
  751. callback.success(res1);
  752. },
  753. fail: err1 => {
  754. callback.fail(err1);
  755. },
  756. complete: () => {
  757. callback.complete();
  758. }
  759. });
  760. // #endif
  761. // #ifdef H5
  762. /* 可以在电脑浏览器下载,移动端iOS不行,安卓微信浏览器不行,安卓外部浏览器可以 */
  763. this.isH5Save = true;
  764. this.tempFilePath = res.tempFilePath;
  765. const aEle = document.createElement('a');
  766. aEle.download = 'uQRCode'; // 设置下载的文件名,默认是'下载'
  767. aEle.href = res.tempFilePath;
  768. document.body.appendChild(aEle);
  769. aEle.click();
  770. aEle.remove(); // 下载之后把创建的元素删除
  771. callback.success({
  772. errMsg: 'ok'
  773. });
  774. callback.complete();
  775. // #endif
  776. },
  777. fail: err => {
  778. callback.fail(err);
  779. callback.complete();
  780. }
  781. });
  782. },
  783. /**
  784. * 注册click事件
  785. */
  786. onClick(e) {
  787. this.$emit('click', e);
  788. }
  789. }
  790. };
  791. </script>
  792. <style scoped>
  793. .uqrcode {
  794. position: relative;
  795. }
  796. .uqrcode-hide {
  797. position: fixed;
  798. left: 7500rpx;
  799. }
  800. .uqrcode-canvas {
  801. transform-origin: top left;
  802. }
  803. .uqrcode-makeing {
  804. position: absolute;
  805. top: 0;
  806. right: 0;
  807. bottom: 0;
  808. left: 0;
  809. z-index: 10;
  810. /* #ifndef APP-NVUE */
  811. display: flex;
  812. /* #endif */
  813. justify-content: center;
  814. align-items: center;
  815. }
  816. .uqrcode-makeing-image {
  817. /* #ifndef APP-NVUE */
  818. display: block;
  819. max-width: 120px;
  820. max-height: 120px;
  821. /* #endif */
  822. }
  823. .uqrcode-error {
  824. position: absolute;
  825. top: 0;
  826. right: 0;
  827. bottom: 0;
  828. left: 0;
  829. /* #ifndef APP-NVUE */
  830. display: flex;
  831. /* #endif */
  832. justify-content: center;
  833. align-items: center;
  834. }
  835. .uqrcode-error-message {
  836. font-size: 12px;
  837. color: #939291;
  838. }
  839. /* #ifdef H5 */
  840. .uqrcode-h5-save {
  841. position: fixed;
  842. top: 0;
  843. right: 0;
  844. bottom: 0;
  845. left: 0;
  846. z-index: 100;
  847. background-color: rgba(0, 0, 0, 0.68);
  848. display: flex;
  849. flex-direction: column;
  850. justify-content: center;
  851. align-items: center;
  852. }
  853. .uqrcode-h5-save-image {
  854. width: 512rpx;
  855. height: 512rpx;
  856. padding: 32rpx;
  857. }
  858. .uqrcode-h5-save-text {
  859. margin-top: 20rpx;
  860. font-size: 32rpx;
  861. font-weight: 700;
  862. color: #ffffff;
  863. }
  864. .uqrcode-h5-save-close {
  865. position: relative;
  866. margin-top: 72rpx;
  867. width: 40rpx;
  868. height: 40rpx;
  869. border: 2rpx solid #ffffff;
  870. border-radius: 40rpx;
  871. padding: 10rpx;
  872. }
  873. .uqrcode-h5-save-close-before {
  874. position: absolute;
  875. top: 50%;
  876. transform: translateY(-50%) rotate(45deg);
  877. width: 40rpx;
  878. height: 4rpx;
  879. background: #ffffff;
  880. }
  881. .uqrcode-h5-save-close-after {
  882. position: absolute;
  883. top: 50%;
  884. transform: translateY(-50%) rotate(-45deg);
  885. width: 40rpx;
  886. height: 4rpx;
  887. background: #ffffff;
  888. }
  889. /* #endif */
  890. </style>