liyongli 1 year ago
parent
commit
4db190906a
4 changed files with 290 additions and 50 deletions
  1. 1 0
      package.json
  2. 52 24
      pnpm-lock.yaml
  3. 125 10
      src/utils/tool.js
  4. 112 16
      src/view/finance/index.vue

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
     "axios": "^1.2.1",
     "core-js": "^3.26.1",
     "element-plus": "^2.2.26",
+    "qrcode": "^1.5.3",
     "vant": "4.0.2",
     "vue": "^3.2.45"
   },

+ 52 - 24
pnpm-lock.yaml

@@ -17,6 +17,9 @@ dependencies:
   element-plus:
     specifier: ^2.2.26
     version: 2.2.26(vue@3.2.45)
+  qrcode:
+    specifier: ^1.5.3
+    version: 1.5.3
   vant:
     specifier: 4.0.2
     version: 4.0.2(vue@3.2.45)
@@ -2746,7 +2749,6 @@ packages:
   /ansi-regex@5.0.1:
     resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
     engines: {node: '>=8'}
-    dev: true
 
   /ansi-styles@3.2.1:
     resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
@@ -2760,7 +2762,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       color-convert: 2.0.1
-    dev: true
 
   /any-promise@1.3.0:
     resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
@@ -3396,7 +3397,6 @@ packages:
   /camelcase@5.3.1:
     resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
     engines: {node: '>=6'}
-    dev: true
 
   /camelcase@6.3.0:
     resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
@@ -3591,7 +3591,6 @@ packages:
       string-width: 4.2.3
       strip-ansi: 6.0.1
       wrap-ansi: 6.2.0
-    dev: true
 
   /cliui@7.0.4:
     resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
@@ -3643,7 +3642,6 @@ packages:
     engines: {node: '>=7.0.0'}
     dependencies:
       color-name: 1.1.4
-    dev: true
 
   /color-name@1.1.3:
     resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
@@ -3651,7 +3649,6 @@ packages:
 
   /color-name@1.1.4:
     resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
-    dev: true
 
   /color-string@1.9.1:
     resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
@@ -4308,7 +4305,6 @@ packages:
   /decamelize@1.2.0:
     resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
     engines: {node: '>=0.10.0'}
-    dev: true
 
   /decode-uri-component@0.2.2:
     resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
@@ -4459,6 +4455,10 @@ packages:
     engines: {node: '>= 8.0.0'}
     dev: true
 
+  /dijkstrajs@1.0.3:
+    resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
+    dev: false
+
   /dir-glob@2.2.2:
     resolution: {integrity: sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==}
     engines: {node: '>=4'}
@@ -4644,7 +4644,6 @@ packages:
 
   /emoji-regex@8.0.0:
     resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
-    dev: true
 
   /emojis-list@2.1.0:
     resolution: {integrity: sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==}
@@ -4656,6 +4655,10 @@ packages:
     engines: {node: '>= 4'}
     dev: true
 
+  /encode-utf8@1.0.3:
+    resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
+    dev: false
+
   /encodeurl@1.0.2:
     resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
     engines: {node: '>= 0.8'}
@@ -5314,7 +5317,6 @@ packages:
     dependencies:
       locate-path: 5.0.0
       path-exists: 4.0.0
-    dev: true
 
   /flat-cache@2.0.1:
     resolution: {integrity: sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==}
@@ -5501,7 +5503,6 @@ packages:
   /get-caller-file@2.0.5:
     resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
     engines: {node: 6.* || 8.* || >= 10.*}
-    dev: true
 
   /get-intrinsic@1.2.1:
     resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
@@ -6353,7 +6354,6 @@ packages:
   /is-fullwidth-code-point@3.0.0:
     resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
     engines: {node: '>=8'}
-    dev: true
 
   /is-glob@3.1.0:
     resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==}
@@ -6786,7 +6786,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       p-locate: 4.1.0
-    dev: true
 
   /lodash-es@4.17.21:
     resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
