Merge branch 'master' of github.com:wyongk/juip-proxy-react

This commit is contained in:
Eamon-meng
2025-06-10 12:52:14 +08:00
23 changed files with 538 additions and 548 deletions

View File

@@ -1,9 +1,15 @@
## TODO
手机端支付页面样式调整为类似电商的底部支付栏
MDX code 块语法高亮
全部替换封装时间范围组件,检查结束时间字段手机端适配问题(需要尾部对齐)
全局修改断点命名
页头链接完善跳转地址
W
树组件优化
sse 向客户端推送通知与支付结果等事件

View File

@@ -1,20 +1,17 @@
import type { NextConfig } from "next"
import createMDX from "@next/mdx"
import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'
const nextConfig: NextConfig = {
output: "standalone",
export default createMDX({
options: {
rehypePlugins: [
['rehype-highlight', null],
],
},
})({
output: 'standalone',
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
experimental: {
mdxRs: true,
}
}
const withMdx = createMDX({
options: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
}
mdxRs: {
mdxType: 'gfm',
},
},
})
export default withMdx(nextConfig)

View File

@@ -25,6 +25,7 @@
"@radix-ui/react-progress": "^1.1.3",
"@radix-ui/react-radio-group": "^1.2.3",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.4",
"@radix-ui/react-tooltip": "^1.2.6",
@@ -42,7 +43,6 @@
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"recharts": "^2.15.3",
"remark-gfm": "^4.0.1",
"sonner": "^2.0.1",
"tailwind-merge": "^3.0.2",
"tailwindcss-animate": "^1.0.7",
@@ -65,7 +65,8 @@
"eslint-plugin-react-hooks": "^5.2.0",
"husky": "^9.1.7",
"tailwindcss": "^4",
"typescript": "^5"
"typescript": "^5",
"rehype-highlight": "^7.0.2"
},
"packageManager": "pnpm@10.5.2+sha512.da9dc28cd3ff40d0592188235ab25d3202add8a207afbedc682220e4a0029ffbff4562102b9e6e46b4e3f9e8bd53e6d05de48544b0c57d4b0179e22c76d1199b",
"pnpm": {
@@ -77,4 +78,4 @@
"react-is": "19.0.0-rc.1"
}
}
}
}

280
pnpm-lock.yaml generated
View File

