Bladeren bron

feat: 添加二维码控件

jiaxing.liao 1 maand geleden
bovenliggende
commit
79f78d03de

+ 2 - 0
package.json

@@ -28,6 +28,7 @@
     "@vueuse/components": "^14.0.0",
     "@vueuse/core": "^14.0.0",
     "@zumer/snapdom": "^2.0.2",
+    "barcode": "^0.1.0",
     "deep-diff": "^1.0.2",
     "element-plus": "^2.11.4",
     "fs-extra": "^11.3.2",
@@ -38,6 +39,7 @@
     "monaco-editor": "^0.54.0",
     "normalize.css": "^8.0.1",
     "pinia": "^3.0.3",
+    "qrcode": "^1.5.4",
     "reka-ui": "^2.6.0",
     "simple-mind-map": "0.14.0-fix.1",
     "uuid": "^13.0.0",

+ 195 - 0
pnpm-lock.yaml

@@ -29,6 +29,9 @@ importers:
       '@zumer/snapdom':
         specifier: ^2.0.2
         version: 2.0.2
+      barcode:
+        specifier: ^0.1.0
+        version: 0.1.0
       deep-diff:
         specifier: ^1.0.2
         version: 1.0.2
@@ -59,6 +62,9 @@ importers:
       pinia:
         specifier: ^3.0.3
         version: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))
+      qrcode:
+        specifier: ^1.5.4
+        version: 1.5.4
       reka-ui:
         specifier: ^2.6.0
         version: 2.6.0(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))
@@ -1376,6 +1382,12 @@ packages:
     resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
     engines: {node: '>=10'}
 
+  array-parallel@0.1.3:
+    resolution: {integrity: sha512-TDPTwSWW5E4oiFiKmz6RGJ/a80Y91GuLgUYuLd49+XBS75tYo8PNgaT2K/OxuQYqkoI852MDGBorg9OcUSTQ8w==}
+
+  array-series@0.1.5:
+    resolution: {integrity: sha512-L0XlBwfx9QetHOsbLDrE/vh2t018w9462HM3iaFfxRiK83aJjAt/Ja3NMkOW7FICwWTlQBa3ZbL5FKhuQWkDrg==}
+
   assert-plus@1.0.0:
     resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
     engines: {node: '>=0.8'}
@@ -1404,6 +1416,9 @@ packages:
   balanced-match@1.0.2:
     resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
 
+  barcode@0.1.0:
+    resolution: {integrity: sha512-GslbXakjG61fwHnIN/vrUkPsa61WVAJDnb7jAwmbjRW5bZdwINymo1+FgjYJrGf2MDHxAt3bUWgmEMF8ETZxHQ==}
+
   base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 
@@ -1492,6 +1507,10 @@ packages:
     resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
     engines: {node: '>=6'}
 
+  camelcase@5.3.1:
+    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+    engines: {node: '>=6'}
+
   caniuse-lite@1.0.30001749:
     resolution: {integrity: sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==}
 
@@ -1537,6 +1556,9 @@ packages:
     resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
     engines: {node: '>=8'}
 
+  cliui@6.0.0:
+    resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
+
   cliui@8.0.1:
     resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
     engines: {node: '>=12'}
@@ -1665,6 +1687,14 @@ packages:
   dayjs@1.11.18:
     resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==}
 