@@ -7548,7 +7547,6 @@ packages:
     engines: {node: '>=6'}
     dependencies:
       p-try: 2.2.0
-    dev: true
 
   /p-locate@3.0.0:
     resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
@@ -7562,7 +7560,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       p-limit: 2.3.0
-    dev: true
 
   /p-map@2.1.0:
     resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
@@ -7579,7 +7576,6 @@ packages:
   /p-try@2.2.0:
     resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
     engines: {node: '>=6'}
-    dev: true
 
   /pac-proxy-agent@5.0.0:
     resolution: {integrity: sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==}
@@ -7707,7 +7703,6 @@ packages:
   /path-exists@4.0.0:
     resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
     engines: {node: '>=8'}
-    dev: true
 
   /path-is-absolute@1.0.1:
     resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
@@ -7828,6 +7823,11 @@ packages:
     resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==}
     dev: true
 
+  /pngjs@5.0.0:
+    resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
+    engines: {node: '>=10.13.0'}
+    dev: false
+
   /pnp-webpack-plugin@1.7.0:
     resolution: {integrity: sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==}
     engines: {node: '>=6'}
@@ -8325,6 +8325,17 @@ packages:
     engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
     dev: true
 
+  /qrcode@1.5.3:
+    resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    dependencies:
+      dijkstrajs: 1.0.3
+      encode-utf8: 1.0.3
+      pngjs: 5.0.0
+      yargs: 15.4.1
+    dev: false
+
   /qs@6.11.0:
     resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
     engines: {node: '>=0.6'}
@@ -8582,11 +8593,9 @@ packages:
   /require-directory@2.1.1:
     resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
     engines: {node: '>=0.10.0'}
-    dev: true
 
   /require-main-filename@2.0.0:
     resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
-    dev: true
 
   /requires-port@1.0.0:
     resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
@@ -8886,7 +8895,6 @@ packages:
 
   /set-blocking@2.0.0:
     resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
-    dev: true
 
   /set-value@2.0.1:
     resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
@@ -9302,7 +9310,6 @@ packages:
       emoji-regex: 8.0.0
       is-fullwidth-code-point: 3.0.0
       strip-ansi: 6.0.1
-    dev: true
 
   /string.prototype.trim@1.2.7:
     resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==}
@@ -9364,7 +9371,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       ansi-regex: 5.0.1
-    dev: true
 
   /strip-eof@1.0.0:
     resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
@@ -10523,7 +10529,6 @@ packages:
 
   /which-module@2.0.1:
     resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
-    dev: true
 
   /which-typed-array@1.1.9:
     resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==}
@@ -10586,7 +10591,6 @@ packages:
       ansi-styles: 4.3.0
       string-width: 4.2.3
       strip-ansi: 6.0.1
-    dev: true
 
   /wrap-ansi@7.0.0:
     resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
@@ -10646,7 +10650,6 @@ packages:
 
   /y18n@4.0.3:
     resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
-    dev: true
 
   /y18n@5.0.8:
     resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
@@ -10672,6 +10675,14 @@ packages:
       decamelize: 1.2.0
     dev: true
 
+  /yargs-parser@18.1.3:
+    resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
+    engines: {node: '>=6'}
+    dependencies:
+      camelcase: 5.3.1
+      decamelize: 1.2.0
+    dev: false
+
   /yargs-parser@20.2.9:
     resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
     engines: {node: '>=10'}
@@ -10692,6 +10703,23 @@ packages:
       yargs-parser: 13.1.2
     dev: true
 
+  /yargs@15.4.1:
+    resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
+    engines: {node: '>=8'}
+    dependencies:
+      cliui: 6.0.0
+      decamelize: 1.2.0
+      find-up: 4.1.0
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      require-main-filename: 2.0.0
+      set-blocking: 2.0.0
+      string-width: 4.2.3
+      which-module: 2.0.1
+      y18n: 4.0.3
+      yargs-parser: 18.1.3
+    dev: false
+
   /yargs@16.2.0:
     resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
     engines: {node: '>=10'}

+ 125 - 10
src/utils/tool.js

