修复提取 ip 接口json类型返回值错误问题;完善 ip 提取页响应式设计

This commit is contained in:
2025-06-09 16:55:44 +08:00
parent 0f34b938e5
commit f15fa7f72d
15 changed files with 517 additions and 531 deletions

View File

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

View File

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

View File

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

280
pnpm-lock.yaml generated
View File

@@ -56,6 +56,9 @@ importers:
'@radix-ui/react-select': '@radix-ui/react-select':
specifier: ^2.1.6 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) 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': '@radix-ui/react-slot':
specifier: ^1.1.2 specifier: ^1.1.2
version: 1.1.2(@types/react@19.0.10)(react@19.0.0) version: 1.1.2(@types/react@19.0.10)(react@19.0.0)
@@ -107,9 +110,6 @@ importers:
recharts: recharts:
specifier: ^2.15.3 specifier: ^2.15.3
version: 2.15.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 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: sonner:
specifier: ^2.0.1 specifier: ^2.0.1
version: 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) version: 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -168,6 +168,9 @@ importers:
husky: husky:
specifier: ^9.1.7 specifier: ^9.1.7
version: 9.1.7 version: 9.1.7
rehype-highlight:
specifier: ^7.0.2
version: 7.0.2
tailwindcss: tailwindcss:
specifier: ^4 specifier: ^4
version: 4.0.9 version: 4.0.9
@@ -1152,6 +1155,19 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true 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': '@radix-ui/react-slot@1.1.2':
resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==} resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==}
peerDependencies: peerDependencies:
@@ -2040,10 +2056,6 @@ packages:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'} 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: eslint-config-next@15.2.1:
resolution: {integrity: sha512-mhsprz7l0no8X+PdDnVHF4dZKu9YBJp2Rf6ztWbXBLJ4h6gxmW//owbbGJMBVUU+PibGJDAqZhW4pt8SC8HSow==} resolution: {integrity: sha512-mhsprz7l0no8X+PdDnVHF4dZKu9YBJp2Rf6ztWbXBLJ4h6gxmW//owbbGJMBVUU+PibGJDAqZhW4pt8SC8HSow==}
peerDependencies: peerDependencies:
@@ -2342,15 +2354,25 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'} 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: hast-util-to-estree@3.1.3:
resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==}
hast-util-to-jsx-runtime@2.3.6: hast-util-to-jsx-runtime@2.3.6:
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} 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: hast-util-whitespace@3.0.0:
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} 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: husky@9.1.7:
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -2657,6 +2679,9 @@ packages:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true hasBin: true
lowlight@3.3.0:
resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==}
lucide-react@0.479.0: lucide-react@0.479.0:
resolution: {integrity: sha512-aBhNnveRhorBOK7uA4gDjgaf+YlHMdMhQ/3cupk6exM10hWlEU+2QtWYOfhXhjAsmdb6LeKR+NZnow4UxRRiTQ==} resolution: {integrity: sha512-aBhNnveRhorBOK7uA4gDjgaf+YlHMdMhQ/3cupk6exM10hWlEU+2QtWYOfhXhjAsmdb6LeKR+NZnow4UxRRiTQ==}
peerDependencies: peerDependencies:
@@ -2666,37 +2691,13 @@ packages:
resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==}
engines: {node: '>=16'} engines: {node: '>=16'}
markdown-table@3.0.4:
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
math-intrinsics@1.1.0: math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'} 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: mdast-util-from-markdown@2.0.2:
resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} 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: mdast-util-mdx-expression@2.0.1:
resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
@@ -2728,27 +2729,6 @@ packages:
micromark-core-commonmark@2.0.3: micromark-core-commonmark@2.0.3:
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} 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: micromark-extension-mdx-expression@3.0.1:
resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==}
@@ -3145,12 +3125,12 @@ packages:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
rehype-highlight@7.0.2:
resolution: {integrity: sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==}
rehype-recma@1.0.0: rehype-recma@1.0.0:
resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==}
remark-gfm@4.0.1:
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
remark-mdx@3.1.0: remark-mdx@3.1.0:
resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==} resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==}
@@ -3160,9 +3140,6 @@ packages:
remark-rehype@11.1.2: remark-rehype@11.1.2:
resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
remark-stringify@11.0.0:
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
require-directory@2.1.1: require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -3461,6 +3438,9 @@ packages:
unified@11.0.5: unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} 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: unist-util-is@6.0.0:
resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
@@ -4517,6 +4497,15 @@ snapshots:
'@types/react': 19.0.10 '@types/react': 19.0.10
'@types/react-dom': 19.0.4(@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)': '@radix-ui/react-slot@1.1.2(@types/react@19.0.10)(react@19.0.0)':
dependencies: dependencies:
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) '@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@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): eslint-config-next@15.2.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.8.2):
dependencies: dependencies:
'@next/eslint-plugin-next': 15.2.1 '@next/eslint-plugin-next': 15.2.1
@@ -5836,6 +5823,10 @@ snapshots:
dependencies: dependencies:
function-bind: 1.1.2 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: hast-util-to-estree@3.1.3:
dependencies: dependencies:
'@types/estree': 1.0.6 '@types/estree': 1.0.6
@@ -5877,10 +5868,19 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - 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: hast-util-whitespace@3.0.0:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
highlight.js@11.11.1: {}
husky@9.1.7: {} husky@9.1.7: {}
ieee754@1.2.1: {} ieee754@1.2.1: {}
@@ -6155,23 +6155,20 @@ snapshots:
dependencies: dependencies:
js-tokens: 4.0.0 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): lucide-react@0.479.0(react@19.0.0):
dependencies: dependencies:
react: 19.0.0 react: 19.0.0
markdown-extensions@2.0.0: {} markdown-extensions@2.0.0: {}
markdown-table@3.0.4: {}
math-intrinsics@1.1.0: {} 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: mdast-util-from-markdown@2.0.2:
dependencies: dependencies:
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
@@ -6189,63 +6186,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - 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: mdast-util-mdx-expression@2.0.1:
dependencies: dependencies:
'@types/estree-jsx': 1.0.5 '@types/estree-jsx': 1.0.5
@@ -6349,64 +6289,6 @@ snapshots:
micromark-util-symbol: 2.0.1 micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2 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: micromark-extension-mdx-expression@3.0.1:
dependencies: dependencies:
'@types/estree': 1.0.6 '@types/estree': 1.0.6
@@ -6964,6 +6846,14 @@ snapshots:
gopd: 1.2.0 gopd: 1.2.0
set-function-name: 2.0.2 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: rehype-recma@1.0.0:
dependencies: dependencies:
'@types/estree': 1.0.6 '@types/estree': 1.0.6
@@ -6972,17 +6862,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - 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: remark-mdx@3.1.0:
dependencies: dependencies:
mdast-util-mdx: 3.0.0 mdast-util-mdx: 3.0.0
@@ -7007,12 +6886,6 @@ snapshots:
unified: 11.0.5 unified: 11.0.5
vfile: 6.0.3 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-directory@2.1.1: {}
require-main-filename@2.0.0: {} require-main-filename@2.0.0: {}
@@ -7388,6 +7261,11 @@ snapshots:
trough: 2.2.0 trough: 2.2.0
vfile: 6.0.3 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: unist-util-is@6.0.0:
dependencies: dependencies:
'@types/unist': 3.0.3 '@types/unist': 3.0.3

View File

@@ -47,8 +47,7 @@ export async function GET(req: NextRequest) {
switch (format) { switch (format) {
case 'json': case 'json':
const body = JSON.stringify(params) return NextResponse.json(result.data)
return NextResponse.json(body)
case 'text': case 'text':
const text = result.data.map((item) => { const text = result.data.map((item) => {
const list = [item.host, item.port] 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"> <div className="flex justify-between gap-8">
{/* logo */} {/* logo */}
<Link href="/" className="flex items-center"> <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> </Link>
{/* 菜单 */} {/* 菜单 */}

View File

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

View File

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

View File

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

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 # qweqwe
`dasdasd`
```typescript
console.log('Hello, world!');
```
## awdasdasd ## awdasdasd
### zxczxczxc ### zxczxczxc

View File

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

View File

@@ -117,7 +117,7 @@ export function Combobox(props: ComboboxProps) {
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent <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" align="start"
collisionPadding={6} collisionPadding={6}
> >

View File

@@ -76,7 +76,7 @@ function FormField<
name={props.name} name={props.name}
control={form.control} control={form.control}
render={({field, fieldState, formState}) => ( 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 */} {/* label */}
{!!props.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}