+  debug@0.7.0:
+    resolution: {integrity: sha512-UWZnvGiX9tQgtrsA+mhGLKnUFvr1moempl9IvqQKyFnEgN0T4kpCE+KJcqTLcVxQjRVRnLF3VSE1Hchki5N98g==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
   debug@4.4.3:
     resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
     engines: {node: '>=6.0'}
@@ -1674,6 +1704,10 @@ packages:
       supports-color:
         optional: true
 
+  decamelize@1.2.0:
+    resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
+    engines: {node: '>=0.10.0'}
+
   decode-named-character-reference@1.2.0:
     resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
 
@@ -1734,6 +1768,9 @@ packages:
     resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
     engines: {node: '>=0.3.1'}
 
+  dijkstrajs@1.0.3:
+    resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
+
   dir-compare@4.2.0:
     resolution: {integrity: sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==}
 
@@ -2025,6 +2062,10 @@ packages:
     resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
     engines: {node: '>=8'}
 
+  find-up@4.1.0:
+    resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+    engines: {node: '>=8'}
+
   find-up@5.0.0:
     resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
     engines: {node: '>=10'}
@@ -2157,6 +2198,11 @@ packages:
     resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
     engines: {node: '>= 0.4'}
 
+  gm@1.16.0:
+    resolution: {integrity: sha512-b5oVGr8wCI7VNfjzeXiFocCZrcpkRUxSoVYfksBMEp/jo2nYwRKhcfOURarxFwjXyW1GvEY2EmmupVLnh0vXjg==}
+    engines: {node: '>= 0.8.0'}
+    deprecated: The gm module has been sunset. Please migrate to an alternative. https://github.com/aheckmann/gm?tab=readme-ov-file#2025-02-24-this-project-is-not-maintained
+
   gopd@1.2.0:
     resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
     engines: {node: '>= 0.4'}
@@ -2481,6 +2527,10 @@ packages:
     resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==}
     engines: {node: '>=14'}
 
+  locate-path@5.0.0:
+    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+    engines: {node: '>=8'}
+
   locate-path@6.0.0:
     resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
     engines: {node: '>=10'}
@@ -2871,10 +2921,18 @@ packages:
     resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
     engines: {node: '>=8'}
 
+  p-limit@2.3.0:
+    resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+    engines: {node: '>=6'}
+
   p-limit@3.1.0:
     resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
     engines: {node: '>=10'}
 
+  p-locate@4.1.0:
+    resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+    engines: {node: '>=8'}
+
   p-locate@5.0.0:
     resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
     engines: {node: '>=10'}
@@ -2883,6 +2941,10 @@ packages:
     resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
     engines: {node: '>=10'}
 
+  p-try@2.2.0:
+    resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+    engines: {node: '>=6'}
+
   package-json-from-dist@1.0.1:
     resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
 
@@ -2972,6 +3034,10 @@ packages:
     resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==}
     engines: {node: '>=10.4.0'}
 
+  pngjs@5.0.0:
+    resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
+    engines: {node: '>=10.13.0'}
+
   postcss-selector-parser@6.1.2:
     resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
     engines: {node: '>=4'}
@@ -3022,6 +3088,11 @@ packages:
     resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
     engines: {node: '>=6'}
 
+  qrcode@1.5.4:
+    resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+
   quansync@0.2.11:
     resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
 
@@ -3083,6 +3154,9 @@ packages:
     resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
     engines: {node: '>=0.10.0'}
 
+  require-main-filename@2.0.0:
+    resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
+
   resedit@1.7.2:
     resolution: {integrity: sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==}
     engines: {node: '>=12', npm: '>=6'}
@@ -3259,6 +3333,10 @@ packages:
     resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==}
     engines: {node: '>= 6'}
 
+  stream-to-buffer@0.0.1:
+    resolution: {integrity: sha512-LsvisgE3iThboRqA+XLmtnY9ktPLVPOj3zZxXMhlezeCcAh0RhomquXJgB8H+lb/RR/pPcbNVGHVKFUwjpoRtw==}
+    engines: {node: '>= 0.8'}
+
   string-width@4.2.3:
     resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
     engines: {node: '>=8'}
@@ -3323,6 +3401,9 @@ packages:
     resolution: {integrity: sha512-Z8uvtdWIlFn1GWy0HW5FhZ8VDryZwoJUdnjZU25C7/PBOltLIn1uv+WF3rVq6S1761YbsmbZYRP/l0ZJBCkvrw==}
     hasBin: true
 
+  through@2.3.8:
+    resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+
   tinycolor2@1.6.0:
     resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
 
@@ -3626,6 +3707,9 @@ packages:
   webpack-virtual-modules@0.6.2:
     resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
 
+  which-module@2.0.1:
+    resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
+
   which@2.0.2:
     resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
     engines: {node: '>= 8'}
@@ -3638,6 +3722,10 @@ packages:
     resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
     engines: {node: '>=0.10.0'}
 
+  wrap-ansi@6.2.0:
+    resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
+    engines: {node: '>=8'}
+
   wrap-ansi@7.0.0:
     resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
     engines: {node: '>=10'}
