mirror of
https://github.com/primedigitaltech/azon_seeker.git
synced 2026-01-19 13:13:22 +08:00
Update
This commit is contained in:
parent
0948413460
commit
64b41f5841
@ -35,6 +35,7 @@
|
||||
"@types/node": "^22.10.5",
|
||||
"@types/webextension-polyfill": "^0.12.1",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "^4.2.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vueuse/core": "^12.3.0",
|
||||
"chokidar": "^4.0.3",
|
||||
|
||||
626
pnpm-lock.yaml
generated
626
pnpm-lock.yaml
generated
@ -25,6 +25,9 @@ importers:
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^5.2.1
|
||||
version: 5.2.1(vite@6.2.4(@types/node@22.10.5)(sass-embedded@1.86.2)(tsx@4.19.2)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.2))
|
||||
'@vitejs/plugin-vue-jsx':
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0(vite@6.2.4(@types/node@22.10.5)(sass-embedded@1.86.2)(tsx@4.19.2)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.2))
|
||||
'@vue/test-utils':
|
||||
specifier: ^2.4.6
|
||||
version: 2.4.6
|
||||
@ -93,7 +96,7 @@ importers:
|
||||
version: 22.1.0(@vue/compiler-sfc@3.5.13)
|
||||
unplugin-vue-components:
|
||||
specifier: ^28.4.1
|
||||
version: 28.4.1(@babel/parser@7.27.0)(vue@3.5.13(typescript@5.8.2))
|
||||
version: 28.4.1(@babel/parser@7.27.5)(vue@3.5.13(typescript@5.8.2))
|
||||
vite:
|
||||
specifier: ^6.2.4
|
||||
version: 6.2.4(@types/node@22.10.5)(sass-embedded@1.86.2)(tsx@4.19.2)(yaml@2.7.1)
|
||||
@ -120,6 +123,13 @@ importers:
|
||||
version: 0.12.0
|
||||
|
||||
packages:
|
||||
'@ampproject/remapping@2.3.0':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==,
|
||||
}
|
||||
engines: { node: '>=6.0.0' }
|
||||
|
||||
'@antfu/install-pkg@1.0.0':
|
||||
resolution:
|
||||
{
|
||||
@ -145,6 +155,110 @@ packages:
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/code-frame@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/compat-data@7.27.5':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/core@7.27.4':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/generator@7.27.5':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-annotate-as-pure@7.27.3':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-compilation-targets@7.27.2':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-create-class-features-plugin@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
|
||||
'@babel/helper-member-expression-to-functions@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-module-imports@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-module-transforms@7.27.3':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
|
||||
'@babel/helper-optimise-call-expression@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-plugin-utils@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-replace-supers@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
|
||||
'@babel/helper-skip-transparent-expression-wrappers@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-string-parser@7.25.9':
|
||||
resolution:
|
||||
{
|
||||
@ -152,6 +266,13 @@ packages:
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-string-parser@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-validator-identifier@7.25.9':
|
||||
resolution:
|
||||
{
|
||||
@ -159,6 +280,27 @@ packages:
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-validator-identifier@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helper-validator-option@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/helpers@7.27.6':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/parser@7.27.0':
|
||||
resolution:
|
||||
{
|
||||
@ -167,6 +309,41 @@ packages:
|
||||
engines: { node: '>=6.0.0' }
|
||||
hasBin: true
|
||||
|
||||
'@babel/parser@7.27.5':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==,
|
||||
}
|
||||
engines: { node: '>=6.0.0' }
|
||||
hasBin: true
|
||||
|
||||
'@babel/plugin-syntax-jsx@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
'@babel/plugin-syntax-typescript@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
'@babel/plugin-transform-typescript@7.27.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
'@babel/runtime@7.26.10':
|
||||
resolution:
|
||||
{
|
||||
@ -174,6 +351,20 @@ packages:
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/template@7.27.2':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/traverse@7.27.4':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/types@7.27.0':
|
||||
resolution:
|
||||
{
|
||||
@ -181,6 +372,13 @@ packages:
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@babel/types@7.27.6':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
'@bufbuild/protobuf@2.2.3':
|
||||
resolution:
|
||||
{
|
||||
@ -826,12 +1024,39 @@ packages:
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.8':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==,
|
||||
}
|
||||
engines: { node: '>=6.0.0' }
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==,
|
||||
}
|
||||
engines: { node: '>=6.0.0' }
|
||||
|
||||
'@jridgewell/set-array@1.2.1':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==,
|
||||
}
|
||||
engines: { node: '>=6.0.0' }
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==,
|
||||
}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.25':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==,
|
||||
}
|
||||
|
||||
'@juggle/resize-observer@3.4.0':
|
||||
resolution:
|
||||
{
|
||||
@ -899,6 +1124,12 @@ packages:
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.17':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-i6p5fc1+lAmR3OHmNlv7/3PIY3EtuUu4kVARjkid38p7cmyIyqr0QFnA+k3xoB06wQUpBA91H1HFlRreZ2v5oA==,
|
||||
}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.39.0':
|
||||
resolution:
|
||||
{
|
||||
@ -1160,6 +1391,16 @@ packages:
|
||||
integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==,
|
||||
}
|
||||
|
||||
'@vitejs/plugin-vue-jsx@4.2.0':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-DSTrmrdLp+0LDNF77fqrKfx7X0ErRbOcUAgJL/HbSesqQwoUvUQ4uYQqaex+rovqgGcoPqVk+AwUh3v9CuiYIw==,
|
||||
}
|
||||
engines: { node: ^18.0.0 || >=20.0.0 }
|
||||
peerDependencies:
|
||||
vite: ^5.0.0 || ^6.0.0
|
||||
vue: ^3.0.0
|
||||
|
||||
'@vitejs/plugin-vue@5.2.1':
|
||||
resolution:
|
||||
{
|
||||
@ -1220,6 +1461,31 @@ packages:
|
||||
integrity: sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==,
|
||||
}
|
||||
|
||||
'@vue/babel-helper-vue-transform-on@1.4.0':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==,
|
||||
}
|
||||
|
||||
'@vue/babel-plugin-jsx@1.4.0':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==,
|
||||
}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
peerDependenciesMeta:
|
||||
'@babel/core':
|
||||
optional: true
|
||||
|
||||
'@vue/babel-plugin-resolve-type@1.4.0':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==,
|
||||
}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
'@vue/compiler-core@3.5.13':
|
||||
resolution:
|
||||
{
|
||||
@ -1659,6 +1925,14 @@ packages:
|
||||
}
|
||||
engines: { node: '>=8' }
|
||||
|
||||
browserslist@4.25.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==,
|
||||
}
|
||||
engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
|
||||
hasBin: true
|
||||
|
||||
buffer-builder@0.2.0:
|
||||
resolution:
|
||||
{
|
||||
@ -1752,6 +2026,12 @@ packages:
|
||||
}
|
||||
engines: { node: '>=16' }
|
||||
|
||||
caniuse-lite@1.0.30001723:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==,
|
||||
}
|
||||
|
||||
chai@5.2.0:
|
||||
resolution:
|
||||
{
|
||||
@ -2014,6 +2294,12 @@ packages:
|
||||
}
|
||||
engines: { node: '>=18' }
|
||||
|
||||
convert-source-map@2.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==,
|
||||
}
|
||||
|
||||
core-util-is@1.0.3:
|
||||
resolution:
|
||||
{
|
||||
@ -2371,6 +2657,12 @@ packages:
|
||||
engines: { node: '>=14' }
|
||||
hasBin: true
|
||||
|
||||
electron-to-chromium@1.5.170:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-GP+M7aeluQo9uAyiTCxgIj/j+PrWhMlY7LFVj8prlsPljd0Fdg9AprlfUi+OCSFWy9Y5/2D/Jrj9HS8Z4rpKWA==,
|
||||
}
|
||||
|
||||
emittery@1.1.0:
|
||||
resolution:
|
||||
{
|
||||
@ -2905,6 +3197,13 @@ packages:
|
||||
}
|
||||
hasBin: true
|
||||
|
||||
gensync@1.0.0-beta.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==,
|
||||
}
|
||||
engines: { node: '>=6.9.0' }
|
||||
|
||||
get-caller-file@2.0.5:
|
||||
resolution:
|
||||
{
|
||||
@ -3017,6 +3316,13 @@ packages:
|
||||
}
|
||||
engines: { node: '>=18' }
|
||||
|
||||
globals@11.12.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==,
|
||||
}
|
||||
engines: { node: '>=4' }
|
||||
|
||||
globals@13.24.0:
|
||||
resolution:
|
||||
{
|
||||
@ -3691,6 +3997,14 @@ packages:
|
||||
canvas:
|
||||
optional: true
|
||||
|
||||
jsesc@3.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==,
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
hasBin: true
|
||||
|
||||
json-buffer@3.0.1:
|
||||
resolution:
|
||||
{
|
||||
@ -3740,6 +4054,14 @@ packages:
|
||||
integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==,
|
||||
}
|
||||
|
||||
json5@2.2.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==,
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
hasBin: true
|
||||
|
||||
jsonfile@6.1.0:
|
||||
resolution:
|
||||
{
|
||||
@ -4001,6 +4323,12 @@ packages:
|
||||
}
|
||||
engines: { node: 20 || >=22 }
|
||||
|
||||
lru-cache@5.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==,
|
||||
}
|
||||
|
||||
magic-string@0.30.17:
|
||||
resolution:
|
||||
{
|
||||
@ -4208,6 +4536,12 @@ packages:
|
||||
integrity: sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==,
|
||||
}
|
||||
|
||||
node-releases@2.0.19:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==,
|
||||
}
|
||||
|
||||
node-rsa@1.1.1:
|
||||
resolution:
|
||||
{
|
||||
@ -5210,6 +5544,13 @@ packages:
|
||||
}
|
||||
hasBin: true
|
||||
|
||||
semver@6.3.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==,
|
||||
}
|
||||
hasBin: true
|
||||
|
||||
semver@7.6.3:
|
||||
resolution:
|
||||
{
|
||||
@ -6012,6 +6353,15 @@ packages:
|
||||
}
|
||||
engines: { node: '>=4' }
|
||||
|
||||
update-browserslist-db@1.1.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==,
|
||||
}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
browserslist: '>= 4.21.0'
|
||||
|
||||
update-notifier@7.3.1:
|
||||
resolution:
|
||||
{
|
||||
@ -6463,6 +6813,12 @@ packages:
|
||||
}
|
||||
engines: { node: '>=10' }
|
||||
|
||||
yallist@3.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==,
|
||||
}
|
||||
|
||||
yaml@2.7.1:
|
||||
resolution:
|
||||
{
|
||||
@ -6519,6 +6875,11 @@ packages:
|
||||
engines: { node: '>= 10' }
|
||||
|
||||
snapshots:
|
||||
'@ampproject/remapping@2.3.0':
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.8
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
|
||||
'@antfu/install-pkg@1.0.0':
|
||||
dependencies:
|
||||
package-manager-detector: 0.2.11
|
||||
@ -6540,23 +6901,188 @@ snapshots:
|
||||
js-tokens: 4.0.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
'@babel/code-frame@7.27.1':
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
js-tokens: 4.0.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
'@babel/compat-data@7.27.5': {}
|
||||
|
||||
'@babel/core@7.27.4':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
'@babel/code-frame': 7.27.1
|
||||
'@babel/generator': 7.27.5
|
||||
'@babel/helper-compilation-targets': 7.27.2
|
||||
'@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4)
|
||||
'@babel/helpers': 7.27.6
|
||||
'@babel/parser': 7.27.5
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/traverse': 7.27.4
|
||||
'@babel/types': 7.27.6
|
||||
convert-source-map: 2.0.0
|
||||
debug: 4.4.0
|
||||
gensync: 1.0.0-beta.2
|
||||
json5: 2.2.3
|
||||
semver: 6.3.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/generator@7.27.5':
|
||||
dependencies:
|
||||
'@babel/parser': 7.27.5
|
||||
'@babel/types': 7.27.6
|
||||
'@jridgewell/gen-mapping': 0.3.8
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
jsesc: 3.1.0
|
||||
|
||||
'@babel/helper-annotate-as-pure@7.27.3':
|
||||
dependencies:
|
||||
'@babel/types': 7.27.6
|
||||
|
||||
'@babel/helper-compilation-targets@7.27.2':
|
||||
dependencies:
|
||||
'@babel/compat-data': 7.27.5
|
||||
'@babel/helper-validator-option': 7.27.1
|
||||
browserslist: 4.25.0
|
||||
lru-cache: 5.1.1
|
||||
semver: 6.3.1
|
||||
|
||||
'@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.27.4)':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/helper-annotate-as-pure': 7.27.3
|
||||
'@babel/helper-member-expression-to-functions': 7.27.1
|
||||
'@babel/helper-optimise-call-expression': 7.27.1
|
||||
'@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.4)
|
||||
'@babel/helper-skip-transparent-expression-wrappers': 7.27.1
|
||||
'@babel/traverse': 7.27.4
|
||||
semver: 6.3.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/helper-member-expression-to-functions@7.27.1':
|
||||
dependencies:
|
||||
'@babel/traverse': 7.27.4
|
||||
'@babel/types': 7.27.6
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/helper-module-imports@7.27.1':
|
||||
dependencies:
|
||||
'@babel/traverse': 7.27.4
|
||||
'@babel/types': 7.27.6
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/helper-module-imports': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
'@babel/traverse': 7.27.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/helper-optimise-call-expression@7.27.1':
|
||||
dependencies:
|
||||
'@babel/types': 7.27.6
|
||||
|
||||
'@babel/helper-plugin-utils@7.27.1': {}
|
||||
|
||||
'@babel/helper-replace-supers@7.27.1(@babel/core@7.27.4)':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/helper-member-expression-to-functions': 7.27.1
|
||||
'@babel/helper-optimise-call-expression': 7.27.1
|
||||
'@babel/traverse': 7.27.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/helper-skip-transparent-expression-wrappers@7.27.1':
|
||||
dependencies:
|
||||
'@babel/traverse': 7.27.4
|
||||
'@babel/types': 7.27.6
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/helper-string-parser@7.25.9': {}
|
||||
|
||||
'@babel/helper-string-parser@7.27.1': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.25.9': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.27.1': {}
|
||||
|
||||
'@babel/helper-validator-option@7.27.1': {}
|
||||
|
||||
'@babel/helpers@7.27.6':
|
||||
dependencies:
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/types': 7.27.6
|
||||
|
||||
'@babel/parser@7.27.0':
|
||||
dependencies:
|
||||
'@babel/types': 7.27.0
|
||||
|
||||
'@babel/parser@7.27.5':
|
||||
dependencies:
|
||||
'@babel/types': 7.27.6
|
||||
|
||||
'@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.4)':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
|
||||
'@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.4)':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
|
||||
'@babel/plugin-transform-typescript@7.27.1(@babel/core@7.27.4)':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/helper-annotate-as-pure': 7.27.3
|
||||
'@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4)
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/helper-skip-transparent-expression-wrappers': 7.27.1
|
||||
'@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.4)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/runtime@7.26.10':
|
||||
dependencies:
|
||||
regenerator-runtime: 0.14.1
|
||||
|
||||
'@babel/template@7.27.2':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.27.1
|
||||
'@babel/parser': 7.27.5
|
||||
'@babel/types': 7.27.6
|
||||
|
||||
'@babel/traverse@7.27.4':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.27.1
|
||||
'@babel/generator': 7.27.5
|
||||
'@babel/parser': 7.27.5
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/types': 7.27.6
|
||||
debug: 4.4.0
|
||||
globals: 11.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/types@7.27.0':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.25.9
|
||||
'@babel/helper-validator-identifier': 7.25.9
|
||||
|
||||
'@babel/types@7.27.6':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
|
||||
'@bufbuild/protobuf@2.2.3': {}
|
||||
|
||||
'@css-render/plugin-bem@0.15.14(css-render@0.15.14)':
|
||||
@ -6851,8 +7377,23 @@ snapshots:
|
||||
wrap-ansi: 8.1.0
|
||||
wrap-ansi-cjs: wrap-ansi@7.0.0
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.8':
|
||||
dependencies:
|
||||
'@jridgewell/set-array': 1.2.1
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2': {}
|
||||
|
||||
'@jridgewell/set-array@1.2.1': {}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.25':
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
|
||||
'@juggle/resize-observer@3.4.0': {}
|
||||
|
||||
'@mdn/browser-compat-data@5.7.3': {}
|
||||
@ -6886,6 +7427,8 @@ snapshots:
|
||||
'@pnpm/network.ca-file': 1.0.2
|
||||
config-chain: 1.1.13
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.17': {}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.39.0':
|
||||
optional: true
|
||||
|
||||
@ -6987,6 +7530,17 @@ snapshots:
|
||||
|
||||
'@ungap/structured-clone@1.2.1': {}
|
||||
|
||||
'@vitejs/plugin-vue-jsx@4.2.0(vite@6.2.4(@types/node@22.10.5)(sass-embedded@1.86.2)(tsx@4.19.2)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.2))':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.4)
|
||||
'@rolldown/pluginutils': 1.0.0-beta.17
|
||||
'@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.27.4)
|
||||
vite: 6.2.4(@types/node@22.10.5)(sass-embedded@1.86.2)(tsx@4.19.2)(yaml@2.7.1)
|
||||
vue: 3.5.13(typescript@5.8.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vitejs/plugin-vue@5.2.1(vite@6.2.4(@types/node@22.10.5)(sass-embedded@1.86.2)(tsx@4.19.2)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.2))':
|
||||
dependencies:
|
||||
vite: 6.2.4(@types/node@22.10.5)(sass-embedded@1.86.2)(tsx@4.19.2)(yaml@2.7.1)
|
||||
@ -7032,6 +7586,35 @@ snapshots:
|
||||
loupe: 3.1.3
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vue/babel-helper-vue-transform-on@1.4.0': {}
|
||||
|
||||
'@vue/babel-plugin-jsx@1.4.0(@babel/core@7.27.4)':
|
||||
dependencies:
|
||||
'@babel/helper-module-imports': 7.27.1
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4)
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/traverse': 7.27.4
|
||||
'@babel/types': 7.27.0
|
||||
'@vue/babel-helper-vue-transform-on': 1.4.0
|
||||
'@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.27.4)
|
||||
'@vue/shared': 3.5.13
|
||||
optionalDependencies:
|
||||
'@babel/core': 7.27.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.27.4)':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.26.2
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/helper-module-imports': 7.27.1
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/parser': 7.27.0
|
||||
'@vue/compiler-sfc': 3.5.13
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vue/compiler-core@3.5.13':
|
||||
dependencies:
|
||||
'@babel/parser': 7.27.0
|
||||
@ -7359,6 +7942,13 @@ snapshots:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
browserslist@4.25.0:
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001723
|
||||
electron-to-chromium: 1.5.170
|
||||
node-releases: 2.0.19
|
||||
update-browserslist-db: 1.1.3(browserslist@4.25.0)
|
||||
|
||||
buffer-builder@0.2.0: {}
|
||||
|
||||
buffer-crc32@0.2.13: {}
|
||||
@ -7406,6 +7996,8 @@ snapshots:
|
||||
|
||||
camelcase@8.0.0: {}
|
||||
|
||||
caniuse-lite@1.0.30001723: {}
|
||||
|
||||
chai@5.2.0:
|
||||
dependencies:
|
||||
assertion-error: 2.0.1
|
||||
@ -7576,6 +8168,8 @@ snapshots:
|
||||
graceful-fs: 4.2.11
|
||||
xdg-basedir: 5.1.0
|
||||
|
||||
convert-source-map@2.0.0: {}
|
||||
|
||||
core-util-is@1.0.3: {}
|
||||
|
||||
cosmiconfig@9.0.0(typescript@5.8.2):
|
||||
@ -7786,6 +8380,8 @@ snapshots:
|
||||
minimatch: 9.0.1
|
||||
semver: 7.6.3
|
||||
|
||||
electron-to-chromium@1.5.170: {}
|
||||
|
||||
emittery@1.1.0: {}
|
||||
|
||||
emoji-regex@10.4.0: {}
|
||||
@ -8221,6 +8817,8 @@ snapshots:
|
||||
which: 1.2.4
|
||||
winreg: 0.0.12
|
||||
|
||||
gensync@1.0.0-beta.2: {}
|
||||
|
||||
get-caller-file@2.0.5: {}
|
||||
|
||||
get-east-asian-width@1.3.0: {}
|
||||
@ -8307,6 +8905,8 @@ snapshots:
|
||||
dependencies:
|
||||
ini: 4.1.1
|
||||
|
||||
globals@11.12.0: {}
|
||||
|
||||
globals@13.24.0:
|
||||
dependencies:
|
||||
type-fest: 0.20.2
|
||||
@ -8654,6 +9254,8 @@ snapshots:
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
json-buffer@3.0.1: {}
|
||||
|
||||
json-merge-patch@1.0.2:
|
||||
@ -8672,6 +9274,8 @@ snapshots:
|
||||
|
||||
json-stable-stringify-without-jsonify@1.0.1: {}
|
||||
|
||||
json5@2.2.3: {}
|
||||
|
||||
jsonfile@6.1.0:
|
||||
dependencies:
|
||||
universalify: 2.0.1
|
||||
@ -8818,6 +9422,10 @@ snapshots:
|
||||
|
||||
lru-cache@11.0.2: {}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
|
||||
magic-string@0.30.17:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
@ -8937,6 +9545,8 @@ snapshots:
|
||||
uuid: 8.3.2
|
||||
which: 2.0.2
|
||||
|
||||
node-releases@2.0.19: {}
|
||||
|
||||
node-rsa@1.1.1:
|
||||
dependencies:
|
||||
asn1: 0.2.6
|
||||
@ -9527,6 +10137,8 @@ snapshots:
|
||||
|
||||
semver@5.7.2: {}
|
||||
|
||||
semver@6.3.1: {}
|
||||
|
||||
semver@7.6.3: {}
|
||||
|
||||
semver@7.7.1: {}
|
||||
@ -9963,7 +10575,7 @@ snapshots:
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.2
|
||||
|
||||
unplugin-vue-components@28.4.1(@babel/parser@7.27.0)(vue@3.5.13(typescript@5.8.2)):
|
||||
unplugin-vue-components@28.4.1(@babel/parser@7.27.5)(vue@3.5.13(typescript@5.8.2)):
|
||||
dependencies:
|
||||
chokidar: 3.6.0
|
||||
debug: 4.4.0
|
||||
@ -9975,7 +10587,7 @@ snapshots:
|
||||
unplugin-utils: 0.2.4
|
||||
vue: 3.5.13(typescript@5.8.2)
|
||||
optionalDependencies:
|
||||
'@babel/parser': 7.27.0
|
||||
'@babel/parser': 7.27.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@ -10001,6 +10613,12 @@ snapshots:
|
||||
|
||||
upath@2.0.1: {}
|
||||
|
||||
update-browserslist-db@1.1.3(browserslist@4.25.0):
|
||||
dependencies:
|
||||
browserslist: 4.25.0
|
||||
escalade: 3.2.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
update-notifier@7.3.1:
|
||||
dependencies:
|
||||
boxen: 8.0.1
|
||||
@ -10331,6 +10949,8 @@ snapshots:
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
yallist@3.1.1: {}
|
||||
|
||||
yaml@2.7.1: {}
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
@ -48,7 +48,18 @@ const emit = defineEmits<{
|
||||
</template>
|
||||
导入
|
||||
</n-button>
|
||||
<n-button type="default" ghost :round="round" :size="size" @click="emit('export')">
|
||||
<n-popover v-if="$slots.exporter" trigger="hover" placement="bottom">
|
||||
<template #trigger>
|
||||
<n-button type="default" ghost :round="round" :size="size" @click="emit('export')">
|
||||
<template #icon>
|
||||
<ion-arrow-up-right-box-outline />
|
||||
</template>
|
||||
导出
|
||||
</n-button>
|
||||
</template>
|
||||
<slot name="exporter" />
|
||||
</n-popover>
|
||||
<n-button v-else type="default" ghost :round="round" :size="size" @click="emit('export')">
|
||||
<template #icon>
|
||||
<ion-arrow-up-right-box-outline />
|
||||
</template>
|
||||
|
||||
135
src/composables/useCloudExporter.ts
Normal file
135
src/composables/useCloudExporter.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import { remoteHost } from '~/env';
|
||||
import { useLongTask } from './useLongTask';
|
||||
|
||||
type AddDataFragmentsCommand = {
|
||||
commandType: 'add-data-fragments';
|
||||
params: Array<DataFragment>;
|
||||
};
|
||||
|
||||
type ExportExcelCommand = {
|
||||
commandType: 'export-excel';
|
||||
};
|
||||
|
||||
type Command = AddDataFragmentsCommand | ExportExcelCommand;
|
||||
|
||||
type WebSocketResponse =
|
||||
| { type: 'progress'; current: number; total: number }
|
||||
| { type: 'result'; result: string };
|
||||
|
||||
class ExportExcelPipeline {
|
||||
private socket: WebSocket;
|
||||
|
||||
constructor() {
|
||||
this.socket = new WebSocket(`ws://${remoteHost}/ws/daa0b9f1-4e4a-4e7c-9269-f5f0e86ae271`);
|
||||
}
|
||||
|
||||
private sendCommand(command: Command) {
|
||||
const commandJson = JSON.stringify({ command });
|
||||
this.socket!.send(commandJson);
|
||||
}
|
||||
|
||||
public load() {
|
||||
switch (this.socket.readyState) {
|
||||
case WebSocket.CLOSED:
|
||||
case WebSocket.CLOSING:
|
||||
this.socket = new WebSocket(`ws://${remoteHost}/ws/daa0b9f1-4e4a-4e7c-9269-f5f0e86ae271`);
|
||||
case WebSocket.CONNECTING:
|
||||
return new Promise<ExportExcelPipeline>((resolve) => {
|
||||
this.socket!.onopen = () => resolve(this);
|
||||
});
|
||||
case WebSocket.OPEN:
|
||||
return Promise.resolve(this);
|
||||
default:
|
||||
return Promise.resolve(this);
|
||||
}
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.socket.close();
|
||||
return new Promise<void>(async (resolve) => {
|
||||
while (this.socket.readyState != WebSocket.CLOSED) {
|
||||
await new Promise((r) => setTimeout(r, 100));
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
public addFragments(...fragments: DataFragment[]) {
|
||||
this.sendCommand({ commandType: 'add-data-fragments', params: fragments });
|
||||
return this;
|
||||
}
|
||||
|
||||
public exportExcel(progress?: (current: number, total: number) => Promise<void> | void) {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
this.socket.onmessage = (ev) => {
|
||||
const response: WebSocketResponse = JSON.parse(ev.data);
|
||||
switch (response.type) {
|
||||
case 'progress':
|
||||
const { current, total } = response;
|
||||
progress && progress(current, total);
|
||||
break;
|
||||
case 'result':
|
||||
this.socket!.onmessage = null;
|
||||
const fileUrl = response.result;
|
||||
resolve(fileUrl);
|
||||
break;
|
||||
default:
|
||||
console.error('Unknown message type:', response);
|
||||
}
|
||||
};
|
||||
this.socket.onclose = () => {
|
||||
reject('Connection is closed');
|
||||
};
|
||||
this.sendCommand({ commandType: 'export-excel' });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export type DataFragment = {
|
||||
data: Array<Record<string, any>>;
|
||||
imageColumn?: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export const useCloudExporter = () => {
|
||||
const { isRunning, startTask } = useLongTask();
|
||||
const progress = reactive({ current: 0, total: 0 });
|
||||
let pipeline: ExportExcelPipeline | null = null;
|
||||
|
||||
const stop = async () => {
|
||||
if (pipeline) {
|
||||
await pipeline.close();
|
||||
pipeline = null;
|
||||
}
|
||||
};
|
||||
|
||||
const doExport = (fragments: DataFragment[]) =>
|
||||
startTask(async () => {
|
||||
progress.current = 0;
|
||||
progress.total = 0;
|
||||
|
||||
pipeline = new ExportExcelPipeline();
|
||||
await pipeline.load();
|
||||
pipeline.addFragments(...fragments);
|
||||
const file = await pipeline
|
||||
.exportExcel((current, total) => {
|
||||
progress.current = current;
|
||||
progress.total = total;
|
||||
})
|
||||
.catch(() => null);
|
||||
await pipeline.close();
|
||||
|
||||
if (file) {
|
||||
const url = `http://${remoteHost}${file}`;
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `${dayjs().format('YYYY-MM-DD')}.xlsx`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
return file?.split('/').pop();
|
||||
});
|
||||
return { isRunning, progress, doExport, stop };
|
||||
};
|
||||
@ -1,17 +1,19 @@
|
||||
export function useLongTask() {
|
||||
const isRunning = ref(false);
|
||||
|
||||
const startTask = async (task: () => Promise<void>) => {
|
||||
isRunning.value = true;
|
||||
|
||||
try {
|
||||
await task();
|
||||
isRunning.value = false;
|
||||
} catch (error) {
|
||||
isRunning.value = false;
|
||||
console.error('Task failed:', error);
|
||||
throw error;
|
||||
const startTask = async <T = undefined>(task: () => Promise<T>) => {
|
||||
if (isRunning.value) {
|
||||
throw Error('Task is still running.');
|
||||
}
|
||||
isRunning.value = true;
|
||||
let result = undefined;
|
||||
try {
|
||||
result = await task();
|
||||
} catch (error) {
|
||||
console.error('Task failed:', error);
|
||||
}
|
||||
isRunning.value = false;
|
||||
return result as T;
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -12,3 +12,5 @@ export function isForbiddenUrl(url: string): boolean {
|
||||
}
|
||||
|
||||
export const isFirefox = navigator.userAgent.includes('Firefox');
|
||||
|
||||
export const remoteHost = __DEV__ ? '127.0.0.1:8000' : '127.0.0.1:8000';
|
||||
|
||||
@ -22,9 +22,6 @@ class Worksheet {
|
||||
const cols = headers.filter((h) => h.ignore?.out !== true);
|
||||
for (let j = 0; j < cols.length; j++) {
|
||||
const header = cols[j];
|
||||
if (header.ignore?.out) {
|
||||
continue;
|
||||
}
|
||||
const value = getAttribute(item, header.prop);
|
||||
if (header.formatOutputValue) {
|
||||
record[header.label] = await header.formatOutputValue(value, i);
|
||||
@ -196,6 +193,30 @@ export type ImportBaseOptions = {
|
||||
headers?: Header[];
|
||||
};
|
||||
|
||||
export function castRecordsByHeaders<T = Record<string, unknown>>(
|
||||
jsonData: Record<string, unknown>[],
|
||||
headers: Omit<Header, 'parseImportValue'>[],
|
||||
): Promise<T[]> {
|
||||
return Promise.all(
|
||||
jsonData.map(async (item, i) => {
|
||||
const record: Record<string, unknown> = {};
|
||||
const cols = headers.filter((h) => h.ignore?.out !== true);
|
||||
for (let j = 0; j < cols.length; j++) {
|
||||
const header = cols[j];
|
||||
const value = getAttribute(item, header.prop);
|
||||
if (header.formatOutputValue) {
|
||||
record[header.label] = await header.formatOutputValue(value, i);
|
||||
} else if (['string', 'number', 'bigint', 'boolean'].includes(typeof value)) {
|
||||
record[header.label] = value;
|
||||
} else {
|
||||
record[header.label] = JSON.stringify(value);
|
||||
}
|
||||
}
|
||||
return record as T;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出为XLSX
|
||||
* @param data 数据数组
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
AmazonDetailPageInjector,
|
||||
AmazonReviewPageInjector,
|
||||
AmazonSearchPageInjector,
|
||||
} from '../web-injectors';
|
||||
} from '~/logic/web-injectors/amazon';
|
||||
import { isForbiddenUrl } from '~/env';
|
||||
|
||||
/**
|
||||
|
||||
@ -2,7 +2,7 @@ import Emittery from 'emittery';
|
||||
import { HomedepotEvents, HomedepotWorker } from './types';
|
||||
import { Tabs } from 'webextension-polyfill';
|
||||
import { withErrorHandling } from '../error-handler';
|
||||
import { HomedepotDetailPageInjector } from '../web-injectors';
|
||||
import { HomedepotDetailPageInjector } from '~/logic/web-injectors/homedepot';
|
||||
|
||||
class HomedepotWorkerImpl implements HomedepotWorker {
|
||||
private static _instance: HomedepotWorker | null = null;
|
||||
|
||||
91
src/logic/page-worker/types.d.ts
vendored
91
src/logic/page-worker/types.d.ts
vendored
@ -39,18 +39,6 @@ type AmazonItem = Pick<AmazonSearchItem, 'asin'> &
|
||||
Partial<AmazonSearchItem> &
|
||||
Partial<AmazonDetailItem> & { hasDetail: boolean };
|
||||
|
||||
type HomedepotDetailItem = {
|
||||
OSMID: string;
|
||||
link: string;
|
||||
brandName?: string;
|
||||
title: string;
|
||||
price: string;
|
||||
rate?: string;
|
||||
innerText: string;
|
||||
reviewCount?: number;
|
||||
mainImageUrl: string;
|
||||
};
|
||||
|
||||
interface AmazonPageWorkerEvents {
|
||||
/**
|
||||
* The event is fired when worker collected links to items on the Amazon search page.
|
||||
@ -86,17 +74,6 @@ interface AmazonPageWorkerEvents {
|
||||
['error']: { message: string; url?: string };
|
||||
}
|
||||
|
||||
interface HomedepotEvents {
|
||||
/**
|
||||
* The event is fired when detail items collect
|
||||
*/
|
||||
['detail-item-collected']: { item: HomedepotDetailItem };
|
||||
/**
|
||||
* The event is fired when error occurs.
|
||||
*/
|
||||
['error']: { message: string; url?: string };
|
||||
}
|
||||
|
||||
interface AmazonPageWorker {
|
||||
/**
|
||||
* The channel for communication with the Amazon page worker.
|
||||
@ -137,6 +114,30 @@ interface AmazonPageWorker {
|
||||
stop(): Promise<void>;
|
||||
}
|
||||
|
||||
type HomedepotDetailItem = {
|
||||
OSMID: string;
|
||||
link: string;
|
||||
brandName?: string;
|
||||
title: string;
|
||||
price: string;
|
||||
rate?: string;
|
||||
innerText: string;
|
||||
reviewCount?: number;
|
||||
mainImageUrl: string;
|
||||
modelInfo?: string;
|
||||
};
|
||||
|
||||
interface HomedepotEvents {
|
||||
/**
|
||||
* The event is fired when detail items collect
|
||||
*/
|
||||
['detail-item-collected']: { item: HomedepotDetailItem };
|
||||
/**
|
||||
* The event is fired when error occurs.
|
||||
*/
|
||||
['error']: { message: string; url?: string };
|
||||
}
|
||||
|
||||
interface HomedepotWorker {
|
||||
/**
|
||||
* The channel for communication with the Homedepot page worker.
|
||||
@ -156,3 +157,47 @@ interface HomedepotWorker {
|
||||
*/
|
||||
stop(): Promise<void>;
|
||||
}
|
||||
|
||||
type LowesDetailItem = {
|
||||
OSMID: string;
|
||||
link: string;
|
||||
brandName?: string;
|
||||
title: string;
|
||||
price: string;
|
||||
rate?: string;
|
||||
innerText: string;
|
||||
reviewCount?: number;
|
||||
mainImageUrl: string;
|
||||
modelInfo?: string;
|
||||
};
|
||||
|
||||
interface LowesEvents {
|
||||
/**
|
||||
* The event is fired when detail items collect
|
||||
*/
|
||||
['detail-item-collected']: { item: LowesDetailItem };
|
||||
/**
|
||||
* The event is fired when error occurs.
|
||||
*/
|
||||
['error']: { message: string; url?: string };
|
||||
}
|
||||
|
||||
interface LowesWorker {
|
||||
/**
|
||||
* The channel for communication with the Lowes page worker.
|
||||
*/
|
||||
readonly channel: Emittery<LowesEvents>;
|
||||
|
||||
/**
|
||||
* Browsing goods detail page and collect target information
|
||||
*/
|
||||
runDetailPageTask(
|
||||
urls: string[],
|
||||
progress?: (remains: string[]) => Promise<void> | void,
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Stop the worker.
|
||||
*/
|
||||
stop(): Promise<void>;
|
||||
}
|
||||
|
||||
@ -1,24 +1,5 @@
|
||||
import { exec } from './execute-script';
|
||||
import type { Tabs } from 'webextension-polyfill';
|
||||
import type { AmazonReview, AmazonSearchItem, HomedepotDetailItem } from './page-worker/types';
|
||||
|
||||
class BaseInjector {
|
||||
readonly _tab: Tabs.Tab;
|
||||
|
||||
readonly _timeout: number;
|
||||
|
||||
constructor(tab: Tabs.Tab, timeout: number = 30000) {
|
||||
this._tab = tab;
|
||||
this._timeout = timeout;
|
||||
}
|
||||
|
||||
run<T, P extends Record<string, unknown>>(
|
||||
func: (payload: P) => Promise<T>,
|
||||
payload?: P,
|
||||
): Promise<T> {
|
||||
return exec(this._tab.id!, func, payload as P, { timeout: this._timeout });
|
||||
}
|
||||
}
|
||||
import { BaseInjector } from './base';
|
||||
import { AmazonReview, AmazonSearchItem } from '../page-worker/types';
|
||||
|
||||
export class AmazonSearchPageInjector extends BaseInjector {
|
||||
public waitForPageLoaded() {
|
||||
@ -167,7 +148,7 @@ export class AmazonDetailPageInjector extends BaseInjector {
|
||||
const targetNode = document.querySelector(
|
||||
'#prodDetails:has(td), #detailBulletsWrapper_feature_div:has(li), .av-page-desktop',
|
||||
);
|
||||
const exceptionalNodeSelectors = ['music-detail-header', '.avu-retail-page'];
|
||||
const exceptionalNodeSelectors = ['.music-detail-header', '.avu-retail-page'];
|
||||
for (const selector of exceptionalNodeSelectors) {
|
||||
if (document.querySelector(selector)) {
|
||||
return false;
|
||||
@ -444,79 +425,3 @@ export class AmazonReviewPageInjector extends BaseInjector {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class HomedepotDetailPageInjector extends BaseInjector {
|
||||
constructor(tab: Tabs.Tab) {
|
||||
super(tab, 60000);
|
||||
}
|
||||
|
||||
public waitForPageLoad() {
|
||||
return this.run(async () => {
|
||||
let timeout = false;
|
||||
setTimeout(() => (timeout = true), 15000);
|
||||
const isLoaded = () => {
|
||||
const reviewPlaceholderEl = document.querySelector(
|
||||
`#product-section-rr div[role='button'][aria-expanded='true']`,
|
||||
);
|
||||
return reviewPlaceholderEl && (document.readyState == 'complete' || timeout);
|
||||
};
|
||||
while (true) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500 + ~~(Math.random() * 500)));
|
||||
document
|
||||
.querySelector<HTMLElement>(
|
||||
`#product-section-rr div[role='button'][aria-expanded='false'], #product-section-overview div[role='button'][aria-expanded='false']`,
|
||||
)
|
||||
?.click();
|
||||
const { scrollHeight, scrollTop } = document.documentElement;
|
||||
scrollHeight - scrollTop > 100
|
||||
? window.scrollBy({ top: 100, behavior: 'smooth' })
|
||||
: document
|
||||
.querySelector('[data-component^="product-details:ProductDetailsTitle"]')
|
||||
?.scrollIntoView({ behavior: 'smooth' });
|
||||
if (isLoaded()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getInfo() {
|
||||
return this.run(async () => {
|
||||
const link = document.location.toString();
|
||||
const brandName = document.querySelector<HTMLDivElement>(
|
||||
`[data-component^="product-details:ProductDetailsBrandCollection"]`,
|
||||
)?.innerText;
|
||||
const title = document.querySelector<HTMLDivElement>(
|
||||
`[data-component^="product-details:ProductDetailsTitle"]`,
|
||||
)!.innerText;
|
||||
const price = document
|
||||
.querySelector<HTMLDivElement>(`#standard-price`)!
|
||||
.innerText.replaceAll('\n', '');
|
||||
const rateEl = document.querySelector<HTMLDivElement>(
|
||||
`[data-component^="ratings-and-reviews"] .sui-mr-1`,
|
||||
);
|
||||
const rate = rateEl ? /\d(\.\d)?/.exec(rateEl.innerText)![0] : undefined;
|
||||
const reviewCount = rateEl
|
||||
? Number(
|
||||
/\d+/.exec(
|
||||
document.querySelector<HTMLDivElement>(
|
||||
`[data-component^="ratings-and-reviews"] [name="simple-rating"] + span`,
|
||||
)!.innerText,
|
||||
)![0],
|
||||
)
|
||||
: undefined;
|
||||
const mainImageUrl = document.querySelector<HTMLImageElement>(
|
||||
`.mediagallery__mainimage img`,
|
||||
)!.src;
|
||||
return {
|
||||
link,
|
||||
brandName,
|
||||
title,
|
||||
price,
|
||||
rate,
|
||||
reviewCount,
|
||||
mainImageUrl,
|
||||
} as Omit<HomedepotDetailItem, 'OSMID'>;
|
||||
});
|
||||
}
|
||||
}
|
||||
20
src/logic/web-injectors/base.ts
Normal file
20
src/logic/web-injectors/base.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Tabs } from 'webextension-polyfill';
|
||||
import { exec } from '~/logic/execute-script';
|
||||
|
||||
export class BaseInjector {
|
||||
readonly _tab: Tabs.Tab;
|
||||
|
||||
readonly _timeout: number;
|
||||
|
||||
constructor(tab: Tabs.Tab, timeout: number = 30000) {
|
||||
this._tab = tab;
|
||||
this._timeout = timeout;
|
||||
}
|
||||
|
||||
run<T, P extends Record<string, unknown>>(
|
||||
func: (payload: P) => Promise<T>,
|
||||
payload?: P,
|
||||
): Promise<T> {
|
||||
return exec(this._tab.id!, func, payload as P, { timeout: this._timeout });
|
||||
}
|
||||
}
|
||||
87
src/logic/web-injectors/homedepot.ts
Normal file
87
src/logic/web-injectors/homedepot.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { Tabs } from 'webextension-polyfill';
|
||||
import { BaseInjector } from './base';
|
||||
import { HomedepotDetailItem } from '../page-worker/types';
|
||||
|
||||
export class HomedepotDetailPageInjector extends BaseInjector {
|
||||
constructor(tab: Tabs.Tab) {
|
||||
super(tab, 60000);
|
||||
}
|
||||
|
||||
public waitForPageLoad() {
|
||||
return this.run(async () => {
|
||||
let timeout = false;
|
||||
setTimeout(() => (timeout = true), 15000);
|
||||
const isLoaded = () => {
|
||||
const reviewPlaceholderEl = document.querySelector(
|
||||
`#product-section-rr div[role='button'][aria-expanded='true']`,
|
||||
);
|
||||
return reviewPlaceholderEl && (document.readyState == 'complete' || timeout);
|
||||
};
|
||||
while (true) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500 + ~~(Math.random() * 500)));
|
||||
document
|
||||
.querySelector<HTMLElement>(
|
||||
`#product-section-rr div[role='button'][aria-expanded='false'], #product-section-overview div[role='button'][aria-expanded='false']`,
|
||||
)
|
||||
?.click();
|
||||
const { scrollHeight, scrollTop } = document.documentElement;
|
||||
scrollHeight - scrollTop > 100
|
||||
? window.scrollBy({ top: 100, behavior: 'smooth' })
|
||||
: document
|
||||
.querySelector('[data-component^="product-details:ProductDetailsTitle"]')
|
||||
?.scrollIntoView({ behavior: 'smooth' });
|
||||
if (isLoaded()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getInfo() {
|
||||
return this.run(async () => {
|
||||
const link = document.location.toString();
|
||||
const brandName = document.querySelector<HTMLDivElement>(
|
||||
`[data-component^="product-details:ProductDetailsBrandCollection"]`,
|
||||
)?.innerText;
|
||||
const title = document.querySelector<HTMLDivElement>(
|
||||
`[data-component^="product-details:ProductDetailsTitle"]`,
|
||||
)!.innerText;
|
||||
const price = document
|
||||
.querySelector<HTMLDivElement>(`#standard-price`)!
|
||||
.innerText.replaceAll('\n', '');
|
||||
const rateEl = document.querySelector<HTMLDivElement>(
|
||||
`[data-component^="ratings-and-reviews"] .sui-mr-1`,
|
||||
);
|
||||
const rate = rateEl ? /\d(\.\d)?/.exec(rateEl.innerText)![0] : undefined;
|
||||
const reviewCount = rateEl
|
||||
? Number(
|
||||
/\d+/.exec(
|
||||
document.querySelector<HTMLDivElement>(
|
||||
`[data-component^="ratings-and-reviews"] [name="simple-rating"] + span`,
|
||||
)!.innerText,
|
||||
)![0],
|
||||
)
|
||||
: undefined;
|
||||
const mainImageUrl = document.querySelector<HTMLImageElement>(
|
||||
`.mediagallery__mainimage img`,
|
||||
)!.src;
|
||||
const modelInfoEl = document.evaluate(
|
||||
`//*[@id="product-section-product-overview"]//*[@class="product-info-bar"]//*[starts-with(text(), "Model #")]`,
|
||||
document,
|
||||
null,
|
||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||
).singleNodeValue as HTMLDivElement | null;
|
||||
const [modelInfo] = /(?<=#\s).+/.exec(modelInfoEl?.innerText || '') || [];
|
||||
return {
|
||||
link,
|
||||
brandName,
|
||||
title,
|
||||
price,
|
||||
rate,
|
||||
reviewCount,
|
||||
mainImageUrl,
|
||||
modelInfo,
|
||||
} as Omit<HomedepotDetailItem, 'OSMID'>;
|
||||
});
|
||||
}
|
||||
}
|
||||
85
src/logic/web-injectors/lowes.ts
Normal file
85
src/logic/web-injectors/lowes.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { Tabs } from 'webextension-polyfill';
|
||||
import { BaseInjector } from './base';
|
||||
|
||||
export class LowesDetailPageInjector extends BaseInjector {
|
||||
constructor(tab: Tabs.Tab) {
|
||||
super(tab, 60000);
|
||||
}
|
||||
|
||||
public waitForPageLoad() {
|
||||
return this.run(async () => {
|
||||
while (true) {
|
||||
if (document.readyState === 'complete') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getBaseInfo() {
|
||||
return this.run(async () => {
|
||||
// 获取Item #
|
||||
const itemNumberEl = document.evaluate(
|
||||
`//p[starts-with(text(), "Item #")]`,
|
||||
document,
|
||||
null,
|
||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||
).singleNodeValue as HTMLDivElement | null;
|
||||
const itemSeries = itemNumberEl?.innerText.replace('Item #', '').trim();
|
||||
|
||||
// 获取Model #
|
||||
const modelNumberEl = document.evaluate(
|
||||
`//p[starts-with(text(), "Model #")]`,
|
||||
document,
|
||||
null,
|
||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||
).singleNodeValue as HTMLDivElement | null;
|
||||
const modelSeries = modelNumberEl?.innerText.replace('Model #', '').trim();
|
||||
|
||||
// 获取品牌名称
|
||||
const brandName = (
|
||||
document.evaluate(
|
||||
`//h1[contains(@class, "product-brand-description")]/parent::*/parent::*/following-sibling::*[1]//a`,
|
||||
document,
|
||||
null,
|
||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||
).singleNodeValue as HTMLDivElement
|
||||
).innerText;
|
||||
|
||||
// 获取标题
|
||||
const title = document.querySelector<HTMLDivElement>(
|
||||
`h1.product-brand-description`,
|
||||
)?.innerText;
|
||||
|
||||
// 获取价格
|
||||
const price = document
|
||||
.querySelector<HTMLDivElement>(`.screen-reader`)
|
||||
?.innerText.replaceAll('\n', '');
|
||||
|
||||
// 获取评分
|
||||
const rate = document.querySelector<HTMLDivElement>(`.avgrating`)?.innerText;
|
||||
|
||||
// 获取评价数量
|
||||
const reviewCount = Number(
|
||||
document.querySelector<HTMLDivElement>(`[data-testid="rating-trigger"] > div > div > span`)
|
||||
?.innerText || '0',
|
||||
);
|
||||
|
||||
// 获取图片URL
|
||||
const mainImageUrl = document.querySelector<HTMLImageElement>(
|
||||
`#mfe-gallery .productImage.tile-img`,
|
||||
)?.src;
|
||||
|
||||
return {
|
||||
brandName,
|
||||
title,
|
||||
price,
|
||||
rate,
|
||||
reviewCount,
|
||||
mainImageUrl,
|
||||
itemSeries,
|
||||
modelSeries,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -15,12 +15,14 @@ const theme: GlobalThemeOverrides = {
|
||||
<template>
|
||||
<!-- Naive UI Wrapper-->
|
||||
<n-config-provider :theme-overrides="theme">
|
||||
<n-message-provider>
|
||||
<n-dialog-provider>
|
||||
<n-modal-provider>
|
||||
<options />
|
||||
</n-modal-provider>
|
||||
</n-dialog-provider>
|
||||
</n-message-provider>
|
||||
<n-loading-bar-provider>
|
||||
<n-message-provider>
|
||||
<n-dialog-provider>
|
||||
<n-modal-provider>
|
||||
<options />
|
||||
</n-modal-provider>
|
||||
</n-dialog-provider>
|
||||
</n-message-provider>
|
||||
</n-loading-bar-provider>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
||||
@ -3,23 +3,31 @@ import { RouterView } from 'vue-router';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<header>
|
||||
<span></span>
|
||||
<h1 class="header-title">采集结果</h1>
|
||||
<!-- <result-table class="result-table" /> -->
|
||||
<span></span>
|
||||
</header>
|
||||
<main>
|
||||
<router-view />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
.header-title {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.header-title {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.result-table {
|
||||
height: 90vh;
|
||||
width: 95vw;
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
<script setup lang="tsx">
|
||||
import { NButton, NSpace } from 'naive-ui';
|
||||
import type { TableColumn } from 'naive-ui/es/data-table/src/interface';
|
||||
import { createWorkbook, Header, importFromXLSX } from '~/logic/data-io';
|
||||
import type { AmazonDetailItem, AmazonItem, AmazonReview } from '~/logic/page-worker/types';
|
||||
import { useCloudExporter } from '~/composables/useCloudExporter';
|
||||
import { castRecordsByHeaders, createWorkbook, Header, importFromXLSX } from '~/logic/data-io';
|
||||
import type { AmazonItem, AmazonReview } from '~/logic/page-worker/types';
|
||||
import { allItems, reviewItems } from '~/logic/storage';
|
||||
import DetailDescription from '~/components/DetailDescription.vue';
|
||||
import ReviewPreview from '~/components/ReviewPreview.vue';
|
||||
|
||||
const message = useMessage();
|
||||
const modal = useModal();
|
||||
const cloudExporter = useCloudExporter();
|
||||
|
||||
const page = reactive({ current: 1, size: 10 });
|
||||
|
||||
@ -49,7 +49,7 @@ const columns: (TableColumn<AmazonItem> & { hidden?: boolean })[] = [
|
||||
type: 'expand',
|
||||
expandable: (row) => row.hasDetail,
|
||||
renderExpand(row) {
|
||||
return h(DetailDescription, { model: row as AmazonDetailItem }, () => '');
|
||||
return <detail-description model={row} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -76,7 +76,7 @@ const columns: (TableColumn<AmazonItem> & { hidden?: boolean })[] = [
|
||||
title: '标题',
|
||||
key: 'title',
|
||||
render(row) {
|
||||
return h('div', { style: {} }, `${row.title}`);
|
||||
return <div>{row.title}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -99,49 +99,40 @@ const columns: (TableColumn<AmazonItem> & { hidden?: boolean })[] = [
|
||||
key: 'actions',
|
||||
minWidth: 100,
|
||||
render(row) {
|
||||
return h(NSpace, {}, () =>
|
||||
[
|
||||
{
|
||||
text: '评论',
|
||||
disabled: !reviewItems.value.has(row.asin),
|
||||
onClick: () => {
|
||||
const asin = row.asin;
|
||||
modal.create({
|
||||
title: `${asin}评论`,
|
||||
preset: 'card',
|
||||
style: {
|
||||
width: '80vw',
|
||||
height: '85vh',
|
||||
},
|
||||
content: () =>
|
||||
h(ReviewPreview, {
|
||||
asin,
|
||||
}),
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '链接',
|
||||
onClick: () => {
|
||||
browser.tabs.create({
|
||||
active: true,
|
||||
url: row.link,
|
||||
});
|
||||
},
|
||||
},
|
||||
].map(({ text, onClick, disabled }) =>
|
||||
h(
|
||||
NButton,
|
||||
return (
|
||||
<n-space>
|
||||
{[
|
||||
{
|
||||
type: 'primary',
|
||||
text: true,
|
||||
size: 'small',
|
||||
disabled: disabled,
|
||||
onClick: onClick,
|
||||
text: '评论',
|
||||
disabled: !reviewItems.value.has(row.asin),
|
||||
onClick: () => {
|
||||
const asin = row.asin;
|
||||
modal.create({
|
||||
title: `${asin}评论`,
|
||||
preset: 'card',
|
||||
style: {
|
||||
width: '80vw',
|
||||
height: '85vh',
|
||||
},
|
||||
content: () => <review-preview asin={asin} />,
|
||||
});
|
||||
},
|
||||
},
|
||||
() => text,
|
||||
),
|
||||
),
|
||||
{
|
||||
text: '链接',
|
||||
onClick: () => {
|
||||
browser.tabs.create({
|
||||
active: true,
|
||||
url: row.link,
|
||||
});
|
||||
},
|
||||
},
|
||||
].map(({ text, onClick, disabled }) => (
|
||||
<n-button type="primary" text size="small" disabled={disabled} onClick={onClick}>
|
||||
{text}
|
||||
</n-button>
|
||||
))}
|
||||
</n-space>
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -231,7 +222,7 @@ const filterItemData = (data: AmazonItem[]): AmazonItem[] => {
|
||||
return data;
|
||||
};
|
||||
|
||||
const handleExport = async () => {
|
||||
const handleLocalExport = async () => {
|
||||
const itemHeaders = getItemHeaders();
|
||||
const items = toRaw(itemView.value).origin;
|
||||
const asins = new Set(items.map((e) => e.asin));
|
||||
@ -249,9 +240,34 @@ const handleExport = async () => {
|
||||
const sheet2 = wb.addSheet('reviews');
|
||||
await sheet2.readJson(reviews, { headers: reviewHeaders });
|
||||
await wb.exportFile(`Items ${dayjs().format('YYYY-MM-DD')}.xlsx`);
|
||||
|
||||
message.info('导出完成');
|
||||
};
|
||||
|
||||
const handleCloudExport = async () => {
|
||||
message.warning('正在导出,请勿关闭当前页面!', { duration: 2000 });
|
||||
|
||||
const itemHeaders = getItemHeaders();
|
||||
const items = toRaw(itemView.value).origin;
|
||||
const asins = new Set(items.map((e) => e.asin));
|
||||
const reviews = toRaw(reviewItems.value)
|
||||
.entries()
|
||||
.filter(([asin]) => asins.has(asin))
|
||||
.reduce<(AmazonReview & { asin: string })[]>((a, [asin, reviews]) => {
|
||||
a.push(...reviews.map((r) => ({ asin, ...r })));
|
||||
return a;
|
||||
}, []);
|
||||
const mappedData1 = await castRecordsByHeaders(items, itemHeaders);
|
||||
const mappedData2 = await castRecordsByHeaders(reviews, reviewHeaders);
|
||||
const fragments = [
|
||||
{ data: mappedData1, imageColumn: '商品图片链接', name: 'items' },
|
||||
{ data: mappedData2, imageColumn: '图片链接', name: 'reviews' },
|
||||
];
|
||||
const filename = await cloudExporter.doExport(fragments);
|
||||
|
||||
filename && message.info(`导出完成`);
|
||||
};
|
||||
|
||||
const handleImport = async (file: File) => {
|
||||
const itemHeaders = getItemHeaders();
|
||||
const wb = await importFromXLSX(file, { asWorkBook: true });
|
||||
@ -306,13 +322,46 @@ const handleClearData = async () => {
|
||||
clearable
|
||||
style="min-width: 230px"
|
||||
/>
|
||||
<control-strip
|
||||
round
|
||||
size="small"
|
||||
@clear="handleClearData"
|
||||
@export="handleExport"
|
||||
@import="handleImport"
|
||||
>
|
||||
<control-strip round size="small" @clear="handleClearData" @import="handleImport">
|
||||
<template #exporter>
|
||||
<ul v-if="!cloudExporter.isRunning.value" class="exporter-menu">
|
||||
<li @click="handleLocalExport">
|
||||
<n-tooltip :delay="1000" placement="right">
|
||||
<template #trigger>
|
||||
<div class="menu-item">
|
||||
<n-icon><lucide-sheet /></n-icon>
|
||||
<span>本地导出</span>
|
||||
</div>
|
||||
</template>
|
||||
不包含图片
|
||||
</n-tooltip>
|
||||
</li>
|
||||
<li @click="handleCloudExport">
|
||||
<n-tooltip :delay="1000" placement="right">
|
||||
<template #trigger>
|
||||
<div class="menu-item">
|
||||
<n-icon><ic-outline-cloud /></n-icon>
|
||||
<span>云端导出</span>
|
||||
</div>
|
||||
</template>
|
||||
包含图片
|
||||
</n-tooltip>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-else class="expoter-progress-panel">
|
||||
<n-progress
|
||||
type="circle"
|
||||
:percentage="
|
||||
(cloudExporter.progress.current * 100) / cloudExporter.progress.total
|
||||
"
|
||||
>
|
||||
<span>
|
||||
{{ cloudExporter.progress.current }}/{{ cloudExporter.progress.total }}
|
||||
</span>
|
||||
</n-progress>
|
||||
<n-button @click="cloudExporter.stop()">停止</n-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #filter>
|
||||
<div class="filter-section">
|
||||
<div class="filter-title">筛选器</div>
|
||||
@ -383,6 +432,49 @@ const handleClearData = async () => {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.exporter-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
font-size: 15px;
|
||||
|
||||
li {
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
color: #222;
|
||||
user-select: none;
|
||||
border-radius: 6px;
|
||||
|
||||
&:hover {
|
||||
background: #f0f6fa;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.expoter-progress-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
padding: 10px;
|
||||
gap: 15px;
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
min-width: 250px;
|
||||
|
||||
|
||||
@ -35,12 +35,14 @@ watch(currentUrl, (newVal) => {
|
||||
<template>
|
||||
<!-- Naive UI Wrapper-->
|
||||
<n-config-provider :theme-overrides="theme">
|
||||
<n-message-provider>
|
||||
<n-dialog-provider>
|
||||
<n-modal-provider>
|
||||
<router-view />
|
||||
</n-modal-provider>
|
||||
</n-dialog-provider>
|
||||
</n-message-provider>
|
||||
<n-loading-bar-provider>
|
||||
<n-message-provider>
|
||||
<n-dialog-provider>
|
||||
<n-modal-provider>
|
||||
<router-view />
|
||||
</n-modal-provider>
|
||||
</n-dialog-provider>
|
||||
</n-message-provider>
|
||||
</n-loading-bar-provider>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
||||
@ -29,7 +29,7 @@ const running = ref(false);
|
||||
|
||||
<template>
|
||||
<div class="side-panel">
|
||||
<div class="header-menu">
|
||||
<header class="header-menu">
|
||||
<n-tabs
|
||||
:tab-style="{ cursor: running ? 'not-allowed' : undefined }"
|
||||
placement="top"
|
||||
@ -46,12 +46,12 @@ const running = ref(false);
|
||||
>
|
||||
<n-tab v-for="tab in tabs" :name="tab.name" />
|
||||
</n-tabs>
|
||||
</div>
|
||||
<div class="main-content">
|
||||
</header>
|
||||
<main class="main-content">
|
||||
<keep-alive>
|
||||
<Component :is="currentComponent" @start="running = true" @stop="running = false" />
|
||||
</keep-alive>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"incremental": false,
|
||||
"target": "es2016",
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "vue",
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"baseUrl": ".",
|
||||
"module": "ESNext",
|
||||
|
||||
@ -4,6 +4,7 @@ import { dirname, relative } from 'node:path';
|
||||
import type { UserConfig } from 'vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import Vue from '@vitejs/plugin-vue';
|
||||
import VueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import Icons from 'unplugin-icons/vite';
|
||||
import IconsResolver from 'unplugin-icons/resolver';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
@ -26,6 +27,7 @@ export const sharedConfig: UserConfig = {
|
||||
},
|
||||
plugins: [
|
||||
Vue(),
|
||||
VueJsx(),
|
||||
AutoImport({
|
||||
imports: [
|
||||
'vue',
|
||||
@ -100,11 +102,11 @@ export default defineConfig(({ command }) => ({
|
||||
sidepanel: r('src/sidepanel/index.html'),
|
||||
options: r('src/options/index.html'),
|
||||
},
|
||||
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
if (id.includes('node_modules')) {
|
||||
if (id.includes('naive-ui')) return 'vendor-naive-ui';
|
||||
if (id.includes('exceljs')) return 'vendor-exceljs';
|
||||
else if (id.includes('naive-ui')) return 'vendor-naive-ui';
|
||||
else if (id.includes('vue')) return 'vendor-vue';
|
||||
return 'vendor';
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user