优化用户数据初始化时机

This commit is contained in:
2025-11-18 19:16:24 +08:00
parent 5b1dae6e6c
commit fa6a4e5121
19 changed files with 52 additions and 52 deletions

View File

@@ -3,7 +3,7 @@
// For a full list of overridable settings, and general information on folder-specific settings, // For a full list of overridable settings, and general information on folder-specific settings,
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files // see the documentation: https://zed.dev/docs/configuring-zed#settings-files
{ {
"language_servers": ["...", "!biome"], "language_servers": ["...", "!biome", "!typescript-language-server"],
"formatter": { "formatter": {
"code_action": "source.fixAll.eslint" "code_action": "source.fixAll.eslint"
} }

View File

@@ -63,6 +63,8 @@
"husky": "^9.1.7", "husky": "^9.1.7",
"rehype-highlight": "^7.0.2", "rehype-highlight": "^7.0.2",
"tailwindcss": "^4", "tailwindcss": "^4",
},
"peerDependencies": {
"typescript": "^5", "typescript": "^5",
}, },
}, },

View File

@@ -67,7 +67,9 @@
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "^5.2.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"rehype-highlight": "^7.0.2", "rehype-highlight": "^7.0.2",
"tailwindcss": "^4", "tailwindcss": "^4"
},
"peerDependencies": {
"typescript": "^5" "typescript": "^5"
}, },
"packageManager": "bun@1.2.19" "packageManager": "bun@1.2.19"

View File