@@ -3698,6 +3786,9 @@ packages:
     peerDependencies:
       yjs: ^13.6.8
 
+  y18n@4.0.3:
+    resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
+
   y18n@5.0.8:
     resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
     engines: {node: '>=10'}
@@ -3708,10 +3799,18 @@ packages:
   yallist@4.0.0:
     resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
 
+  yargs-parser@18.1.3:
+    resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
+    engines: {node: '>=6'}
+
   yargs-parser@21.1.1:
     resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
     engines: {node: '>=12'}
 
+  yargs@15.4.1:
+    resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
+    engines: {node: '>=8'}
+
   yargs@17.7.2:
     resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
     engines: {node: '>=12'}
@@ -5195,6 +5294,10 @@ snapshots:
     dependencies:
       tslib: 2.8.1
 
+  array-parallel@0.1.3: {}
+
+  array-series@0.1.5: {}
+
   assert-plus@1.0.0:
     optional: true
 
@@ -5213,6 +5316,12 @@ snapshots:
 
   balanced-match@1.0.2: {}
 
+  barcode@0.1.0:
+    dependencies:
+      gm: 1.16.0
+    transitivePeerDependencies:
+      - supports-color
+
   base64-js@1.5.1: {}
 
   baseline-browser-mapping@2.8.16: {}
@@ -5345,6 +5454,8 @@ snapshots:
 
   callsites@3.1.0: {}
 
+  camelcase@5.3.1: {}
+
   caniuse-lite@1.0.30001749: {}
 
   chalk@4.1.2:
@@ -5390,6 +5501,12 @@ snapshots:
       string-width: 4.2.3
     optional: true
 
+  cliui@6.0.0:
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 6.2.0
+
   cliui@8.0.1:
     dependencies:
       string-width: 4.2.3
@@ -5526,10 +5643,14 @@ snapshots:
 
   dayjs@1.11.18: {}
 
+  debug@0.7.0: {}
+
   debug@4.4.3:
     dependencies:
       ms: 2.1.3
 
+  decamelize@1.2.0: {}
+
   decode-named-character-reference@1.2.0:
     dependencies:
       character-entities: 2.0.2
@@ -5581,6 +5702,8 @@ snapshots:
 
   diff@5.2.0: {}
 
+  dijkstrajs@1.0.3: {}
+
   dir-compare@4.2.0:
     dependencies:
       minimatch: 3.1.2
@@ -5967,6 +6090,11 @@ snapshots:
     dependencies:
       to-regex-range: 5.0.1
 
+  find-up@4.1.0:
+    dependencies:
+      locate-path: 5.0.0
+      path-exists: 4.0.0
+
   find-up@5.0.0:
     dependencies:
       locate-path: 6.0.0
@@ -6134,6 +6262,16 @@ snapshots:
       gopd: 1.2.0
     optional: true
 
+  gm@1.16.0:
+    dependencies:
+      array-parallel: 0.1.3
+      array-series: 0.1.5
+      debug: 0.7.0
+      stream-to-buffer: 0.0.1
+      through: 2.3.8
+    transitivePeerDependencies:
+      - supports-color
+
   gopd@1.2.0: {}
 
   got@11.8.6:
@@ -6430,6 +6568,10 @@ snapshots:
       pkg-types: 2.3.0
       quansync: 0.2.11
 
+  locate-path@5.0.0:
+    dependencies:
+      p-locate: 4.1.0
+
   locate-path@6.0.0:
     dependencies:
       p-locate: 5.0.0
@@ -6909,10 +7051,18 @@ snapshots:
 
   p-cancelable@2.1.1: {}
 
+  p-limit@2.3.0:
+    dependencies:
+      p-try: 2.2.0
+
   p-limit@3.1.0:
     dependencies:
       yocto-queue: 0.1.0
 
+  p-locate@4.1.0:
+    dependencies:
+      p-limit: 2.3.0
+
   p-locate@5.0.0:
     dependencies:
       p-limit: 3.1.0
@@ -6921,6 +7071,8 @@ snapshots:
     dependencies:
       aggregate-error: 3.1.0
 
+  p-try@2.2.0: {}
+
   package-json-from-dist@1.0.1: {}
 
   package-manager-detector@1.5.0: {}