@@ -5,18 +5,18 @@ import {
   isWindows,
   isMac,
   isWechat,
-} from "./isTerminal";
+} from './isTerminal';
 /**
  * 动态加载script
  * @param {string} url
  * @param {string} callback
  */
 export function loadScript(url, callback) {
-  let script = document.createElement("script");
+  let script = document.createElement('script');
   if (script.readyState) {
     // IE
     script.onreadystatechange = function () {
-      if (script.readyState === "loaded" || script.readyState === "complete") {
+      if (script.readyState === 'loaded' || script.readyState === 'complete') {
         script.onreadystatechange = null;
         callback();
       }
@@ -28,7 +28,7 @@ export function loadScript(url, callback) {
     };
   }
   script.src = url;
-  document.getElementsByTagName("head")[0].appendChild(script);
+  document.getElementsByTagName('head')[0].appendChild(script);
 }
 
 /**
@@ -44,12 +44,12 @@ export function getUser(next, isNotToLogin) {
   if (isIpad || isIpod || isIphone) {
     if (!window.webkit || !window.webkit.messageHandlers) return next && next();
     window.setUser = user => {
-      if (user == "{}" && !isNotToLogin)
+      if (user == '{}' && !isNotToLogin)
         return window.webkit.messageHandlers.iosJumpLogin.postMessage([]);
-      const u1 = JSON.parse(user || "{}");
+      const u1 = JSON.parse(user || '{}');
       window.webkit.messageHandlers.getAppInfo.postMessage([]);
       window.setAppInfo = userJson => {
-        const u2 = JSON.parse(userJson || "{}");
+        const u2 = JSON.parse(userJson || '{}');
         window.$shanshipin = {
           ...u1,
           ...u2,
@@ -61,9 +61,9 @@ export function getUser(next, isNotToLogin) {
     window.webkit.messageHandlers.tideGetUser.postMessage([]);
   } else {
     if (!window.TideApp) return next && next();
-    const u1 = JSON.parse(window.TideApp.getUser() || "{}");
+    const u1 = JSON.parse(window.TideApp.getUser() || '{}');
     if (!u1.UserId && !isNotToLogin) window.TideApp.login();
-    const u2 = JSON.parse(window.TideApp.getAppInfo() || "{}");
+    const u2 = JSON.parse(window.TideApp.getAppInfo() || '{}');
     window.$shanshipin = {
       ...u1,
       ...u2,
@@ -74,7 +74,7 @@ export function getUser(next, isNotToLogin) {
 
 /**
  * 帧动画
- * @returns 
+ * @returns
  */
 export function requestAnimationFrame() {
   return (
@@ -88,3 +88,118 @@ export function requestAnimationFrame() {
     }
   );
 }
+
+/**
+ * 通过canvas生成图片
+ */
+export async function canvasToImage(layers) {
+  let canvas = document.createElement('canvas');
+  canvas.width = layers.width;
+  canvas.height = layers.height;
+  let ctx = canvas.getContext('2d');
+  for (let i = 0; i < layers.list.length; i++) {
+    const v = layers.list[i];
+    /**
+     * 将图层绘制到canvas上
+     */
+    if (v.type === 'image') {
+      const img = await createDate(v.src);
+      ctx.drawImage(img, v.x, v.y, v.width, v.height);
+    } else if (v.type === 'new_image') {
+      ctx.drawImage(v.image, v.x, v.y, v.width, v.height);
+    } else if (v.type === 'text') {
+      ctx.font = v.font;
+      ctx.fillStyle = v.color;
+      ctx.fillText(v.text, v.x, v.y);
+    }
+  }
+  return {
+    image: canvas.toDataURL('image/png'),
+    width: layers.width,
+    height: layers.height,
+  };
+}
+
+function createDate(src) {
+  return new Promise((resolve, reject) => {
+    try {
+      const img = new Image();
+      img.src = src;
+      console.log(src)
+      img.setAttribute('crossOrigin', 'anonymous')
+      img.onload = () => {
+        resolve(img);
+      };
+    } catch (e) {
+      reject(e);
+    }
+  });
+}
+
+/**
+ * 高斯模糊
+ */
+export function gaussianBlur(imgData, radius) {
+  let pixels = imgData.data;
+  let width = imgData.width;
+  let height = imgData.height;
+  let length = Math.floor(radius * 2) + 1;
+  let arr = [];
+
+  // 创建高斯模糊核
+  for (let i = 0; i < length; i++) {
+    arr[i] =
+      Math.exp(-((i - radius) * (i - radius)) / (2 * radius * radius)) /
+      (Math.sqrt(2 * Math.PI) * radius);
+  }
+
+  // x方向模糊
+  for (let y = 0; y < height; y++) {
+    for (let x = 0; x < width; x++) {
+      let r = 0,
+        g = 0,
+        b = 0,
+        a = 0;
+      let wsum = 0;
+      for (let i = -radius; i <= radius; i++) {
+        let idx = (y * width + Math.min(width - 1, Math.max(0, x + i))) * 4;
+        r += pixels[idx] * arr[i + radius];
+        g += pixels[idx + 1] * arr[i + radius];
+        b += pixels[idx + 2] * arr[i + radius];
+        a += pixels[idx + 3] * arr[i + radius];
+        wsum += arr[i + radius];
+      }
+      let idx = (y * width + x) * 4;
+      pixels[idx] = r / wsum;
+      pixels[idx + 1] = g / wsum;
+      pixels[idx + 2] = b / wsum;
+      pixels[idx + 3] = a / wsum;
+    }
+  }
+
+  // y方向模糊
+  for (let x = 0; x < width; x++) {
+    for (let y = 0; y < height; y++) {
+      let r = 0,
+        g = 0,
+        b = 0,
+        a = 0;
+      let wsum = 0;
+      for (let i = -radius; i <= radius; i++) {
+        let idx = (Math.min(height - 1, Math.max(0, y + i)) * width + x) * 4;
+        r += pixels[idx] * arr[i + radius];
+        g += pixels[idx + 1] * arr[i + radius];
+        b += pixels[idx + 2] * arr[i + radius];
+        a += pixels[idx + 3] * arr[i + radius];
+        wsum += arr[i + radius];
+      }
+      let idx = (y * width + x) * 4;
+      pixels[idx] = r / wsum;
+      pixels[idx + 1] = g / wsum;
+      pixels[idx + 2] = b / wsum;
+      pixels[idx + 3] = a / wsum;
+    }
+  }
+
+  return imgData;
+}

+ 112 - 16
src/view/finance/index.vue

@@ -127,25 +127,15 @@
     <van-dialog
       v-model:show="hxrDialog"
       confirm-button-text="投票"
+      cancel-button-text="为他拉票"
       :show-cancel-button="true"
       @close="close"
+      @cancel="cancel"
       @confirm="shear_page"
       style="padding: 5px"
     >
-      <van-row>
-        <van-col span="5" style="text-align: center">
-          <van-image
-            round
-            width="3rem"
-            height="3rem"
-            :src="create_url_image(selectItem.workTitle)"
-          />
-        </van-col>
-        <van-col span="19">
-          <div class="van-ellipsis" v-text="selectItem.name"></div>
-          <div class="van-ellipsis unit" v-text="selectItem.university"></div>
-        </van-col>
-      </van-row>
+      <div class="van-ellipsis" style="margin: 0.5em 0;" v-text="selectItem.name"></div>
+      <div class="van-ellipsis unit" style="margin: 0.5em 0;" v-text="selectItem.university"></div>
       <div style="position: relative" @click="toDetal(selectItem.file)">
         <van-icon
           size="50"
@@ -164,10 +154,37 @@
         />
       </div>
     </van-dialog>
+
+    <div class="zhezhao" v-show="imgObj.image">
+      <div class="zhezhao_img">
+        <img :src="imgObj.image" style="width: 100%" />
+        <p style="text-align: center; color: #fff; margin: 1em 0">
+          长按保存图片,发给好友一起参与
+        </p>
+        <van-button
+          @click="
+            () => {
+              imgObj = {};
+            }
+          "
+          color="#fff"
+          style="
+            margin: 0 auto;
+            display: block;
+            border: none;
+            background-color: #ffffff00;
+          "
+          icon="cross"
+          type="default"
+          round
+        />
+      </div>
+    </div>
   </div>
 </template>
 <script setup>
 import { ref, reactive, watch, onMounted } from 'vue';
+import QRCode from 'qrcode';
 import { showDialog, showConfirmDialog, showToast } from 'vant';
 import {
   getEnrollList,
@@ -177,6 +194,7 @@ import {
   getSendVerifyCode,
   verifyCode,
 } from '@/api/SilkRoadSpringGala.js';
+import { canvasToImage } from '@/utils/tool.js';
 
 import Shanshipin from '@/components/shanshipin.vue';
 const theme = '#38379f';
@@ -189,6 +207,7 @@ const hxrDialog = ref(false);
 const showPaihang = ref(false);
 const showLogin = ref(false);
 const selectItem = ref({});
+const imgObj = ref({});
 
 const rankList = ref({});
 const orilist = ref([]);
@@ -245,7 +264,7 @@ const getList = call => {
       time_old = setTimeout(() => {
         clickShowVideo(item);
         clearTimeout(time_old);
-      }, 200);
+      }, 500);
     }
     ids.value = ids_list;
     orilist.value = obj;
@@ -312,6 +331,66 @@ const shear_page = () => {
 const toDetal = file => {
   location.href = file;
 };
+const cancel = async () => {
+  const w = 900 * window.devicePixelRatio;
+  const w1 = w * 0.8;
+  const fs = w / 20;
+  const bs = w / 15;
+  const title =
+    selectItem.value.name.length >= 16
+      ? selectItem.value.name.split('').splice(0, 13).join('') + '...'
+      : selectItem.value.name;
+  const qt = await QRCode.toDataURL(
+    [location.host, '/finance.html?id=', selectItem.value.id].join('')
+  );
+  const layers = {
+    list: [
+      {
+        type: 'image',
+        src: 'https://cxzx.smcic.net/topic/tool/img/0.jpg',
+        width: w,
+        height: w,
+        x: 0,
+        y: 0,
+      },
+      {
+        type: 'image',
+        src: create_url_image(selectItem.value.workTitle),
+        width: w1,
+        height: (w1 / 16) * 9,
+        x: w / 10,
+        y: bs,
+      },
+      {
+        type: 'text',
+        color: '#ffffff',
+        font: fs + 'px Arial',
+        text: title,
+        x: w / 10,
+        y: (w1 / 16) * 9 + bs * 2,
+      },
+      {
+        type: 'text',
+        color: '#ffffff',
+        font: fs + 'px Arial',
+        text: '快来为我投上一票把!',
+        x: w / 10,
+        y: (w1 / 16) * 9 + bs * 3,
+      },
+      {
+        type: 'image',
+        src: qt,
+        width: w / 5,
+        height: w / 5,
+        x: w1 + bs - w / 5,
+        y: (w1 / 16) * 9 + bs * 4,
+      },
+    ],
+    width: w,
+    height: w,
+  };
+  imgObj.value = await canvasToImage(layers);
+};
 
 let time = null;
 watch(search, res => {
@@ -465,7 +544,6 @@ const clickShowVideo = item => {
     selectItem.value.id +
     'v=' +
     Date.now();
-  console.log(window.wx);
   if (window.wx.updateAppMessageShareData)
     window.wx.updateAppMessageShareData({
       title: selectItem.value.name, // 分享标题
@@ -581,5 +659,23 @@ const close = () => {
     font-size: 14px;
     color: #646566;
   }
+
+  .zhezhao {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100vw;
+    height: 100vh;
+    background-color: rgba(0, 0, 0, 0.8);
+    z-index: 10;
+
+    .zhezhao_img {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      width: 80vw;
+    }
+  }
 }
 </style>