@@ -32,8 +32,8 @@ export default function CollectPage(props: CollectPageProps) {
]}/> ]}/>
<main className="flex flex-col gap-16 lg:gap-32 mb-16 lg:mb-32"> <main className="flex flex-col gap-16 lg:gap-32 mb-16 lg:mb-32">
{/* banner */} {/* banner */}
<section className="flex-none basis-40 relative flex flex-col gap-4 justify-center"> <section className="flex-none basis-40 relative flex flex-col gap-4 justify-center">
<Wrap className="relative pt-30 pb-48 max-md:pt-32 max-md:pb-24 min-h-[500px]"> <Wrap className="relative pt-30 pb-48 max-md:pt-32 max-md:pb-24 min-h-[500px]">
<Image src={s12} alt="背景图" className="absolute inset-0 w-full h-full object-cover"/> <Image src={s12} alt="背景图" className="absolute inset-0 w-full h-full object-cover"/>
<div className="relative pl-20 w-1/2 flex-1"> <div className="relative pl-20 w-1/2 flex-1">
<h1 className="text-4xl"></h1> <h1 className="text-4xl"></h1>
@@ -129,7 +129,7 @@ export default function CollectPage(props: CollectPageProps) {
</Section> </Section>
{/* 优势 2 */} {/* 优势 2 */}
<Section title="产品核心优势 "> <Section title="产品核心优势 ">
<ul className={merge('grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8')}> <ul className={merge('grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8')}>
<li className={merge('p-8 flex flex-col gap-5 shadow-[4px_4px_20px_4px] shadow-blue-50 bg-white rounded-lg max-md:items-center')}> <li className={merge('p-8 flex flex-col gap-5 shadow-[4px_4px_20px_4px] shadow-blue-50 bg-white rounded-lg max-md:items-center')}>
<Image <Image

View File

@@ -18,8 +18,8 @@ export default function Footer(props: FooterProps) {
<p className={`text-sm text-gray-500 `}>/微信:18751847847</p> <p className={`text-sm text-gray-500 `}>/微信:18751847847</p>
<p className={`text-sm text-gray-500 `}>QQ号:800180559</p> <p className={`text-sm text-gray-500 `}>QQ号:800180559</p>
<h3 className="hidden sm:block"></h3> <h3 className="hidden sm:block"></h3>
<p className="text-sm text-gray-500 hidden sm:block"></p> <p className="text-sm text-gray-500 hidden sm:block"></p>
<p className="text-sm text-gray-500 hidden sm:block"></p> <p className="text-sm text-gray-500 hidden sm:block"></p>
</div> </div>
<SiteNavList <SiteNavList

View File

@@ -2,10 +2,11 @@
import {useCallback, useEffect, useMemo, useState, PointerEvent, ComponentProps} from 'react' import {useCallback, useEffect, useMemo, useState, PointerEvent, ComponentProps} from 'react'
import Link from 'next/link' import Link from 'next/link'
import Image from 'next/image' import Image from 'next/image'
import SolutionMenu from './menu-solution' import {HeaderContext} from './_components/header/common'
import ProductMenu from './menu-product' import SolutionMenu from './_components/header/menu-solution'
import HelpMenu from './menu-help' import ProductMenu from './_components/header/menu-product'
import MobileMenu from './menu-mobile' import HelpMenu from './_components/header/menu-help'
import MobileMenu from './_components/header/menu-mobile'
import Wrap from '@/components/wrap' import Wrap from '@/components/wrap'
import logo from '@/assets/logo.webp' import logo from '@/assets/logo.webp'
import {Button} from '@/components/ui/button' import {Button} from '@/components/ui/button'
@@ -14,11 +15,11 @@ import UserCenter from '@/components/composites/user-center'
import {MenuIcon} from 'lucide-react' import {MenuIcon} from 'lucide-react'
import down from '@/assets/header/down.svg' import down from '@/assets/header/down.svg'
import {merge} from '@/lib/utils' import {merge} from '@/lib/utils'
import {HeaderContext} from './common' import {User} from '@/lib/models'
export type ProviderProps = {} export type HeaderProps = {}
export default function Page(props: ProviderProps) { export default function Header(props: HeaderProps) {
// ====================== // ======================
// 滚动条状态 // 滚动条状态
// ====================== // ======================
@@ -163,7 +164,7 @@ export default function Page(props: ProviderProps) {
{/* 登录 */} {/* 登录 */}
<div className="flex items-center"> <div className="flex items-center">
{profile == undefined {profile == null
? ( ? (
<> <>
<Link <Link
@@ -175,7 +176,7 @@ export default function Page(props: ProviderProps) {
<Link <Link
href="/login?type=sms" href="/login?type=sms"
className={[ className={[
`w-20 lg:w-24 h-10 lg:h-12 bg-gradient-to-r rounded-sm flex items-center justify-center lg:text-lg text-white`, `w-20 lg:w-24 h-10 lg:h-12 bg-linear-to-r rounded-sm flex items-center justify-center lg:text-lg text-white`,
`transition-colors duration-200 ease-in-out`, `transition-colors duration-200 ease-in-out`,
`from-blue-500 to-cyan-400 hover:from-blue-500 hover:to-cyan-300`, `from-blue-500 to-cyan-400 hover:from-blue-500 hover:to-cyan-300`,
].join(' ')} ].join(' ')}

View File

@@ -1,6 +1,6 @@
import Header from '@/app/(home)/@header/page'
import Footer from '@/app/(home)/@footer/page'
import {ReactNode} from 'react' import {ReactNode} from 'react'
import Header from './header'
import Footer from './footer'
export type HomeLayoutProps = { export type HomeLayoutProps = {
children: ReactNode children: ReactNode

View File

@@ -2,7 +2,7 @@
import {ReactNode} from 'react' import {ReactNode} from 'react'
import Wrap from '@/components/wrap' import Wrap from '@/components/wrap'
import Image from 'next/image' import Image from 'next/image'
import React, {useState} from 'react' import React from 'react'
import {useRouter} from 'next/navigation' import {useRouter} from 'next/navigation'
import {useProfileStore} from '@/components/stores-provider' import {useProfileStore} from '@/components/stores-provider'
@@ -15,7 +15,7 @@ export default function Home() {
<main className="flex flex-col gap-16 lg:gap-32 mb-16 lg:mb-32"> <main className="flex flex-col gap-16 lg:gap-32 mb-16 lg:mb-32">
{/* banner */} {/* banner */}
<section className={`w-full bg-[url('/banner.webp')] bg-cover bg-[center_right_40%]`}> <section className={`w-full bg-[url('/banner.webp')] bg-cover bg-position-[center_right_40%]`}>
<Wrap className="pt-64 pb-48 max-md:pt-32 max-md:pb-24"> <Wrap className="pt-64 pb-48 max-md:pt-32 max-md:pb-24">
<h1 className="text-4xl"></h1> <h1 className="text-4xl"></h1>
<p className="mt-10 text-gray-500">IP代理服务</p> <p className="mt-10 text-gray-500">IP代理服务</p>
@@ -38,7 +38,7 @@ export default function Home() {
<button <button
className={[ className={[
`mt-32 max-md:mt-20 w-96 max-md:w-full h-16 md:h-24 rounded-lg shadow-lg`, `mt-32 max-md:mt-20 w-96 max-md:w-full h-16 md:h-24 rounded-lg shadow-lg`,
`bg-gradient-to-r from-blue-500 to-cyan-400 text-white text-xl lg:text-4xl`, `bg-linear-to-r from-blue-500 to-cyan-400 text-white text-xl lg:text-4xl`,
].join(' ')} ].join(' ')}
onClick={() => { onClick={() => {
if (profile) { if (profile) {
@@ -59,22 +59,22 @@ export default function Home() {
<ul className="shadow-[0_0_20px_4px] shadow-blue-50 p-8 flex max-lg:flex-col"> <ul className="shadow-[0_0_20px_4px] shadow-blue-50 p-8 flex max-lg:flex-col">
<li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200"> <li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200">
<p className="text-xl">线</p> <p className="text-xl">线</p>
<p className="mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">350+</p> <p className="mt-9 max-lg:mt-2 text-5xl bg-linear-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">350+</p>
<div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div> <div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div>
</li> </li>
<li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200"> <li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200">
<p className="text-xl">IP数量</p> <p className="text-xl">IP数量</p>
<p className="mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">1,350,129</p> <p className="mt-9 max-lg:mt-2 text-5xl bg-linear-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">1,350,129</p>
<div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div> <div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div>
</li> </li>
<li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200"> <li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200">
<p className="text-xl"></p> <p className="text-xl"></p>
<p className="mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">26,578</p> <p className="mt-9 max-lg:mt-2 text-5xl bg-linear-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">26,578</p>
<div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div> <div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div>
</li> </li>
<li className="flex-1 flex flex-col items-center justify-center"> <li className="flex-1 flex flex-col items-center justify-center">
<p className="text-xl">IP可用率</p> <p className="text-xl">IP可用率</p>
<p className="mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">99%</p> <p className="mt-9 max-lg:mt-2 text-5xl bg-linear-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">99%</p>
</li> </li>
</ul> </ul>
<img src="/map.webp" alt="map" className="w-[1200px]"/> <img src="/map.webp" alt="map" className="w-[1200px]"/>

View File

@@ -108,7 +108,7 @@ function Pins(props: DashboardPinsProps) {
<div className="flex-1 flex items-center justify-between"> <div className="flex-1 flex items-center justify-between">
<h4 className="text-balance"></h4> <h4 className="text-balance"></h4>
<p className="flex flex-col items-end"> <p className="flex flex-col items-end">
<span className="text-sm text-weak" ></span> <span className="text-sm text-weak" ></span>
<span className="text-sm">{props.long.resource_daily_free_sum}</span> <span className="text-sm">{props.long.resource_daily_free_sum}</span>
</p> </p>
</div> </div>
@@ -116,7 +116,7 @@ function Pins(props: DashboardPinsProps) {
<div className="flex-1 flex items-center justify-between"> <div className="flex-1 flex items-center justify-between">
<h4 className="text-balance"></h4> <h4 className="text-balance"></h4>
<p className="flex flex-col items-end"> <p className="flex flex-col items-end">
<span className="text-sm text-weak"></span> <span className="text-sm text-weak"></span>
<span className="text-sm">{props.long.resource_quota_sum}</span> <span className="text-sm">{props.long.resource_quota_sum}</span>
</p> </p>
</div> </div>
@@ -149,7 +149,7 @@ function Announcements(props: Props) {
return ( return (
<Card className="h-full"> <Card className="h-full">
<CardHeader> <CardHeader>
<div className="flex justify-between gap-2"> <div className="flex justify-between gap-2">
<CardTitle></CardTitle> <CardTitle></CardTitle>
{/* <Button {/* <Button
theme="text" theme="text"

View File

@@ -103,7 +103,7 @@ export default function IdentifyPage(props: IdentifyPageProps) {
return ( return (
<Page className="flex-row max-md:flex-col"> <Page className="flex-row max-md:flex-col">
<div className="flex-3/4 flex flex-col bg-white rounded-lg gap-16 max-md:gap-0"> <div className="flex-3/4 flex flex-col bg-white rounded-lg gap-16 max-md:gap-0">
{/* banner */} {/* banner */}
<section className="flex-none basis-40 relative flex flex-col gap-4 pl-8 pr-4 justify-center"> <section className="flex-none basis-40 relative flex flex-col gap-4 pl-8 pr-4 justify-center">

View File

@@ -47,14 +47,14 @@ export default function ProfilePage(props: ProfilePageProps) {
<Page className="lg:flex-row lg:items-stretch md:flex-col max-sm:flex-col"> <Page className="lg:flex-row lg:items-stretch md:flex-col max-sm:flex-col">
<div className="flex-3/4 flex flex-col gap-4"> <div className="flex-3/4 flex flex-col gap-4">
{/* banner */} {/* banner */}
<section className="flex-none relative rounded-lg p-16 pr-4 overflow-hidden flex max-sm:flex-col flex-col gap-4 pl-8 justify-center"> <section className="flex-none relative rounded-lg p-16 pr-4 overflow-hidden flex max-sm:flex-col flex-col gap-4 pl-8 justify-center">
<Image src={banner} alt="背景图" aria-hidden className="absolute inset-0 w-full h-full object-cover"/> <Image src={banner} alt="背景图" aria-hidden className="absolute inset-0 w-full h-full object-cover"/>
<h3 className="text-lg font-bold z-10 relative">HTTP邀请您参与</h3> <h3 className="text-lg font-bold z-10 relative">HTTP邀请您参与</h3>
<p className="text-sm text-gray-600 z-10 relative"></p> <p className="text-sm text-gray-600 z-10 relative"></p>
</section> </section>
{/* 块信息 */} {/* 块信息 */}
<div className="flex gap-4 max-md:flex-col max-sm:flex-col"> <div className="flex gap-4 max-md:flex-col max-sm:flex-col">
<Card className="flex-1 "> <Card className="flex-1 ">
<CardHeader> <CardHeader>
@@ -99,7 +99,7 @@ export default function ProfilePage(props: ProfilePageProps) {
</Card> </Card>
</div> </div>
<div className="flex-none rounded-lg bg-white p-4 flex max-sm:flex-col flex-col gap-8"> <div className="flex-none rounded-lg bg-white p-4 flex max-sm:flex-col flex-col gap-8">
{/* 安全信息 */} {/* 安全信息 */}
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
@@ -276,7 +276,7 @@ function BasicForm(props: {
<Input {...field} placeholder="请输入微信号" className="w-48 max-xl:w-full"/> <Input {...field} placeholder="请输入微信号" className="w-48 max-xl:w-full"/>
)} )}
</FormField> </FormField>
<div className="flex justify-end gap-4 md:col-span-2 justify-self-stretch"> <div className="flex justify-end gap-4 md:col-span-2 justify-self-stretch">
<Button <Button
theme="outline" theme="outline"
type="button" type="button"

View File

@@ -5,7 +5,6 @@ import './globals.css'
import localFont from 'next/font/local' import localFont from 'next/font/local'
import {Toaster} from '@/components/ui/sonner' import {Toaster} from '@/components/ui/sonner'
import StoresProvider from '@/components/stores-provider' import StoresProvider from '@/components/stores-provider'
import {getProfile} from '@/actions/auth'
import Effects from '@/app/effects' import Effects from '@/app/effects'
const font = localFont({ const font = localFont({
@@ -23,16 +22,11 @@ export default async function RootLayout({
}: Readonly<{ }: Readonly<{
children: ReactNode children: ReactNode
}>) { }>) {
const result = await getProfile()
const user = result.success ? result.data : null
return ( return (
<html lang="zh-CN"> <html lang="zh-CN">
<body className={`${font.className}`}> <body className={`${font.className}`}>
<StoresProvider user={user}> <StoresProvider>
<Effects> <Effects>{children}</Effects>
{children}
</Effects>
</StoresProvider> </StoresProvider>
<Toaster position="top-center" richColors expand/> <Toaster position="top-center" richColors expand/>
</body> </body>

View File

@@ -1,5 +1,4 @@
'use client' 'use client'
import {User} from '@/lib/models'
import {createContext, ReactNode, useContext, useEffect, useRef} from 'react' import {createContext, ReactNode, useContext, useEffect, useRef} from 'react'
import {StoreApi} from 'zustand/vanilla' import {StoreApi} from 'zustand/vanilla'
import {useStore} from 'zustand/react' import {useStore} from 'zustand/react'
@@ -38,16 +37,20 @@ export function useClientStore<T>(selector: (store: ClientStore) => T) {
} }
export type ProfileProviderProps = { export type ProfileProviderProps = {
user: User | null
children: ReactNode children: ReactNode
} }
export default function StoresProvider(props: ProfileProviderProps) { export default function StoresProvider(props: ProfileProviderProps) {
// 用户信息
const profile = useRef<StoreApi<ProfileStore>>(null) const profile = useRef<StoreApi<ProfileStore>>(null)
if (!profile.current) { if (!profile.current) {
console.log('📦 create profile store') console.log('📦 create profile store')
profile.current = createProfileStore(props.user) profile.current = createProfileStore()
} }
const profileStore = useRef(useStore(profile.current))
useEffect(() => {
profileStore.current.refreshProfile()
}, [])
const layout = useRef<StoreApi<LayoutStore>>(null) const layout = useRef<StoreApi<LayoutStore>>(null)
if (!layout.current) { if (!layout.current) {

View File

@@ -12,14 +12,12 @@ export type ProfileActions = {
refreshProfile: () => Promise<void> refreshProfile: () => Promise<void>
} }
export const createProfileStore = (init: User | null) => { export const createProfileStore = () => {
return createStore<ProfileStore>()(setState => ({ return createStore<ProfileStore>()(set => ({
profile: init, profile: null,
refreshProfile: async () => { refreshProfile: async () => {
const profile = await getProfile() const result = await getProfile()
setState({ set({profile: result.success ? result.data : null})
profile: profile.success ? profile.data : null,
})
}, },
})) }))
} }