@@ -6997,6 +7149,8 @@ snapshots:
       base64-js: 1.5.1
       xmlbuilder: 15.1.1
 
+  pngjs@5.0.0: {}
+
   postcss-selector-parser@6.1.2:
     dependencies:
       cssesc: 3.0.0
@@ -7036,6 +7190,12 @@ snapshots:
 
   punycode@2.3.1: {}
 
+  qrcode@1.5.4:
+    dependencies:
+      dijkstrajs: 1.0.3
+      pngjs: 5.0.0
+      yargs: 15.4.1
+
   quansync@0.2.11: {}
 
   queue-microtask@1.2.3: {}
@@ -7135,6 +7295,8 @@ snapshots:
 
   require-directory@2.1.1: {}
 
+  require-main-filename@2.0.0: {}
+
   resedit@1.7.2:
     dependencies:
       pe-library: 0.4.1
@@ -7353,6 +7515,8 @@ snapshots:
 
   stat-mode@1.0.0: {}
 
+  stream-to-buffer@0.0.1: {}
+
   string-width@4.2.3:
     dependencies:
       emoji-regex: 8.0.0
@@ -7439,6 +7603,8 @@ snapshots:
       minimatch: 3.1.2
       resolve-from: 2.0.0
 
+  through@2.3.8: {}
+
   tinycolor2@1.6.0: {}
 
   tinyexec@1.0.2: {}
@@ -7742,6 +7908,8 @@ snapshots:
 
   webpack-virtual-modules@0.6.2: {}
 
+  which-module@2.0.1: {}
+
   which@2.0.2:
     dependencies:
       isexe: 2.0.0
@@ -7752,6 +7920,12 @@ snapshots:
 
   word-wrap@1.2.5: {}
 
+  wrap-ansi@6.2.0:
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+
   wrap-ansi@7.0.0:
     dependencies:
       ansi-styles: 4.3.0
@@ -7797,14 +7971,35 @@ snapshots:
       - supports-color
       - utf-8-validate
 
+  y18n@4.0.3: {}
+
   y18n@5.0.8: {}
 
   yallist@3.1.1: {}
 
   yallist@4.0.0: {}
 
+  yargs-parser@18.1.3:
+    dependencies:
+      camelcase: 5.3.1
+      decamelize: 1.2.0
+
   yargs-parser@21.1.1: {}
 
+  yargs@15.4.1:
+    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
+
   yargs@17.7.2:
     dependencies:
       cliui: 8.0.1

+ 2 - 1
src/renderer/src/locales/en_US.json

@@ -146,5 +146,6 @@
   "lottie": "Lottie",
   "video": "Video",
   "media": "Media",
-  "advance": "Advance"
+  "advance": "Advance",
+  "qrcode": "QRCode"
 }

+ 2 - 1
src/renderer/src/locales/zh_CN.json

@@ -145,5 +145,6 @@
   "analogClock": "模拟时钟",
   "video": "视频",
   "media": "多媒体",
-  "advance": "高级"
+  "advance": "高级",
+  "qrcode": "二维码"
 }

+ 3 - 1
src/renderer/src/lvgl-widgets/index.ts

@@ -39,6 +39,7 @@ import AnalogClock from './analog-clock/index'
 
 import Lottie from './lottie'
 import Video from './video/index'
+import QRCode from './qrcode/index'
 
 import Page from './page'
 import { IComponentModelConfig } from './type'
