diff --git a/src/actions/base.ts b/src/actions/base.ts index f8fdbb9..4a96400 100644 --- a/src/actions/base.ts +++ b/src/actions/base.ts @@ -172,7 +172,7 @@ async function getUserToken(refresh = false): Promise { // 使用用户令牌的API调用函数 async function callByUser( endpoint: string, - data: unknown, + data?: unknown, ): Promise> { try { let token = await getUserToken() @@ -185,7 +185,7 @@ async function callByUser( 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, - body: JSON.stringify(data), + body: data ? JSON.stringify(data) : undefined, } response = await fetch(`${API_BASE_URL}${endpoint}`, requestOptions) diff --git a/src/actions/resource.ts b/src/actions/resource.ts index 51452a8..0ae8575 100644 --- a/src/actions/resource.ts +++ b/src/actions/resource.ts @@ -17,6 +17,10 @@ async function listResourcePss(props: { return await callByUser>('/api/resource/list/pss', props) } +async function allResource(){ + return callByUser('/api/resource/all') +} + async function createResourceByBalance(props: { type: number live: number @@ -37,6 +41,7 @@ async function createResourceByWechat() { export { listResourcePss, + allResource, createResourceByBalance, createResourceByAlipay, createResourceByWechat, diff --git a/src/app/(auth)/login/captcha.tsx b/src/app/(auth)/login/captcha.tsx index ba45a77..643819b 100644 --- a/src/app/(auth)/login/captcha.tsx +++ b/src/app/(auth)/login/captcha.tsx @@ -53,7 +53,7 @@ export default function Captcha(props: CaptchaProps) { onClick={refreshCaptcha} /> diff --git a/src/app/(root)/collect/_client/form-section.tsx b/src/app/(root)/collect/_client/form-section.tsx deleted file mode 100644 index 6053f89..0000000 --- a/src/app/(root)/collect/_client/form-section.tsx +++ /dev/null @@ -1,244 +0,0 @@ -'use client' -import {z} from 'zod' -import {zodResolver} from '@hookform/resolvers/zod' -import {Form, FormField, FormLabel} from '@/components/ui/form' -import {RadioGroup, RadioGroupItem} from '@/components/ui/radio-group' -import {Input} from '@/components/ui/input' -import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select' -import {Button} from '@/components/ui/button' -import {useForm} from 'react-hook-form' - -const formSchema = z.object({ - type: z.enum([`num`, `time`]), - order: z.number(), - region: z.string(), - provider: z.string(), - proto: z.string(), - distinct: z.string(), - format: z.enum([`txt`, `json`]), - separator: z.string(), - count: z.number(), -}) -type FormValues = z.infer - -type FormSectionProps = {} - -export default function FormSection(props: FormSectionProps) { - - const form = useForm({ - resolver: zodResolver(formSchema), - defaultValues: { - type: `num`, - order: 0, - region: ``, - provider: ``, - proto: ``, - distinct: ``, - format: `txt`, - separator: `,`, - count: 0, - }, - }) - - const onSubmit = (values: z.infer) => { - console.log(values) - // 在这里处理表单提交 - } - - return ( -
-
    -
  • - -
  • -
  • - -
  • -
-

- {`warn`} - 提取IP前需要将本机IP添加到白名单后才可使用 -

- -
- {/* 套餐类型 */} -
- name="type" label={`套餐类型`}> - {({id, field}) => ( - -
- - -
-
- - -
-
- )} - -
- - {/* 已购套餐 */} -
- - {({field}) => ( - - )} - -
- - {/* 地区筛选 */} -
- - {({field}) => ( - - )} - -
- - {/* 运营商筛选 */} -
- - {({field}) => ( - - )} - -
- - {/* 协议类型 */} -
- - {({field}) => ( - - )} - -
- - {/* 去重选项 */} -
- - {({field}) => ( - - )} - -
- - {/* 导出格式 */} -
- - {({id, field}) => ( - -
- - TXT格式 -
-
- - JSON格式 -
-
- )} -
-
- - {/* 分隔符 */} -
- - {({id, field}) => ( - - )} - -
- - {/* 提取数量 */} -
- - {({id, field}) => ( - field.onChange(Number(e.target.value))} - className="h-10" - placeholder="输入提取数量" - /> - )} - -
-
- -
- -
-
- ) -} diff --git a/src/app/(root)/collect/page.tsx b/src/app/(root)/collect/page.tsx index 174f8f5..3973a76 100644 --- a/src/app/(root)/collect/page.tsx +++ b/src/app/(root)/collect/page.tsx @@ -1,6 +1,6 @@ import BreadCrumb from '@/components/bread-crumb' import Wrap from '@/components/wrap' -import FormSection from '@/app/(root)/collect/_client/form-section' +import Extract from '@/components/composites/extract' export type CollectPageProps = {} @@ -11,7 +11,7 @@ export default function CollectPage(props: CollectPageProps) { - + ) diff --git a/src/app/admin/bills/page.tsx b/src/app/admin/bills/page.tsx index 1380909..152949f 100644 --- a/src/app/admin/bills/page.tsx +++ b/src/app/admin/bills/page.tsx @@ -193,7 +193,7 @@ export default function BillsPage(props: BillsPageProps) { 筛选 - diff --git a/src/app/admin/extract/page.tsx b/src/app/admin/extract/page.tsx index 4ccad08..94d1e6a 100644 --- a/src/app/admin/extract/page.tsx +++ b/src/app/admin/extract/page.tsx @@ -1,13 +1,12 @@ -import {ReactNode} from 'react' import Page from '@/components/page' +import Extract from '@/components/composites/extract' -export type ExtractPageProps = { -} +export type ExtractPageProps = {} export default async function ExtractPage(props: ExtractPageProps) { return ( - + ) } diff --git a/src/app/admin/purchase/page.tsx b/src/app/admin/purchase/page.tsx index bcad2db..cd99552 100644 --- a/src/app/admin/purchase/page.tsx +++ b/src/app/admin/purchase/page.tsx @@ -1,4 +1,4 @@ -import Purchase from '@/components/composites/purchase/purchase' +import Purchase from '@/components/composites/purchase' import Page from '@/components/page' export type PurchasePageProps = {} diff --git a/src/app/admin/resources/page.tsx b/src/app/admin/resources/page.tsx index 2ba3bef..02acbf7 100644 --- a/src/app/admin/resources/page.tsx +++ b/src/app/admin/resources/page.tsx @@ -204,7 +204,7 @@ export default function ResourcesPage(props: ResourcesPageProps) { 筛选 - + - + + diff --git a/src/app/globals.css b/src/app/globals.css index 3a65dab..559df2b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,26 +2,37 @@ @plugin "tailwindcss-animate"; -@custom-variant dark (&:is(.dark *)); - :root { - --radius: 0.625rem; --background: oklch(1 0 0); - --foreground: oklch(0.13 0.028 261.692); - --card: oklch(1 0 0); - --card-foreground: oklch(0.13 0.028 261.692); + --foreground: oklch(0.25 0 0); + --weak: oklch(0.5 0 0); + + --primary: oklch(0.65 0.16 265); + --primary-text: oklch(1 0 0); + --primary-weak: oklch(0.5 0 0); + + --secondary: oklch(0.95 0 0); + --secondary-text: oklch(0.25 0 0); + + --accent: oklch(0.769 0.188 70.08); + --accent-text: oklch(0.985 0.002 247.839); + + --done: oklch(0.65 0.16 145); + --done-text: oklch(1 0 0); + + --warn: oklch(0.72 0.16 55); + --warn-text: oklch(1 0 0); + + --fail: oklch(0.65 0.16 25); + --fail-text: oklch(1 0 0); + + --card: oklch(0.975 0 0); + --card-foreground: oklch(0.25 0 0); + --popover: oklch(1 0 0); - --popover-foreground: oklch(0.13 0.028 261.692); - --primary: oklch(0.64 0.1597 265); - --primary-foreground: oklch(0.985 0.002 247.839); - --secondary: oklch(0.967 0.003 264.542); - --secondary-foreground: oklch(0.21 0.034 264.665); + --popover-foreground: oklch(0.25 0 0); --muted: oklch(0.967 0.003 264.542); --muted-foreground: oklch(0.551 0.027 264.364); - --accent: oklch(0.769 0.188 70.08); - --accent-foreground: oklch(0.985 0.002 247.839); - --destructive: oklch(0.64 0.1597 25); - --destructive-foreground: oklch(0.985 0.002 247.839); --border: oklch(0.928 0.006 264.531); --input: oklch(0.928 0.006 264.531); --ring: oklch(0.882 0.059 254.128); @@ -31,7 +42,7 @@ --chart-4: oklch(0.828 0.189 84.429); --chart-5: oklch(0.769 0.188 70.08); --sidebar: oklch(0.985 0.002 247.839); - --sidebar-foreground: oklch(0.13 0.028 261.692); + --sidebar-foreground: oklch(0.25 0 0); --sidebar-primary: oklch(0.21 0.034 264.665); --sidebar-primary-foreground: oklch(0.985 0.002 247.839); --sidebar-accent: oklch(0.967 0.003 264.542); @@ -40,60 +51,37 @@ --sidebar-ring: oklch(0.707 0.022 261.325); } -.dark { - --background: oklch(0.13 0.028 261.692); - --foreground: oklch(0.985 0.002 247.839); - --card: oklch(0.21 0.034 264.665); - --card-foreground: oklch(0.985 0.002 247.839); - --popover: oklch(0.21 0.034 264.665); - --popover-foreground: oklch(0.985 0.002 247.839); - --primary: oklch(0.928 0.006 264.531); - --primary-foreground: oklch(0.21 0.034 264.665); - --secondary: oklch(0.278 0.033 256.848); - --secondary-foreground: oklch(0.985 0.002 247.839); - --muted: oklch(0.278 0.033 256.848); - --muted-foreground: oklch(0.707 0.022 261.325); - --accent: oklch(0.278 0.033 256.848); - --accent-foreground: oklch(0.985 0.002 247.839); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.551 0.027 264.364); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.21 0.034 264.665); - --sidebar-foreground: oklch(0.985 0.002 247.839); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0.002 247.839); - --sidebar-accent: oklch(0.278 0.033 256.848); - --sidebar-accent-foreground: oklch(0.985 0.002 247.839); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.551 0.027 264.364); -} - @theme inline { - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); --color-background: var(--background); --color-foreground: var(--foreground); + --color-weak: var(--weak); + + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-text); + --color-primary-weak: var(--primary-weak); + + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-text); + + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-text); + + --color-done: var(--done); + --color-done-foreground: var(--done-text); + + --color-warn: var(--warn); + --color-warn-foreground: var(--warn-text); + + --color-fail: var(--fail); + --color-fail-foreground: var(--fail-text); + --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - --color-destructive: var(--destructive); + --color-destructive: var(--fail); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); @@ -120,8 +108,8 @@ body { @apply bg-background text-foreground; } -} -body { - color: hsl(0, 0%, 10%); + th { + @apply font-normal; + } } diff --git a/src/components/composites/extract/index.tsx b/src/components/composites/extract/index.tsx new file mode 100644 index 0000000..deccb20 --- /dev/null +++ b/src/components/composites/extract/index.tsx @@ -0,0 +1,392 @@ +'use client' +import {z} from 'zod' +import {zodResolver} from '@hookform/resolvers/zod' +import {Form, FormField, FormLabel} from '@/components/ui/form' +import {RadioGroup, RadioGroupItem} from '@/components/ui/radio-group' +import {Input} from '@/components/ui/input' +import {Select, SelectContent, SelectItem, SelectSeparator, SelectTrigger, SelectValue} from '@/components/ui/select' +import {Button} from '@/components/ui/button' +import {useForm} from 'react-hook-form' +import {Alert, AlertTitle} from '@/components/ui/alert' +import {Box, CircleAlert, Loader, Timer} from 'lucide-react' +import {useEffect, useState} from 'react' +import {useStatus} from '@/lib/states' +import {allResource} from '@/actions/resource' +import {Resource, name} from '@/lib/models' +import {format, intlFormatDistance} from 'date-fns' + + +type ExtractProps = {} + +export default function Extract(props: ExtractProps) { + const [resources, setResources] = useState([]) + const [status, setStatus] = useStatus() + + const schema = z.object({ + resource: z.number().optional(), + prov: z.string().optional(), + city: z.string().optional(), + regionType: z.enum(['unlimited', 'specific']).default('unlimited'), + isp: z.enum(['all', '1', '2', '3']), + proto: z.enum(['all', '1', '2', '3']), + distinct: z.enum(['1', '0']), + format: z.enum(['text', 'json']), + separator: z.string(), + breaker: z.string(), + count: z.number().min(1), + }) + + type Schema = z.infer + + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: { + regionType: 'unlimited', + isp: 'all', + proto: 'all', + count: 1, + distinct: '1', + format: 'text', + breaker: '\\n', + separator: '|', + }, + }) + + const regionType = form.watch('regionType') + + const onSubmit = (values: z.infer) => { + console.log(values) + } + + const getResources = async () => { + setStatus('load') + try { + const resp = await allResource() + if (!resp.success) { + throw new Error('Unable to fetch packages.') + } + setResources(resp.data) + setStatus('done') + } + catch (error) { + console.error('Error fetching packages:', error) + setStatus('fail') + } + } + + useEffect(() => { + getResources().then() + }, []) + + return ( +
+ + + 提取IP前需要将本机IP添加到白名单后才可使用 + + +
+ {/* 选择套餐 */} +
+ + {({field}) => ( + + )} + +
+ + {/* 地区筛选 */} +
+ + {({id, field}) => ( + + + + 不限地区 + + + + 指定地区 + + + )} + + + {regionType === 'specific' && ( +
+ + {({field}) => ( + + )} + + + + {({field}) => ( + + )} + +
+ )} +
+ + {/* 运营商筛选 */} +
+ + {({id, field}) => ( + + + + 不限 + + + + 电信 + + + + 移动 + + + + 联通 + + + )} + +
+ + {/* 协议类型 */} +
+ + {({id, field}) => ( + + + + 不限 + + + + HTTP + + + + HTTPS + + + + SOCKS5 + + + )} + +
+ + {/* 去重选项 */} +
+ + {({id, field}) => ( + + + + 去重 + + + + 不去重 + + + )} + +
+ + {/* 导出格式 */} +
+ + {({id, field}) => ( + + + + TXT 格式 + + + + JSON 格式 + + + )} + +
+ + {/* 分隔符 */} +
+ + {({id, field}) => ( + + + + 竖线 ( | ) + + + + 冒号 ( : ) + + + + 制表符 ( \t ) + + + )} + +
+ + {/* 换行符 */} +
+ + {({id, field}) => ( + + + + 换行 ( \n ) + + + + 回车 ( \r ) + + + + 回车换行 ( \r\n ) + + + )} + +
+ + {/* 提取数量 */} +
+ + {({id, field}) => ( + field.onChange(Number(e.target.value))} + className="h-10 w-84" + placeholder="输入提取数量" + /> + )} + +
+
+ +
+ +
+
+ ) +} diff --git a/src/components/composites/purchase/_client/center.tsx b/src/components/composites/purchase/_client/center.tsx index 34f013e..044cfb2 100644 --- a/src/components/composites/purchase/_client/center.tsx +++ b/src/components/composites/purchase/_client/center.tsx @@ -83,7 +83,7 @@ export default function Center() { {({id, field}) => (
+ diff --git a/src/components/composites/purchase/purchase.tsx b/src/components/composites/purchase/index.tsx similarity index 100% rename from src/components/composites/purchase/purchase.tsx rename to src/components/composites/purchase/index.tsx diff --git a/src/components/data-table.tsx b/src/components/data-table.tsx index 3438b4f..51b6b69 100644 --- a/src/components/data-table.tsx +++ b/src/components/data-table.tsx @@ -54,7 +54,7 @@ export default function DataTable>(props: Data {props.status === 'fail' ? ( - 加载失败 + 加载失败 ) : !props.data?.length ? ( diff --git a/src/components/date-picker.tsx b/src/components/date-picker.tsx index 5dc84f4..8296496 100644 --- a/src/components/date-picker.tsx +++ b/src/components/date-picker.tsx @@ -26,7 +26,7 @@ export default function DatePicker(props: DatePickerProps) {