Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2187adb05 | ||
|
|
4b18c91157 | ||
|
|
2125f1ef9e | ||
|
|
85f241e8e3 | ||
|
|
671ad8ab9d | ||
|
|
fc47ec9d18 | ||
|
|
7dc562aad0 |
3
.vscode/settings.json
vendored
@@ -7,4 +7,7 @@
|
|||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.defaultFormatter": "vscode.json-language-features"
|
"editor.defaultFormatter": "vscode.json-language-features"
|
||||||
},
|
},
|
||||||
|
"[typescriptreact]": {
|
||||||
|
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "lanhu-web",
|
"name": "lanhu-web",
|
||||||
"version": "1.1.0",
|
"version": "1.1.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -H 0.0.0.0 --turbopack",
|
"dev": "next dev -H 0.0.0.0 --turbopack",
|
||||||
|
|||||||
16
publish.ps1
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
if (-not $args) {
|
||||||
|
Write-Error "需要指定版本号"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$confrim = Read-Host "构建版本为 [web:$($args[0])],是否继续?(y/n)"
|
||||||
|
if ($confrim -ne "y") {
|
||||||
|
Write-Host "已取消构建"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
docker build -t 43.226.58.254:53000/lanhu/web:latest .
|
||||||
|
docker build -t 43.226.58.254:53000/lanhu/web:$($args[0]) .
|
||||||
|
|
||||||
|
docker push 43.226.58.254:53000/lanhu/web:latest
|
||||||
|
docker push 43.226.58.254:53000/lanhu/web:$($args[0])
|
||||||
@@ -11,21 +11,21 @@ export function ArticlesSection() {
|
|||||||
icon={<BookOpen className="w-12 h-12"/>}
|
icon={<BookOpen className="w-12 h-12"/>}
|
||||||
title="浏览器设置代理教程"
|
title="浏览器设置代理教程"
|
||||||
description="快速上手,5分钟学会在浏览器中配置代理服务器"
|
description="快速上手,5分钟学会在浏览器中配置代理服务器"
|
||||||
href="/docs/client/browser-proxy"
|
href="/docs/browser-proxy"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ArticleCard
|
<ArticleCard
|
||||||
icon={<Smartphone className="w-12 h-12"/>}
|
icon={<Smartphone className="w-12 h-12"/>}
|
||||||
title="Windows10 代理配置"
|
title="Windows10 代理配置"
|
||||||
description="详细图文教程,帮助你在 Windows 系统中设置代理"
|
description="详细图文教程,帮助你在 Windows 系统中设置代理"
|
||||||
href="/docs/client/windows10-proxy"
|
href="/docs/windows10-proxy"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ArticleCard
|
<ArticleCard
|
||||||
icon={<HelpCircle className="w-12 h-12"/>}
|
icon={<HelpCircle className="w-12 h-12"/>}
|
||||||
title="常见问题总览"
|
title="常见问题总览"
|
||||||
description="解决使用过程中遇到的各类问题,快速找到答案"
|
description="解决使用过程中遇到的各类问题,快速找到答案"
|
||||||
href="/docs/faqs/faq-general"
|
href="/docs/faq-general"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
|
|||||||
@@ -25,18 +25,18 @@ export default function HelpMenu() {
|
|||||||
icon={h02}
|
icon={h02}
|
||||||
title="操作指南"
|
title="操作指南"
|
||||||
items={[
|
items={[
|
||||||
{lead: '修改信息', href: '/docs/operation/profile-settings'},
|
{lead: '修改信息', href: '/docs/profile-settings'},
|
||||||
{lead: '提取链接', href: '/docs/operation/extract-link'},
|
{lead: '提取链接', href: '/docs/extract-link'},
|
||||||
{lead: '查看记录', href: '/docs/operation/payment-records'},
|
{lead: '查看记录', href: '/docs/payment-records'},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
icon={h03}
|
icon={h03}
|
||||||
title="平台教程"
|
title="平台教程"
|
||||||
items={[
|
items={[
|
||||||
{lead: 'iOS 设置', href: '/docs/client/ios-proxy'},
|
{lead: 'iOS 设置', href: '/docs/ios-proxy'},
|
||||||
{lead: 'Android 设置', href: '/docs/client/android-proxy'},
|
{lead: 'Android 设置', href: '/docs/android-proxy'},
|
||||||
{lead: 'Windows 设置', href: '/docs/client/windows10-proxy'},
|
{lead: 'Windows 设置', href: '/docs/windows10-proxy'},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Image src={banner} alt="banner" className="hidden lg:block"/>
|
<Image src={banner} alt="banner" className="hidden lg:block"/>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
@@ -13,38 +13,38 @@ const MENU_ITEMS = [
|
|||||||
{
|
{
|
||||||
group: '产品文档',
|
group: '产品文档',
|
||||||
items: [
|
items: [
|
||||||
{key: 'product/product-overview', label: '产品介绍'},
|
{key: 'product-overview', label: '产品介绍'},
|
||||||
{key: 'product/choose-product', label: '如何选择产品'},
|
{key: 'choose-product', label: '如何选择产品'},
|
||||||
{key: 'product/why-verify', label: '为什么需要实名认证'},
|
{key: 'why-verify', label: '为什么需要实名认证'},
|
||||||
{key: 'product/city-lines', label: '有哪些城市线路'},
|
{key: 'city-lines', label: '有哪些城市线路'},
|
||||||
{key: 'product/api-docs', label: 'ip提取接口文档'},
|
{key: 'api-docs', label: 'ip提取接口文档'},
|
||||||
// 服务条款
|
// 服务条款
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: '操作指南',
|
group: '操作指南',
|
||||||
items: [
|
items: [
|
||||||
{key: 'operation/profile-settings', label: '修改个人信息和重置密码'},
|
{key: 'profile-settings', label: '修改个人信息和重置密码'},
|
||||||
{key: 'operation/whitelist-guide', label: '如何添加白名单'},
|
{key: 'whitelist-guide', label: '如何添加白名单'},
|
||||||
{key: 'operation/verify-guide', label: '如何进行实名认证'},
|
{key: 'verify-guide', label: '如何进行实名认证'},
|
||||||
{key: 'operation/extract-link', label: '如何生成提取链接'},
|
{key: 'extract-link', label: '如何生成提取链接'},
|
||||||
{key: 'operation/payment-records', label: '查看支付和使用记录'},
|
{key: 'payment-records', label: '查看支付和使用记录'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: '客户端教程',
|
group: '客户端教程',
|
||||||
items: [
|
items: [
|
||||||
{key: 'client/browser-proxy', label: '浏览器设置代理教程'},
|
{key: 'browser-proxy', label: '浏览器设置代理教程'},
|
||||||
{key: 'client/ios-proxy', label: 'iOS设置代理教程'},
|
{key: 'ios-proxy', label: 'iOS设置代理教程'},
|
||||||
{key: 'client/android-proxy', label: '安卓手机设置代理教程'},
|
{key: 'android-proxy', label: '安卓手机设置代理教程'},
|
||||||
{key: 'client/windows10-proxy', label: 'Windows10设置代理教程'},
|
{key: 'windows10-proxy', label: 'Windows10设置代理教程'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: '常见问题',
|
group: '常见问题',
|
||||||
items: [
|
items: [
|
||||||
{key: 'faqs/faq-general', label: '常见问题总览'},
|
{key: 'faq-general', label: '常见问题总览'},
|
||||||
{key: 'faqs/faq-billing', label: '计费与套餐问题'},
|
{key: 'faq-billing', label: '计费与套餐问题'},
|
||||||
// 业务场景集成方案
|
// 业务场景集成方案
|
||||||
// 故障排查
|
// 故障排查
|
||||||
],
|
],
|
||||||
@@ -52,8 +52,8 @@ const MENU_ITEMS = [
|
|||||||
{
|
{
|
||||||
group: '新闻资讯',
|
group: '新闻资讯',
|
||||||
items: [
|
items: [
|
||||||
{key: 'news/news-latest', label: '了解代理服务器的工作原理'},
|
{key: 'news-latest', label: '了解代理服务器的工作原理'},
|
||||||
{key: 'news/news-announce', label: '网站公告'},
|
{key: 'news-announce', label: '网站公告'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default function Footer(props: FooterProps) {
|
|||||||
items={[
|
items={[
|
||||||
{name: `产品订购`, href: `/product`},
|
{name: `产品订购`, href: `/product`},
|
||||||
{name: `获取代理`, href: `/collect`},
|
{name: `获取代理`, href: `/collect`},
|
||||||
{name: `帮助中心`, href: `/docs/faqs/faq-general`},
|
{name: `帮助中心`, href: `/docs/faq-general`},
|
||||||
{name: `企业服务`, href: `/custom`},
|
{name: `企业服务`, href: `/custom`},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
@@ -69,9 +69,9 @@ export default function Footer(props: FooterProps) {
|
|||||||
<SiteNavList
|
<SiteNavList
|
||||||
title="帮助文档"
|
title="帮助文档"
|
||||||
items={[
|
items={[
|
||||||
{name: `产品功能`, href: `/docs/product/product-features`},
|
{name: `产品功能`, href: `/docs/product-overview`},
|
||||||
{name: `使用教程`, href: `/docs/client/browser-proxy`},
|
{name: `使用教程`, href: `/docs/browser-proxy`},
|
||||||
{name: `行业资讯`, href: `/docs/news/news-latest`},
|
{name: `行业资讯`, href: `/docs/news-latest`},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {ReactNode} from 'react'
|
import {ReactNode} from 'react'
|
||||||
import Header from './header'
|
import Header from './header'
|
||||||
import Footer from './footer'
|
import Footer from './footer'
|
||||||
|
import Script from 'next/script'
|
||||||
|
|
||||||
export type HomeLayoutProps = {
|
export type HomeLayoutProps = {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
@@ -17,6 +18,8 @@ export default function HomeLayout(props: HomeLayoutProps) {
|
|||||||
|
|
||||||
{/* 页脚 */}
|
{/* 页脚 */}
|
||||||
<Footer/>
|
<Footer/>
|
||||||
|
|
||||||
|
<Script id="qd2852138148beb7882a4a6a3e5ff5b569436003e7dc" src="https://wp.qiye.qq.com/qidian/2852138148/beb7882a4a6a3e5ff5b569436003e7dc" async defer></Script>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default async function UserCenter() {
|
|||||||
<Card className="h-full">
|
<Card className="h-full">
|
||||||
<CardContent className="flex-auto flex flex-col justify-between gap-4">
|
<CardContent className="flex-auto flex flex-col justify-between gap-4">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<p>{profile.username?.trim() || profile.email || profile.phone}</p>
|
<p>{profile.username?.trim() || profile.phone }</p>
|
||||||
<p className="text-sm text-weak">{`最后登录:${format(profile.last_login, 'yyyy-MM-dd HH:mm')}`}</p>
|
<p className="text-sm text-weak">{`最后登录:${format(profile.last_login, 'yyyy-MM-dd HH:mm')}`}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className={merge(
|
<div className={merge(
|
||||||
|
|||||||
@@ -75,19 +75,19 @@ export function Content(props: {children: ReactNode}) {
|
|||||||
}
|
}
|
||||||
function ContentResolved() {
|
function ContentResolved() {
|
||||||
const profile = use(useProfileStore(store => store.profile))
|
const profile = use(useProfileStore(store => store.profile))
|
||||||
if (!profile) throw new Error('登录状态异常')
|
if (profile)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RealnameAuthDialog
|
<RealnameAuthDialog
|
||||||
triggerClassName="hidden"
|
triggerClassName="hidden"
|
||||||
defaultOpen={!profile.id_token}
|
defaultOpen={!profile.id_token}
|
||||||
/>
|
/>
|
||||||
<ChangePasswordDialog
|
<ChangePasswordDialog
|
||||||
triggerClassName="hidden"
|
triggerClassName="hidden"
|
||||||
defaultOpen={!profile.has_password}
|
defaultOpen={!profile.has_password}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
@@ -127,8 +127,7 @@ export function Header() {
|
|||||||
|
|
||||||
function HeaderUserCenter() {
|
function HeaderUserCenter() {
|
||||||
const profile = use(useProfileStore(store => store.profile))
|
const profile = use(useProfileStore(store => store.profile))
|
||||||
if (!profile) throw new Error('登录状态异常')
|
if (profile) return <UserCenter profile={profile}/>
|
||||||
return <UserCenter profile={profile}/>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Navbar() {
|
export function Navbar() {
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export default async function RootLayout(props: Readonly<{
|
|||||||
<Effects>{props.children}</Effects>
|
<Effects>{props.children}</Effects>
|
||||||
</StoreProviders>
|
</StoreProviders>
|
||||||
<Toaster position="top-center" richColors expand/>
|
<Toaster position="top-center" richColors expand/>
|
||||||
<Script id="qd2852138148beb7882a4a6a3e5ff5b569436003e7dc" src="https://wp.qiye.qq.com/qidian/2852138148/beb7882a4a6a3e5ff5b569436003e7dc" async defer></Script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export function ChangePasswordDialog({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 提交处理
|
// 提交处理
|
||||||
const handler = form.handleSubmit(async (value) => {
|
const handler = async (value: Schema) => {
|
||||||
try {
|
try {
|
||||||
const resp = await updatePassword({
|
const resp = await updatePassword({
|
||||||
phone: value.phone,
|
phone: value.phone,
|
||||||
@@ -90,7 +90,7 @@ export function ChangePasswordDialog({
|
|||||||
description: e instanceof Error ? e.message : String(e),
|
description: e instanceof Error ? e.message : String(e),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={actualOpen} onOpenChange={actualOnOpenChange}>
|
<Dialog open={actualOpen} onOpenChange={actualOnOpenChange}>
|
||||||
@@ -98,10 +98,16 @@ export function ChangePasswordDialog({
|
|||||||
<Button theme="outline" className={triggerClassName || 'w-24 h-9'}>修改密码</Button>
|
<Button theme="outline" className={triggerClassName || 'w-24 h-9'}>修改密码</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<Form
|
||||||
<DialogTitle>修改密码</DialogTitle>
|
form={form}
|
||||||
</DialogHeader>
|
handler={async () => {
|
||||||
<Form form={form} handler={handler} className="flex flex-col gap-4 mt-4">
|
const data = form.getValues()
|
||||||
|
await handler(data)
|
||||||
|
}}
|
||||||
|
className="flex flex-col gap-4 mt-4">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>修改密码</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
{/* 手机号输入 */}
|
{/* 手机号输入 */}
|
||||||
<FormField<Schema> name="phone" label="手机号" className="flex-auto">
|
<FormField<Schema> name="phone" label="手机号" className="flex-auto">
|
||||||
{({field}) => (
|
{({field}) => (
|
||||||
@@ -132,22 +138,20 @@ export function ChangePasswordDialog({
|
|||||||
<Input {...field} placeholder="请再次输入新密码" type="password" autoComplete="new-password"/>
|
<Input {...field} placeholder="请再次输入新密码" type="password" autoComplete="new-password"/>
|
||||||
)}
|
)}
|
||||||
</FormField>
|
</FormField>
|
||||||
</Form>
|
|
||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
theme="outline"
|
theme="outline"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
actualOnOpenChange(false)
|
actualOnOpenChange(false)
|
||||||
form.reset()
|
form.reset()
|
||||||
}}>
|
}}>
|
||||||
关闭
|
关闭
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handler}>
|
<Button type="submit">保存</Button>
|
||||||
保存
|
</DialogFooter>
|
||||||
</Button>
|
</Form>
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ 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 '@/app/(home)/docs/product/api-docs/page.md'
|
import ExtractDocs from '@/app/(home)/docs/(product)/api-docs/page.md'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import {useProfileStore} from '@/components/stores/profile'
|
import {useProfileStore} from '@/components/stores/profile'
|
||||||
|
|
||||||
|
|||||||
@@ -107,13 +107,9 @@ function Pagination({
|
|||||||
const paginationItems = generatePaginationItems()
|
const paginationItems = generatePaginationItems()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-wrap items-center justify-end gap-4 ${className || ''}`}>
|
<div className={`flex flex-wrap items-center gap-4 ${className || ''}`}>
|
||||||
<div className="flex-none flex items-center gap-2 text-sm text-muted-foreground">
|
<div className="flex-none flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
共
|
共 {total} 条记录,每页
|
||||||
{' '}
|
|
||||||
{total}
|
|
||||||
{' '}
|
|
||||||
条记录,每页
|
|
||||||
<Select
|
<Select
|
||||||
value={size.toString()}
|
value={size.toString()}
|
||||||
onValueChange={handlePageSizeChange}
|
onValueChange={handlePageSizeChange}
|
||||||
|
|||||||