@@ -88,7 +89,8 @@ export const ComponentArray = [
 
   Video,
 
-  Lottie
+  Lottie,
+  QRCode
 ] as IComponentModelConfig[]
 
 const componentMap: { [key: string]: IComponentModelConfig } = ComponentArray.reduce((acc, cur) => {

+ 34 - 0
src/renderer/src/lvgl-widgets/lottie/index.ts

@@ -6,6 +6,7 @@ import { stateList } from '@/constants'
 import defaultStyle from './style.json'
 import { h } from 'vue'
 import { BsFiletypeJson } from 'vue-icons-plus/bs'
+import { flagOptions, stateOptions } from '@/constants'
 
 export default {
   label: 'Lottie',
@@ -29,6 +30,21 @@ export default {
       y: 0,
       width: 100,
       height: 100,
+      flags: [
+        'LV_OBJ_FLAG_CLICKABLE',
+        'LV_OBJ_FLAG_CLICK_FOCUSABLE',
+        'LV_OBJ_FLAG_SCROLLABLE',
+        'LV_OBJ_FLAG_SCROLL_ELASTIC',
+        'LV_OBJ_FLAG_SCROLL_MOMENTUM',
+        'LV_OBJ_FLAG_SCROLL_CHAIN_HOR',
+        'LV_OBJ_FLAG_SCROLL_CHAIN_VER',
+        'LV_OBJ_FLAG_SCROLL_CHAIN',
+        'LV_OBJ_FLAG_SCROLL_WITH_ARROW',
+        'LV_OBJ_FLAG_SNAPPABLE',
+        'LV_OBJ_FLAG_PRESS_LOCK',
+        'LV_OBJ_FLAG_GESTURE_BUBBLE'
+      ],
+      states: [],
       lottie: ''
     },
     styles: [
@@ -101,6 +117,24 @@ export default {
               max: 10000
             },
             slots: { prefix: 'H' }
+          },
+          {
+            label: '标识',
+            field: 'props.flags',
+            valueType: 'checkbox',
+            componentProps: {
+              options: flagOptions,
+              defaultCollapsed: true
+            }
+          },
+          {
+            label: '状态',
+            field: 'props.states',
+            valueType: 'checkbox',
+            componentProps: {
+              options: stateOptions,
+              defaultCollapsed: true
+            }
           }
         ]
       }

+ 74 - 0
src/renderer/src/lvgl-widgets/qrcode/QRCode.vue

@@ -0,0 +1,74 @@
+<template>
+  <div
+    :style="getStyle"
+    class="relative overflow-hidden box-border flex items-center justify-center"
+  >
+    <canvas ref="qrcodeCanvas" class="max-w-full max-h-full"></canvas>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, watch, onMounted } from 'vue'
+import { useWidgetStyle } from '../hooks/useWidgetStyle'
+import QRCode from 'qrcode'
+
+const props = defineProps<{
+  styles: any
+  state?: string
+  part?: string
+  lightColor?: string
+  darkColor?: string
+  size?: number
+  text?: string
+}>()
+
+const qrcodeCanvas = ref<HTMLCanvasElement | null>(null)
+
+const styleMap = useWidgetStyle({
+  widget: 'qrcode',
+  props
+})
+
+const getStyle = computed(() => {
+  return {
+    width: props.size + 'px',
+    height: props.size + 'px',
+    ...styleMap.value
+  }
+})
+
+const qrcodeSize = computed(() => {
+  return Math.max(1, props.size || 1)
+})
+
+const renderQrcode = async () => {
+  if (!qrcodeCanvas.value) return
+
+  const context = qrcodeCanvas.value.getContext('2d')
+  context?.clearRect(0, 0, qrcodeCanvas.value.width, qrcodeCanvas.value.height)
+
+  try {
+    await QRCode.toCanvas(qrcodeCanvas.value, props.text?.trim() || 'https://www.sv-elec.com', {
+      width: qrcodeSize.value,
+      margin: 0,
+      color: {
+        dark: props.darkColor || '#000000ff',
+        light: props.lightColor || '#ffffffff'
+      }
+    })
+  } catch (error) {
+    console.error('Failed to render QR code:', error)
+  }
+}
+
+watch(
+  () => [props.text, props.size, props.darkColor, props.lightColor],
+  () => {
+    renderQrcode()
+  }
+)
+
+onMounted(() => {
+  renderQrcode()
+})
+</script>

+ 118 - 0
src/renderer/src/lvgl-widgets/qrcode/index.ts

