20261104 #17
@ -10,3 +10,6 @@ VITE_API_ASSET_ORIGIN=https://api.test.ideship.com/admin
|
|||||||
VITE_OMS_ADMIN_URL=https://api.test.ideship.com/admin
|
VITE_OMS_ADMIN_URL=https://api.test.ideship.com/admin
|
||||||
|
|
||||||
VITE_WECOM_KF_URL=https://work.weixin.qq.com/kfid/kfcae09c206f6c7045a
|
VITE_WECOM_KF_URL=https://work.weixin.qq.com/kfid/kfcae09c206f6c7045a
|
||||||
|
|
||||||
|
# 测试站可选:填同一 GA4 或单独测试媒体资源;空则不加载
|
||||||
|
# VITE_GA_MEASUREMENT_ID=
|
||||||
|
|||||||
@ -11,3 +11,7 @@ VITE_API_ASSET_ORIGIN=https://api.ideship.com/admin
|
|||||||
VITE_OMS_ADMIN_URL=https://api.ideship.com/admin
|
VITE_OMS_ADMIN_URL=https://api.ideship.com/admin
|
||||||
|
|
||||||
VITE_WECOM_KF_URL=https://work.weixin.qq.com/kfid/kfcae09c206f6c7045a
|
VITE_WECOM_KF_URL=https://work.weixin.qq.com/kfid/kfcae09c206f6c7045a
|
||||||
|
|
||||||
|
# Google Analytics 4(官网 https://ideship.com)
|
||||||
|
# 地域/设备/来源自动有;首页点击见事件 home_*
|
||||||
|
VITE_GA_MEASUREMENT_ID=G-1XWZ7LL1PW
|
||||||
|
|||||||
@ -49,6 +49,7 @@
|
|||||||
"@codemirror/theme-one-dark": "^6.1.3",
|
"@codemirror/theme-one-dark": "^6.1.3",
|
||||||
"@codemirror/view": "^6.39.17",
|
"@codemirror/view": "^6.39.17",
|
||||||
"@fontsource/zcool-qingke-huangyou": "^5.3.0",
|
"@fontsource/zcool-qingke-huangyou": "^5.3.0",
|
||||||
|
"@glass-sdk/liquid-glass": "~0.0.2",
|
||||||
"@handsontable/vue3": "^16.2.0",
|
"@handsontable/vue3": "^16.2.0",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@vueuse/core": "^9.3.0",
|
"@vueuse/core": "^9.3.0",
|
||||||
|
|||||||
275
pnpm-lock.yaml
generated
275
pnpm-lock.yaml
generated
@ -39,6 +39,9 @@ importers:
|
|||||||
'@fontsource/zcool-qingke-huangyou':
|
'@fontsource/zcool-qingke-huangyou':
|
||||||
specifier: ^5.3.0
|
specifier: ^5.3.0
|
||||||
version: 5.3.0
|
version: 5.3.0
|
||||||
|
'@glass-sdk/liquid-glass':
|
||||||
|
specifier: ~0.0.2
|
||||||
|
version: 0.0.2(react-dom@19.3.1(react@19.3.1))(react@19.3.1)
|
||||||
'@handsontable/vue3':
|
'@handsontable/vue3':
|
||||||
specifier: ^16.2.0
|
specifier: ^16.2.0
|
||||||
version: 16.2.0(handsontable@16.2.0)(vue@3.5.18(typescript@4.9.5))
|
version: 16.2.0(handsontable@16.2.0)(vue@3.5.18(typescript@4.9.5))
|
||||||
@ -443,6 +446,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==}
|
resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/runtime@8.1.0':
|
||||||
|
resolution: {integrity: sha512-MQye/pU7gzXerMzlhScFNH1u4Je6rR8cqJWSgAfD+rIwbbtwG8tVDegoI7sWfcXkfVRW09G7vk2vjxGIuKFfwg==}
|
||||||
|
|
||||||
'@babel/template@7.27.2':
|
'@babel/template@7.27.2':
|
||||||
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
|
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@ -455,6 +461,33 @@ packages:
|
|||||||
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
|
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@base-ui/react@1.9.0':
|
||||||
|
resolution: {integrity: sha512-S8CSYosASBKtpL8SWUfS78rFElfLijiCgA7oHOK22jCJ95FULsYyKwBNFOgudt0RA+/7yNc7F81bOvQnRgbBiQ==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@date-fns/tz': ^1.2.0
|
||||||
|
'@types/react': ^17 || ^18 || ^19
|
||||||
|
date-fns: ^4.0.0
|
||||||
|
react: ^17 || ^18 || ^19
|
||||||
|
react-dom: ^17 || ^18 || ^19
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@date-fns/tz':
|
||||||
|
optional: true
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
date-fns:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@base-ui/utils@0.5.0':
|
||||||
|
resolution: {integrity: sha512-dYGGcEttjkYiMoa5nHm8vLaJ2UcuTWxo3BFAwOYXeRg/q7UVVvA6cacEIXEs3FgBfK1CGtW1ptElqs7wsx+t9A==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': ^17 || ^18 || ^19
|
||||||
|
react: ^17 || ^18 || ^19
|
||||||
|
react-dom: ^17 || ^18 || ^19
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@braintree/sanitize-url@7.1.2':
|
'@braintree/sanitize-url@7.1.2':
|
||||||
resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==}
|
resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==}
|
||||||
|
|
||||||
@ -929,15 +962,36 @@ packages:
|
|||||||
'@floating-ui/core@1.7.4':
|
'@floating-ui/core@1.7.4':
|
||||||
resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==}
|
resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==}
|
||||||
|
|
||||||
|
'@floating-ui/core@1.8.0':
|
||||||
|
resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==}
|
||||||
|
|
||||||
'@floating-ui/dom@1.7.5':
|
'@floating-ui/dom@1.7.5':
|
||||||
resolution: {integrity: sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==}
|
resolution: {integrity: sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==}
|
||||||
|
|
||||||
|
'@floating-ui/dom@1.8.0':
|
||||||
|
resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==}
|
||||||
|
|
||||||
|
'@floating-ui/react-dom@2.1.9':
|
||||||
|
resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8.0'
|
||||||
|
react-dom: '>=16.8.0'
|
||||||
|
|
||||||
'@floating-ui/utils@0.2.10':
|
'@floating-ui/utils@0.2.10':
|
||||||
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
||||||
|
|
||||||
|
'@floating-ui/utils@0.2.12':
|
||||||
|
resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==}
|
||||||
|
|
||||||
'@fontsource/zcool-qingke-huangyou@5.3.0':
|
'@fontsource/zcool-qingke-huangyou@5.3.0':
|
||||||
resolution: {integrity: sha512-6/ld8x/knq1NjDQxLSyg9pDnBPOmtWDb30ZTFk+n8OxfkBoUvPAxh3aV/6+UFGmA5CekJfcgpUwd2/x/weLvSQ==}
|
resolution: {integrity: sha512-6/ld8x/knq1NjDQxLSyg9pDnBPOmtWDb30ZTFk+n8OxfkBoUvPAxh3aV/6+UFGmA5CekJfcgpUwd2/x/weLvSQ==}
|
||||||
|
|
||||||
|
'@glass-sdk/liquid-glass@0.0.2':
|
||||||
|
resolution: {integrity: sha512-bDVDBXj6Cw++U0k9fcrSPEexnVnj/S4BjxF1t7NVQqW8La1gSVtxzPl9dG2VQxKs7Ik9kWR8Zi32JzyggI5Seg==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=19.0.0'
|
||||||
|
react-dom: '>=19.0.0'
|
||||||
|
|
||||||
'@handsontable/pikaday@1.0.0':
|
'@handsontable/pikaday@1.0.0':
|
||||||
resolution: {integrity: sha512-1VN6N38t5/DcjJ7y7XUYrDx1LuzvvzlrFdBdMG90Qo1xc8+LXHqbWbsTEm5Ec5gXTEbDEO53vUT35R+2COmOyg==}
|
resolution: {integrity: sha512-1VN6N38t5/DcjJ7y7XUYrDx1LuzvvzlrFdBdMG90Qo1xc8+LXHqbWbsTEm5Ec5gXTEbDEO53vUT35R+2COmOyg==}
|
||||||
|
|
||||||
@ -1027,12 +1081,24 @@ packages:
|
|||||||
'@lezer/xml@1.0.6':
|
'@lezer/xml@1.0.6':
|
||||||
resolution: {integrity: sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==}
|
resolution: {integrity: sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==}
|
||||||
|
|
||||||
|
'@lisse/core@0.7.3':
|
||||||
|
resolution: {integrity: sha512-WJWGskrOInlIs+DqK1CfAHNIjWBJ8t4AtM1cPn5RZfu2OmQumBdNGAhFuaIg+tgpibqP17GSwEiMq/fT+NCoIw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@marijn/find-cluster-break@1.0.2':
|
'@marijn/find-cluster-break@1.0.2':
|
||||||
resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
|
resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
|
||||||
|
|
||||||
'@mermaid-js/parser@1.2.0':
|
'@mermaid-js/parser@1.2.0':
|
||||||
resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==}
|
resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==}
|
||||||
|
|
||||||
|
'@modelcontextprotocol/core@2.0.0':
|
||||||
|
resolution: {integrity: sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@modelcontextprotocol/server@2.0.0':
|
||||||
|
resolution: {integrity: sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
'@mrmlnc/readdir-enhanced@2.2.1':
|
'@mrmlnc/readdir-enhanced@2.2.1':
|
||||||
resolution: {integrity: sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==}
|
resolution: {integrity: sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@ -1841,6 +1907,26 @@ packages:
|
|||||||
'@upsetjs/venn.js@2.0.0':
|
'@upsetjs/venn.js@2.0.0':
|
||||||
resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==}
|
resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==}
|
||||||
|
|
||||||
|
'@vgpu/adapter-mock@0.5.0':
|
||||||
|
resolution: {integrity: sha512-YtgXOFn1qI+8oTJenG2wWbQPA28V0CfE5UJs5Xqbt6qyR/xaZ5LHH2XWTwFcjnqn2b1xQCyTatJCMyyOdweUnw==}
|
||||||
|
|
||||||
|
'@vgpu/adapter-node@0.5.0':
|
||||||
|
resolution: {integrity: sha512-lgzqFcLDejwRgwlaNStzkwLNU9b4aN2OoPpWqT1UyPmK521BSw2OIQKzAuDIkzNlREla6KNC12G6bthhOzZKzw==}
|
||||||
|
|
||||||
|
'@vgpu/core@0.5.0':
|
||||||
|
resolution: {integrity: sha512-z80DHRHViv6+FKKkPzggEtT1ZZs/WCKRQbzJuwYdA135DZaSbCMoRK4anhM+I/nOHuDpf7H3xjkwE6rfolWZUw==}
|
||||||
|
|
||||||
|
'@vgpu/wgsl-std@0.5.0':
|
||||||
|
resolution: {integrity: sha512-NjMyJxwy4evFNCSqFQy4EqAbZ7iU+NUyJYyG9tLUhbdDLzBmfZlk1S3PyRZ9olBFFfEiVW2JNVw10maPKLEs1A==}
|
||||||
|
|
||||||
|
'@vgpu/wgsl@0.5.0':
|
||||||
|
resolution: {integrity: sha512-PDlDJem7J28C9bk+vPy2kYZEoW8+nYIx2oN4nu7/WF2V7ETx32BtmWcawwMbhiu/CVsobFafML0h0eqLRBNa3Q==}
|
||||||
|
peerDependencies:
|
||||||
|
'@vgpu/adapter-node': '>=0.1.6 <1.0.0 || ^0.5.0-rc.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@vgpu/adapter-node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@vitejs/plugin-vue-jsx@2.1.1':
|
'@vitejs/plugin-vue-jsx@2.1.1':
|
||||||
resolution: {integrity: sha512-JgDhxstQlwnHBvZ1BSnU5mbmyQ14/t5JhREc6YH5kWyu2QdAAOsLF6xgHoIWarj8tddaiwFrNzLbWJPudpXKYA==}
|
resolution: {integrity: sha512-JgDhxstQlwnHBvZ1BSnU5mbmyQ14/t5JhREc6YH5kWyu2QdAAOsLF6xgHoIWarj8tddaiwFrNzLbWJPudpXKYA==}
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
@ -1947,6 +2033,9 @@ packages:
|
|||||||
'@vueuse/shared@9.13.0':
|
'@vueuse/shared@9.13.0':
|
||||||
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
|
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
|
||||||
|
|
||||||
|
'@webgpu/types@0.1.74':
|
||||||
|
resolution: {integrity: sha512-lgiI4hbuLcI9unnm2cL/tvCaQU45dp0xcLWJh5uB/9MBGvIA4F8XIk7nSiBeg+K4xWGYwgZKn80LmERI5CxoTA==}
|
||||||
|
|
||||||
'@wtto00/html2canvas@1.4.3':
|
'@wtto00/html2canvas@1.4.3':
|
||||||
resolution: {integrity: sha512-jwsb+xL8N+gjrSNABSaFdxmWtE4c7RNFjP20lo1G7gs63Qqo1phhxVBTzxc/apDVh6LgXsU2l5bwKtXd9uz65w==}
|
resolution: {integrity: sha512-jwsb+xL8N+gjrSNABSaFdxmWtE4c7RNFjP20lo1G7gs63Qqo1phhxVBTzxc/apDVh6LgXsU2l5bwKtXd9uz65w==}
|
||||||
engines: {node: '>=8.0.0'}
|
engines: {node: '>=8.0.0'}
|
||||||
@ -5469,6 +5558,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==}
|
resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
pixelmatch@7.2.0:
|
||||||
|
resolution: {integrity: sha512-xhcb4yHu9sM/G7foGzoLtXYcC0zHEaOXXjRKhGup0fw78Nf2Tkiapv4EQyMzrbcmQPsllAI7DbFY2UT7PlI9Pg==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
pkg-types@1.3.1:
|
pkg-types@1.3.1:
|
||||||
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
||||||
|
|
||||||
@ -5479,6 +5572,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
|
pngjs@7.0.0:
|
||||||
|
resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==}
|
||||||
|
engines: {node: '>=14.19.0'}
|
||||||
|
|
||||||
pngquant-bin@6.0.1:
|
pngquant-bin@6.0.1:
|
||||||
resolution: {integrity: sha512-Q3PUyolfktf+hYio6wsg3SanQzEU/v8aICg/WpzxXcuCMRb7H2Q81okfpcEztbMvw25ILjd3a87doj2N9kvbpQ==}
|
resolution: {integrity: sha512-Q3PUyolfktf+hYio6wsg3SanQzEU/v8aICg/WpzxXcuCMRb7H2Q81okfpcEztbMvw25ILjd3a87doj2N9kvbpQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -5679,6 +5776,15 @@ packages:
|
|||||||
raf@3.4.1:
|
raf@3.4.1:
|
||||||
resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
|
resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
|
||||||
|
|
||||||
|
react-dom@19.3.1:
|
||||||
|
resolution: {integrity: sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^19.3.1
|
||||||
|
|
||||||
|
react@19.3.1:
|
||||||
|
resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
read-pkg-up@1.0.1:
|
read-pkg-up@1.0.1:
|
||||||
resolution: {integrity: sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==}
|
resolution: {integrity: sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -5791,6 +5897,9 @@ packages:
|
|||||||
require-main-filename@2.0.0:
|
require-main-filename@2.0.0:
|
||||||
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
||||||
|
|
||||||
|
reselect@5.3.0:
|
||||||
|
resolution: {integrity: sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==}
|
||||||
|
|
||||||
reserved-words@0.1.2:
|
reserved-words@0.1.2:
|
||||||
resolution: {integrity: sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==}
|
resolution: {integrity: sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==}
|
||||||
|
|
||||||
@ -6044,6 +6153,9 @@ packages:
|
|||||||
sax@1.4.1:
|
sax@1.4.1:
|
||||||
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
|
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
|
||||||
|
|
||||||
|
scheduler@0.28.0:
|
||||||
|
resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==}
|
||||||
|
|
||||||
screenfull@5.2.0:
|
screenfull@5.2.0:
|
||||||
resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==}
|
resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -6919,6 +7031,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==}
|
resolution: {integrity: sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
|
use-sync-external-store@1.7.0:
|
||||||
|
resolution: {integrity: sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
use@3.1.1:
|
use@3.1.1:
|
||||||
resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==}
|
resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -6966,6 +7083,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-KoJRp8V2EsmeW2pg33CVEOEfGDTmcVLL0wk0fOQJjgXfAunciPylOueKovuzlbWSrTQap1UzgNn8xr5zbBsnPA==}
|
resolution: {integrity: sha512-KoJRp8V2EsmeW2pg33CVEOEfGDTmcVLL0wk0fOQJjgXfAunciPylOueKovuzlbWSrTQap1UzgNn8xr5zbBsnPA==}
|
||||||
engines: {node: '>=18.18.0'}
|
engines: {node: '>=18.18.0'}
|
||||||
|
|
||||||
|
vgpu@0.5.0:
|
||||||
|
resolution: {integrity: sha512-isQfMHRTysYFfT8UWTzqQI/ROw2L7lVkj7xoUJlvIF6FjYxBNS/YNG7qp3tZ7ZvZZ3uwYjso+geQv/bEq5Hl3w==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
three: '>=0.180.0 <0.200.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
three:
|
||||||
|
optional: true
|
||||||
|
|
||||||
vite-plugin-compression@0.5.1:
|
vite-plugin-compression@0.5.1:
|
||||||
resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==}
|
resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -7152,12 +7278,18 @@ packages:
|
|||||||
w3c-keyname@2.2.8:
|
w3c-keyname@2.2.8:
|
||||||
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
|
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
|
||||||
|
|
||||||
|
webgpu@0.4.0:
|
||||||
|
resolution: {integrity: sha512-F5pimn3Aoi0zWjuRdiVs5TnrUwSzD2lESBohsIUsqyitWkGRQlXU2fhV6ycXlQTa1bvAf3sjqiUpBEpmSQ5ptA==}
|
||||||
|
|
||||||
webidl-conversions@3.0.1:
|
webidl-conversions@3.0.1:
|
||||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
|
|
||||||
webpack-virtual-modules@0.6.2:
|
webpack-virtual-modules@0.6.2:
|
||||||
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
|
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
|
||||||
|
|
||||||
|
wgpu-matrix@3.4.2:
|
||||||
|
resolution: {integrity: sha512-IfZFbG7olEYBD76VCZy2TzqM57GKy3LT++AZh37tMHPPByO6R2h61zXGBZFNPi2IzxNivNi8y5dvze5PjaczjA==}
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
whatwg-url@5.0.0:
|
||||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||||
|
|
||||||
@ -7325,6 +7457,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
zod@4.6.7:
|
||||||
|
resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==}
|
||||||
|
|
||||||
zrender@5.6.1:
|
zrender@5.6.1:
|
||||||
resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
|
resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
|
||||||
|
|
||||||
@ -7517,6 +7652,8 @@ snapshots:
|
|||||||
|
|
||||||
'@babel/runtime@7.28.6': {}
|
'@babel/runtime@7.28.6': {}
|
||||||
|
|
||||||
|
'@babel/runtime@8.1.0': {}
|
||||||
|
|
||||||
'@babel/template@7.27.2':
|
'@babel/template@7.27.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.27.1
|
'@babel/code-frame': 7.27.1
|
||||||
@ -7540,6 +7677,25 @@ snapshots:
|
|||||||
'@babel/helper-string-parser': 7.27.1
|
'@babel/helper-string-parser': 7.27.1
|
||||||
'@babel/helper-validator-identifier': 7.27.1
|
'@babel/helper-validator-identifier': 7.27.1
|
||||||
|
|
||||||
|
'@base-ui/react@1.9.0(react-dom@19.3.1(react@19.3.1))(react@19.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 8.1.0
|
||||||
|
'@base-ui/utils': 0.5.0(react-dom@19.3.1(react@19.3.1))(react@19.3.1)
|
||||||
|
'@floating-ui/react-dom': 2.1.9(react-dom@19.3.1(react@19.3.1))(react@19.3.1)
|
||||||
|
'@floating-ui/utils': 0.2.12
|
||||||
|
react: 19.3.1
|
||||||
|
react-dom: 19.3.1(react@19.3.1)
|
||||||
|
use-sync-external-store: 1.7.0(react@19.3.1)
|
||||||
|
|
||||||
|
'@base-ui/utils@0.5.0(react-dom@19.3.1(react@19.3.1))(react@19.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 8.1.0
|
||||||
|
'@floating-ui/utils': 0.2.12
|
||||||
|
react: 19.3.1
|
||||||
|
react-dom: 19.3.1(react@19.3.1)
|
||||||
|
reselect: 5.3.0
|
||||||
|
use-sync-external-store: 1.7.0(react@19.3.1)
|
||||||
|
|
||||||
'@braintree/sanitize-url@7.1.2': {}
|
'@braintree/sanitize-url@7.1.2': {}
|
||||||
|
|
||||||
'@bufbuild/protobuf@2.10.1': {}
|
'@bufbuild/protobuf@2.10.1': {}
|
||||||
@ -7955,15 +8111,46 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@floating-ui/utils': 0.2.10
|
'@floating-ui/utils': 0.2.10
|
||||||
|
|
||||||
|
'@floating-ui/core@1.8.0':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/utils': 0.2.12
|
||||||
|
|
||||||
'@floating-ui/dom@1.7.5':
|
'@floating-ui/dom@1.7.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@floating-ui/core': 1.7.4
|
'@floating-ui/core': 1.7.4
|
||||||
'@floating-ui/utils': 0.2.10
|
'@floating-ui/utils': 0.2.10
|
||||||
|
|
||||||
|
'@floating-ui/dom@1.8.0':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/core': 1.8.0
|
||||||
|
'@floating-ui/utils': 0.2.12
|
||||||
|
|
||||||
|
'@floating-ui/react-dom@2.1.9(react-dom@19.3.1(react@19.3.1))(react@19.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/dom': 1.8.0
|
||||||
|
react: 19.3.1
|
||||||
|
react-dom: 19.3.1(react@19.3.1)
|
||||||
|
|
||||||
'@floating-ui/utils@0.2.10': {}
|
'@floating-ui/utils@0.2.10': {}
|
||||||
|
|
||||||
|
'@floating-ui/utils@0.2.12': {}
|
||||||
|
|
||||||
'@fontsource/zcool-qingke-huangyou@5.3.0': {}
|
'@fontsource/zcool-qingke-huangyou@5.3.0': {}
|
||||||
|
|
||||||
|
'@glass-sdk/liquid-glass@0.0.2(react-dom@19.3.1(react@19.3.1))(react@19.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@base-ui/react': 1.9.0(react-dom@19.3.1(react@19.3.1))(react@19.3.1)
|
||||||
|
'@lisse/core': 0.7.3
|
||||||
|
react: 19.3.1
|
||||||
|
react-dom: 19.3.1(react@19.3.1)
|
||||||
|
vgpu: 0.5.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@date-fns/tz'
|
||||||
|
- '@types/react'
|
||||||
|
- date-fns
|
||||||
|
- supports-color
|
||||||
|
- three
|
||||||
|
|
||||||
'@handsontable/pikaday@1.0.0': {}
|
'@handsontable/pikaday@1.0.0': {}
|
||||||
|
|
||||||
'@handsontable/vue3@16.2.0(handsontable@16.2.0)(vue@3.5.18(typescript@4.9.5))':
|
'@handsontable/vue3@16.2.0(handsontable@16.2.0)(vue@3.5.18(typescript@4.9.5))':
|
||||||
@ -8078,12 +8265,23 @@ snapshots:
|
|||||||
'@lezer/highlight': 1.2.3
|
'@lezer/highlight': 1.2.3
|
||||||
'@lezer/lr': 1.4.8
|
'@lezer/lr': 1.4.8
|
||||||
|
|
||||||
|
'@lisse/core@0.7.3': {}
|
||||||
|
|
||||||
'@marijn/find-cluster-break@1.0.2': {}
|
'@marijn/find-cluster-break@1.0.2': {}
|
||||||
|
|
||||||
'@mermaid-js/parser@1.2.0':
|
'@mermaid-js/parser@1.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@chevrotain/types': 11.1.2
|
'@chevrotain/types': 11.1.2
|
||||||
|
|
||||||
|
'@modelcontextprotocol/core@2.0.0':
|
||||||
|
dependencies:
|
||||||
|
zod: 4.6.7
|
||||||
|
|
||||||
|
'@modelcontextprotocol/server@2.0.0':
|
||||||
|
dependencies:
|
||||||
|
'@modelcontextprotocol/core': 2.0.0
|
||||||
|
zod: 4.6.7
|
||||||
|
|
||||||
'@mrmlnc/readdir-enhanced@2.2.1':
|
'@mrmlnc/readdir-enhanced@2.2.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
call-me-maybe: 1.0.2
|
call-me-maybe: 1.0.2
|
||||||
@ -8907,6 +9105,33 @@ snapshots:
|
|||||||
d3-selection: 3.0.0
|
d3-selection: 3.0.0
|
||||||
d3-transition: 3.0.1(d3-selection@3.0.0)
|
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||||
|
|
||||||
|
'@vgpu/adapter-mock@0.5.0(@vgpu/adapter-node@0.5.0)':
|
||||||
|
dependencies:
|
||||||
|
'@vgpu/core': 0.5.0(@vgpu/adapter-node@0.5.0)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vgpu/adapter-node'
|
||||||
|
|
||||||
|
'@vgpu/adapter-node@0.5.0':
|
||||||
|
dependencies:
|
||||||
|
'@vgpu/core': 0.5.0(@vgpu/adapter-node@0.5.0)
|
||||||
|
webgpu: 0.4.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@vgpu/core@0.5.0(@vgpu/adapter-node@0.5.0)':
|
||||||
|
dependencies:
|
||||||
|
'@vgpu/wgsl': 0.5.0(@vgpu/adapter-node@0.5.0)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vgpu/adapter-node'
|
||||||
|
|
||||||
|
'@vgpu/wgsl-std@0.5.0': {}
|
||||||
|
|
||||||
|
'@vgpu/wgsl@0.5.0(@vgpu/adapter-node@0.5.0)':
|
||||||
|
dependencies:
|
||||||
|
'@vgpu/wgsl-std': 0.5.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@vgpu/adapter-node': 0.5.0
|
||||||
|
|
||||||
'@vitejs/plugin-vue-jsx@2.1.1(vite@6.3.5(@types/node@20.5.1)(jiti@1.21.7)(less@4.4.0)(sass-embedded@1.93.3)(sass@1.93.3)(sugarss@2.0.0)(tsx@4.20.3))(vue@3.5.18(typescript@4.9.5))':
|
'@vitejs/plugin-vue-jsx@2.1.1(vite@6.3.5(@types/node@20.5.1)(jiti@1.21.7)(less@4.4.0)(sass-embedded@1.93.3)(sass@1.93.3)(sugarss@2.0.0)(tsx@4.20.3))(vue@3.5.18(typescript@4.9.5))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.28.0
|
'@babel/core': 7.28.0
|
||||||
@ -9090,6 +9315,8 @@ snapshots:
|
|||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
|
'@webgpu/types@0.1.74': {}
|
||||||
|
|
||||||
'@wtto00/html2canvas@1.4.3':
|
'@wtto00/html2canvas@1.4.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
css-line-break: 2.1.0
|
css-line-break: 2.1.0
|
||||||
@ -12931,6 +13158,10 @@ snapshots:
|
|||||||
|
|
||||||
pinkie@2.0.4: {}
|
pinkie@2.0.4: {}
|
||||||
|
|
||||||
|
pixelmatch@7.2.0:
|
||||||
|
dependencies:
|
||||||
|
pngjs: 7.0.0
|
||||||
|
|
||||||
pkg-types@1.3.1:
|
pkg-types@1.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
confbox: 0.1.8
|
confbox: 0.1.8
|
||||||
@ -12945,6 +13176,8 @@ snapshots:
|
|||||||
|
|
||||||
pngjs@5.0.0: {}
|
pngjs@5.0.0: {}
|
||||||
|
|
||||||
|
pngjs@7.0.0: {}
|
||||||
|
|
||||||
pngquant-bin@6.0.1:
|
pngquant-bin@6.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
bin-build: 3.0.0
|
bin-build: 3.0.0
|
||||||
@ -13129,6 +13362,13 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
performance-now: 2.1.0
|
performance-now: 2.1.0
|
||||||
|
|
||||||
|
react-dom@19.3.1(react@19.3.1):
|
||||||
|
dependencies:
|
||||||
|
react: 19.3.1
|
||||||
|
scheduler: 0.28.0
|
||||||
|
|
||||||
|
react@19.3.1: {}
|
||||||
|
|
||||||
read-pkg-up@1.0.1:
|
read-pkg-up@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
find-up: 1.1.2
|
find-up: 1.1.2
|
||||||
@ -13293,6 +13533,8 @@ snapshots:
|
|||||||
|
|
||||||
require-main-filename@2.0.0: {}
|
require-main-filename@2.0.0: {}
|
||||||
|
|
||||||
|
reselect@5.3.0: {}
|
||||||
|
|
||||||
reserved-words@0.1.2: {}
|
reserved-words@0.1.2: {}
|
||||||
|
|
||||||
resize-detector@0.3.0: {}
|
resize-detector@0.3.0: {}
|
||||||
@ -13535,6 +13777,8 @@ snapshots:
|
|||||||
sax@1.4.1:
|
sax@1.4.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
scheduler@0.28.0: {}
|
||||||
|
|
||||||
screenfull@5.2.0: {}
|
screenfull@5.2.0: {}
|
||||||
|
|
||||||
scroll-into-view-if-needed@2.2.31:
|
scroll-into-view-if-needed@2.2.31:
|
||||||
@ -14610,6 +14854,10 @@ snapshots:
|
|||||||
|
|
||||||
url-to-options@1.0.1: {}
|
url-to-options@1.0.1: {}
|
||||||
|
|
||||||
|
use-sync-external-store@1.7.0(react@19.3.1):
|
||||||
|
dependencies:
|
||||||
|
react: 19.3.1
|
||||||
|
|
||||||
use@3.1.1: {}
|
use@3.1.1: {}
|
||||||
|
|
||||||
util-deprecate@1.0.2: {}
|
util-deprecate@1.0.2: {}
|
||||||
@ -14680,6 +14928,22 @@ snapshots:
|
|||||||
- typescript
|
- typescript
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
|
vgpu@0.5.0:
|
||||||
|
dependencies:
|
||||||
|
'@modelcontextprotocol/server': 2.0.0
|
||||||
|
'@vgpu/adapter-mock': 0.5.0(@vgpu/adapter-node@0.5.0)
|
||||||
|
'@vgpu/adapter-node': 0.5.0
|
||||||
|
'@vgpu/core': 0.5.0(@vgpu/adapter-node@0.5.0)
|
||||||
|
'@vgpu/wgsl': 0.5.0(@vgpu/adapter-node@0.5.0)
|
||||||
|
'@vgpu/wgsl-std': 0.5.0
|
||||||
|
'@webgpu/types': 0.1.74
|
||||||
|
pixelmatch: 7.2.0
|
||||||
|
pngjs: 7.0.0
|
||||||
|
wgpu-matrix: 3.4.2
|
||||||
|
zod: 4.6.7
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
vite-plugin-compression@0.5.1(vite@6.3.5(@types/node@20.5.1)(jiti@1.21.7)(less@4.4.0)(sass-embedded@1.93.3)(sass@1.93.3)(sugarss@2.0.0)(tsx@4.20.3)):
|
vite-plugin-compression@0.5.1(vite@6.3.5(@types/node@20.5.1)(jiti@1.21.7)(less@4.4.0)(sass-embedded@1.93.3)(sass@1.93.3)(sugarss@2.0.0)(tsx@4.20.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
@ -14893,10 +15157,19 @@ snapshots:
|
|||||||
|
|
||||||
w3c-keyname@2.2.8: {}
|
w3c-keyname@2.2.8: {}
|
||||||
|
|
||||||
|
webgpu@0.4.0:
|
||||||
|
dependencies:
|
||||||
|
'@webgpu/types': 0.1.74
|
||||||
|
debug: 4.4.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
webidl-conversions@3.0.1: {}
|
webidl-conversions@3.0.1: {}
|
||||||
|
|
||||||
webpack-virtual-modules@0.6.2: {}
|
webpack-virtual-modules@0.6.2: {}
|
||||||
|
|
||||||
|
wgpu-matrix@3.4.2: {}
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
whatwg-url@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
tr46: 0.0.3
|
tr46: 0.0.3
|
||||||
@ -15076,6 +15349,8 @@ snapshots:
|
|||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
|
zod@4.6.7: {}
|
||||||
|
|
||||||
zrender@5.6.1:
|
zrender@5.6.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.3.0
|
tslib: 2.3.0
|
||||||
|
|||||||
@ -3,8 +3,10 @@
|
|||||||
*/
|
*/
|
||||||
import { axiosInstance } from '@/api/rbac'
|
import { axiosInstance } from '@/api/rbac'
|
||||||
import { assertBusinessOk, omsUrl, parseListPayload, unwrapData } from '@/api/oms/common'
|
import { assertBusinessOk, omsUrl, parseListPayload, unwrapData } from '@/api/oms/common'
|
||||||
|
import { CUSTOMER_STATUS } from '@/utils/customerStatus'
|
||||||
|
|
||||||
export type CustomerStatus = 0 | 1 | 2
|
/** 与后端 CustomerStatus 对齐:0=None,1=Disabled,2=Normal,3=Frozen */
|
||||||
|
export type CustomerStatus = 0 | 1 | 2 | 3
|
||||||
|
|
||||||
export type CustomerGroup = 0 | 1 | 2 | 3 | 4 | 5
|
export type CustomerGroup = 0 | 1 | 2 | 3 | 4 | 5
|
||||||
|
|
||||||
@ -154,7 +156,7 @@ export const CustomerService = {
|
|||||||
balance: data.accountBalance ?? data.balance ?? 0,
|
balance: data.accountBalance ?? data.balance ?? 0,
|
||||||
totalQuota: data.totalQuota ?? 0,
|
totalQuota: data.totalQuota ?? 0,
|
||||||
usedQuota: data.usedQuota ?? 0,
|
usedQuota: data.usedQuota ?? 0,
|
||||||
status: data.status ?? 1,
|
status: data.status ?? CUSTOMER_STATUS.normal,
|
||||||
remark: data.remark,
|
remark: data.remark,
|
||||||
salesman: data.salesman ?? data.salesPerson,
|
salesman: data.salesman ?? data.salesPerson,
|
||||||
customerService: data.customerService,
|
customerService: data.customerService,
|
||||||
@ -186,7 +188,8 @@ export const CustomerService = {
|
|||||||
balance: data.accountBalance ?? data.balance,
|
balance: data.accountBalance ?? data.balance,
|
||||||
totalQuota: data.totalQuota,
|
totalQuota: data.totalQuota,
|
||||||
usedQuota: data.usedQuota,
|
usedQuota: data.usedQuota,
|
||||||
status: data.status ?? 1,
|
// 后端:1=Disabled,2=Normal;勿默认 1,否则公开注册账号无法登录
|
||||||
|
status: data.status ?? CUSTOMER_STATUS.normal,
|
||||||
role: data.role,
|
role: data.role,
|
||||||
remark: data.remark,
|
remark: data.remark,
|
||||||
salesman: data.salesman ?? data.salesPerson,
|
salesman: data.salesman ?? data.salesPerson,
|
||||||
|
|||||||
@ -167,8 +167,37 @@ export async function sendBatchEmailOne(body: BatchEmailSendOneBody) {
|
|||||||
assertBusinessOk(res.data)
|
assertBusinessOk(res.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BatchEmailDuplicatePair {
|
||||||
|
to: string
|
||||||
|
subject: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BatchEmailDuplicateHit {
|
||||||
|
toEmail: string
|
||||||
|
subject: string
|
||||||
|
sendTime: string
|
||||||
|
sender: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDuplicateHit(raw: unknown): BatchEmailDuplicateHit | null {
|
||||||
|
if (raw == null || typeof raw !== 'object') return null
|
||||||
|
const row = raw as Record<string, unknown>
|
||||||
|
const toEmail = pickStr(row, 'toEmail', 'ToEmail')
|
||||||
|
if (!toEmail) return null
|
||||||
|
return {
|
||||||
|
toEmail,
|
||||||
|
subject: pickStr(row, 'subject', 'Subject'),
|
||||||
|
sendTime: pickStr(row, 'sendTime', 'SendTime'),
|
||||||
|
sender: pickStr(row, 'sender', 'Sender'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** GET /system/batch-email/records/list */
|
/** GET /system/batch-email/records/list */
|
||||||
export async function listBatchEmailRecords(params?: { page?: number; pageSize?: number }) {
|
export async function listBatchEmailRecords(params?: {
|
||||||
|
page?: number
|
||||||
|
pageSize?: number
|
||||||
|
keyword?: string
|
||||||
|
}) {
|
||||||
const res = await axiosInstance.get(omsUrl('/system/batch-email/records/list'), { params })
|
const res = await axiosInstance.get(omsUrl('/system/batch-email/records/list'), { params })
|
||||||
const page = unwrapPageResult(res.data)
|
const page = unwrapPageResult(res.data)
|
||||||
return {
|
return {
|
||||||
@ -178,3 +207,15 @@ export async function listBatchEmailRecords(params?: { page?: number; pageSize?:
|
|||||||
total: page.total,
|
total: page.total,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** POST /system/batch-email/check-duplicates — 近 N 天同邮箱同主题预检 */
|
||||||
|
export async function checkBatchEmailDuplicates(body: {
|
||||||
|
pairs: BatchEmailDuplicatePair[]
|
||||||
|
withinDays?: number
|
||||||
|
}): Promise<BatchEmailDuplicateHit[]> {
|
||||||
|
const res = await axiosInstance.post(omsUrl('/system/batch-email/check-duplicates'), body)
|
||||||
|
const list = unwrapList(res.data)
|
||||||
|
return list
|
||||||
|
.map(normalizeDuplicateHit)
|
||||||
|
.filter((item): item is BatchEmailDuplicateHit => item != null)
|
||||||
|
}
|
||||||
|
|||||||
4
src/env.d.ts
vendored
4
src/env.d.ts
vendored
@ -53,4 +53,8 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_API_ORIGIN?: string;
|
readonly VITE_API_ORIGIN?: string;
|
||||||
/** 静态资源 origin;本地默认测试 admin */
|
/** 静态资源 origin;本地默认测试 admin */
|
||||||
readonly VITE_API_ASSET_ORIGIN?: string;
|
readonly VITE_API_ASSET_ORIGIN?: string;
|
||||||
|
/** 企微客服链接 */
|
||||||
|
readonly VITE_WECOM_KF_URL?: string;
|
||||||
|
/** Google Analytics 4 Measurement ID,如 G-XXXXXXXX;空则不加载 */
|
||||||
|
readonly VITE_GA_MEASUREMENT_ID?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4399,11 +4399,24 @@ export default {
|
|||||||
'system.batchEmail.errLoadTemplates': 'Failed to load templates',
|
'system.batchEmail.errLoadTemplates': 'Failed to load templates',
|
||||||
'system.batchEmail.errLoadRecords': 'Failed to load email records',
|
'system.batchEmail.errLoadRecords': 'Failed to load email records',
|
||||||
'system.batchEmail.recordList': 'Email records',
|
'system.batchEmail.recordList': 'Email records',
|
||||||
|
'system.batchEmail.recordSearch': 'Search',
|
||||||
|
'system.batchEmail.recordSearchPlaceholder': 'Email / subject / body / sender',
|
||||||
'system.batchEmail.record.toEmail': 'Email',
|
'system.batchEmail.record.toEmail': 'Email',
|
||||||
'system.batchEmail.record.subject': 'Subject',
|
'system.batchEmail.record.subject': 'Subject',
|
||||||
'system.batchEmail.record.content': 'Content',
|
'system.batchEmail.record.content': 'Content',
|
||||||
'system.batchEmail.record.sendTime': 'Sent at',
|
'system.batchEmail.record.sendTime': 'Sent at',
|
||||||
'system.batchEmail.record.sender': 'Sender',
|
'system.batchEmail.record.sender': 'Sender',
|
||||||
|
'system.batchEmail.errCheckDup': 'Duplicate check failed. Please try again.',
|
||||||
|
'system.batchEmail.dupConfirmTitle': 'Duplicates in the last 30 days',
|
||||||
|
'system.batchEmail.dupConfirmBody': '{count} item(s) share the same email + subject within {days} days (including in-batch duplicates):',
|
||||||
|
'system.batchEmail.dupConfirmMore': '{n} more not shown',
|
||||||
|
'system.batchEmail.dupConfirmBatchOnly': '{n} more are duplicates within this batch',
|
||||||
|
'system.batchEmail.dupBatchOnlyTip': '{n} duplicate group(s) in this batch — only the first of each email+subject will be sent',
|
||||||
|
'system.batchEmail.dupConfirmHint': 'Duplicates are skipped by default. Click “Send anyway” to include them.',
|
||||||
|
'system.batchEmail.dupConfirmForce': 'Send anyway',
|
||||||
|
'system.batchEmail.dupConfirmSkip': 'Skip duplicates',
|
||||||
|
'system.batchEmail.failDupRecent': 'Already sent with the same subject within {days} days',
|
||||||
|
'system.batchEmail.failDupInBatch': 'Duplicate in this batch, skipped',
|
||||||
'system.batchEmail.warnEmptyContent': 'Template body is required',
|
'system.batchEmail.warnEmptyContent': 'Template body is required',
|
||||||
'system.batchEmail.warnEmptyName': 'Template name is required',
|
'system.batchEmail.warnEmptyName': 'Template name is required',
|
||||||
'system.batchEmail.confirmOverwriteTitle': 'Overwrite?',
|
'system.batchEmail.confirmOverwriteTitle': 'Overwrite?',
|
||||||
@ -4451,6 +4464,33 @@ export default {
|
|||||||
'system.batchEmail.logSendFail': 'failed - {msg}',
|
'system.batchEmail.logSendFail': 'failed - {msg}',
|
||||||
'system.batchEmail.logSendDone': 'Done: {total} total, {successCount} ok, {failCount} failed',
|
'system.batchEmail.logSendDone': 'Done: {total} total, {successCount} ok, {failCount} failed',
|
||||||
'system.batchEmail.msgSendDone': 'Done: {successCount} succeeded, {failCount} failed',
|
'system.batchEmail.msgSendDone': 'Done: {successCount} succeeded, {failCount} failed',
|
||||||
|
'system.batchEmail.accountReady': 'Mailbox ready · {account}',
|
||||||
|
'system.batchEmail.accountNotReady': 'Mailbox not configured — set it up before sending',
|
||||||
|
'system.batchEmail.templateEdit': 'Edit template',
|
||||||
|
'system.batchEmail.placeholderInsertHint': 'Click to insert a placeholder at the subject/body cursor',
|
||||||
|
'system.batchEmail.sendProgressTitle': 'Batch send progress',
|
||||||
|
'system.batchEmail.sendProgressMeta': '{current}/{total} processed · {success} ok · {fail} failed',
|
||||||
|
'system.batchEmail.sendProgressCurrent': 'Current: {email}',
|
||||||
|
'system.batchEmail.sendConfirmTitle': 'Confirm batch send',
|
||||||
|
'system.batchEmail.sendConfirmOk': 'Send now',
|
||||||
|
'system.batchEmail.sendConfirmAccount': 'From',
|
||||||
|
'system.batchEmail.sendConfirmCount': 'Recipients',
|
||||||
|
'system.batchEmail.sendConfirmHint': 'Messages are sent one by one with the current template. You can cancel remaining sends; already sent emails cannot be recalled.',
|
||||||
|
'system.batchEmail.cancelSend': 'Cancel remaining',
|
||||||
|
'system.batchEmail.sendFailList': 'Failures ({n})',
|
||||||
|
'system.batchEmail.copyFailList': 'Copy list',
|
||||||
|
'system.batchEmail.msgCopyFailOk': 'Failure list copied',
|
||||||
|
'system.batchEmail.errCopyFail': 'Copy failed — select the text manually',
|
||||||
|
'system.batchEmail.failInvalidEmail': 'Invalid email',
|
||||||
|
'system.batchEmail.failEmptyBody': 'Empty body',
|
||||||
|
'system.batchEmail.msgSendCancelled': 'Cancelled: {successCount} ok, {failCount} failed',
|
||||||
|
'system.batchEmail.replacePreview': 'Placeholder preview',
|
||||||
|
'system.batchEmail.replacePreviewEmpty': 'Add recipients to preview placeholder replacement',
|
||||||
|
'system.batchEmail.replacePreviewManual': 'Based on {email} (manual emails leave order/track placeholders empty)',
|
||||||
|
'system.batchEmail.replacePreviewSample': 'Preview using first recipient {email}',
|
||||||
|
'system.batchEmail.confirmSwitchTplTitle': 'Switch template?',
|
||||||
|
'system.batchEmail.confirmSwitchTplBody': 'Subject/body were edited. Switching will discard unsaved changes.',
|
||||||
|
'system.batchEmail.removeRecipient': 'Remove this email',
|
||||||
|
|
||||||
// System - Announcement
|
// System - Announcement
|
||||||
'system.announcement.search.placeholder': 'Search title or content',
|
'system.announcement.search.placeholder': 'Search title or content',
|
||||||
|
|||||||
@ -4423,11 +4423,24 @@ export default {
|
|||||||
'system.batchEmail.errLoadTemplates': '加载模板失败',
|
'system.batchEmail.errLoadTemplates': '加载模板失败',
|
||||||
'system.batchEmail.errLoadRecords': '加载邮件记录失败',
|
'system.batchEmail.errLoadRecords': '加载邮件记录失败',
|
||||||
'system.batchEmail.recordList': '邮件记录',
|
'system.batchEmail.recordList': '邮件记录',
|
||||||
|
'system.batchEmail.recordSearch': '查询',
|
||||||
|
'system.batchEmail.recordSearchPlaceholder': '邮箱 / 主题 / 内容 / 发送人',
|
||||||
'system.batchEmail.record.toEmail': '邮箱',
|
'system.batchEmail.record.toEmail': '邮箱',
|
||||||
'system.batchEmail.record.subject': '主题',
|
'system.batchEmail.record.subject': '主题',
|
||||||
'system.batchEmail.record.content': '内容',
|
'system.batchEmail.record.content': '内容',
|
||||||
'system.batchEmail.record.sendTime': '发送时间',
|
'system.batchEmail.record.sendTime': '发送时间',
|
||||||
'system.batchEmail.record.sender': '发送人',
|
'system.batchEmail.record.sender': '发送人',
|
||||||
|
'system.batchEmail.errCheckDup': '查重失败,请稍后重试',
|
||||||
|
'system.batchEmail.dupConfirmTitle': '发现近 30 天重复发送',
|
||||||
|
'system.batchEmail.dupConfirmBody': '有 {count} 条「同邮箱 + 同主题」在近 {days} 天内已发送过(含本批重复):',
|
||||||
|
'system.batchEmail.dupConfirmMore': '另有 {n} 条未展示',
|
||||||
|
'system.batchEmail.dupConfirmBatchOnly': '另有 {n} 条为本批内重复收件',
|
||||||
|
'system.batchEmail.dupBatchOnlyTip': '本批有 {n} 组重复收件,同邮箱同主题仅发送首条',
|
||||||
|
'system.batchEmail.dupConfirmHint': '默认跳过重复项;若仍要发送,请点「仍要发送」。',
|
||||||
|
'system.batchEmail.dupConfirmForce': '仍要发送',
|
||||||
|
'system.batchEmail.dupConfirmSkip': '仅发送未重复',
|
||||||
|
'system.batchEmail.failDupRecent': '近 {days} 天已发送过同主题',
|
||||||
|
'system.batchEmail.failDupInBatch': '本批内重复,已跳过',
|
||||||
'system.batchEmail.warnEmptyContent': '模板内容不能为空',
|
'system.batchEmail.warnEmptyContent': '模板内容不能为空',
|
||||||
'system.batchEmail.warnEmptyName': '模板名称不能为空',
|
'system.batchEmail.warnEmptyName': '模板名称不能为空',
|
||||||
'system.batchEmail.confirmOverwriteTitle': '确认覆盖',
|
'system.batchEmail.confirmOverwriteTitle': '确认覆盖',
|
||||||
@ -4475,6 +4488,33 @@ export default {
|
|||||||
'system.batchEmail.logSendFail': '收件人, 发送失败 - {msg}',
|
'system.batchEmail.logSendFail': '收件人, 发送失败 - {msg}',
|
||||||
'system.batchEmail.logSendDone': '结束发送: 共发送{total}封邮箱, 成功{successCount}, 失败{failCount}',
|
'system.batchEmail.logSendDone': '结束发送: 共发送{total}封邮箱, 成功{successCount}, 失败{failCount}',
|
||||||
'system.batchEmail.msgSendDone': '发送完成:成功 {successCount},失败 {failCount}',
|
'system.batchEmail.msgSendDone': '发送完成:成功 {successCount},失败 {failCount}',
|
||||||
|
'system.batchEmail.accountReady': '邮箱已配置 · {account}',
|
||||||
|
'system.batchEmail.accountNotReady': '邮箱未配置,发送前请先完成设置',
|
||||||
|
'system.batchEmail.templateEdit': '模板编辑',
|
||||||
|
'system.batchEmail.placeholderInsertHint': '点击插入占位符(插入到主题或正文光标处)',
|
||||||
|
'system.batchEmail.sendProgressTitle': '批量发送进度',
|
||||||
|
'system.batchEmail.sendProgressMeta': '已处理 {current}/{total} · 成功 {success} · 失败 {fail}',
|
||||||
|
'system.batchEmail.sendProgressCurrent': '当前:{email}',
|
||||||
|
'system.batchEmail.sendConfirmTitle': '确认批量发送',
|
||||||
|
'system.batchEmail.sendConfirmOk': '确认发送',
|
||||||
|
'system.batchEmail.sendConfirmAccount': '发件账号',
|
||||||
|
'system.batchEmail.sendConfirmCount': '收件人数',
|
||||||
|
'system.batchEmail.sendConfirmHint': '将按当前模板逐一发送,发送过程可取消;已发出的邮件无法撤回。',
|
||||||
|
'system.batchEmail.cancelSend': '取消剩余',
|
||||||
|
'system.batchEmail.sendFailList': '失败明细({n})',
|
||||||
|
'system.batchEmail.copyFailList': '复制明细',
|
||||||
|
'system.batchEmail.msgCopyFailOk': '失败明细已复制',
|
||||||
|
'system.batchEmail.errCopyFail': '复制失败,请手动选择文本',
|
||||||
|
'system.batchEmail.failInvalidEmail': '邮箱无效',
|
||||||
|
'system.batchEmail.failEmptyBody': '正文为空',
|
||||||
|
'system.batchEmail.msgSendCancelled': '已取消:成功 {successCount},失败 {failCount}',
|
||||||
|
'system.batchEmail.replacePreview': '替换预览',
|
||||||
|
'system.batchEmail.replacePreviewEmpty': '添加收件人后,可预览占位符替换效果',
|
||||||
|
'system.batchEmail.replacePreviewManual': '基于 {email}(手动邮箱无单号/轨迹时,对应占位符会为空)',
|
||||||
|
'system.batchEmail.replacePreviewSample': '基于首个收件人 {email} 的替换效果',
|
||||||
|
'system.batchEmail.confirmSwitchTplTitle': '切换模板?',
|
||||||
|
'system.batchEmail.confirmSwitchTplBody': '当前主题/正文已修改,切换将覆盖未保存的内容。',
|
||||||
|
'system.batchEmail.removeRecipient': '移除该邮箱',
|
||||||
|
|
||||||
// 系统 - 公告通知
|
// 系统 - 公告通知
|
||||||
'system.announcement.search.placeholder': '搜索标题或正文',
|
'system.announcement.search.placeholder': '搜索标题或正文',
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
import { initAuthKeepAlive } from '@/utils/auth-keepalive';
|
import { initAuthKeepAlive } from '@/utils/auth-keepalive';
|
||||||
import { installWeChatH5HistoryGuard } from '@/utils/device';
|
import { installWeChatH5HistoryGuard } from '@/utils/device';
|
||||||
import { installChunkReloadHandlers, reloadOnChunkLoadError } from '@/utils/chunkReload';
|
import { installChunkReloadHandlers, reloadOnChunkLoadError } from '@/utils/chunkReload';
|
||||||
|
import { initGa } from '@/utils/ga';
|
||||||
import { setAuthToken } from './api/rbac';
|
import { setAuthToken } from './api/rbac';
|
||||||
import { useRbacUserStore } from '@/store/modules/rbac-user';
|
import { useRbacUserStore } from '@/store/modules/rbac-user';
|
||||||
import { applyRbacUserToUserStore } from '@/utils/enter-system';
|
import { applyRbacUserToUserStore } from '@/utils/enter-system';
|
||||||
@ -138,6 +139,8 @@ async function bootstrap() {
|
|||||||
;(window as any).__APP_MOUNTED__ = true;
|
;(window as any).__APP_MOUNTED__ = true;
|
||||||
installWeChatH5HistoryGuard(router);
|
installWeChatH5HistoryGuard(router);
|
||||||
initAuthKeepAlive();
|
initAuthKeepAlive();
|
||||||
|
// GA4:有 VITE_GA_MEASUREMENT_ID 才加载;地域/设备等自动采集
|
||||||
|
initGa();
|
||||||
|
|
||||||
// 登录页勿预热 VXE/Element:体积大且会注入全局 CSS,和首屏抢带宽
|
// 登录页勿预热 VXE/Element:体积大且会注入全局 CSS,和首屏抢带宽
|
||||||
if (isHomePath(entryPath) && !isH5LikePath(entryPath) && !light) {
|
if (isHomePath(entryPath) && !isH5LikePath(entryPath) && !light) {
|
||||||
|
|||||||
@ -10,7 +10,12 @@ function setupPageGuard(router: Router) {
|
|||||||
setRouteEmitter(to);
|
setRouteEmitter(to);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.afterEach(() => {
|
router.afterEach((to) => {
|
||||||
|
// SPA 页面浏览(未配置 VITE_GA_MEASUREMENT_ID 时为空操作)
|
||||||
|
const fullPath = to.fullPath || to.path || '/';
|
||||||
|
void import('@/utils/ga').then(({ trackPageView }) => {
|
||||||
|
trackPageView(fullPath);
|
||||||
|
});
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -318,7 +318,11 @@ export const useRbacUserStore = defineStore('rbacUser', () => {
|
|||||||
enableKeepAlive()
|
enableKeepAlive()
|
||||||
await hydrateUserAfterLogin(result)
|
await hydrateUserAfterLogin(result)
|
||||||
return true
|
return true
|
||||||
} catch {
|
} catch (error: unknown) {
|
||||||
|
// 交给登录页展示业务文案(如「客户已禁用」),勿一律当成账号密码错误
|
||||||
|
const anyErr = error as { body?: { message?: string }; message?: string }
|
||||||
|
const msg = String(anyErr?.body?.message || anyErr?.message || '').trim()
|
||||||
|
if (msg) throw new Error(msg)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
80
src/utils/ga.ts
Normal file
80
src/utils/ga.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* Google Analytics 4(gtag)
|
||||||
|
* 通过 VITE_GA_MEASUREMENT_ID(如 G-XXXXXXXX)启用;未配置则整段空操作。
|
||||||
|
* 地域分布、设备、来源等由 GA4 自动采集,业务点击用 trackEvent / trackSiteEvent。
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
dataLayer: unknown[];
|
||||||
|
gtag?: (...args: unknown[]) => void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MEASUREMENT_ID = String(import.meta.env.VITE_GA_MEASUREMENT_ID || '').trim();
|
||||||
|
|
||||||
|
let booted = false;
|
||||||
|
|
||||||
|
export function isGaEnabled(): boolean {
|
||||||
|
return /^G-[A-Z0-9]+$/i.test(MEASUREMENT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGaMeasurementId(): string {
|
||||||
|
return isGaEnabled() ? MEASUREMENT_ID : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 注入 gtag 脚本并 config;可重复调用,仅首次生效 */
|
||||||
|
export function initGa(): void {
|
||||||
|
if (!isGaEnabled() || typeof window === 'undefined' || booted) return;
|
||||||
|
booted = true;
|
||||||
|
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
window.gtag = function gtag(...args: unknown[]) {
|
||||||
|
window.dataLayer.push(args);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.gtag('js', new Date());
|
||||||
|
window.gtag('config', MEASUREMENT_ID, {
|
||||||
|
// SPA 由路由 afterEach 主动发 page_view,避免首屏重复
|
||||||
|
send_page_view: false,
|
||||||
|
anonymize_ip: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.async = true;
|
||||||
|
script.src = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(MEASUREMENT_ID)}`;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function trackPageView(path: string, title?: string): void {
|
||||||
|
if (!isGaEnabled() || !window.gtag) return;
|
||||||
|
const pagePath = path || '/';
|
||||||
|
window.gtag('event', 'page_view', {
|
||||||
|
page_path: pagePath,
|
||||||
|
page_title: title || document.title,
|
||||||
|
page_location: typeof window !== 'undefined' ? window.location.href : undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function trackEvent(
|
||||||
|
name: string,
|
||||||
|
params?: Record<string, string | number | boolean | undefined | null>,
|
||||||
|
): void {
|
||||||
|
if (!isGaEnabled() || !window.gtag) return;
|
||||||
|
const cleaned: Record<string, string | number | boolean> = {};
|
||||||
|
if (params) {
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
if (value === undefined || value === null || value === '') return;
|
||||||
|
cleaned[key] = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.gtag('event', name, cleaned);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 官网营销事件(统一带 surface,方便在探索里筛选) */
|
||||||
|
export function trackSiteEvent(
|
||||||
|
name: string,
|
||||||
|
params?: Record<string, string | number | boolean | undefined | null>,
|
||||||
|
): void {
|
||||||
|
trackEvent(name, { surface: 'official_site', ...params });
|
||||||
|
}
|
||||||
@ -350,11 +350,19 @@ async function handleSubmit() {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
errorBox.value = '';
|
errorBox.value = '';
|
||||||
|
|
||||||
const success = await rbacUserStore.login(
|
let success = false;
|
||||||
|
try {
|
||||||
|
success = await rbacUserStore.login(
|
||||||
loginForm.account,
|
loginForm.account,
|
||||||
loginForm.password,
|
loginForm.password,
|
||||||
props.loginType,
|
props.loginType,
|
||||||
);
|
);
|
||||||
|
} catch (loginErr: unknown) {
|
||||||
|
const msg = loginErr instanceof Error ? loginErr.message.trim() : '';
|
||||||
|
errorBox.value = msg || t('login.form.login.failed');
|
||||||
|
loginCharacters.value?.onLoginFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (success && rbacUserStore.userInfo) {
|
if (success && rbacUserStore.userInfo) {
|
||||||
const userInfoData = rbacUserStore.userInfo;
|
const userInfoData = rbacUserStore.userInfo;
|
||||||
|
|||||||
@ -11,8 +11,8 @@
|
|||||||
IconCopy,
|
IconCopy,
|
||||||
IconSettings,
|
IconSettings,
|
||||||
} from '@arco-design/web-vue/es/icon';
|
} from '@arco-design/web-vue/es/icon';
|
||||||
import SlidingTabs from '@/components/SlidingTabs/index.vue';
|
|
||||||
import ExcelFileIcon from '@/components/icons/ExcelFileIcon.vue';
|
import ExcelFileIcon from '@/components/icons/ExcelFileIcon.vue';
|
||||||
|
import SlidingTabs from '@/components/SlidingTabs/index.vue';
|
||||||
import CustomTemplateModal from './components/CustomTemplateModal.vue';
|
import CustomTemplateModal from './components/CustomTemplateModal.vue';
|
||||||
import SelectDeclarationSkuModal from './components/SelectDeclarationSkuModal.vue';
|
import SelectDeclarationSkuModal from './components/SelectDeclarationSkuModal.vue';
|
||||||
import FormLabelWithPreview from '@/views/channel/salesProducts/components/FormLabelWithPreview.vue';
|
import FormLabelWithPreview from '@/views/channel/salesProducts/components/FormLabelWithPreview.vue';
|
||||||
@ -5777,6 +5777,7 @@ import SelectDeclarationSkuModal from './components/SelectDeclarationSkuModal.vu
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5787,6 +5788,22 @@ import SelectDeclarationSkuModal from './components/SelectDeclarationSkuModal.vu
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.t-tab[aria-selected='true'] .t-tab__label) {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #22c55e;
|
||||||
|
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.place-order-pane {
|
.place-order-pane {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|||||||
@ -325,6 +325,8 @@ void main() {
|
|||||||
canvas.style.height = '100%';
|
canvas.style.height = '100%';
|
||||||
canvas.style.display = 'block';
|
canvas.style.display = 'block';
|
||||||
canvas.style.pointerEvents = 'none';
|
canvas.style.pointerEvents = 'none';
|
||||||
|
canvas.style.opacity = '0';
|
||||||
|
canvas.style.transition = 'opacity 0.55s ease';
|
||||||
container.appendChild(canvas);
|
container.appendChild(canvas);
|
||||||
|
|
||||||
const geometry = new Triangle(gl);
|
const geometry = new Triangle(gl);
|
||||||
@ -367,6 +369,9 @@ void main() {
|
|||||||
const w = Math.max(1, Math.floor(rect.width * scale));
|
const w = Math.max(1, Math.floor(rect.width * scale));
|
||||||
const h = Math.max(1, Math.floor(rect.height * scale));
|
const h = Math.max(1, Math.floor(rect.height * scale));
|
||||||
renderer.setSize(w, h);
|
renderer.setSize(w, h);
|
||||||
|
// OGL setSize writes pixel style width/height; keep CSS 100% so parent resize always fills.
|
||||||
|
canvas.style.width = '100%';
|
||||||
|
canvas.style.height = '100%';
|
||||||
const res = mu.iResolution.value;
|
const res = mu.iResolution.value;
|
||||||
res[0] = gl.drawingBufferWidth;
|
res[0] = gl.drawingBufferWidth;
|
||||||
res[1] = gl.drawingBufferHeight;
|
res[1] = gl.drawingBufferHeight;
|
||||||
@ -454,6 +459,11 @@ void main() {
|
|||||||
|
|
||||||
tryStart();
|
tryStart();
|
||||||
syncUniforms();
|
syncUniforms();
|
||||||
|
// 先画一帧再淡入,盖在静态海天底上,避免灰/白闪
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
paint(performance.now());
|
||||||
|
canvas.style.opacity = '1';
|
||||||
|
});
|
||||||
|
|
||||||
teardown = () => {
|
teardown = () => {
|
||||||
tryStop();
|
tryStop();
|
||||||
@ -491,5 +501,13 @@ void main() {
|
|||||||
display: block;
|
display: block;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
|
will-change: opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.gradient-waves :deep(canvas) {
|
||||||
|
transition: none !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,33 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="ai-advisor-shell" :class="{ 'ai-history-open': historyOpen }">
|
<div
|
||||||
<div class="ai-shell-bg" aria-hidden="true">
|
id="ai-advisor-shell"
|
||||||
<GradientWaves
|
:class="{ 'ai-history-open': historyOpen }"
|
||||||
class="ai-shell-bg__waves"
|
>
|
||||||
horizon-color="#023e8a"
|
|
||||||
wave-color="#0077b6"
|
|
||||||
crest-color="#caf0f8"
|
|
||||||
:speed="0.34"
|
|
||||||
:amplitude="3.3"
|
|
||||||
:wave-scale="0.72"
|
|
||||||
:wave-ratio="0.82"
|
|
||||||
:swell="42"
|
|
||||||
:turbulence="26"
|
|
||||||
:tilt="1.08"
|
|
||||||
:zoom="0.92"
|
|
||||||
:height="4.8"
|
|
||||||
:fog-depth="22"
|
|
||||||
detail="low"
|
|
||||||
:brightness="1.08"
|
|
||||||
:opacity="1.0"
|
|
||||||
:mouse-interaction="true"
|
|
||||||
:parallax-strength="0.4"
|
|
||||||
:grain="false"
|
|
||||||
:fps="30"
|
|
||||||
:render-scale="0.72"
|
|
||||||
:max-dpr="1.25"
|
|
||||||
/>
|
|
||||||
<div class="ai-shell-bg__veil" aria-hidden="true"></div>
|
|
||||||
</div>
|
|
||||||
<div class="ai-advisor-layout">
|
<div class="ai-advisor-layout">
|
||||||
<div class="ai-advisor-app">
|
<div class="ai-advisor-app">
|
||||||
<div v-if="chatting" class="ai-stage-tools">
|
<div v-if="chatting" class="ai-stage-tools">
|
||||||
@ -57,7 +32,7 @@
|
|||||||
<div class="ai-welcome__intro">
|
<div class="ai-welcome__intro">
|
||||||
<h2 class="ai-welcome-title" :aria-label="shortTitle ? copy.titleShort : copy.title">
|
<h2 class="ai-welcome-title" :aria-label="shortTitle ? copy.titleShort : copy.title">
|
||||||
<span class="ai-welcome-title__brand" aria-hidden="true">
|
<span class="ai-welcome-title__brand" aria-hidden="true">
|
||||||
IDE<span class="ai-welcome-title__dot">.</span>
|
IDE<span class="ai-welcome-title__dot"> · </span>
|
||||||
</span>
|
</span>
|
||||||
<TextType
|
<TextType
|
||||||
v-if="titleAnimate"
|
v-if="titleAnimate"
|
||||||
@ -65,7 +40,8 @@
|
|||||||
as="span"
|
as="span"
|
||||||
class-name="ai-welcome-title__type"
|
class-name="ai-welcome-title__type"
|
||||||
:text="shortTitle ? copy.titleRestShort : copy.titleRest"
|
:text="shortTitle ? copy.titleRestShort : copy.titleRest"
|
||||||
:typing-speed="36"
|
:typing-speed="48"
|
||||||
|
:initial-delay="280"
|
||||||
:pause-duration="900"
|
:pause-duration="900"
|
||||||
:show-cursor="false"
|
:show-cursor="false"
|
||||||
:loop="false"
|
:loop="false"
|
||||||
@ -128,7 +104,11 @@
|
|||||||
v-for="door in primaryDoors"
|
v-for="door in primaryDoors"
|
||||||
:key="door.id"
|
:key="door.id"
|
||||||
type="button"
|
type="button"
|
||||||
class="ai-intent-door is-primary"
|
class="ai-intent-door is-primary lg-surface"
|
||||||
|
:class="{
|
||||||
|
'is-glass-ready': glassReady,
|
||||||
|
'is-glass-fallback': glassFallback,
|
||||||
|
}"
|
||||||
:disabled="busy"
|
:disabled="busy"
|
||||||
@click="onHomeDoor(door)"
|
@click="onHomeDoor(door)"
|
||||||
>
|
>
|
||||||
@ -149,25 +129,25 @@
|
|||||||
v-for="door in growthDoors"
|
v-for="door in growthDoors"
|
||||||
:key="door.id"
|
:key="door.id"
|
||||||
type="button"
|
type="button"
|
||||||
class="ai-intent-door"
|
class="ai-intent-door lg-surface"
|
||||||
:class="{
|
:class="{
|
||||||
'is-agent-tall': door.id === 'agent',
|
'is-glass-ready': glassReady,
|
||||||
'is-growth-wide': door.id === 'newcomer',
|
'is-glass-fallback': glassFallback,
|
||||||
}"
|
}"
|
||||||
:disabled="busy"
|
:disabled="busy"
|
||||||
@click="onHomeDoor(door)"
|
@click="onHomeDoor(door)"
|
||||||
>
|
>
|
||||||
|
<span class="ai-intent-door__head">
|
||||||
<span class="ai-intent-door__icon">
|
<span class="ai-intent-door__icon">
|
||||||
<SiteIllusIcon :name="door.icon" />
|
<SiteIllusIcon :name="door.icon" />
|
||||||
</span>
|
</span>
|
||||||
<span class="ai-intent-door__copy">
|
|
||||||
<span class="ai-intent-door__title">{{
|
<span class="ai-intent-door__title">{{
|
||||||
isZh ? door.zh.title : door.en.title
|
isZh ? door.zh.title : door.en.title
|
||||||
}}</span>
|
}}</span>
|
||||||
|
</span>
|
||||||
<span class="ai-intent-door__hint">{{
|
<span class="ai-intent-door__hint">{{
|
||||||
isZh ? door.zh.hint : door.en.hint
|
isZh ? door.zh.hint : door.en.hint
|
||||||
}}</span>
|
}}</span>
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -287,7 +267,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
import SiteComposer from './SiteComposer.vue';
|
import SiteComposer from './SiteComposer.vue';
|
||||||
import SitePartnerMarquee from './SitePartnerMarquee.vue';
|
import SitePartnerMarquee from './SitePartnerMarquee.vue';
|
||||||
import SiteMessageCard from './SiteMessageCard.vue';
|
import SiteMessageCard from './SiteMessageCard.vue';
|
||||||
@ -295,8 +275,7 @@
|
|||||||
import TextType from './TextType.vue';
|
import TextType from './TextType.vue';
|
||||||
import { HOME_DOORS, HERO_STATS, SITE_FOLLOWUP_INTENTS, WELCOME_COPY } from './lib/site-welcome';
|
import { HOME_DOORS, HERO_STATS, SITE_FOLLOWUP_INTENTS, WELCOME_COPY } from './lib/site-welcome';
|
||||||
import { ROUTE_DEST_PRESETS } from './lib/portal-catalog';
|
import { ROUTE_DEST_PRESETS } from './lib/portal-catalog';
|
||||||
|
import { SITE_GLASS_PANEL_OPTS, useSiteLiquidGlass } from './lib/site-liquid-glass';
|
||||||
const GradientWaves = defineAsyncComponent(() => import('./GradientWaves.vue'));
|
|
||||||
|
|
||||||
export type SiteChatMessage = {
|
export type SiteChatMessage = {
|
||||||
role: 'user' | 'assistant';
|
role: 'user' | 'assistant';
|
||||||
@ -342,11 +321,35 @@
|
|||||||
choice: [payload: { id: string; label: string; flowId: string }];
|
choice: [payload: { id: string; label: string; flowId: string }];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const siteGlass = useSiteLiquidGlass();
|
||||||
|
const glassReady = computed(() => !!siteGlass?.ready.value);
|
||||||
|
const glassFallback = computed(() => !!siteGlass?.fallback.value);
|
||||||
|
let removeGlassSurfaces: Array<() => void> = [];
|
||||||
|
|
||||||
|
function teardownLiquidGlass() {
|
||||||
|
removeGlassSurfaces.forEach((remove) => remove());
|
||||||
|
removeGlassSurfaces = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupLiquidGlass() {
|
||||||
|
await nextTick();
|
||||||
|
if (!siteGlass || props.chatting) return;
|
||||||
|
teardownLiquidGlass();
|
||||||
|
const doorSurfaces = Array.from(
|
||||||
|
document.querySelectorAll<HTMLElement>(
|
||||||
|
'#ai-advisor-shell .ai-welcome__command .ai-intent-door.lg-surface',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
removeGlassSurfaces = doorSurfaces.map((el) =>
|
||||||
|
siteGlass.registerSurface(el, SITE_GLASS_PANEL_OPTS),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const copy = computed(() => (props.isZh ? WELCOME_COPY.zh : WELCOME_COPY.en));
|
const copy = computed(() => (props.isZh ? WELCOME_COPY.zh : WELCOME_COPY.en));
|
||||||
const homeDests = ROUTE_DEST_PRESETS.filter((item) => item.id !== 'all');
|
const homeDests = ROUTE_DEST_PRESETS.filter((item) => item.id !== 'all');
|
||||||
const primaryDoors = HOME_DOORS.filter((d) => d.primary);
|
const primaryDoors = HOME_DOORS.filter((d) => d.primary);
|
||||||
const growthDoors = HOME_DOORS.filter((d) => !d.primary);
|
const growthDoors = HOME_DOORS.filter((d) => !d.primary);
|
||||||
// 首屏即按视口决定短标题,避免挂载后再切导致 TextType 卡住只剩「IDE.」
|
// 首屏即按视口决定短标题,避免挂载后再切导致 TextType 卡住只剩品牌前缀
|
||||||
const shortTitle = ref(
|
const shortTitle = ref(
|
||||||
typeof window !== 'undefined' && window.matchMedia('(max-width: 640px)').matches,
|
typeof window !== 'undefined' && window.matchMedia('(max-width: 640px)').matches,
|
||||||
);
|
);
|
||||||
@ -397,16 +400,30 @@
|
|||||||
titleAnimate.value = !mqReduce?.matches;
|
titleAnimate.value = !mqReduce?.matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.chatting,
|
||||||
|
async (chatting) => {
|
||||||
|
if (chatting) {
|
||||||
|
teardownLiquidGlass();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await nextTick();
|
||||||
|
void setupLiquidGlass();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
mqNarrow = window.matchMedia('(max-width: 640px)');
|
mqNarrow = window.matchMedia('(max-width: 640px)');
|
||||||
mqReduce = window.matchMedia('(prefers-reduced-motion: reduce)');
|
mqReduce = window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||||
syncMediaPrefs();
|
syncMediaPrefs();
|
||||||
mqNarrow.addEventListener('change', syncMediaPrefs);
|
mqNarrow.addEventListener('change', syncMediaPrefs);
|
||||||
mqReduce.addEventListener('change', syncMediaPrefs);
|
mqReduce.addEventListener('change', syncMediaPrefs);
|
||||||
|
void setupLiquidGlass();
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
mqNarrow?.removeEventListener('change', syncMediaPrefs);
|
mqNarrow?.removeEventListener('change', syncMediaPrefs);
|
||||||
mqReduce?.removeEventListener('change', syncMediaPrefs);
|
mqReduce?.removeEventListener('change', syncMediaPrefs);
|
||||||
|
teardownLiquidGlass();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -29,12 +29,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li ref="dockRef" class="site-nav-dock">
|
<li ref="dockRef" class="site-nav-dock">
|
||||||
<span class="site-nav-dock__glass" aria-hidden="true"></span>
|
<span class="site-nav-dock__glass" aria-hidden="true"></span>
|
||||||
<span
|
<span ref="sliderRef" class="site-nav-dock__slider" aria-hidden="true"></span>
|
||||||
class="site-nav-dock__slider"
|
|
||||||
:class="{ 'is-ready': sliderReady }"
|
|
||||||
:style="sliderStyle"
|
|
||||||
aria-hidden="true"
|
|
||||||
></span>
|
|
||||||
<a
|
<a
|
||||||
v-for="item in items"
|
v-for="item in items"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@ -210,61 +205,61 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
const dockRef = ref<HTMLElement | null>(null);
|
const dockRef = ref<HTMLElement | null>(null);
|
||||||
const sliderReady = ref(false);
|
const sliderRef = ref<HTMLElement | null>(null);
|
||||||
const sliderStyle = ref<Record<string, string>>({
|
|
||||||
left: '0px',
|
|
||||||
width: '0px',
|
|
||||||
opacity: '0',
|
|
||||||
});
|
|
||||||
const sliderPos = { x: 0, w: 0 };
|
|
||||||
let resizeObserver: ResizeObserver | null = null;
|
|
||||||
let sliderRaf = 0;
|
|
||||||
|
|
||||||
const SLIDER_INSET = 4;
|
const SLIDER_INSET = 4;
|
||||||
|
const sliderPos = { x: 0, w: 0, visible: false };
|
||||||
|
let sliderReady = false;
|
||||||
|
let sliderRaf = 0;
|
||||||
|
let resizeObserver: ResizeObserver | null = null;
|
||||||
|
let mqNarrow: MediaQueryList | null = null;
|
||||||
|
let mqReduce: MediaQueryList | null = null;
|
||||||
|
|
||||||
|
function paintSlider(x: number, w: number, visible: boolean, animate: boolean) {
|
||||||
|
const el = sliderRef.value;
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
const reduce = !!mqReduce?.matches;
|
||||||
|
const canAnimate = animate && sliderReady && sliderPos.visible && !reduce && sliderPos.w > 0;
|
||||||
|
el.style.transition = canAnimate
|
||||||
|
? 'transform 0.28s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.18s ease'
|
||||||
|
: 'none';
|
||||||
|
el.style.width = `${w}px`;
|
||||||
|
el.style.transform = `translate3d(${x}px, 0, 0)`;
|
||||||
|
el.style.opacity = visible ? '1' : '0';
|
||||||
|
|
||||||
function applySlider(x: number, w: number, opacity = '1') {
|
|
||||||
sliderPos.x = x;
|
sliderPos.x = x;
|
||||||
sliderPos.w = w;
|
sliderPos.w = w;
|
||||||
sliderStyle.value = {
|
sliderPos.visible = visible;
|
||||||
left: `${x}px`,
|
|
||||||
width: `${w}px`,
|
if (!sliderReady && visible) {
|
||||||
opacity,
|
requestAnimationFrame(() => {
|
||||||
};
|
sliderReady = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSlider(animate: boolean) {
|
function updateSlider(animate: boolean) {
|
||||||
const dock = dockRef.value;
|
const dock = dockRef.value;
|
||||||
const active = dock?.querySelector<HTMLElement>('.site-nav-dock__item.is-active');
|
const active = dock?.querySelector<HTMLElement>('.site-nav-dock__item.is-active');
|
||||||
if (!dock || !active || window.matchMedia('(max-width: 1279px)').matches) {
|
if (!dock || !active || mqNarrow?.matches) {
|
||||||
sliderStyle.value = { ...sliderStyle.value, opacity: '0' };
|
paintSlider(sliderPos.x, sliderPos.w, false, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const x = active.offsetLeft + SLIDER_INSET;
|
const x = active.offsetLeft + SLIDER_INSET;
|
||||||
const w = Math.max(active.offsetWidth - SLIDER_INSET * 2, 0);
|
const w = Math.max(active.offsetWidth - SLIDER_INSET * 2, 0);
|
||||||
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
if (
|
||||||
|
animate &&
|
||||||
// 首帧或关动画:先贴到位再开 transition,避免初次跳动
|
Math.abs(sliderPos.x - x) < 0.5 &&
|
||||||
if (!animate || !sliderReady.value || sliderPos.w <= 0 || reduceMotion) {
|
Math.abs(sliderPos.w - w) < 0.5 &&
|
||||||
sliderReady.value = false;
|
sliderPos.visible
|
||||||
applySlider(x, w);
|
) {
|
||||||
requestAnimationFrame(() => {
|
|
||||||
sliderReady.value = true;
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
paintSlider(x, w, true, animate);
|
||||||
if (Math.abs(sliderPos.x - x) < 0.5 && Math.abs(sliderPos.w - w) < 0.5) {
|
|
||||||
applySlider(x, w);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 单段滑移:left/width 一次到位,交给 CSS 缓动
|
function syncSlider(animate: boolean) {
|
||||||
applySlider(x, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function syncSlider(animate: boolean) {
|
|
||||||
await nextTick();
|
|
||||||
if (sliderRaf) cancelAnimationFrame(sliderRaf);
|
if (sliderRaf) cancelAnimationFrame(sliderRaf);
|
||||||
sliderRaf = requestAnimationFrame(() => {
|
sliderRaf = requestAnimationFrame(() => {
|
||||||
sliderRaf = 0;
|
sliderRaf = 0;
|
||||||
@ -273,17 +268,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onViewportResize() {
|
function onViewportResize() {
|
||||||
updateSlider(false);
|
sliderReady = false;
|
||||||
|
syncSlider(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
void hydrateSession();
|
void hydrateSession();
|
||||||
void syncSlider(false);
|
mqNarrow = window.matchMedia('(max-width: 1279px)');
|
||||||
if (typeof ResizeObserver !== 'undefined') {
|
mqReduce = window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||||
|
void nextTick(() => syncSlider(false));
|
||||||
|
if (typeof ResizeObserver !== 'undefined' && dockRef.value) {
|
||||||
resizeObserver = new ResizeObserver(onViewportResize);
|
resizeObserver = new ResizeObserver(onViewportResize);
|
||||||
if (dockRef.value) resizeObserver.observe(dockRef.value);
|
resizeObserver.observe(dockRef.value);
|
||||||
}
|
}
|
||||||
window.addEventListener('resize', onViewportResize);
|
window.addEventListener('resize', onViewportResize, { passive: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
@ -301,8 +299,9 @@
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [props.nav, props.isZh] as const,
|
() => [props.nav, props.isZh] as const,
|
||||||
() => {
|
async () => {
|
||||||
void syncSlider(true);
|
await nextTick();
|
||||||
|
syncSlider(true);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,13 +1,55 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="official-site"
|
ref="glassSceneEl"
|
||||||
|
class="official-site lg-scene"
|
||||||
:class="{
|
:class="{
|
||||||
'is-routes-open': panel === 'routes',
|
'is-routes-open': panel === 'routes',
|
||||||
'is-services-open': panel === 'services',
|
'is-services-open': panel === 'services',
|
||||||
'is-topic-open': panel === 'pricing' || panel === 'about' || panel === 'contact',
|
'is-topic-open': panel === 'pricing' || panel === 'about' || panel === 'contact',
|
||||||
|
'is-glass-ready': glass.ready.value,
|
||||||
|
'is-glass-fallback': glass.fallback.value,
|
||||||
}"
|
}"
|
||||||
@click.capture="onOfficialLoginCapture"
|
@click.capture="onOfficialLoginCapture"
|
||||||
>
|
>
|
||||||
|
<!--
|
||||||
|
lg-content 会被 liquid-glass 在 resize 时改写 left/right/宽高;
|
||||||
|
波浪放在固定视口 __paint 层,避免缩窗后右侧留白。
|
||||||
|
-->
|
||||||
|
<div
|
||||||
|
ref="glassContentEl"
|
||||||
|
class="ai-shell-bg lg-content"
|
||||||
|
data-layout="overlay"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div class="ai-shell-bg__paint">
|
||||||
|
<GradientWaves
|
||||||
|
class="ai-shell-bg__waves"
|
||||||
|
horizon-color="#023e8a"
|
||||||
|
wave-color="#0077b6"
|
||||||
|
crest-color="#caf0f8"
|
||||||
|
:speed="0.34"
|
||||||
|
:amplitude="3.3"
|
||||||
|
:wave-scale="0.72"
|
||||||
|
:wave-ratio="0.82"
|
||||||
|
:swell="42"
|
||||||
|
:turbulence="26"
|
||||||
|
:tilt="1.08"
|
||||||
|
:zoom="0.92"
|
||||||
|
:height="4.8"
|
||||||
|
:fog-depth="22"
|
||||||
|
detail="low"
|
||||||
|
:brightness="1.08"
|
||||||
|
:opacity="1.0"
|
||||||
|
:mouse-interaction="true"
|
||||||
|
:parallax-strength="0.4"
|
||||||
|
:grain="false"
|
||||||
|
:fps="30"
|
||||||
|
:render-scale="0.72"
|
||||||
|
:max-dpr="1.25"
|
||||||
|
/>
|
||||||
|
<div class="ai-shell-bg__veil" aria-hidden="true"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<SiteHeader
|
<SiteHeader
|
||||||
:nav="nav"
|
:nav="nav"
|
||||||
:is-zh="isZh"
|
:is-zh="isZh"
|
||||||
@ -34,7 +76,7 @@
|
|||||||
:active-thread-id="activeThreadId"
|
:active-thread-id="activeThreadId"
|
||||||
:routes-pending="routesPending"
|
:routes-pending="routesPending"
|
||||||
@submit="onSubmit"
|
@submit="onSubmit"
|
||||||
@intent="advisor.askIntent"
|
@intent="onIntent"
|
||||||
@human="onNav('contact')"
|
@human="onNav('contact')"
|
||||||
@wechat="openWecom"
|
@wechat="openWecom"
|
||||||
@prefill="draft = $event"
|
@prefill="draft = $event"
|
||||||
@ -91,7 +133,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import SiteHeader from './SiteHeader.vue';
|
import SiteHeader from './SiteHeader.vue';
|
||||||
import SiteAdvisorShell from './SiteAdvisorShell.vue';
|
import SiteAdvisorShell from './SiteAdvisorShell.vue';
|
||||||
@ -99,18 +141,25 @@
|
|||||||
import SiteServicesPanel from './SiteServicesPanel.vue';
|
import SiteServicesPanel from './SiteServicesPanel.vue';
|
||||||
import SiteTopicPanel from './SiteTopicPanel.vue';
|
import SiteTopicPanel from './SiteTopicPanel.vue';
|
||||||
import SiteWecomModal from './SiteWecomModal.vue';
|
import SiteWecomModal from './SiteWecomModal.vue';
|
||||||
|
import GradientWaves from './GradientWaves.vue';
|
||||||
import {
|
import {
|
||||||
goOfficialLoginFromHref,
|
goOfficialLoginFromHref,
|
||||||
officialLoginUrl,
|
officialLoginUrl,
|
||||||
warmupOfficialLogin,
|
warmupOfficialLogin,
|
||||||
} from './lib/site-urls';
|
} from './lib/site-urls';
|
||||||
import { shouldOpenWecomDirect, WECOM_KF_URL } from './lib/site-wecom';
|
import { shouldOpenWecomDirect, WECOM_KF_URL } from './lib/site-wecom';
|
||||||
|
import { provideSiteLiquidGlass } from './lib/site-liquid-glass';
|
||||||
import { useSiteAdvisor } from './useSiteAdvisor';
|
import { useSiteAdvisor } from './useSiteAdvisor';
|
||||||
|
import { trackSiteEvent } from '@/utils/ga';
|
||||||
import './styles/vendor.css';
|
import './styles/vendor.css';
|
||||||
import './styles/icons.css';
|
import './styles/icons.css';
|
||||||
import './styles/main.css';
|
import './styles/main.css';
|
||||||
import './styles/ai-advisor.css';
|
import './styles/ai-advisor.css';
|
||||||
|
|
||||||
|
const glass = provideSiteLiquidGlass();
|
||||||
|
const glassSceneEl = ref<HTMLElement | null>(null);
|
||||||
|
const glassContentEl = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
type PanelId = 'routes' | 'services' | 'pricing' | 'about' | 'contact' | null;
|
type PanelId = 'routes' | 'services' | 'pricing' | 'about' | 'contact' | null;
|
||||||
const NAV_PANELS: PanelId[] = ['routes', 'services', 'pricing', 'about', 'contact'];
|
const NAV_PANELS: PanelId[] = ['routes', 'services', 'pricing', 'about', 'contact'];
|
||||||
|
|
||||||
@ -133,9 +182,17 @@
|
|||||||
function onOfficialLoginCapture(e: MouseEvent) {
|
function onOfficialLoginCapture(e: MouseEvent) {
|
||||||
const target = e.target as Element | null;
|
const target = e.target as Element | null;
|
||||||
const anchor = target?.closest?.('a[href]') as HTMLAnchorElement | null;
|
const anchor = target?.closest?.('a[href]') as HTMLAnchorElement | null;
|
||||||
if (!anchor || anchor.classList.contains('site-nav-login')) return;
|
if (!anchor) return;
|
||||||
const href = anchor.getAttribute('href') || '';
|
const href = anchor.getAttribute('href') || '';
|
||||||
if (!href.includes('from=official-site')) return;
|
if (!href.includes('from=official-site')) return;
|
||||||
|
trackSiteEvent('home_login_click', {
|
||||||
|
link_role: anchor.classList.contains('site-nav-login')
|
||||||
|
? 'nav_login'
|
||||||
|
: anchor.classList.contains('ai-welcome-cta-link')
|
||||||
|
? 'hero_cta'
|
||||||
|
: 'other',
|
||||||
|
});
|
||||||
|
if (anchor.classList.contains('site-nav-login')) return;
|
||||||
goOfficialLoginFromHref(router, href, e);
|
goOfficialLoginFromHref(router, href, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,14 +301,22 @@
|
|||||||
panel.value = id;
|
panel.value = id;
|
||||||
nav.value = id;
|
nav.value = id;
|
||||||
syncHash(id);
|
syncHash(id);
|
||||||
|
trackSiteEvent('home_panel_open', { panel_id: id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function openRoutes(dest?: string) {
|
function openRoutes(dest?: string) {
|
||||||
|
const destId = dest || 'all';
|
||||||
advisor.clearRoutesPending();
|
advisor.clearRoutesPending();
|
||||||
routesDest.value = dest || 'all';
|
routesDest.value = destId;
|
||||||
|
trackSiteEvent('home_dest_click', { dest_id: destId });
|
||||||
openPanel('routes');
|
openPanel('routes');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onIntent(intentId: string) {
|
||||||
|
trackSiteEvent('home_intent_click', { intent_id: intentId });
|
||||||
|
advisor.askIntent(intentId);
|
||||||
|
}
|
||||||
|
|
||||||
function closePanel(id?: string) {
|
function closePanel(id?: string) {
|
||||||
if (id && panel.value && panel.value !== id) return;
|
if (id && panel.value && panel.value !== id) return;
|
||||||
if (!panel.value) return;
|
if (!panel.value) return;
|
||||||
@ -266,6 +331,7 @@
|
|||||||
historyOpen.value = false;
|
historyOpen.value = false;
|
||||||
nav.value = 'home';
|
nav.value = 'home';
|
||||||
syncHash('home');
|
syncHash('home');
|
||||||
|
trackSiteEvent('home_nav', { nav_id: 'home' });
|
||||||
advisor.startNewChat();
|
advisor.startNewChat();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,6 +341,7 @@
|
|||||||
onHome();
|
onHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
trackSiteEvent('home_nav', { nav_id: id });
|
||||||
if (NAV_PANELS.includes(id as PanelId)) {
|
if (NAV_PANELS.includes(id as PanelId)) {
|
||||||
openPanel(id as Exclude<PanelId, null>);
|
openPanel(id as Exclude<PanelId, null>);
|
||||||
return;
|
return;
|
||||||
@ -288,6 +355,10 @@
|
|||||||
const fromRoutes = panel.value === 'routes';
|
const fromRoutes = panel.value === 'routes';
|
||||||
closePanel();
|
closePanel();
|
||||||
draft.value = '';
|
draft.value = '';
|
||||||
|
trackSiteEvent('home_ask', {
|
||||||
|
intent_id: payload.intentId,
|
||||||
|
from_routes: fromRoutes,
|
||||||
|
});
|
||||||
advisor.ask(payload.text, { intentId: payload.intentId, fromRoutes });
|
advisor.ask(payload.text, { intentId: payload.intentId, fromRoutes });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,11 +366,16 @@
|
|||||||
const text = draft.value.trim();
|
const text = draft.value.trim();
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
draft.value = '';
|
draft.value = '';
|
||||||
|
trackSiteEvent('home_ask', { intent_id: 'free_text' });
|
||||||
advisor.ask(text);
|
advisor.ask(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChoice(payload: { id: string; label: string; flowId: string }) {
|
function onChoice(payload: { id: string; label: string; flowId: string }) {
|
||||||
draft.value = '';
|
draft.value = '';
|
||||||
|
trackSiteEvent('home_choice', {
|
||||||
|
choice_id: payload.id,
|
||||||
|
flow_id: payload.flowId,
|
||||||
|
});
|
||||||
advisor.ask(payload.label, { kind: 'choice', flowId: payload.flowId, choice: payload.id });
|
advisor.ask(payload.label, { kind: 'choice', flowId: payload.flowId, choice: payload.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,6 +385,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openWecom() {
|
function openWecom() {
|
||||||
|
trackSiteEvent('home_wecom_click', { entry: 'advisor' });
|
||||||
if (shouldOpenWecomDirect()) {
|
if (shouldOpenWecomDirect()) {
|
||||||
window.location.href = WECOM_KF_URL;
|
window.location.href = WECOM_KF_URL;
|
||||||
return;
|
return;
|
||||||
@ -317,6 +394,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onTopicWechat() {
|
function onTopicWechat() {
|
||||||
|
trackSiteEvent('home_wecom_click', { entry: 'topic' });
|
||||||
if (shouldOpenWecomDirect()) {
|
if (shouldOpenWecomDirect()) {
|
||||||
window.location.href = WECOM_KF_URL;
|
window.location.href = WECOM_KF_URL;
|
||||||
return;
|
return;
|
||||||
@ -328,6 +406,7 @@
|
|||||||
const next = lang.value === 'zh' ? 'en' : 'zh';
|
const next = lang.value === 'zh' ? 'en' : 'zh';
|
||||||
lang.value = next;
|
lang.value = next;
|
||||||
localStorage.setItem('ide-language', next);
|
localStorage.setItem('ide-language', next);
|
||||||
|
trackSiteEvent('home_lang_toggle', { lang: next });
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeydown(e: KeyboardEvent) {
|
function onKeydown(e: KeyboardEvent) {
|
||||||
@ -378,9 +457,15 @@
|
|||||||
if (hash === '9610' || hash === 'services') openPanel('services');
|
if (hash === '9610' || hash === 'services') openPanel('services');
|
||||||
else if (NAV_PANELS.includes(hash as PanelId)) openPanel(hash as Exclude<PanelId, null>);
|
else if (NAV_PANELS.includes(hash as PanelId)) openPanel(hash as Exclude<PanelId, null>);
|
||||||
else if (hash) nav.value = hash;
|
else if (hash) nav.value = hash;
|
||||||
|
void nextTick(() => {
|
||||||
|
if (glassSceneEl.value && glassContentEl.value) {
|
||||||
|
glass.init(glassSceneEl.value, glassContentEl.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
glass.dispose();
|
||||||
window.removeEventListener('keydown', onKeydown);
|
window.removeEventListener('keydown', onKeydown);
|
||||||
window.removeEventListener('resize', syncHeaderOffset);
|
window.removeEventListener('resize', syncHeaderOffset);
|
||||||
window.removeEventListener('resize', syncVisualViewport);
|
window.removeEventListener('resize', syncVisualViewport);
|
||||||
@ -395,7 +480,9 @@
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.official-site {
|
.official-site {
|
||||||
|
position: relative;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
isolation: isolate;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@ -419,7 +506,7 @@
|
|||||||
|
|
||||||
.site-fade-enter-active,
|
.site-fade-enter-active,
|
||||||
.site-fade-leave-active {
|
.site-fade-leave-active {
|
||||||
transition: opacity 0.32s cubic-bezier(0.22, 1, 0.36, 1);
|
transition: opacity 0.22s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-fade-enter-from,
|
.site-fade-enter-from,
|
||||||
@ -430,9 +517,8 @@
|
|||||||
.site-sheet-enter-active,
|
.site-sheet-enter-active,
|
||||||
.site-sheet-leave-active {
|
.site-sheet-leave-active {
|
||||||
transition:
|
transition:
|
||||||
opacity 0.34s cubic-bezier(0.22, 1, 0.36, 1),
|
opacity 0.24s cubic-bezier(0.22, 1, 0.36, 1),
|
||||||
transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
|
transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
filter 0.34s cubic-bezier(0.22, 1, 0.36, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-sheet-leave-active {
|
.site-sheet-leave-active {
|
||||||
@ -441,25 +527,21 @@
|
|||||||
|
|
||||||
.site-sheet-enter-from {
|
.site-sheet-enter-from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
filter: blur(2px);
|
transform: translateX(-50%) translateY(-8px);
|
||||||
transform: translateX(-50%) translateY(-10px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-sheet-leave-to {
|
.site-sheet-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
filter: blur(1px);
|
transform: translateX(-50%) translateY(-4px);
|
||||||
transform: translateX(-50%) translateY(-6px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 780px) {
|
@media (max-width: 780px) {
|
||||||
.site-sheet-enter-from {
|
.site-sheet-enter-from {
|
||||||
filter: none;
|
transform: translateY(12px);
|
||||||
transform: translateY(16px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-sheet-leave-to {
|
.site-sheet-leave-to {
|
||||||
filter: none;
|
transform: translateY(8px);
|
||||||
transform: translateY(10px);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
231
src/views/site/lib/site-liquid-glass.ts
Normal file
231
src/views/site/lib/site-liquid-glass.ts
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
import { inject, provide, ref, type InjectionKey, type Ref } from 'vue';
|
||||||
|
import {
|
||||||
|
attachSelectionLens,
|
||||||
|
createGlassScene,
|
||||||
|
type GlassSceneController,
|
||||||
|
type SurfaceOptions,
|
||||||
|
} from '@glass-sdk/liquid-glass/dom';
|
||||||
|
import '@glass-sdk/liquid-glass/styles.css';
|
||||||
|
|
||||||
|
export type SiteGlassSurfaceOpts = SurfaceOptions;
|
||||||
|
|
||||||
|
type QueuedJob =
|
||||||
|
| { kind: 'surface'; el: HTMLElement; opts?: SiteGlassSurfaceOpts; setRemove: (fn: () => void) => void }
|
||||||
|
| { kind: 'indicator'; el: HTMLElement; opts?: SiteGlassSurfaceOpts; setRemove: (fn: () => void) => void };
|
||||||
|
|
||||||
|
export type SiteLiquidGlassApi = {
|
||||||
|
ready: Ref<boolean>;
|
||||||
|
fallback: Ref<boolean>;
|
||||||
|
init: (root: HTMLElement, content: HTMLElement) => void;
|
||||||
|
registerSurface: (el: HTMLElement | null | undefined, opts?: SiteGlassSurfaceOpts) => () => void;
|
||||||
|
registerTabIndicator: (el: HTMLElement | null | undefined, opts?: SiteGlassSurfaceOpts) => () => void;
|
||||||
|
dispose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SITE_LIQUID_GLASS_KEY: InjectionKey<SiteLiquidGlassApi> = Symbol('site-liquid-glass');
|
||||||
|
|
||||||
|
/** 履约 / 权益门卡:低折射 + 关闭 fluid,减轻矮卡左右竖边光学校弯 */
|
||||||
|
export const SITE_GLASS_PANEL_OPTS: SiteGlassSurfaceOpts = {
|
||||||
|
material: 'clear',
|
||||||
|
appearance: 'light',
|
||||||
|
radius: 12,
|
||||||
|
refraction: 20,
|
||||||
|
fluid: false,
|
||||||
|
interactive: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SITE_GLASS_TABS_OPTS: SiteGlassSurfaceOpts = {
|
||||||
|
material: 'clear',
|
||||||
|
appearance: 'light',
|
||||||
|
radius: 'capsule',
|
||||||
|
refraction: 56,
|
||||||
|
interactive: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SITE_GLASS_TAB_INDICATOR_OPTS: SiteGlassSurfaceOpts = {
|
||||||
|
material: 'clear',
|
||||||
|
appearance: 'light',
|
||||||
|
radius: 'capsule',
|
||||||
|
refraction: 40,
|
||||||
|
interactive: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Welcome input — frosted glass, no tint */
|
||||||
|
export const SITE_GLASS_COMPOSER_OPTS: SiteGlassSurfaceOpts = {
|
||||||
|
material: 'regular',
|
||||||
|
appearance: 'light',
|
||||||
|
radius: 12,
|
||||||
|
refraction: 32,
|
||||||
|
fluid: true,
|
||||||
|
interactive: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function provideSiteLiquidGlass(): SiteLiquidGlassApi {
|
||||||
|
const ready = ref(false);
|
||||||
|
const fallback = ref(false);
|
||||||
|
|
||||||
|
let scene: GlassSceneController | null = null;
|
||||||
|
let removers: Array<() => void> = [];
|
||||||
|
let queue: QueuedJob[] = [];
|
||||||
|
let readyObserver: MutationObserver | null = null;
|
||||||
|
let fallbackTimer: number | null = null;
|
||||||
|
const trackedSurfaces = new Set<HTMLElement>();
|
||||||
|
|
||||||
|
function clearFallbackTimer() {
|
||||||
|
if (fallbackTimer != null) {
|
||||||
|
window.clearTimeout(fallbackTimer);
|
||||||
|
fallbackTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isClipReady(el: HTMLElement) {
|
||||||
|
return !!el.getAttribute('style')?.includes('clip-path');
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncReady() {
|
||||||
|
if ([...trackedSurfaces].some(isClipReady)) {
|
||||||
|
ready.value = true;
|
||||||
|
fallback.value = false;
|
||||||
|
clearFallbackTimer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function observeSurface(el: HTMLElement) {
|
||||||
|
trackedSurfaces.add(el);
|
||||||
|
if (!readyObserver) {
|
||||||
|
readyObserver = new MutationObserver(() => {
|
||||||
|
syncReady();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
readyObserver.observe(el, { attributes: true, attributeFilter: ['style'] });
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindSurface(el: HTMLElement, opts?: SiteGlassSurfaceOpts) {
|
||||||
|
if (!scene) return () => undefined;
|
||||||
|
const remove = scene.addSurface(el, opts);
|
||||||
|
observeSurface(el);
|
||||||
|
syncReady();
|
||||||
|
return () => {
|
||||||
|
trackedSurfaces.delete(el);
|
||||||
|
remove();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindIndicator(el: HTMLElement, opts?: SiteGlassSurfaceOpts) {
|
||||||
|
if (!scene) return () => undefined;
|
||||||
|
const removeSurface = bindSurface(el, opts ?? SITE_GLASS_TAB_INDICATOR_OPTS);
|
||||||
|
const removeAnimator = scene.addAnimator(attachSelectionLens(el, () => 'full'));
|
||||||
|
return () => {
|
||||||
|
removeAnimator();
|
||||||
|
removeSurface();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function enqueue(
|
||||||
|
kind: 'surface' | 'indicator',
|
||||||
|
el: HTMLElement,
|
||||||
|
opts?: SiteGlassSurfaceOpts,
|
||||||
|
) {
|
||||||
|
let removeFn = () => undefined;
|
||||||
|
let flushed = false;
|
||||||
|
const job: QueuedJob = {
|
||||||
|
kind,
|
||||||
|
el,
|
||||||
|
opts,
|
||||||
|
setRemove: (fn) => {
|
||||||
|
flushed = true;
|
||||||
|
removeFn = fn;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
queue.push(job);
|
||||||
|
return () => {
|
||||||
|
if (!flushed) {
|
||||||
|
queue = queue.filter((item) => item !== job);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
removeFn();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerSurface(el: HTMLElement | null | undefined, opts?: SiteGlassSurfaceOpts) {
|
||||||
|
if (!el) return () => undefined;
|
||||||
|
if (scene) {
|
||||||
|
const remove = bindSurface(el, opts);
|
||||||
|
removers.push(remove);
|
||||||
|
return remove;
|
||||||
|
}
|
||||||
|
return enqueue('surface', el, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerTabIndicator(el: HTMLElement | null | undefined, opts?: SiteGlassSurfaceOpts) {
|
||||||
|
if (!el) return () => undefined;
|
||||||
|
if (scene) {
|
||||||
|
const remove = bindIndicator(el, opts);
|
||||||
|
removers.push(remove);
|
||||||
|
return remove;
|
||||||
|
}
|
||||||
|
return enqueue('indicator', el, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(root: HTMLElement, content: HTMLElement) {
|
||||||
|
if (scene) return;
|
||||||
|
|
||||||
|
fallbackTimer = window.setTimeout(() => {
|
||||||
|
if (!ready.value) fallback.value = true;
|
||||||
|
}, 2200);
|
||||||
|
|
||||||
|
scene = createGlassScene(root, {
|
||||||
|
maxSurfaces: 16,
|
||||||
|
onDiagnostic: (diagnostic) => {
|
||||||
|
if (diagnostic.error) {
|
||||||
|
fallback.value = true;
|
||||||
|
ready.value = false;
|
||||||
|
} else if (diagnostic.maps > 0) {
|
||||||
|
syncReady();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
scene.setContent(content);
|
||||||
|
|
||||||
|
for (const job of queue) {
|
||||||
|
const remove =
|
||||||
|
job.kind === 'indicator' ? bindIndicator(job.el, job.opts) : bindSurface(job.el, job.opts);
|
||||||
|
job.setRemove(remove);
|
||||||
|
removers.push(remove);
|
||||||
|
}
|
||||||
|
queue = [];
|
||||||
|
syncReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispose() {
|
||||||
|
clearFallbackTimer();
|
||||||
|
readyObserver?.disconnect();
|
||||||
|
readyObserver = null;
|
||||||
|
removers.forEach((remove) => remove());
|
||||||
|
removers = [];
|
||||||
|
queue = [];
|
||||||
|
trackedSurfaces.clear();
|
||||||
|
scene?.dispose();
|
||||||
|
scene = null;
|
||||||
|
ready.value = false;
|
||||||
|
fallback.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const api: SiteLiquidGlassApi = {
|
||||||
|
ready,
|
||||||
|
fallback,
|
||||||
|
init,
|
||||||
|
registerSurface,
|
||||||
|
registerTabIndicator,
|
||||||
|
dispose,
|
||||||
|
};
|
||||||
|
|
||||||
|
provide(SITE_LIQUID_GLASS_KEY, api);
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSiteLiquidGlass(): SiteLiquidGlassApi | null {
|
||||||
|
return inject(SITE_LIQUID_GLASS_KEY, null);
|
||||||
|
}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
export const WELCOME_COPY = {
|
export const WELCOME_COPY = {
|
||||||
en: {
|
en: {
|
||||||
title: 'IDE. Your cross-border logistics advisor',
|
title: 'IDE · One-stop cross-border logistics advisor',
|
||||||
titleShort: 'IDE. Logistics advisor',
|
titleShort: 'IDE · Logistics advisor',
|
||||||
titleRest: ' Your cross-border logistics advisor',
|
titleRest: 'One-stop cross-border logistics advisor',
|
||||||
titleRestShort: 'Logistics advisor',
|
titleRestShort: 'Logistics advisor',
|
||||||
placeholder: 'e.g. 5 kg general cargo to the US, how many days?',
|
placeholder: 'e.g. 5 kg general cargo to the US, how many days?',
|
||||||
sendLabel: 'Ask',
|
sendLabel: 'Ask',
|
||||||
@ -26,9 +26,9 @@ export const WELCOME_COPY = {
|
|||||||
fallback: 'Quotes are in the customer system after you register. You can also talk to an advisor.',
|
fallback: 'Quotes are in the customer system after you register. You can also talk to an advisor.',
|
||||||
},
|
},
|
||||||
zh: {
|
zh: {
|
||||||
title: 'IDE.您身边的跨境物流顾问',
|
title: 'IDE · 一站式跨境物流顾问',
|
||||||
titleShort: 'IDE.跨境物流顾问',
|
titleShort: 'IDE · 跨境物流顾问',
|
||||||
titleRest: '您身边的跨境物流顾问',
|
titleRest: '一站式跨境物流顾问',
|
||||||
titleRestShort: '跨境物流顾问',
|
titleRestShort: '跨境物流顾问',
|
||||||
placeholder: '例如:发美国普货,5kg,要几天',
|
placeholder: '例如:发美国普货,5kg,要几天',
|
||||||
sendLabel: '提问',
|
sendLabel: '提问',
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user