@@ -56,6 +56,9 @@ importers:
'@radix-ui/react-select':
specifier: ^2.1.6
version: 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-separator':
specifier: ^1.1.7
version: 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-slot':
specifier: ^1.1.2
version: 1.1.2(@types/react@19.0.10)(react@19.0.0)
@@ -107,9 +110,6 @@ importers:
recharts:
specifier: ^2.15.3
version: 2.15.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
remark-gfm:
specifier: ^4.0.1
version: 4.0.1
sonner:
specifier: ^2.0.1
version: 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -168,6 +168,9 @@ importers:
husky:
specifier: ^9.1.7
version: 9.1.7
rehype-highlight:
specifier: ^7.0.2
version: 7.0.2
tailwindcss:
specifier: ^4
version: 4.0.9
@@ -1152,6 +1155,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-separator@1.1.7':
resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-slot@1.1.2':
resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==}
peerDependencies:
@@ -2040,10 +2056,6 @@ packages:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
escape-string-regexp@5.0.0:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'}
eslint-config-next@15.2.1:
resolution: {integrity: sha512-mhsprz7l0no8X+PdDnVHF4dZKu9YBJp2Rf6ztWbXBLJ4h6gxmW//owbbGJMBVUU+PibGJDAqZhW4pt8SC8HSow==}
peerDependencies:
@@ -2342,15 +2354,25 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
hast-util-is-element@3.0.0:
resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
hast-util-to-estree@3.1.3:
resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==}
hast-util-to-jsx-runtime@2.3.6:
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
hast-util-to-text@4.0.2:
resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==}
hast-util-whitespace@3.0.0:
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
highlight.js@11.11.1:
resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
engines: {node: '>=12.0.0'}
husky@9.1.7:
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
engines: {node: '>=18'}
@@ -2657,6 +2679,9 @@ packages:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
lowlight@3.3.0:
resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==}
lucide-react@0.479.0:
resolution: {integrity: sha512-aBhNnveRhorBOK7uA4gDjgaf+YlHMdMhQ/3cupk6exM10hWlEU+2QtWYOfhXhjAsmdb6LeKR+NZnow4UxRRiTQ==}
peerDependencies:
@@ -2666,37 +2691,13 @@ packages:
resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==}
engines: {node: '>=16'}
markdown-table@3.0.4:
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
mdast-util-find-and-replace@3.0.2:
resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
mdast-util-from-markdown@2.0.2:
resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==}
mdast-util-gfm-autolink-literal@2.0.1:
resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
mdast-util-gfm-footnote@2.1.0:
resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
mdast-util-gfm-strikethrough@2.0.0:
resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
mdast-util-gfm-table@2.0.0:
resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
mdast-util-gfm-task-list-item@2.0.0:
resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
mdast-util-gfm@3.1.0:
resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
mdast-util-mdx-expression@2.0.1:
resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
@@ -2728,27 +2729,6 @@ packages:
micromark-core-commonmark@2.0.3:
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
micromark-extension-gfm-autolink-literal@2.1.0:
resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
micromark-extension-gfm-footnote@2.1.0:
resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
micromark-extension-gfm-strikethrough@2.1.0:
resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
micromark-extension-gfm-table@2.1.1:
resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==}
micromark-extension-gfm-tagfilter@2.0.0:
resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
micromark-extension-gfm-task-list-item@2.1.0:
resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
micromark-extension-gfm@3.0.0:
resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
micromark-extension-mdx-expression@3.0.1:
resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==}
@@ -3145,12 +3125,12 @@ packages:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
rehype-highlight@7.0.2:
resolution: {integrity: sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==}
rehype-recma@1.0.0:
resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==}
remark-gfm@4.0.1:
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
remark-mdx@3.1.0:
resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==}
@@ -3160,9 +3140,6 @@ packages:
remark-rehype@11.1.2:
resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
remark-stringify@11.0.0:
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -3461,6 +3438,9 @@ packages:
unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
unist-util-find-after@5.0.0:
resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
unist-util-is@6.0.0:
resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
@@ -4517,6 +4497,15 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
'@radix-ui/react-separator@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
'@radix-ui/react-slot@1.1.2(@types/react@19.0.10)(react@19.0.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
@@ -5446,8 +5435,6 @@ snapshots:
escape-string-regexp@4.0.0: {}
escape-string-regexp@5.0.0: {}
eslint-config-next@15.2.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.8.2):
dependencies:
'@next/eslint-plugin-next': 15.2.1
@@ -5836,6 +5823,10 @@ snapshots:
dependencies:
function-bind: 1.1.2
hast-util-is-element@3.0.0:
dependencies:
'@types/hast': 3.0.4
hast-util-to-estree@3.1.3:
dependencies:
'@types/estree': 1.0.6
@@ -5877,10 +5868,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
hast-util-to-text@4.0.2:
dependencies:
'@types/hast': 3.0.4
'@types/unist': 3.0.3
hast-util-is-element: 3.0.0
unist-util-find-after: 5.0.0
hast-util-whitespace@3.0.0:
dependencies:
'@types/hast': 3.0.4
highlight.js@11.11.1: {}
husky@9.1.7: {}
ieee754@1.2.1: {}
@@ -6155,23 +6155,20 @@ snapshots:
dependencies:
js-tokens: 4.0.0
lowlight@3.3.0:
dependencies:
'@types/hast': 3.0.4
devlop: 1.1.0
highlight.js: 11.11.1
lucide-react@0.479.0(react@19.0.0):
dependencies:
react: 19.0.0
markdown-extensions@2.0.0: {}
markdown-table@3.0.4: {}
math-intrinsics@1.1.0: {}
mdast-util-find-and-replace@3.0.2:
dependencies:
'@types/mdast': 4.0.4
escape-string-regexp: 5.0.0
unist-util-is: 6.0.0
unist-util-visit-parents: 6.0.1
mdast-util-from-markdown@2.0.2:
dependencies:
'@types/mdast': 4.0.4
@@ -6189,63 +6186,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
mdast-util-gfm-autolink-literal@2.0.1:
dependencies:
'@types/mdast': 4.0.4
ccount: 2.0.1
devlop: 1.1.0
mdast-util-find-and-replace: 3.0.2
micromark-util-character: 2.1.1
mdast-util-gfm-footnote@2.1.0:
dependencies:
'@types/mdast': 4.0.4
devlop: 1.1.0
mdast-util-from-markdown: 2.0.2
mdast-util-to-markdown: 2.1.2
micromark-util-normalize-identifier: 2.0.1
transitivePeerDependencies:
- supports-color
mdast-util-gfm-strikethrough@2.0.0:
dependencies:
'@types/mdast': 4.0.4
mdast-util-from-markdown: 2.0.2
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
mdast-util-gfm-table@2.0.0:
dependencies:
'@types/mdast': 4.0.4
devlop: 1.1.0
markdown-table: 3.0.4
mdast-util-from-markdown: 2.0.2
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
mdast-util-gfm-task-list-item@2.0.0:
dependencies:
'@types/mdast': 4.0.4
devlop: 1.1.0
mdast-util-from-markdown: 2.0.2
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
mdast-util-gfm@3.1.0:
dependencies:
mdast-util-from-markdown: 2.0.2
mdast-util-gfm-autolink-literal: 2.0.1
mdast-util-gfm-footnote: 2.1.0
mdast-util-gfm-strikethrough: 2.0.0
mdast-util-gfm-table: 2.0.0
mdast-util-gfm-task-list-item: 2.0.0
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
mdast-util-mdx-expression@2.0.1:
dependencies:
'@types/estree-jsx': 1.0.5
@@ -6349,64 +6289,6 @@ snapshots:
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
micromark-extension-gfm-autolink-literal@2.1.0:
dependencies:
micromark-util-character: 2.1.1
micromark-util-sanitize-uri: 2.0.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
micromark-extension-gfm-footnote@2.1.0:
dependencies:
devlop: 1.1.0
micromark-core-commonmark: 2.0.3
micromark-factory-space: 2.0.1
micromark-util-character: 2.1.1
micromark-util-normalize-identifier: 2.0.1
micromark-util-sanitize-uri: 2.0.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
micromark-extension-gfm-strikethrough@2.1.0:
dependencies:
devlop: 1.1.0
micromark-util-chunked: 2.0.1
micromark-util-classify-character: 2.0.1
micromark-util-resolve-all: 2.0.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
micromark-extension-gfm-table@2.1.1:
dependencies:
devlop: 1.1.0
micromark-factory-space: 2.0.1
micromark-util-character: 2.1.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
micromark-extension-gfm-tagfilter@2.0.0:
dependencies:
micromark-util-types: 2.0.2
micromark-extension-gfm-task-list-item@2.1.0:
dependencies:
devlop: 1.1.0
micromark-factory-space: 2.0.1
micromark-util-character: 2.1.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
micromark-extension-gfm@3.0.0:
dependencies:
micromark-extension-gfm-autolink-literal: 2.1.0
micromark-extension-gfm-footnote: 2.1.0
micromark-extension-gfm-strikethrough: 2.1.0
micromark-extension-gfm-table: 2.1.1
micromark-extension-gfm-tagfilter: 2.0.0
micromark-extension-gfm-task-list-item: 2.1.0
micromark-util-combine-extensions: 2.0.1
micromark-util-types: 2.0.2
micromark-extension-mdx-expression@3.0.1:
dependencies:
'@types/estree': 1.0.6
@@ -6964,6 +6846,14 @@ snapshots:
gopd: 1.2.0
set-function-name: 2.0.2
rehype-highlight@7.0.2:
dependencies:
'@types/hast': 3.0.4
hast-util-to-text: 4.0.2
lowlight: 3.3.0
unist-util-visit: 5.0.0
vfile: 6.0.3
rehype-recma@1.0.0:
dependencies:
'@types/estree': 1.0.6
@@ -6972,17 +6862,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
remark-gfm@4.0.1:
dependencies:
'@types/mdast': 4.0.4
mdast-util-gfm: 3.1.0
micromark-extension-gfm: 3.0.0
remark-parse: 11.0.0
remark-stringify: 11.0.0
unified: 11.0.5
transitivePeerDependencies:
- supports-color
remark-mdx@3.1.0:
dependencies:
mdast-util-mdx: 3.0.0
@@ -7007,12 +6886,6 @@ snapshots:
unified: 11.0.5
vfile: 6.0.3
remark-stringify@11.0.0:
dependencies:
'@types/mdast': 4.0.4
mdast-util-to-markdown: 2.1.2
unified: 11.0.5
require-directory@2.1.1: {}
require-main-filename@2.0.0: {}
@@ -7388,6 +7261,11 @@ snapshots:
trough: 2.2.0
vfile: 6.0.3
unist-util-find-after@5.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-is: 6.0.0
unist-util-is@6.0.0:
dependencies:
'@types/unist': 3.0.3