@@ -0,0 +1,118 @@
+import QRCode from './QRCode.vue'
+import icon from '../assets/icon/icon_40crcode.svg'
+import type { IComponentModelConfig } from '../type'
+import i18n from '@/locales'
+import { stateList } from '@/constants'
+import defaultStyle from './style.json'
+
+export default {
+  label: i18n.global.t('qrcode'),
+  icon,
+  component: QRCode,
+  key: 'qrcode',
+  group: i18n.global.t('advance'),
+  sort: 2,
+  hasChildren: false,
+  defaultStyle,
+  parts: [
+    {
+      name: 'main',
+      stateList
+    }
+  ],
+  defaultSchema: {
+    name: 'qrcode',
+    props: {
+      x: 0,
+      y: 0,
+      flags: [
+        'LV_OBJ_FLAG_CLICKABLE',
+        'LV_OBJ_FLAG_CLICK_FOCUSABLE',
+        'LV_OBJ_FLAG_SCROLLABLE',
+        'LV_OBJ_FLAG_SCROLL_ELASTIC',
+        'LV_OBJ_FLAG_SCROLL_MOMENTUM',
+        'LV_OBJ_FLAG_SCROLL_CHAIN_HOR',
+        'LV_OBJ_FLAG_SCROLL_CHAIN_VER',
+        'LV_OBJ_FLAG_SCROLL_CHAIN',
+        'LV_OBJ_FLAG_SCROLL_WITH_ARROW',
+        'LV_OBJ_FLAG_SNAPPABLE',
+        'LV_OBJ_FLAG_PRESS_LOCK',
+        'LV_OBJ_FLAG_GESTURE_BUBBLE'
+      ],
+      states: [],
+      text: 'https://www.sv-elec.com',
+      lightColor: '#ffffffff',
+      darkColor: '#000000ff',
+      size: 100
+    },
+    styles: []
+  },
+  config: {
+    props: [
+      {
+        label: 'Name',
+        field: 'name',
+        valueType: 'text',
+        componentProps: {
+          placeholder: 'Enter name',
+          type: 'text'
+        }
+      },
+      {
+        label: 'Position / Size',
+        valueType: 'group',
+        children: [
+          {
+            label: '',
+            field: 'props.x',
+            valueType: 'number',
+            componentProps: {
+              span: 12,
+              min: -10000,
+              max: 10000
+            },
+            slots: { prefix: 'X' }
+          },
+          {
+            label: '',
+            field: 'props.y',
+            valueType: 'number',
+            componentProps: {
+              span: 12,
+              min: -10000,
+              max: 10000
+            },
+            slots: { prefix: 'Y' }
+          }
+        ]
+      }
+    ],
+    coreProps: [
+      {
+        label: 'Dark Color',
+        field: 'props.darkColor',
+        valueType: 'color'
+      },
+      {
+        label: 'Light Color',
+        field: 'props.lightColor',
+        valueType: 'color'
+      },
+      {
+        label: 'Size',
+        field: 'props.size',
+        valueType: 'number',
+        componentProps: {
+          min: 0,
+          max: 1000
+        }
+      },
+      {
+        label: 'Text',
+        field: 'props.text',
+        valueType: 'textarea'
+      }
+    ],
+    styles: []
+  }
+} as IComponentModelConfig

+ 5 - 0
src/renderer/src/lvgl-widgets/qrcode/style.json

@@ -0,0 +1,5 @@
+{
+  "widget": "qrcode",
+  "styleName": "defualt",
+  "part": []
+}

+ 10 - 4
src/renderer/src/lvgl-widgets/video/Video.vue

@@ -16,13 +16,19 @@ import { LuPlay } from 'vue-icons-plus/lu'
 const props = defineProps<{
   width: number
   height: number
-  text?: string
   styles: any
   state?: string
   part?: string
-  longMode?: 'circular' | 'clip' | 'dot' | 'scroll' | 'wrap'
-  lightStart?: number
-  lightEnd?: number
+  source: {
+    type: 'file' | 'signal' // file文件 signal信号
+    file: {
+      id: string
+      sd_path: string
+      autoplay: boolean
+      loop: boolean
+    }
+    signal: string
+  }
 }>()
 
 const styleMap = useWidgetStyle({

+ 2 - 2
src/renderer/src/views/designer/workspace/stage/Moveable.vue

@@ -148,10 +148,10 @@ const zIndex = ref()
 
 const individualGroupableProps = (element: HTMLElement | SVGElement | null | undefined) => {
   const widgetType = element?.getAttribute('widget-type') || ''
-  if (['lv_checkbox'].includes(widgetType)) {
+  if (['lv_checkbox', 'qrcode'].includes(widgetType)) {
     return { resizable: false }
   }
-  if (element?.getAttribute('widget-type') === 'lv_image') {
+  if (['lv_image'].includes(widgetType)) {
     return { rotatable: true }
   }
   // 不拖拽