25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 

436 satır
13 KiB

  1. <template>
  2. <view class="u-qrcode"
  3. :id="rootId"
  4. :style="{
  5. width: useRootHeightAndWidth ? '100%' : 'auto',
  6. height: useRootHeightAndWidth ? '100%' : 'auto',
  7. }"
  8. @longpress="longpress">
  9. <view class="u-qrcode__content" @click="preview">
  10. <up-canvas
  11. ref="qrcodeCanvas"
  12. class="u-qrcode__canvas"
  13. :canvas-id="cid"
  14. :width="sizeLocal"
  15. :height="sizeLocal"
  16. :unit="unit"
  17. bg-color="transparent"
  18. :style="{ width: sizeLocal + unit, height: sizeLocal + unit }" />
  19. <view v-if="showLoading && loading" class="u-qrcode__loading"
  20. :style="{ width: sizeLocal + unit, height: sizeLocal + unit }">
  21. <up-loading-icon vertical :text="loadingText" textSize="14px"></up-loading-icon>
  22. </view>
  23. </view>
  24. </view>
  25. </template>
  26. <script>
  27. import QRCode from "./qrcode.js"
  28. let qrcode
  29. export default {
  30. name: "u-qrcode",
  31. props: {
  32. cid: {
  33. type: String,
  34. default: () => {
  35. return `u-qrcode-canvas${Math.floor(Math.random() * 1000000)}`
  36. }
  37. },
  38. size: {
  39. type: Number,
  40. default: 200
  41. },
  42. unit: {
  43. type: String,
  44. default: 'px'
  45. },
  46. show: {
  47. type: Boolean,
  48. default: true
  49. },
  50. val: {
  51. type: String,
  52. default: ''
  53. },
  54. background: {
  55. type: String,
  56. default: '#ffffff'
  57. },
  58. foreground: {
  59. type: String,
  60. default: '#000000'
  61. },
  62. pdground: {
  63. type: String,
  64. default: '#000000'
  65. },
  66. icon: {
  67. type: String,
  68. default: ''
  69. },
  70. iconSize: {
  71. type: Number,
  72. default: 40
  73. },
  74. lv: {
  75. type: Number,
  76. default: 3
  77. },
  78. quietZone: {
  79. type: Number,
  80. default: 0
  81. },
  82. onval: {
  83. type: Boolean,
  84. default: true
  85. },
  86. loadMake: {
  87. type: Boolean,
  88. default: true
  89. },
  90. usingComponents: {
  91. type: Boolean,
  92. default: true
  93. },
  94. showLoading: {
  95. type: Boolean,
  96. default: true
  97. },
  98. loadingText: {
  99. type: String,
  100. default: '生成中'
  101. },
  102. allowPreview: {
  103. type: Boolean,
  104. default: false
  105. },
  106. // 是否使用根节点宽高
  107. useRootHeightAndWidth: {
  108. type: Boolean,
  109. default: () => false
  110. },
  111. },
  112. emits: ['preview', 'result', 'longpressCallback'],
  113. data() {
  114. return {
  115. loading: false,
  116. result: '',
  117. popupShow: false,
  118. list: [
  119. {
  120. name: '保存二维码',
  121. }
  122. ],
  123. rootId: `rootId${Number(Math.random() * 100).toFixed(0)}`,
  124. canvasObj: {},
  125. sizeLocal: this.size,
  126. ctx: null,
  127. isNvue: false,
  128. canvasHost: null
  129. }
  130. },
  131. async mounted(){
  132. // 如果使用根节点的宽高 则 重新设置 size
  133. if(this.useRootHeightAndWidth){
  134. await this.setNewSize()
  135. }
  136. // #ifdef APP-NVUE
  137. this.isNvue = true
  138. // #endif
  139. await this.initCanvas()
  140. if (this.loadMake) {
  141. if (!this._empty(this.val)) {
  142. setTimeout(() => {
  143. setTimeout(()=>{
  144. this._makeCode()
  145. })
  146. }, 0);
  147. }
  148. }
  149. },
  150. methods: {
  151. async _makeCode() {
  152. let that = this
  153. if (!this.ctx) {
  154. await this.initCanvas(true)
  155. }
  156. if (!this.ctx) return
  157. if (!this._empty(this.val)) {
  158. // #ifndef APP-NVUE
  159. this.loading = true
  160. // #endif
  161. qrcode = new QRCode({
  162. vuectx: that, // 上下文环境
  163. canvasId: that.cid, // canvas-id
  164. ctx: that.ctx,
  165. canvasHost: that.canvasHost,
  166. isNvue: that.isNvue,
  167. usingComponents: that.usingComponents, // 是否是自定义组件
  168. showLoading: false, // 是否显示loading
  169. loadingText: that.loadingText, // loading文字
  170. text: that.val, // 生成内容
  171. size: that.sizeLocal, // 二维码大小
  172. width: that.sizeLocal,
  173. height: that.sizeLocal,
  174. background: that.background, // 背景色
  175. foreground: that.foreground, // 前景色
  176. pdground: that.pdground, // 定位角点颜色
  177. quietZone: that.quietZone, // 静区宽度
  178. correctLevel: that.lv, // 容错级别
  179. image: that.icon, // 二维码图标
  180. imageSize: that.iconSize,// 二维码图标大小
  181. cbResult: function (res) { // 生成二维码的回调
  182. that._result(res)
  183. },
  184. });
  185. } else {
  186. uni.showToast({
  187. title: '二维码内容不能为空',
  188. icon: 'none',
  189. duration: 2000
  190. });
  191. }
  192. },
  193. _clearCode() {
  194. this._result('')
  195. if (qrcode) qrcode.clear()
  196. },
  197. _saveCode() {
  198. let that = this;
  199. if (this.result != "") {
  200. uni.saveImageToPhotosAlbum({
  201. filePath: that.result,
  202. success: function () {
  203. uni.showToast({
  204. title: '二维码保存成功',
  205. icon: 'success',
  206. duration: 2000
  207. });
  208. }
  209. });
  210. } else {
  211. this.toTempFilePath({
  212. success: res => {
  213. that.result = res.tempFilePath
  214. uni.saveImageToPhotosAlbum({
  215. filePath: that.result,
  216. success: function () {
  217. uni.showToast({
  218. title: '二维码保存成功',
  219. icon: 'success',
  220. duration: 2000
  221. });
  222. }
  223. });
  224. },
  225. fail: err => {
  226. }
  227. })
  228. }
  229. },
  230. preview(e) {
  231. // 预览图片
  232. // console.log(this.result)
  233. if (this.allowPreview) {
  234. uni.previewImage({
  235. urls: [this.result],
  236. longPressActions: {
  237. itemList: ['保存二维码图片'],
  238. success: function(data) {
  239. // console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
  240. switch (data.tapIndex) {
  241. case 0:
  242. that._saveCode();
  243. break;
  244. }
  245. },
  246. fail: function(err) {
  247. console.log(err.errMsg);
  248. }
  249. }
  250. });
  251. }
  252. this.$emit('preview', {
  253. url: this.result
  254. }, e)
  255. },
  256. async toTempFilePath({success, fail}) {
  257. if (!this.canvasHost) {
  258. await this.initCanvas(true)
  259. }
  260. if (!this.canvasHost) {
  261. if (fail) fail(new Error('无法获取二维码画布实例'))
  262. return
  263. }
  264. this.canvasHost.toTempFilePath({
  265. width: this.sizeLocal,
  266. height: this.sizeLocal,
  267. destWidth: this.sizeLocal,
  268. destHeight: this.sizeLocal,
  269. success,
  270. fail
  271. }).catch(err => {
  272. if (fail) fail(err)
  273. })
  274. },
  275. async longpress() {
  276. this.toTempFilePath({
  277. success: res => {
  278. this.$emit('longpressCallback', res.tempFilePath)
  279. },
  280. fail: err => {
  281. }
  282. })
  283. },
  284. /**
  285. * 使用根节点宽高 设置新的size
  286. * @return {Promise<void>}
  287. */
  288. async setNewSize(){
  289. const rootNode = await this.getRootNode();
  290. if (!rootNode) return
  291. const { width , height } = rootNode;
  292. // 将最短的设置为二维码 的size
  293. if(width > height){
  294. this.sizeLocal = height
  295. }
  296. else{
  297. this.sizeLocal = width
  298. }
  299. },
  300. async getRootNode() {
  301. return new Promise((resolve, reject) => {
  302. try {
  303. const query = uni.createSelectorQuery().in(this).select(`#${this.rootId}`);
  304. query.fields({
  305. size: true
  306. })
  307. .exec((res) => {
  308. resolve(res[0] || false)
  309. })
  310. } catch (e) {
  311. console.error("获取二维码根节点失败", e)
  312. resolve(false)
  313. }
  314. })
  315. },
  316. async initCanvas(force = false) {
  317. await this.$nextTick()
  318. const canvasRef = this.$refs.qrcodeCanvas
  319. if (!canvasRef) return false
  320. await canvasRef.initCanvas(force)
  321. this.canvasHost = canvasRef
  322. this.ctx = canvasRef.getRawContext()
  323. return !!this.ctx
  324. },
  325. getUPCanvasContext() {
  326. return this.canvasHost ? this.canvasHost.getRawContext() : null
  327. },
  328. async drawImage(url, x, y, w, h) {
  329. try {
  330. if (!this.canvasHost) {
  331. await this.initCanvas(true)
  332. }
  333. if (this.canvasHost) {
  334. await this.canvasHost.drawImage(url, x, y, w, h)
  335. }
  336. } catch (error) {
  337. console.log('drawImage绘制出错', error)
  338. }
  339. },
  340. selectClick(index) {
  341. switch (index) {
  342. case 0:
  343. alert('保存二维码')
  344. this._saveCode();
  345. break;
  346. }
  347. },
  348. _result(res) {
  349. this.loading = false;
  350. this.result = res;
  351. this.$emit('result', res);
  352. },
  353. _empty(v) {
  354. let tp = typeof v,
  355. rt = false;
  356. if (tp == "number" && String(v) == "") {
  357. rt = true
  358. } else if (tp == "undefined") {
  359. rt = true
  360. } else if (tp == "object") {
  361. if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
  362. } else if (tp == "string") {
  363. if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
  364. } else if (tp == "function") {
  365. rt = false
  366. }
  367. return rt
  368. },
  369. },
  370. watch: {
  371. size: function (n, o) {
  372. if (n != o && !this._empty(n)) {
  373. this.cSize = n
  374. if (!this._empty(this.val)) {
  375. setTimeout(() => {
  376. this._makeCode()
  377. }, 100);
  378. }
  379. }
  380. },
  381. val: function (n, o) {
  382. if (this.onval) {
  383. if (n != o && !this._empty(n)) {
  384. setTimeout(() => {
  385. this._makeCode()
  386. }, 0);
  387. }
  388. }
  389. }
  390. },
  391. computed: {
  392. }
  393. }
  394. </script>
  395. <style lang="scss" scoped>
  396. .u-qrcode {
  397. &__loading {
  398. display: flex;
  399. justify-content: center;
  400. align-items: center;
  401. background-color: #f7f7f7;
  402. position: absolute;
  403. top: 0;
  404. bottom: 0;
  405. left: 0;
  406. right: 0;
  407. }
  408. /* #ifdef MP-TOUTIAO */
  409. /**字节小程序在编译时会出现一个 [hidde]:{ display: none !important; } 这个样式
  410. * 会导致canvas 隐藏掉 没有找到具体原因先这样处理
  411. */
  412. &__canvas {
  413. display: block !important;
  414. }
  415. /* #endif */
  416. &__content {
  417. position: relative;
  418. &__canvas {
  419. position: fixed;
  420. top: -99999rpx;
  421. left: -99999rpx;
  422. z-index: -99999;
  423. }
  424. }
  425. }
  426. </style>