View File

@@ -47,8 +47,7 @@ export async function GET(req: NextRequest) {
switch (format) {
case 'json':
const body = JSON.stringify(params)
return NextResponse.json(body)
return NextResponse.json(result.data)
case 'text':
const text = result.data.map((item) => {
const list = [item.host, item.port]

View File

@@ -74,7 +74,7 @@ export default function Provider(props: ProviderProps) {
<div className="flex justify-between gap-8">
{/* logo */}
<Link href="/" className="flex items-center">
<Image src={logo} alt="logo" height={48}/>
<Image src={logo} alt="logo" height={40} className="translate-y-0.5"/>
</Link>
{/* 菜单 */}

View File

@@ -81,7 +81,7 @@ export default function Charts() {
<Card className="h-full">
<CardContent className="overflow-hidden">
<Tabs defaultValue="dynamic" className="h-full gap-4">
<TabsList>
<TabsList className="h-9">
<TabsTrigger value="dynamic" className="data-[state=active]:text-primary">
IP
</TabsTrigger>

View File

@@ -29,7 +29,7 @@ export default function Navbar(props: NavbarProps) {
data-expand={navbar}
className={merge(
`transition-[flex-basis] duration-300 ease-in-out`,
`flex flex-col overflow-hidden group`,
`h-full flex flex-col overflow-hidden group`,
`data-[expand=true]:basis-52 data-[expand=false]:basis-16`,
)}>
{/* logo */}
@@ -54,7 +54,7 @@ export default function Navbar(props: NavbarProps) {
{/* routes */}
<section className={merge(
`transition-[padding] duration-300 ease-in-out`,
`flex-auto overflow-x-hidden overflow-y-auto`,
`flex-auto overflow-x-hidden overflow-y-auto pb-4`,
`group-data-[expand=true]:px-4 group-data-[expand=false]:px-3`,
)}>
<TooltipProvider>

View File

@@ -6,7 +6,7 @@ export type ExtractPageProps = {}
export default async function ExtractPage(props: ExtractPageProps) {
return (
<Page>
<Extract className="p-8"/>
<Extract/>
</Page>
)
}

View File

@@ -9,7 +9,7 @@ import {Button} from '@/components/ui/button'
import {useForm, useFormContext} from 'react-hook-form'
import {Alert, AlertTitle} from '@/components/ui/alert'
import {Box, CircleAlert, CopyIcon, ExternalLinkIcon, Loader, Timer} from 'lucide-react'
import {memo, useEffect, useRef, useState} from 'react'
import {memo, ReactNode, useEffect, useRef, useState} from 'react'
import {useStatus} from '@/lib/states'
import {allResource} from '@/actions/resource'
import {Resource} from '@/lib/models'
@@ -18,6 +18,8 @@ import {toast} from 'sonner'
import {merge} from '@/lib/utils'
import {Combobox} from '@/components/ui/combobox'
import cities from './_assets/cities.json'
import ExtractDocs from '@/components/docs/extract.mdx'
import Markdown from '@/components/markdown'
const schema = z.object({
resource: z.number({required_error: '请选择套餐'}),
@@ -64,25 +66,44 @@ export default function Extract(props: ExtractProps) {
<Form
form={form}
className={merge(
`bg-white flex flex-col gap-4 rounded-md`,
props.className,
`flex flex-col gap-6`,
)}
>
<Alert variant="warn">
<CircleAlert/>
<AlertTitle>IP前需要将本机IP添加到白名单后才可使用</AlertTitle>
</Alert>
<CardSection>
<Alert variant="warn" >
<CircleAlert/>
<AlertTitle>IP前需要将本机IP添加到白名单后才可使用</AlertTitle>
</Alert>
<FormFields/>
<FormFields/>
</CardSection>
<ApplyLink/>
<CardSection>
<ApplyLink/>
</CardSection>
<CardSection>
<Markdown>
<ExtractDocs/>
</Markdown>
</CardSection>
</Form>
)
}
function CardSection(props: {
children: ReactNode
}) {
return (
<div className="flex flex-col gap-4 p-4 md:p-6 bg-white rounded-lg">
{props.children}
</div>
)
}
const FormFields = memo(() => {
return (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-6 items-stretch max-w-[calc(160px*4+1rem*3)]">
{/* 选择套餐 */}
<SelectResource/>
@@ -90,192 +111,177 @@ const FormFields = memo(() => {
<SelectRegion/>
{/* 运营商筛选 */}
<div className="flex items-center">
<FormField name="isp" label="运营商筛选">
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex gap-4">
<FormLabel htmlFor={`${id}-v-all`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="all" id={`${id}-v-all`}/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-telecom`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="1" id={`${id}-v-telecom`}/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-mobile`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="2" id={`${id}-v-mobile`}/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-unicom`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="3" id={`${id}-v-unicom`}/>
<span></span>
</FormLabel>
</RadioGroup>
)}
</FormField>
</div>
<FormField name="isp" label="运营商筛选" classNames={{label: 'max-md:text-sm'}}>
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="grid grid-cols-2 md:grid-cols-4 gap-4"
>
<FormLabel htmlFor={`${id}-v-all`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="all" id={`${id}-v-all`}/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-telecom`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="1" id={`${id}-v-telecom`}/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-mobile`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="2" id={`${id}-v-mobile`}/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-unicom`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="3" id={`${id}-v-unicom`}/>
<span></span>
</FormLabel>
</RadioGroup>
)}
</FormField>
{/* 协议类型 */}
<div className="flex items-center">
<FormField name="proto" label="协议类型">
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex gap-4">
<FormLabel htmlFor={`${id}-v-all`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="all" id={`${id}-v-all`} className="mr-2"/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-http`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="1" id={`${id}-v-http`} className="mr-2"/>
<span>HTTP</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-https`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="2" id={`${id}-v-https`} className="mr-2"/>
<span>HTTPS</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-socks5`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="3" id={`${id}-v-socks5`} className="mr-2"/>
<span>SOCKS5</span>
</FormLabel>
</RadioGroup>
)}
</FormField>
</div>
<FormField name="proto" label="协议类型" classNames={{label: 'max-md:text-sm'}}>
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="grid grid-cols-2 md:grid-cols-4 gap-4">
<FormLabel htmlFor={`${id}-v-all`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="all" id={`${id}-v-all`} className="mr-2"/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-http`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="1" id={`${id}-v-http`} className="mr-2"/>
<span>HTTP</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-https`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="2" id={`${id}-v-https`} className="mr-2"/>
<span>HTTPS</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-socks5`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="3" id={`${id}-v-socks5`} className="mr-2"/>
<span>SOCKS5</span>
</FormLabel>
</RadioGroup>
)}
</FormField>
{/* 认证方式 */}
<div className="flex items-center">
<FormField name="authType" label="协议类型">
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex gap-4">
<FormLabel htmlFor={`${id}-v-http`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="1" id={`${id}-v-http`} className="mr-2"/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-https`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="2" id={`${id}-v-https`} className="mr-2"/>
<span></span>
</FormLabel>
</RadioGroup>
)}
</FormField>
</div>
<FormField name="authType" className="md:max-w-[calc(160px*2+1rem)]" label="认证方式" classNames={{label: 'max-md:text-sm'}}>
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex gap-4">
<FormLabel htmlFor={`${id}-v-http`} className="px-3 h-10 flex-1 border rounded-md flex items-center text-sm">
<RadioGroupItem value="1" id={`${id}-v-http`} className="mr-2"/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-https`} className="px-3 h-10 flex-1 border rounded-md flex items-center text-sm">
<RadioGroupItem value="2" id={`${id}-v-https`} className="mr-2"/>
<span></span>
</FormLabel>
</RadioGroup>
)}
</FormField>
{/* 去重选项 */}
<div className="flex items-center">
<FormField name="distinct" label="去重选项">
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex gap-4">
<FormLabel htmlFor={`${id}-v-true`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="1" id={`${id}-v-true`} className="mr-2"/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-false`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="0" id={`${id}-v-false`} className="mr-2"/>
<span></span>
</FormLabel>
</RadioGroup>
)}
</FormField>
</div>
<FormField name="distinct" className="md:max-w-[calc(160px*2+1rem)]" label="去重选项" classNames={{label: 'max-md:text-sm'}}>
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex gap-4">
<FormLabel htmlFor={`${id}-v-true`} className="px-3 h-10 flex-1 border rounded-md flex items-center text-sm">
<RadioGroupItem value="1" id={`${id}-v-true`} className="mr-2"/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-false`} className="px-3 h-10 flex-1 border rounded-md flex items-center text-sm">
<RadioGroupItem value="0" id={`${id}-v-false`} className="mr-2"/>
<span></span>
</FormLabel>
</RadioGroup>
)}
</FormField>
{/* 导出格式 */}
<div className="flex items-center">
<FormField name="format" label="导出格式">
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex gap-4"
>
<FormLabel htmlFor={`${id}-v-txt`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="text" id={`${id}-v-txt`} className="mr-2"/>
<span>TXT </span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-json`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="json" id={`${id}-v-json`} className="mr-2"/>
<span>JSON </span>
</FormLabel>
</RadioGroup>
)}
</FormField>
</div>
<FormField name="format" className="md:max-w-[calc(160px*2+1rem)]" label="导出格式" classNames={{label: 'max-md:text-sm'}}>
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex gap-4"
>
<FormLabel htmlFor={`${id}-v-txt`} className="px-3 h-10 flex-1 border rounded-md flex items-center text-sm">
<RadioGroupItem value="text" id={`${id}-v-txt`} className="mr-2"/>
<span>TXT </span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-json`} className="px-3 h-10 flex-1 border rounded-md flex items-center text-sm">
<RadioGroupItem value="json" id={`${id}-v-json`} className="mr-2"/>
<span>JSON </span>
</FormLabel>
</RadioGroup>
)}
</FormField>
{/* 分隔符 */}
<div className="flex items-center">
<FormField name="separator" label="分隔符">
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex gap-4">
<FormLabel htmlFor={`${id}-v-comma`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="124" id={`${id}-v-comma`} className="mr-2"/>
<span>线 ( | )</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-semicolon`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="58" id={`${id}-v-semicolon`} className="mr-2"/>
<span> ( : )</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-space`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="9" id={`${id}-v-space`} className="mr-2"/>
<span> ( \t )</span>
</FormLabel>
</RadioGroup>
)}
</FormField>
</div>
<FormField name="separator" className="md:max-w-[calc(160px*3+1rem*2)]" label="分隔符" classNames={{label: 'max-md:text-sm'}}>
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="grid grid-cols-2 md:grid-cols-3 gap-4">
<FormLabel htmlFor={`${id}-v-comma`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="124" id={`${id}-v-comma`} className="mr-2"/>
<span>线 ( | )</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-semicolon`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="58" id={`${id}-v-semicolon`} className="mr-2"/>
<span> ( : )</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-space`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="9" id={`${id}-v-space`} className="mr-2"/>
<span> ( \t )</span>
</FormLabel>
</RadioGroup>
)}
</FormField>
{/* 换行符 */}
<div className="flex items-center">
<FormField name="breaker" label="换行符">
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex gap-4">
<FormLabel htmlFor={`${id}-v-newline2`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="13,10" id={`${id}-v-newline2`} className="mr-2"/>
<span> ( \r\n )</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-newline`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="10" id={`${id}-v-newline`} className="mr-2"/>
<span> ( \n )</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-newline3`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<RadioGroupItem value="13" id={`${id}-v-newline3`} className="mr-2"/>
<span> ( \r )</span>
</FormLabel>
</RadioGroup>
)}
</FormField>
</div>
<FormField name="breaker" className="md:max-w-[calc(160px*3+1rem*2)]" label="换行符" classNames={{label: 'max-md:text-sm'}}>
{({id, field}) => (
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="grid grid-cols-2 md:grid-cols-3 gap-4">
<FormLabel htmlFor={`${id}-v-newline2`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="13,10" id={`${id}-v-newline2`} className="mr-2"/>
<span> ( \r\n )</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-newline`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="10" id={`${id}-v-newline`} className="mr-2"/>
<span> ( \n )</span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-newline3`} className="px-3 h-10 border rounded-md flex items-center text-sm">
<RadioGroupItem value="13" id={`${id}-v-newline3`} className="mr-2"/>
<span> ( \r )</span>
</FormLabel>
</RadioGroup>
)}
</FormField>
{/* 提取数量 */}
<div className="flex items-center">
<FormField name="count" label="提取数量">
{({id, field}) => (
<Input
{...field}
id={id}
type="number"
onChange={e => field.onChange(Number(e.target.value))}
className="h-10 w-84"
placeholder="输入提取数量"
/>
)}
</FormField>
</div>
<FormField name="count" className="max-w[160px*2+1rem]" label="提取数量" classNames={{label: 'max-md:text-sm'}}>
{({id, field}) => (
<Input
{...field}
id={id}
type="number"
onChange={e => field.onChange(Number(e.target.value))}
className="h-10"
placeholder="输入提取数量"
/>
)}
</FormField>
</div>
)
})
@@ -308,116 +314,114 @@ function SelectResource() {
}, [])
return (
<div className="flex items-center">
<FormField name="resource" label="选择套餐">
{({field}) => (
<Select
value={field.value ? String(field.value) : undefined}
onValueChange={value => field.onChange(Number(value))}
>
<SelectTrigger className="min-h-10 h-auto w-84">
<SelectValue placeholder="选择套餐"/>
</SelectTrigger>
<SelectContent>
{status === 'load' ? (
<div className="p-4 flex gap-1 items-center">
<Loader className="animate-spin" size={20}/>
<span>...</span>
</div>
) : resources.length === 0 ? (
<div className="p-4 flex gap-1 items-center">
<Loader className="animate-spin" size={20}/>
<span></span>
</div>
) : resources.map((resource, i) => (
<>
<SelectItem
key={`${resource.id}`}
value={String(resource.id)}
className="p-3">
<div className="flex flex-col gap-2 w-72">
{resource.type === 1 && resource.short.type === 1 && (
<>
<div className="flex gap-2 items-center bg-green-50 w-fit px-2 py-1 rounded-md text-sm">
<Timer size={20}/>
<span>{name(resource)}</span>
</div>
<div className="flex justify-between gap-2 text-xs text-weak">
<span>
{format(resource.short.expire, 'yyyy-MM-dd HH:mm')}
</span>
<span>{intlFormatDistance(resource.short.expire, new Date())}</span>
</div>
</>
)}
{resource.type === 1 && resource.short.type === 2 && (
<>
<div className="flex gap-2 items-center bg-blue-50 w-fit px-2 py-1 rounded-md text-sm">
<Box size={20}/>
<span>{name(resource)}</span>
</div>
<div className="flex justify-between gap-2 text-xs text-weak">
<span>
{resource.short.used}
{' '}
/
{resource.short.quota}
</span>
<span>
{resource.short.quota - resource.short.used}
</span>
</div>
</>
)}
{resource.type === 2 && resource.long.type === 1 && (
<>
<div className="flex gap-2 items-center bg-green-50 w-fit px-2 py-1 rounded-md text-sm">
<Timer size={20}/>
<span>{name(resource)}</span>
</div>
<div className="flex justify-between gap-2 text-xs text-weak">
<span>
{format(resource.long.expire, 'yyyy-MM-dd HH:mm')}
</span>
<span>{intlFormatDistance(resource.long.expire, new Date())}</span>
</div>
</>
)}
{resource.type === 2 && resource.long.type === 2 && (
<>
<div className="flex gap-2 items-center bg-blue-50 w-fit px-2 py-1 rounded-md text-sm">
<Box size={20}/>
<span>{name(resource)}</span>
</div>
<div className="flex justify-between gap-2 text-xs text-weak">
<span>
{resource.long.used}
{' '}
/
{resource.long.quota}
</span>
<span>
{resource.long.quota - resource.long.used}
</span>
</div>
</>
)}
</div>
</SelectItem>
{i < resources.length - 1 && <SelectSeparator className="m-1"/>}
</>
))}
</SelectContent>
</Select>
)}
</FormField>
</div>
<FormField name="resource" className="md:max-w-[calc(160px*2+1rem)]" label="选择套餐" classNames={{label: 'max-md:text-sm'}}>
{({field}) => (
<Select
value={field.value ? String(field.value) : undefined}
onValueChange={value => field.onChange(Number(value))}
>
<SelectTrigger className="min-h-10 h-auto w-full">
<SelectValue placeholder="选择套餐"/>
</SelectTrigger>
<SelectContent>
{status === 'load' ? (
<div className="p-4 flex gap-1 items-center">
<Loader className="animate-spin" size={20}/>
<span>...</span>
</div>
) : resources.length === 0 ? (
<div className="p-4 flex gap-1 items-center">
<Loader className="animate-spin" size={20}/>
<span></span>
</div>
) : resources.map((resource, i) => (
<>
<SelectItem
key={`${resource.id}`}
value={String(resource.id)}
className="p-3">
<div className="flex flex-col gap-2 w-72">
{resource.type === 1 && resource.short.type === 1 && (
<>
<div className="flex gap-2 items-center bg-green-50 w-fit px-2 py-1 rounded-md text-sm">
<Timer size={20}/>
<span>{name(resource)}</span>
</div>
<div className="flex justify-between gap-2 text-xs text-weak">
<span>
{format(resource.short.expire, 'yyyy-MM-dd HH:mm')}
</span>
<span>{intlFormatDistance(resource.short.expire, new Date())}</span>
</div>
</>
)}
{resource.type === 1 && resource.short.type === 2 && (
<>
<div className="flex gap-2 items-center bg-blue-50 w-fit px-2 py-1 rounded-md text-sm">
<Box size={20}/>
<span>{name(resource)}</span>
</div>
<div className="flex justify-between gap-2 text-xs text-weak">
<span>
{resource.short.used}
{' '}
/
{resource.short.quota}
</span>
<span>
{resource.short.quota - resource.short.used}
</span>
</div>
</>
)}
{resource.type === 2 && resource.long.type === 1 && (
<>
<div className="flex gap-2 items-center bg-green-50 w-fit px-2 py-1 rounded-md text-sm">
<Timer size={20}/>
<span>{name(resource)}</span>
</div>
<div className="flex justify-between gap-2 text-xs text-weak">
<span>
{format(resource.long.expire, 'yyyy-MM-dd HH:mm')}
</span>
<span>{intlFormatDistance(resource.long.expire, new Date())}</span>
</div>
</>
)}
{resource.type === 2 && resource.long.type === 2 && (
<>
<div className="flex gap-2 items-center bg-blue-50 w-fit px-2 py-1 rounded-md text-sm">
<Box size={20}/>
<span>{name(resource)}</span>
</div>
<div className="flex justify-between gap-2 text-xs text-weak">
<span>
{resource.long.used}
{' '}
/
{resource.long.quota}
</span>
<span>
{resource.long.quota - resource.long.used}
</span>
</div>
</>
)}
</div>
</SelectItem>
{i < resources.length - 1 && <SelectSeparator className="m-1"/>}
</>
))}
</SelectContent>
</Select>
)}
</FormField>
)
}
@@ -428,8 +432,8 @@ function SelectRegion() {
const city = form.watch('city')
return (
<div className="flex flex-col gap-4">
<FormField name="regionType" label="地区筛选">
<div className="flex flex-col gap-4 md:max-w-[calc(160px*2+1rem)]">
<FormField name="regionType" label="地区筛选" classNames={{label: 'max-md:text-sm'}}>
{({id, field}) => (
<RadioGroup
onValueChange={(e) => {
@@ -442,11 +446,11 @@ function SelectRegion() {
defaultValue={field.value}
className="flex gap-4"
>
<FormLabel htmlFor={`${id}-v-unlimited`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<FormLabel htmlFor={`${id}-v-unlimited`} className="px-3 h-10 flex-1 border rounded-md flex items-center text-sm">
<RadioGroupItem value="unlimited" id={`${id}-v-unlimited`} className="mr-2"/>
<span></span>
</FormLabel>
<FormLabel htmlFor={`${id}-v-specific`} className="px-3 h-10 border rounded-md flex items-center w-40 text-sm">
<FormLabel htmlFor={`${id}-v-specific`} className="px-3 h-10 flex-1 border rounded-md flex items-center text-sm">
<RadioGroupItem value="specific" id={`${id}-v-specific`} className="mr-2"/>
<span></span>
</FormLabel>
@@ -456,7 +460,6 @@ function SelectRegion() {
{regionType === 'specific' && (
<Combobox
className="w-84"
placeholder="请选择地区"
options={cities.options}
value={[prov || '', city || '']}
@@ -531,11 +534,13 @@ function ApplyLink() {
return (
<div className={merge(
`flex flex-col gap-4 sticky bottom-0 bg-muted p-4`,
`flex flex-col gap-4`,
`rounded-lg`,
)}>
<h4>API </h4>
{/* 展示链接地址 */}
<div className="bg-neutral-900 text-white p-4 rounded-md break-all">
<div className="bg-secondary p-4 rounded-md break-all">
{link(values)}
</div>

View File

@@ -9,13 +9,14 @@ import Image from 'next/image'
import check from '@/components/composites/purchase/_assets/check.svg'
import {Schema} from '@/components/composites/purchase/long/form'
import {useFormContext} from 'react-hook-form'
import {Card} from '@/components/ui/card'
export default function Center() {
const form = useFormContext<Schema>()
const type = form.watch('type')
return (
<div className="flex-auto p-8 flex flex-col gap-8 relative">
<Card className="flex-auto p-6 flex flex-col gap-6 relative">
{/* 计费方式 */}
<FormField
@@ -27,7 +28,7 @@ export default function Center() {
id={id}
defaultValue={field.value}
onValueChange={field.onChange}
className="flex gap-4">
className="flex gap-4 max-md:flex-col">
<FormOption
id={`${id}-2`}
@@ -166,7 +167,7 @@ export default function Center() {
{/* 产品特性 */}
<div className="space-y-6">
<h3></h3>
<div className="grid grid-cols-3 auto-rows-fr gap-y-6">
<div className="grid grid-cols-2 md:grid-cols-3 auto-rows-fr gap-4 gap-y-6">
<p className="flex gap-2 items-center">
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
<span className="text-sm text-gray-500"></span>
@@ -205,6 +206,6 @@ export default function Center() {
</p>
</div>
</div>
</div>
</Card>
)
}

View File

@@ -41,7 +41,7 @@ export default function LongForm() {
})
return (
<Form form={form} className="bg-white rounded-lg flex flex-row">
<Form form={form} className="flex flex-col lg:flex-row gap-4">
<LongFormContext.Provider value={{form}}>
<Center/>
<Right/>

View File

@@ -16,6 +16,7 @@ import Link from 'next/link'
import {merge} from '@/lib/utils'
import {useFormContext} from 'react-hook-form'
import {Schema} from '@/components/composites/purchase/long/form'
import {Card} from '@/components/ui/card'
export default function Right() {
const profile = useProfileStore(store => store.profile)
@@ -44,9 +45,8 @@ export default function Right() {
}, [dailyLimit, expire, live, quota, mode])
return (
<div className={merge(
<Card className={merge(
`flex-none basis-90 p-6 flex flex-col gap-6 relative`,
`after:absolute after:inset-0 after:my-6 after:border-l after:border-gray-200 after:select-none after:pointer-events-none`,
)}>
<h3></h3>
<ul className="flex flex-col gap-3">
@@ -166,6 +166,6 @@ export default function Right() {
</Link>
)}
</div>
</Card>
)
}

View File

@@ -9,13 +9,14 @@ import Image from 'next/image'
import check from '@/components/composites/purchase/_assets/check.svg'
import {useFormContext} from 'react-hook-form'
import {Schema} from '@/components/composites/purchase/short/form'
import {Card} from '@/components/ui/card'
export default function Center() {
const form = useFormContext<Schema>()
const type = form.watch('type')
return (
<div className="flex-auto p-8 flex flex-col gap-8 relative">
<Card className="flex-auto p-6 flex flex-col gap-6 relative">
{/* 计费方式 */}
<FormField<Schema, 'type'>
@@ -27,7 +28,7 @@ export default function Center() {
id={id}
defaultValue={field.value}
onValueChange={field.onChange}
className="flex gap-4">
className="flex gap-4 max-md:flex-col">
<FormOption
id={`${id}-2`}
@@ -166,7 +167,7 @@ export default function Center() {
{/* 产品特性 */}
<div className="space-y-6">
<h3></h3>
<div className="grid grid-cols-3 auto-rows-fr gap-y-6">
<div className="grid grid-cols-2 md:grid-cols-3 auto-rows-fr gap-4 md:gap-y-6">
<p className="flex gap-2 items-center">
<Image src={check} alt="check" aria-hidden className="w-4 h-4"/>
<span className="text-sm text-gray-500"></span>
@@ -205,6 +206,6 @@ export default function Center() {
</p>
</div>
</div>
</div>
</Card>
)
}

View File

@@ -43,7 +43,7 @@ export default function PurchaseForm(props: PurchaseFormProps) {
})
return (
<Form form={form} className="bg-white rounded-lg flex flex-row">
<Form form={form} className="flex flex-col lg:flex-row gap-4">
<Center/>
<Right/>
</Form>

View File

@@ -15,6 +15,7 @@ import Link from 'next/link'
import {merge} from '@/lib/utils'
import Pay from '@/components/composites/purchase/pay'
import {useFormContext} from 'react-hook-form'
import {Card} from '@/components/ui/card'
export default function Right() {
const profile = useProfileStore(store => store.profile)
@@ -55,9 +56,8 @@ export default function Right() {
}, [dailyLimit, expire, live, quota, mode])
return (
<div className={merge(
<Card className={merge(
`flex-none basis-90 p-6 flex flex-col gap-6 relative`,
`after:absolute after:inset-0 after:my-6 after:border-l after:border-gray-200 after:select-none after:pointer-events-none`,
)}>
<h3></h3>
<ul className="flex flex-col gap-3">
@@ -177,6 +177,6 @@ export default function Right() {
</Link>
)}
</div>
</Card>
)
}

View File

@@ -0,0 +1,68 @@
# 提取代理接口文档
## 请求方式
`GET https://lanhuip.com/api/extract`
## 请求参数
| 参数名 | 类型 | 必填 | 描述 |
|--------|----------|------|----------------------------------------------------------------------------------------------------------------------------|
| i | number | 是 | 用于提取的套餐 ID |
| t | number | 是 | 认证类型1 - 白名单2 - 密码 |
| a | string | 否 | 归属地省份。默认全局随机 |
| b | string | 否 | 归属地城市。默认全局随机 |
| s | string | 否 | 归属地运营商。默认全局随机 |
| d | string | 否 | 是否去重1 - 是0 - 否。默认为是 |
| rt | string | 否 | 返回类型1 - TXT2 - JSON。默认 TXT |
| rs | number[] | 否 | 返回时要使用的分隔符,值为该字符的 ascii 编码,可以有多个字符,多个字符用半角逗号连接。默认为 13,10即回车 + 换行(\r\n |
| rb | number[] | 否 | 返回时要使用的换行符,值为该字符的 ascii 编码,可以有多个字符,多个字符用半角逗号连接。默认为 124即垂直线 \| |
| n | number | 否 | 提取数量。默认为 1 |
## 响应参数
如果请求参数中返回类型为 TXT则响应为纯文本格式内容为提取的代理列表每个代理信息占一行。
如果请求参数中返回类型为 JSON则响应为 JSON 格式,内容为提取的代理列表,每个代理信息为一个对象,包含以下字段:
| 参数名 | 类型 | 描述 |
|----------|--------|--------------------------------------------- |
| host | string | 代理服务器地址 |
| port | number | 代理服务器端口 |
| username | string | 代理服务器用户名(仅在认证类型为密码时返回) |
| password | string | 代理服务器密码(仅在认证类型为密码时返回) |
## 示例
### 请求示例
```http
GET https://lanhuip.com/api/extract?i=1&t=2&a=广东省&b=广州市&s=移动&d=1&rt=2&n=3
```
### 响应示例
```json
[
{
"host": "fwd1.lanhuip.com",
"port": 20000,
"username": "user1",
"password": "pass1"
},
{
"host": "fwd1.lanhuip.com",
"port": 20001,
"username": "user2",
"password": "pass2"
},
{
"host": "fwd1.lanhuip.com",
"port": 20002,
"username": "user3",
"password": "pass3"
}
]
```

View File

@@ -2,6 +2,12 @@ import {Button} from '@/components/ui/button';
# qweqwe
`dasdasd`
```typescript
console.log('Hello, world!');
```
## awdasdasd
### zxczxczxc

View File

@@ -2,13 +2,13 @@ import {merge} from '@/lib/utils'
export default function Markdown(props: React.ComponentProps<'div'>) {
return (
<div
<article
{...props}
className={merge(
`prose`,
`prose max-w-none`,
props.className,
)}>
{props.children}
</div>
</article>
)
}

View File

@@ -117,7 +117,7 @@ export function Combobox(props: ComboboxProps) {
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0 rounded-lg h-[var(--radix-popover-content-available-height)] flex flex-col overflow-hidden"
className="p-0 rounded-lg w-[var(--radix-popover-trigger-width)] h-[var(--radix-popover-content-available-height)] flex flex-col overflow-hidden"
align="start"
collisionPadding={6}
>

View File

@@ -76,7 +76,7 @@ function FormField<
name={props.name}
control={form.control}
render={({field, fieldState, formState}) => (
<div data-slot="form-field" className={merge('grid gap-2', props.className)}>
<div data-slot="form-field" className={merge('flex flex-col gap-2', props.className)}>
{/* label */}
{!!props.label

View File

@@ -0,0 +1,28 @@
'use client'
import * as React from 'react'
import * as SeparatorPrimitive from '@radix-ui/react-separator'
import {merge} from '@/lib/utils'
function Separator({
className,
orientation = 'horizontal',
decorative = true,
...props
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
return (
<SeparatorPrimitive.Root
data-slot="separator"
decorative={decorative}
orientation={orientation}
className={merge(
'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',
className,
)}
{...props}
/>
)
}
export {Separator}