This commit is contained in:
parent
7ecb4ef976
commit
8e1f3fbcc8
|
|
@ -62,6 +62,7 @@
|
||||||
"@tarojs/shared": "4.2.0",
|
"@tarojs/shared": "4.2.0",
|
||||||
"@tarojs/taro": "4.2.0",
|
"@tarojs/taro": "4.2.0",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18.0.0"
|
"react-dom": "^18.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -76,6 +77,7 @@
|
||||||
"@tarojs/vite-runner": "4.2.0",
|
"@tarojs/vite-runner": "4.2.0",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/minimatch": "^5",
|
"@types/minimatch": "^5",
|
||||||
|
"@types/qrcode": "^1.5.6",
|
||||||
"@types/react": "^18.0.0",
|
"@types/react": "^18.0.0",
|
||||||
"@vitejs/plugin-react": "^4.3.0",
|
"@vitejs/plugin-react": "^4.3.0",
|
||||||
"babel-preset-taro": "4.2.0",
|
"babel-preset-taro": "4.2.0",
|
||||||
|
|
|
||||||
142
pnpm-lock.yaml
142
pnpm-lock.yaml
|
|
@ -59,6 +59,9 @@ importers:
|
||||||
crypto-js:
|
crypto-js:
|
||||||
specifier: ^4.2.0
|
specifier: ^4.2.0
|
||||||
version: 4.2.0
|
version: 4.2.0
|
||||||
|
qrcode:
|
||||||
|
specifier: ^1.5.4
|
||||||
|
version: 1.5.4
|
||||||
react:
|
react:
|
||||||
specifier: ^18.0.0
|
specifier: ^18.0.0
|
||||||
version: 18.3.1
|
version: 18.3.1
|
||||||
|
|
@ -96,6 +99,9 @@ importers:
|
||||||
'@types/minimatch':
|
'@types/minimatch':
|
||||||
specifier: ^5
|
specifier: ^5
|
||||||
version: 5.1.2
|
version: 5.1.2
|
||||||
|
'@types/qrcode':
|
||||||
|
specifier: ^1.5.6
|
||||||
|
version: 1.5.6
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.0.0
|
specifier: ^18.0.0
|
||||||
version: 18.3.31
|
version: 18.3.31
|
||||||
|
|
@ -2126,6 +2132,9 @@ packages:
|
||||||
'@types/prop-types@15.7.15':
|
'@types/prop-types@15.7.15':
|
||||||
resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
|
resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
|
||||||
|
|
||||||
|
'@types/qrcode@1.5.6':
|
||||||
|
resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==}
|
||||||
|
|
||||||
'@types/react@18.3.31':
|
'@types/react@18.3.31':
|
||||||
resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==}
|
resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==}
|
||||||
|
|
||||||
|
|
@ -2552,6 +2561,10 @@ packages:
|
||||||
camel-case@4.1.2:
|
camel-case@4.1.2:
|
||||||
resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
|
resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
|
||||||
|
|
||||||
|
camelcase@5.3.1:
|
||||||
|
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
caniuse-lite@1.0.30001797:
|
caniuse-lite@1.0.30001797:
|
||||||
resolution: {integrity: sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==}
|
resolution: {integrity: sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==}
|
||||||
|
|
||||||
|
|
@ -2628,6 +2641,9 @@ packages:
|
||||||
resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
|
resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
|
cliui@6.0.0:
|
||||||
|
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
|
||||||
|
|
||||||
cliui@7.0.4:
|
cliui@7.0.4:
|
||||||
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
||||||
|
|
||||||
|
|
@ -2802,6 +2818,10 @@ packages:
|
||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
decamelize@1.2.0:
|
||||||
|
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
decode-uri-component@0.2.2:
|
decode-uri-component@0.2.2:
|
||||||
resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
|
resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
|
||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
|
|
@ -2875,6 +2895,9 @@ packages:
|
||||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
dijkstrajs@1.0.3:
|
||||||
|
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
||||||
|
|
||||||
dingtalk-jsapi@2.15.6:
|
dingtalk-jsapi@2.15.6:
|
||||||
resolution: {integrity: sha512-804mFz2AFV/H9ysmo7dLqMjSGOQgREsgQIuep+Xg+yNQeQtnUOYntElEzlB798Sj/691e4mMKz9mtQ7v9qdjuA==}
|
resolution: {integrity: sha512-804mFz2AFV/H9ysmo7dLqMjSGOQgREsgQIuep+Xg+yNQeQtnUOYntElEzlB798Sj/691e4mMKz9mtQ7v9qdjuA==}
|
||||||
|
|
||||||
|
|
@ -3221,6 +3244,10 @@ packages:
|
||||||
resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==}
|
resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
|
|
||||||
|
find-up@4.1.0:
|
||||||
|
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
find-up@5.0.0:
|
find-up@5.0.0:
|
||||||
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -3899,6 +3926,10 @@ packages:
|
||||||
resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==}
|
resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==}
|
||||||
engines: {node: '>= 12.13.0'}
|
engines: {node: '>= 12.13.0'}
|
||||||
|
|
||||||
|
locate-path@5.0.0:
|
||||||
|
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
locate-path@6.0.0:
|
locate-path@6.0.0:
|
||||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -4206,6 +4237,10 @@ packages:
|
||||||
resolution: {integrity: sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg==}
|
resolution: {integrity: sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
p-limit@2.3.0:
|
||||||
|
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
p-limit@3.1.0:
|
p-limit@3.1.0:
|
||||||
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -4214,6 +4249,10 @@ packages:
|
||||||
resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
|
resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
|
||||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
|
||||||
|
p-locate@4.1.0:
|
||||||
|
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
p-locate@5.0.0:
|
p-locate@5.0.0:
|
||||||
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -4226,6 +4265,10 @@ packages:
|
||||||
resolution: {integrity: sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==}
|
resolution: {integrity: sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
p-try@2.2.0:
|
||||||
|
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
package-json@6.5.0:
|
package-json@6.5.0:
|
||||||
resolution: {integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==}
|
resolution: {integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -4336,6 +4379,10 @@ packages:
|
||||||
platform@1.3.6:
|
platform@1.3.6:
|
||||||
resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==}
|
resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==}
|
||||||
|
|
||||||
|
pngjs@5.0.0:
|
||||||
|
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
possible-typed-array-names@1.1.0:
|
possible-typed-array-names@1.1.0:
|
||||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -4475,6 +4522,11 @@ packages:
|
||||||
resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==}
|
resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
qrcode@1.5.4:
|
||||||
|
resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
query-string@5.1.1:
|
query-string@5.1.1:
|
||||||
resolution: {integrity: sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==}
|
resolution: {integrity: sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
@ -4589,6 +4641,9 @@ packages:
|
||||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
require-main-filename@2.0.0:
|
||||||
|
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
||||||
|
|
||||||
resolve-from@4.0.0:
|
resolve-from@4.0.0:
|
||||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
@ -4721,6 +4776,9 @@ packages:
|
||||||
resolution: {integrity: sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==}
|
resolution: {integrity: sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
set-blocking@2.0.0:
|
||||||
|
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||||
|
|
||||||
set-function-length@1.2.2:
|
set-function-length@1.2.2:
|
||||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -5303,6 +5361,9 @@ packages:
|
||||||
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
|
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
which-module@2.0.1:
|
||||||
|
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
||||||
|
|
||||||
which-typed-array@1.1.22:
|
which-typed-array@1.1.22:
|
||||||
resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==}
|
resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -5355,6 +5416,9 @@ packages:
|
||||||
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
||||||
engines: {node: '>=0.4'}
|
engines: {node: '>=0.4'}
|
||||||
|
|
||||||
|
y18n@4.0.3:
|
||||||
|
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
|
||||||
|
|
||||||
y18n@5.0.8:
|
y18n@5.0.8:
|
||||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -5367,6 +5431,10 @@ packages:
|
||||||
engines: {node: '>= 14.6'}
|
engines: {node: '>= 14.6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
yargs-parser@18.1.3:
|
||||||
|
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
yargs-parser@20.2.9:
|
yargs-parser@20.2.9:
|
||||||
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
|
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -5375,6 +5443,10 @@ packages:
|
||||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
yargs@15.4.1:
|
||||||
|
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
yargs@16.2.0:
|
yargs@16.2.0:
|
||||||
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
|
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -7480,6 +7552,10 @@ snapshots:
|
||||||
|
|
||||||
'@types/prop-types@15.7.15': {}
|
'@types/prop-types@15.7.15': {}
|
||||||
|
|
||||||
|
'@types/qrcode@1.5.6':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 25.9.2
|
||||||
|
|
||||||
'@types/react@18.3.31':
|
'@types/react@18.3.31':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/prop-types': 15.7.15
|
'@types/prop-types': 15.7.15
|
||||||
|
|
@ -8038,6 +8114,8 @@ snapshots:
|
||||||
pascal-case: 3.1.2
|
pascal-case: 3.1.2
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
camelcase@5.3.1: {}
|
||||||
|
|
||||||
caniuse-lite@1.0.30001797: {}
|
caniuse-lite@1.0.30001797: {}
|
||||||
|
|
||||||
capital-case@1.0.4:
|
capital-case@1.0.4:
|
||||||
|
|
@ -8140,6 +8218,12 @@ snapshots:
|
||||||
|
|
||||||
cli-width@3.0.0: {}
|
cli-width@3.0.0: {}
|
||||||
|
|
||||||
|
cliui@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
wrap-ansi: 6.2.0
|
||||||
|
|
||||||
cliui@7.0.4:
|
cliui@7.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
string-width: 4.2.3
|
string-width: 4.2.3
|
||||||
|
|
@ -8304,6 +8388,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
|
decamelize@1.2.0: {}
|
||||||
|
|
||||||
decode-uri-component@0.2.2: {}
|
decode-uri-component@0.2.2: {}
|
||||||
|
|
||||||
decode-uri-component@0.4.1: {}
|
decode-uri-component@0.4.1: {}
|
||||||
|
|
@ -8381,6 +8467,8 @@ snapshots:
|
||||||
detect-libc@2.1.2:
|
detect-libc@2.1.2:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
dijkstrajs@1.0.3: {}
|
||||||
|
|
||||||
dingtalk-jsapi@2.15.6:
|
dingtalk-jsapi@2.15.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
promise-polyfill: 7.1.2
|
promise-polyfill: 7.1.2
|
||||||
|
|
@ -8919,6 +9007,11 @@ snapshots:
|
||||||
|
|
||||||
filter-obj@5.1.0: {}
|
filter-obj@5.1.0: {}
|
||||||
|
|
||||||
|
find-up@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
locate-path: 5.0.0
|
||||||
|
path-exists: 4.0.0
|
||||||
|
|
||||||
find-up@5.0.0:
|
find-up@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
locate-path: 6.0.0
|
locate-path: 6.0.0
|
||||||
|
|
@ -9633,6 +9726,10 @@ snapshots:
|
||||||
|
|
||||||
loader-utils@3.3.1: {}
|
loader-utils@3.3.1: {}
|
||||||
|
|
||||||
|
locate-path@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
p-locate: 4.1.0
|
||||||
|
|
||||||
locate-path@6.0.0:
|
locate-path@6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
|
|
@ -9916,6 +10013,10 @@ snapshots:
|
||||||
|
|
||||||
p-is-promise@1.1.0: {}
|
p-is-promise@1.1.0: {}
|
||||||
|
|
||||||
|
p-limit@2.3.0:
|
||||||
|
dependencies:
|
||||||
|
p-try: 2.2.0
|
||||||
|
|
||||||
p-limit@3.1.0:
|
p-limit@3.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
yocto-queue: 0.1.0
|
yocto-queue: 0.1.0
|
||||||
|
|
@ -9924,6 +10025,10 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
yocto-queue: 1.2.2
|
yocto-queue: 1.2.2
|
||||||
|
|
||||||
|
p-locate@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
p-limit: 2.3.0
|
||||||
|
|
||||||
p-locate@5.0.0:
|
p-locate@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-limit: 3.1.0
|
p-limit: 3.1.0
|
||||||
|
|
@ -9936,6 +10041,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-finally: 1.0.0
|
p-finally: 1.0.0
|
||||||
|
|
||||||
|
p-try@2.2.0: {}
|
||||||
|
|
||||||
package-json@6.5.0:
|
package-json@6.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
got: 9.6.0
|
got: 9.6.0
|
||||||
|
|
@ -10028,6 +10135,8 @@ snapshots:
|
||||||
|
|
||||||
platform@1.3.6: {}
|
platform@1.3.6: {}
|
||||||
|
|
||||||
|
pngjs@5.0.0: {}
|
||||||
|
|
||||||
possible-typed-array-names@1.1.0: {}
|
possible-typed-array-names@1.1.0: {}
|
||||||
|
|
||||||
postcss-css-variables@0.19.0(postcss@8.5.15):
|
postcss-css-variables@0.19.0(postcss@8.5.15):
|
||||||
|
|
@ -10149,6 +10258,12 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
hookified: 2.2.0
|
hookified: 2.2.0
|
||||||
|
|
||||||
|
qrcode@1.5.4:
|
||||||
|
dependencies:
|
||||||
|
dijkstrajs: 1.0.3
|
||||||
|
pngjs: 5.0.0
|
||||||
|
yargs: 15.4.1
|
||||||
|
|
||||||
query-string@5.1.1:
|
query-string@5.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
decode-uri-component: 0.2.2
|
decode-uri-component: 0.2.2
|
||||||
|
|
@ -10281,6 +10396,8 @@ snapshots:
|
||||||
|
|
||||||
require-from-string@2.0.2: {}
|
require-from-string@2.0.2: {}
|
||||||
|
|
||||||
|
require-main-filename@2.0.0: {}
|
||||||
|
|
||||||
resolve-from@4.0.0: {}
|
resolve-from@4.0.0: {}
|
||||||
|
|
||||||
resolve-from@5.0.0: {}
|
resolve-from@5.0.0: {}
|
||||||
|
|
@ -10457,6 +10574,8 @@ snapshots:
|
||||||
|
|
||||||
seroval@1.5.4: {}
|
seroval@1.5.4: {}
|
||||||
|
|
||||||
|
set-blocking@2.0.0: {}
|
||||||
|
|
||||||
set-function-length@1.2.2:
|
set-function-length@1.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
define-data-property: 1.1.4
|
define-data-property: 1.1.4
|
||||||
|
|
@ -11106,6 +11225,8 @@ snapshots:
|
||||||
is-weakmap: 2.0.2
|
is-weakmap: 2.0.2
|
||||||
is-weakset: 2.0.4
|
is-weakset: 2.0.4
|
||||||
|
|
||||||
|
which-module@2.0.1: {}
|
||||||
|
|
||||||
which-typed-array@1.1.22:
|
which-typed-array@1.1.22:
|
||||||
dependencies:
|
dependencies:
|
||||||
available-typed-arrays: 1.0.7
|
available-typed-arrays: 1.0.7
|
||||||
|
|
@ -11166,16 +11287,37 @@ snapshots:
|
||||||
|
|
||||||
xtend@4.0.2: {}
|
xtend@4.0.2: {}
|
||||||
|
|
||||||
|
y18n@4.0.3: {}
|
||||||
|
|
||||||
y18n@5.0.8: {}
|
y18n@5.0.8: {}
|
||||||
|
|
||||||
yallist@3.1.1: {}
|
yallist@3.1.1: {}
|
||||||
|
|
||||||
yaml@2.9.0: {}
|
yaml@2.9.0: {}
|
||||||
|
|
||||||
|
yargs-parser@18.1.3:
|
||||||
|
dependencies:
|
||||||
|
camelcase: 5.3.1
|
||||||
|
decamelize: 1.2.0
|
||||||
|
|
||||||
yargs-parser@20.2.9: {}
|
yargs-parser@20.2.9: {}
|
||||||
|
|
||||||
yargs-parser@21.1.1: {}
|
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@16.2.0:
|
yargs@16.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
cliui: 7.0.4
|
cliui: 7.0.4
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,28 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { hasServiceContact, pickServiceContact } from './contact'
|
import { getWeixinContactAction, hasServiceContact, pickServiceContact, selectServiceWeixinLink } from './contact'
|
||||||
|
|
||||||
|
describe('selectServiceWeixinLink', () => {
|
||||||
|
it('returns a single configured link unchanged', () => {
|
||||||
|
expect(selectServiceWeixinLink('https://u.wechat.com/example')).toBe('https://u.wechat.com/example')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('trims candidates and ignores empty entries', () => {
|
||||||
|
expect(selectServiceWeixinLink(
|
||||||
|
' https://work.weixin.qq.com/example-a, , https://u.wechat.com/example-b, ',
|
||||||
|
() => 0.5,
|
||||||
|
)).toBe('https://u.wechat.com/example-b')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getWeixinContactAction', () => {
|
||||||
|
it('shows a QR code for personal wechat u.wechat.com links', () => {
|
||||||
|
expect(getWeixinContactAction('https://u.wechat.com/ELkCkT_hVVAz8wNghBA3hMc?s=2')).toBe('qrcode')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('copies the link for work weixin customer-service urls', () => {
|
||||||
|
expect(getWeixinContactAction('https://work.weixin.qq.com/ca/cawcde9d9bc540aebe')).toBe('copy')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('pickServiceContact', () => {
|
describe('pickServiceContact', () => {
|
||||||
it('reads mobile online phone and weixin from client config', () => {
|
it('reads mobile online phone and weixin from client config', () => {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,32 @@ export function pickServiceContact (config?: Record<string, any> | null): Servic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectServiceWeixinLink (value: unknown, random: () => number = Math.random) {
|
||||||
|
if (typeof value !== 'string') return ''
|
||||||
|
|
||||||
|
const links = value
|
||||||
|
.split(',')
|
||||||
|
.map((link) => link.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
if (links.length === 0) return ''
|
||||||
|
return links[Math.floor(random() * links.length)] || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWeixinContactAction (url?: string): 'qrcode' | 'copy' {
|
||||||
|
const value = String(url || '').trim()
|
||||||
|
if (!value) return 'copy'
|
||||||
|
|
||||||
|
try {
|
||||||
|
const host = new URL(value).hostname.toLowerCase()
|
||||||
|
if (host === 'u.wechat.com' || host.endsWith('.u.wechat.com')) return 'qrcode'
|
||||||
|
} catch {
|
||||||
|
if (/u\.wechat\.com/i.test(value)) return 'qrcode'
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'copy'
|
||||||
|
}
|
||||||
|
|
||||||
export function hasServiceContact (config?: Record<string, any> | null) {
|
export function hasServiceContact (config?: Record<string, any> | null) {
|
||||||
const contact = pickServiceContact(config)
|
const contact = pickServiceContact(config)
|
||||||
return Boolean(contact.online || contact.phone || contact.weixin)
|
return Boolean(contact.online || contact.phone || contact.weixin)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { buildPublicQrImageUrl } from './qrImage'
|
||||||
|
|
||||||
|
describe('buildPublicQrImageUrl', () => {
|
||||||
|
it('builds a domestic public qr image url with the wechat link encoded', () => {
|
||||||
|
expect(buildPublicQrImageUrl('https://u.wechat.com/ELkCkT_hVVAz8wNghBA3hMc?s=2')).toBe(
|
||||||
|
'https://api.qrtool.cn/?text=https%3A%2F%2Fu.wechat.com%2FELkCkT_hVVAz8wNghBA3hMc%3Fs%3D2&size=400&margin=16&level=H',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns empty string for blank text', () => {
|
||||||
|
expect(buildPublicQrImageUrl('')).toBe('')
|
||||||
|
expect(buildPublicQrImageUrl(' ')).toBe('')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
export function buildPublicQrImageUrl (text: string) {
|
||||||
|
const value = String(text || '').trim()
|
||||||
|
if (!value) return ''
|
||||||
|
return `https://api.qrtool.cn/?text=${encodeURIComponent(value)}&size=400&margin=16&level=H`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { bytesToBase64, encodeQrPng } from './qrPng'
|
||||||
|
|
||||||
|
describe('encodeQrPng', () => {
|
||||||
|
it('encodes a wechat personal link into a png buffer', () => {
|
||||||
|
const png = encodeQrPng('https://u.wechat.com/ELkCkT_hVVAz8wNghBA3hMc?s=2')
|
||||||
|
expect(Array.from(png.slice(0, 8))).toEqual([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])
|
||||||
|
expect(bytesToBase64(png).startsWith('iVBOR')).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
import QRCode from 'qrcode'
|
||||||
|
|
||||||
|
const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]
|
||||||
|
const CRC_TABLE = makeCrcTable()
|
||||||
|
const BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
||||||
|
|
||||||
|
export function encodeQrPng (text: string, scale = 6, margin = 4) {
|
||||||
|
const qr = QRCode.create(text, { errorCorrectionLevel: 'M' })
|
||||||
|
const moduleCount = qr.modules.size
|
||||||
|
const dim = (moduleCount + margin * 2) * scale
|
||||||
|
const raw = new Uint8Array((dim * 3 + 1) * dim)
|
||||||
|
let offset = 0
|
||||||
|
|
||||||
|
for (let y = 0; y < dim; y += 1) {
|
||||||
|
raw[offset] = 0
|
||||||
|
offset += 1
|
||||||
|
const moduleY = Math.floor(y / scale) - margin
|
||||||
|
for (let x = 0; x < dim; x += 1) {
|
||||||
|
const moduleX = Math.floor(x / scale) - margin
|
||||||
|
const dark = moduleX >= 0 && moduleX < moduleCount && moduleY >= 0 && moduleY < moduleCount
|
||||||
|
? Boolean(qr.modules.get(moduleX, moduleY))
|
||||||
|
: false
|
||||||
|
const value = dark ? 0 : 255
|
||||||
|
raw[offset] = value
|
||||||
|
raw[offset + 1] = value
|
||||||
|
raw[offset + 2] = value
|
||||||
|
offset += 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ihdr = [
|
||||||
|
...u32(dim),
|
||||||
|
...u32(dim),
|
||||||
|
8,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
]
|
||||||
|
const idat = zlibStore(raw)
|
||||||
|
const bytes = new Uint8Array([
|
||||||
|
...PNG_SIGNATURE,
|
||||||
|
...chunk('IHDR', ihdr),
|
||||||
|
...chunk('IDAT', idat),
|
||||||
|
...chunk('IEND', []),
|
||||||
|
])
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bytesToBase64 (bytes: Uint8Array) {
|
||||||
|
let result = ''
|
||||||
|
for (let i = 0; i < bytes.length; i += 3) {
|
||||||
|
const a = bytes[i]
|
||||||
|
const b = i + 1 < bytes.length ? bytes[i + 1] : 0
|
||||||
|
const c = i + 2 < bytes.length ? bytes[i + 2] : 0
|
||||||
|
const triple = (a << 16) | (b << 8) | c
|
||||||
|
result += BASE64_CHARS[(triple >> 18) & 63]
|
||||||
|
result += BASE64_CHARS[(triple >> 12) & 63]
|
||||||
|
result += i + 1 < bytes.length ? BASE64_CHARS[(triple >> 6) & 63] : '='
|
||||||
|
result += i + 2 < bytes.length ? BASE64_CHARS[triple & 63] : '='
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function zlibStore (raw: Uint8Array) {
|
||||||
|
const blocks: number[] = [0x78, 0x01]
|
||||||
|
const max = 65535
|
||||||
|
for (let i = 0; i < raw.length; i += max) {
|
||||||
|
const end = Math.min(i + max, raw.length)
|
||||||
|
const chunkData = raw.subarray(i, end)
|
||||||
|
const last = end === raw.length ? 1 : 0
|
||||||
|
const len = chunkData.length
|
||||||
|
blocks.push(last, len & 0xff, (len >> 8) & 0xff, (~len) & 0xff, ((~len) >> 8) & 0xff)
|
||||||
|
for (let j = 0; j < chunkData.length; j += 1) {
|
||||||
|
blocks.push(chunkData[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blocks.push(...u32(adler32(raw)))
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
function chunk (type: string, data: number[]) {
|
||||||
|
const typeBytes = [type.charCodeAt(0), type.charCodeAt(1), type.charCodeAt(2), type.charCodeAt(3)]
|
||||||
|
return [
|
||||||
|
...u32(data.length),
|
||||||
|
...typeBytes,
|
||||||
|
...data,
|
||||||
|
...u32(crc32([...typeBytes, ...data])),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function u32 (value: number) {
|
||||||
|
return [
|
||||||
|
(value >>> 24) & 0xff,
|
||||||
|
(value >>> 16) & 0xff,
|
||||||
|
(value >>> 8) & 0xff,
|
||||||
|
value & 0xff,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function crc32 (bytes: number[]) {
|
||||||
|
let crc = 0xffffffff
|
||||||
|
for (let i = 0; i < bytes.length; i += 1) {
|
||||||
|
crc = CRC_TABLE[(crc ^ bytes[i]) & 0xff] ^ (crc >>> 8)
|
||||||
|
}
|
||||||
|
return (crc ^ 0xffffffff) >>> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function adler32 (bytes: Uint8Array) {
|
||||||
|
let a = 1
|
||||||
|
let b = 0
|
||||||
|
for (let i = 0; i < bytes.length; i += 1) {
|
||||||
|
a = (a + bytes[i]) % 65521
|
||||||
|
b = (b + a) % 65521
|
||||||
|
}
|
||||||
|
return ((b << 16) | a) >>> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeCrcTable () {
|
||||||
|
const table = new Uint32Array(256)
|
||||||
|
for (let n = 0; n < 256; n += 1) {
|
||||||
|
let c = n
|
||||||
|
for (let k = 0; k < 8; k += 1) {
|
||||||
|
c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1)
|
||||||
|
}
|
||||||
|
table[n] = c >>> 0
|
||||||
|
}
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { Image, Text, View } from '@tarojs/components'
|
import { Image, Text, View } from '@tarojs/components'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { CORP_ASSET_BASE } from '@/lib/cdn'
|
import { useEffect, useState } from 'react'
|
||||||
import type { ServiceContact } from '@/lib/contact'
|
import { CORP_ASSET_BASE, MAP_ASSET_BASE } from '@/lib/cdn'
|
||||||
|
import { getWeixinContactAction, selectServiceWeixinLink, type ServiceContact } from '@/lib/contact'
|
||||||
|
import WeixinQrImage from './weixin-qr'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
open: boolean
|
open: boolean
|
||||||
|
|
@ -32,11 +34,31 @@ function ContactItem ({
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ContactModal ({ open, contact, onCancel }: Props) {
|
export default function ContactModal ({ open, contact, onCancel }: Props) {
|
||||||
|
const [weixin, setWeixin] = useState('')
|
||||||
|
const [showQr, setShowQr] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) {
|
||||||
|
setShowQr(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setWeixin(selectServiceWeixinLink(contact.weixin))
|
||||||
|
}, [open, contact.weixin])
|
||||||
|
|
||||||
if (!open) return null
|
if (!open) return null
|
||||||
|
|
||||||
const copyLink = async (url: string, title: string) => {
|
const copyLink = async (url: string) => {
|
||||||
await Taro.setClipboardData({ data: url })
|
await Taro.setClipboardData({ data: url })
|
||||||
Taro.showToast({ title: `${title}链接已复制`, icon: 'none' })
|
Taro.showToast({ title: '微信客服链接已复制', icon: 'none' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleWeixin = () => {
|
||||||
|
if (!weixin) return
|
||||||
|
if (getWeixinContactAction(weixin) === 'qrcode') {
|
||||||
|
setShowQr((current) => !current)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
void copyLink(weixin)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePhone = () => {
|
const handlePhone = () => {
|
||||||
|
|
@ -44,30 +66,30 @@ export default function ContactModal ({ open, contact, onCancel }: Props) {
|
||||||
Taro.makePhoneCall({ phoneNumber: contact.phone.replace(/-/g, '') })
|
Taro.makePhoneCall({ phoneNumber: contact.phone.replace(/-/g, '') })
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasAny = Boolean(contact.online || contact.phone || contact.weixin)
|
const hasAny = Boolean(weixin || contact.phone)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='contact-mask' onClick={onCancel}>
|
<View className='contact-mask' onClick={onCancel}>
|
||||||
<View className='contact-dialog' onClick={(event) => event.stopPropagation()}>
|
<View className='contact-shell' onClick={(event) => event.stopPropagation()}>
|
||||||
|
<View className='contact-dialog'>
|
||||||
|
<View className='contact-dialog__header'>
|
||||||
<Text className='contact-dialog__title'>联系客服</Text>
|
<Text className='contact-dialog__title'>联系客服</Text>
|
||||||
|
</View>
|
||||||
|
<View className='contact-dialog__body'>
|
||||||
<Text className='contact-dialog__hours'>工作时间段:法定工作日 9:00-18:00</Text>
|
<Text className='contact-dialog__hours'>工作时间段:法定工作日 9:00-18:00</Text>
|
||||||
|
|
||||||
{contact.online ? (
|
{weixin ? (
|
||||||
<ContactItem
|
<>
|
||||||
icon={`${CORP_ASSET_BASE}/images-pc/icon-contactus-service.png`}
|
|
||||||
title='在线客服'
|
|
||||||
desc='竭诚为您服务~'
|
|
||||||
onClick={() => void copyLink(contact.online, '在线客服')}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{contact.weixin ? (
|
|
||||||
<ContactItem
|
<ContactItem
|
||||||
icon={`${CORP_ASSET_BASE}/images-pc/icon-service-weixin.png`}
|
icon={`${CORP_ASSET_BASE}/images-pc/icon-service-weixin.png`}
|
||||||
title='微信客服'
|
title='微信客服'
|
||||||
desc='一对一为您服务~'
|
desc='一对一为您服务~'
|
||||||
onClick={() => void copyLink(contact.weixin, '微信客服')}
|
onClick={handleWeixin}
|
||||||
/>
|
/>
|
||||||
|
{showQr && getWeixinContactAction(weixin) === 'qrcode' ? (
|
||||||
|
<WeixinQrImage url={weixin} />
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{contact.phone ? (
|
{contact.phone ? (
|
||||||
|
|
@ -82,11 +104,15 @@ export default function ContactModal ({ open, contact, onCancel }: Props) {
|
||||||
{!hasAny ? (
|
{!hasAny ? (
|
||||||
<Text className='contact-dialog__empty'>暂无客服信息,请稍后再试</Text>
|
<Text className='contact-dialog__empty'>暂无客服信息,请稍后再试</Text>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<View className='contact-dialog__close' onClick={onCancel}>
|
|
||||||
<Text>关闭</Text>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<Image
|
||||||
|
className='contact-close'
|
||||||
|
src={`${MAP_ASSET_BASE}/service-modal-close.png`}
|
||||||
|
onClick={onCancel}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,13 @@
|
||||||
color: #c9cdd4;
|
color: #c9cdd4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home-search__clear {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
margin: 0 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.home-search__button {
|
.home-search__button {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -498,29 +505,53 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: rgba(0, 0, 0, 0.65);
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-shell {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-dialog {
|
.contact-dialog {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 576px;
|
width: 576px;
|
||||||
padding: 32px 28px 28px;
|
overflow: hidden;
|
||||||
border-radius: 32px;
|
border-radius: 40px;
|
||||||
background: #fff;
|
background: #f9fbff;
|
||||||
|
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-dialog__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 120px;
|
||||||
|
padding: 0 28px;
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(249, 251, 255, 0) 0%, #f9fbff 100%),
|
||||||
|
linear-gradient(90deg, #c1ebff 0%, #bdf3ff 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-dialog__title {
|
.contact-dialog__title {
|
||||||
display: block;
|
|
||||||
color: #222;
|
color: #222;
|
||||||
font-size: 34px;
|
font-size: 34px;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
line-height: 48px;
|
line-height: 44px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contact-dialog__body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 28px;
|
||||||
|
padding: 0 28px 28px;
|
||||||
|
}
|
||||||
|
|
||||||
.contact-dialog__hours {
|
.contact-dialog__hours {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 16px 0 28px;
|
margin-bottom: 12px;
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
|
|
@ -538,12 +569,13 @@
|
||||||
.contact-item {
|
.contact-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 124px;
|
box-sizing: border-box;
|
||||||
margin-bottom: 20px;
|
width: 520px;
|
||||||
|
min-height: 124px;
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 0 20px rgba(147, 153, 161, 0.12);
|
box-shadow: 0 0 20px rgba(147, 153, 161, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-item__icon {
|
.contact-item__icon {
|
||||||
|
|
@ -566,19 +598,40 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-item__desc {
|
.contact-item__desc {
|
||||||
margin-top: 4px;
|
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
|
font-weight: 400;
|
||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-dialog__close {
|
.contact-close {
|
||||||
margin-top: 12px;
|
width: 60px;
|
||||||
height: 80px;
|
height: 60px;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-qrcode {
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 520px;
|
||||||
|
padding: 24px 24px 28px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
background: #f5f6f8;
|
background: #fff;
|
||||||
color: #666;
|
box-shadow: 0 0 20px rgba(147, 153, 161, 0.1);
|
||||||
font-size: 30px;
|
}
|
||||||
line-height: 80px;
|
|
||||||
|
.contact-qrcode__image {
|
||||||
|
width: 320px;
|
||||||
|
height: 320px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-qrcode__hint {
|
||||||
|
margin-top: 16px;
|
||||||
|
color: #999;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 28px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,13 @@ export default function Index () {
|
||||||
value={inputPhone}
|
value={inputPhone}
|
||||||
onInput={(event) => setInputPhone(event.detail.value.replace(/\D/g, ''))}
|
onInput={(event) => setInputPhone(event.detail.value.replace(/\D/g, ''))}
|
||||||
/>
|
/>
|
||||||
|
{inputPhone ? (
|
||||||
|
<Image
|
||||||
|
className='home-search__clear'
|
||||||
|
src={LOCAL_IMAGES.clear}
|
||||||
|
onClick={() => setInputPhone('')}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
<Button
|
<Button
|
||||||
className='home-search__button'
|
className='home-search__button'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { Image, Text, View } from '@tarojs/components'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { buildPublicQrImageUrl } from '@/lib/qrImage'
|
||||||
|
|
||||||
|
export default function WeixinQrImage ({ url }: { url: string }) {
|
||||||
|
const [src, setSrc] = useState('')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false
|
||||||
|
setSrc('')
|
||||||
|
const remote = buildPublicQrImageUrl(url)
|
||||||
|
if (!remote) return
|
||||||
|
|
||||||
|
if (Taro.getEnv() !== Taro.ENV_TYPE.WEAPP) {
|
||||||
|
setSrc(remote)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Taro.downloadFile({
|
||||||
|
url: remote,
|
||||||
|
success (res) {
|
||||||
|
if (cancelled) return
|
||||||
|
if (res.statusCode === 200 && res.tempFilePath) {
|
||||||
|
setSrc(res.tempFilePath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Taro.showToast({ title: '二维码加载失败', icon: 'none' })
|
||||||
|
},
|
||||||
|
fail () {
|
||||||
|
if (!cancelled) {
|
||||||
|
Taro.showToast({ title: '二维码加载失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true
|
||||||
|
}
|
||||||
|
}, [url])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className='contact-qrcode'>
|
||||||
|
{src ? (
|
||||||
|
<Image
|
||||||
|
className='contact-qrcode__image'
|
||||||
|
src={src}
|
||||||
|
showMenuByLongpress
|
||||||
|
onLongPress={() => Taro.previewImage({ current: src, urls: [src] })}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<View className='contact-qrcode__image' />
|
||||||
|
)}
|
||||||
|
<Text className='contact-qrcode__hint'>截图并使用微信扫码识别添加客服微信</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue