登陆页面功能,优化项目结构,字体字重重新调整

This commit is contained in:
2025-03-19 12:59:25 +08:00
parent 705af45d2a
commit eaae095d0e
20 changed files with 194 additions and 561 deletions

View File

@@ -9,22 +9,27 @@
"lint": "next lint"
},
"dependencies": {
"@hookform/resolvers": "^4.1.3",
"next": "15.2.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-radio-group": "^1.2.3",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-slot": "^1.1.2",
"lucide-react": "^0.479.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.479.0",
"motion": "^12.5.0",
"next": "15.2.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"tailwind-merge": "^3.0.2",
"react-hook-form": "^7.54.2",
"@hookform/resolvers": "^4.1.3",
"zod": "^3.24.2",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.2"
"motion": "^12.5.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@@ -40,4 +45,4 @@
"typescript": "^5"
},
"packageManager": "pnpm@10.5.2+sha512.da9dc28cd3ff40d0592188235ab25d3202add8a207afbedc682220e4a0029ffbff4562102b9e6e46b4e3f9e8bd53e6d05de48544b0c57d4b0179e22c76d1199b"
}
}

32
pnpm-lock.yaml generated
View File

@@ -11,6 +11,9 @@ importers:
'@hookform/resolvers':
specifier: ^4.1.3
version: 4.1.3(react-hook-form@7.54.2(react@19.0.0))
'@radix-ui/react-checkbox':
specifier: ^1.1.4
version: 1.1.4(@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-label':
specifier: ^2.1.2
version: 2.1.2(@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)
@@ -384,6 +387,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-checkbox@1.1.4':
resolution: {integrity: sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==}
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-collection@1.1.2':
resolution: {integrity: sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==}
peerDependencies:
@@ -2321,6 +2337,22 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
'@radix-ui/react-checkbox@1.1.4(@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/primitive': 1.1.1
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-presence': 1.1.2(@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-primitive': 2.0.2(@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-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-use-size': 1.1.0(@types/react@19.0.10)(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-collection@1.1.2(@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-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)

BIN
public/login/bg.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -1,243 +0,0 @@
import {ReactNode} from 'react'
export type DashboardPageProps = {
}
export default function DashboardPage(props: DashboardPageProps) {
return (
<div className="flex gap-6">
{/* 左侧主要内容区域 */}
<div className="flex-1">
{/* 页面标题 */}
<div className="mb-8 bg-gradient-to-r from-blue-50 to-indigo-50 p-6 rounded-xl border border-blue-100">
<div className="flex items-center gap-4">
<div className="p-3 bg-blue-600 rounded-lg">
<svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
</div>
<div>
<h1 className="text-3xl font-bold text-gray-900">IP资源监控中心</h1>
<p className="mt-2 text-gray-600">IP资源使用情况和系统性能指标</p>
</div>
</div>
</div>
{/* 数据卡片网格 */}
<div className="grid grid-cols-3 gap-4 mb-6">
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow transition-shadow">
<div className="flex items-center">
<div className="p-3 bg-blue-100 rounded-lg">
<svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
</svg>
</div>
<div className="ml-4">
<h2 className="text-sm font-medium text-gray-600">IP数</h2>
<p className="text-2xl font-semibold text-gray-900">8,721</p>
<p className="text-sm text-green-600">12% </p>
</div>
</div>
</div>
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow transition-shadow">
<div className="flex items-center">
<div className="p-3 bg-green-100 rounded-lg">
<svg className="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<div className="ml-4">
<h2 className="text-sm font-medium text-gray-600">IP可用率</h2>
<p className="text-2xl font-semibold text-gray-900">99.9%</p>
<p className="text-sm text-green-600">SLA标准</p>
</div>
</div>
</div>
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow transition-shadow">
<div className="flex items-center">
<div className="p-3 bg-purple-100 rounded-lg">
<svg className="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<div className="ml-4">
<h2 className="text-sm font-medium text-gray-600"></h2>
<p className="text-2xl font-semibold text-gray-900">47ms</p>
<p className="text-sm text-green-600"></p>
</div>
</div>
</div>
</div>
{/* 监控指标区域 */}
<div className="grid grid-cols-2 gap-4 mb-6">
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow transition-shadow">
<h3 className="text-lg font-medium text-gray-900 mb-4">使</h3>
<div className="h-64 bg-gray-50 rounded-lg flex items-center justify-center">
使
</div>
<div className="mt-4 grid grid-cols-3 gap-4 text-center text-sm">
<div>
<p className="text-gray-600"></p>
<p className="font-semibold">1.2 Gbps</p>
</div>
<div>
<p className="text-gray-600"></p>
<p className="font-semibold">2.5 Gbps</p>
</div>
<div>
<p className="text-gray-600"></p>
<p className="font-semibold">800 Mbps</p>
</div>
</div>
</div>
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow transition-shadow">
<h3 className="text-lg font-medium text-gray-900 mb-4">IP地理分布</h3>
<div className="h-64 bg-gray-50 rounded-lg flex items-center justify-center">
</div>
<div className="mt-4 grid grid-cols-3 gap-4 text-center text-sm">
<div>
<p className="text-gray-600"></p>
<p className="font-semibold">45%</p>
</div>
<div>
<p className="text-gray-600"></p>
<p className="font-semibold">35%</p>
</div>
<div>
<p className="text-gray-600"></p>
<p className="font-semibold">20%</p>
</div>
</div>
</div>
</div>
{/* 告警信息 */}
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow transition-shadow">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-medium text-gray-900"></h3>
<span className="text-sm text-blue-600 cursor-pointer hover:text-blue-700"></span>
</div>
<div className="space-y-3">
<div className="flex items-center p-4 bg-red-50 rounded-lg">
<div className="p-2 bg-red-100 rounded-lg mr-4">
<svg className="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<div>
<h4 className="text-sm font-medium text-red-900">使</h4>
<p className="text-xs text-red-600 mt-1">5</p>
</div>
</div>
<div className="flex items-center p-4 bg-yellow-50 rounded-lg">
<div className="p-2 bg-yellow-100 rounded-lg mr-4">
<svg className="w-5 h-5 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<div>
<h4 className="text-sm font-medium text-yellow-900">IP资源池使用率达到80%</h4>
<p className="text-xs text-yellow-600 mt-1">20</p>
</div>
</div>
</div>
</div>
</div>
{/* 右侧边栏 */}
<div className="w-80 space-y-6">
{/* 用户信息卡片 */}
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow transition-shadow">
<div className="flex items-center space-x-4">
<div className="relative">
<div className="w-14 h-14 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center">
<span className="text-xl font-bold text-white">A</span>
</div>
<div className="absolute -bottom-1 -right-1 w-4 h-4 bg-green-500 rounded-full border-2 border-white"></div>
</div>
<div>
<h4 className="font-semibold text-gray-900"></h4>
<p className="text-sm text-gray-600">admin@example.com</p>
</div>
</div>
<div className="mt-6 pt-4 border-t border-gray-100">
<div className="grid grid-cols-2 gap-4 text-center">
<div className="bg-blue-50 p-3 rounded-lg">
<p className="text-sm font-medium text-gray-600">线</p>
<p className="text-lg font-semibold text-blue-600">4.5h</p>
</div>
<div className="bg-green-50 p-3 rounded-lg">
<p className="text-sm font-medium text-gray-600"></p>
<p className="text-lg font-semibold text-green-600">12</p>
</div>
</div>
</div>
</div>
{/* 快捷操作 */}
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow transition-shadow">
<h3 className="text-lg font-semibold text-gray-900 mb-4"></h3>
<div className="grid grid-cols-2 gap-3">
<button className="flex items-center justify-center space-x-2 bg-blue-50 text-blue-700 p-3 rounded-lg hover:bg-blue-100 transition-colors">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
<span></span>
</button>
<button className="flex items-center justify-center space-x-2 bg-purple-50 text-purple-700 p-3 rounded-lg hover:bg-purple-100 transition-colors">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
<span></span>
</button>
<button className="flex items-center justify-center space-x-2 bg-green-50 text-green-700 p-3 rounded-lg hover:bg-green-100 transition-colors">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<span></span>
</button>
<button className="flex items-center justify-center space-x-2 bg-orange-50 text-orange-700 p-3 rounded-lg hover:bg-orange-100 transition-colors">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span></span>
</button>
</div>
</div>
{/* 系统公告 */}
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow transition-shadow">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold text-gray-900"></h3>
<span className="text-sm text-blue-600 hover:text-blue-700 cursor-pointer"></span>
</div>
<div className="space-y-4">
<div className="flex items-start space-x-3">
<div className="w-2 h-2 mt-2 bg-red-500 rounded-full"></div>
<div>
<p className="text-sm text-gray-900">2</p>
<p className="text-xs text-gray-500 mt-1">2024-01-20 10:00</p>
</div>
</div>
<div className="flex items-start space-x-3">
<div className="w-2 h-2 mt-2 bg-blue-500 rounded-full"></div>
<div>
<p className="text-sm text-gray-900">IP资源管理系统V2.0</p>
<p className="text-xs text-gray-500 mt-1">2024-01-18 15:30</p>
</div>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@@ -1,198 +0,0 @@
import Link from 'next/link'
import {ReactNode} from 'react'
export type AdminLayoutProps = {
children: ReactNode
}
export default function AdminLayout(props: AdminLayoutProps) {
return (
<div className="min-w-[1200px] overflow-auto bg-gray-50 flex">
{/* 左侧导航栏 */}
<aside className="w-64 h-screen overflow-y-auto bg-white border-r border-gray-200">
{/* Logo区域 */}
<div className="h-16 flex items-center px-6">
<svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
<h1 className="ml-3 text-xl font-bold text-gray-900"></h1>
</div>
{/* 导航菜单 */}
<nav className="mt-4 px-4">
<div className="space-y-6">
{/* 概览 */}
<div>
<div className="px-3 mb-2 text-xs font-medium text-gray-500 uppercase tracking-wider"></div>
<Link
href="/dashboard"
className="flex items-center px-3 py-2 text-sm font-medium text-blue-600 bg-blue-50 rounded-md">
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
</svg>
</Link>
</div>
{/* 用户管理 */}
<div>
<div className="px-3 mb-2 text-xs font-medium text-gray-500 uppercase tracking-wider"></div>
<div className="space-y-1">
<a
href="/users"
className="flex items-center px-3 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-50">
<svg className="w-5 h-5 mr-2 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
</svg>
</a>
<a
href="/users/roles"
className="flex items-center px-3 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-50">
<svg className="w-5 h-5 mr-2 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/>
</svg>
</a>
</div>
</div>
{/* 订单管理 */}
<div>
<div className="px-3 mb-2 text-xs font-medium text-gray-500 uppercase tracking-wider"></div>
<div className="space-y-1">
<a
href="/orders"
className="flex items-center px-3 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-50">
<svg className="w-5 h-5 mr-2 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
</svg>
</a>
<a
href="/orders/refunds"
className="flex items-center px-3 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-50">
<svg className="w-5 h-5 mr-2 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M16 15v-1a4 4 0 00-4-4H8m0 0l3 3m-3-3l3-3m9 14V5a2 2 0 00-2-2H6a2 2 0 00-2 2v16l4-2 4 2 4-2 4 2z"/>
</svg>
退
</a>
</div>
</div>
{/* 系统设置 */}
<div>
<div className="px-3 mb-2 text-xs font-medium text-gray-500 uppercase tracking-wider"></div>
<a
href="/settings"
className="flex items-center px-3 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-50">
<svg className="w-5 h-5 mr-2 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path
strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
</a>
</div>
</div>
</nav>
</aside>
{/* 右侧主内容区 */}
<div className="flex-1 h-screen flex flex-col">
{/* 顶部导航栏 */}
<header className="h-16 bg-white border-b border-gray-200 px-6 flex items-center justify-between">
{/* 面包屑导航 */}
<div className="flex items-center">
<a href="/dashboard" className="text-gray-600 hover:text-blue-600"></a>
<svg className="w-4 h-4 mx-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7"/>
</svg>
<span className="text-gray-400"></span>
</div>
{/* 右侧工具栏 */}
<div className="flex items-center space-x-3">
{/* 搜索框 */}
<div className="w-56">
<div className="relative">
<input
type="text"
placeholder="搜索..."
className="w-full h-10 pl-10 pr-4 text-sm bg-gray-50 border border-gray-200 rounded-lg focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500"
/>
<svg
className="w-5 h-5 text-gray-400 absolute left-3 top-2.5" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path
strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg>
</div>
</div>
{/* 全屏切换 */}
<button
className="p-2 text-gray-500 hover:text-gray-600 hover:bg-gray-100 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"/>
</svg>
</button>
{/* 通知 */}
<button
className="relative p-2 text-gray-500 hover:text-gray-600 hover:bg-gray-100 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>
</svg>
<span className="absolute top-2 right-2 w-2 h-2 bg-red-500 rounded-full"></span>
</button>
{/* 用户菜单 */}
<div className="relative">
<button
className="flex items-center space-x-3 p-1.5 rounded-lg hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500">
<img
src="/images/avatar.jpg"
alt="用户头像"
className="w-9 h-9 rounded-full object-cover border-2 border-gray-200"
/>
<div className="text-left">
<div className="font-medium text-gray-800"></div>
<div className="text-gray-500 text-xs"></div>
</div>
<svg className="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7"/>
</svg>
</button>
</div>
</div>
</header>
{/* 主内容区域 */}
<main className={`flex-1 overflow-auto p-6`}>
{props.children}
</main>
</div>
</div>
)
}

View File

@@ -1,102 +1,101 @@
import {ReactNode} from 'react'
'use client'
import { ReactNode, useState } from 'react'
import Image from 'next/image'
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
export type LoginPageProps = {}
export default function LoginPage(props: LoginPageProps) {
return (
<div className="flex h-screen w-screen">
{/* 左侧背景图 */}
<div className="relative flex-1 hidden lg:block">
<img
src="/images/login-bg.jpg"
alt="登录背景"
className="object-cover w-full h-full"
/>
<div className="absolute inset-0 bg-black/30">
<div className="flex items-center justify-center h-full">
<h1 className="text-4xl font-bold text-white">
</h1>
</div>
</div>
</div>
const [countdown, setCountdown] = useState(0);
{/* 右侧登录表单 */}
<div className="w-[600px] flex items-center justify-center p-8">
const handleSendCode = () => {
// 这里实现发送验证码的逻辑
setCountdown(60);
const timer = setInterval(() => {
setCountdown((prev) => {
if (prev <= 1) {
clearInterval(timer);
return 0;
}
return prev - 1;
});
}, 1000);
};
return (
<main className="h-screen w-screen lg:pr-80 bg-[url(/login/bg.webp)] bg-cover bg-left flex justify-center lg:justify-end items-center">
{/* 登录表单 */}
<div className="w-96 mx-4 p-8 lg:p-12 bg-white rounded-lg flex items-center justify-center">
<div className="w-full space-y-8">
<div className="text-center">
<h2 className="text-3xl font-bold text-gray-900">
<h2 className="text-2xl text-gray-900">
/
</h2>
<p className="mt-2 text-sm text-gray-600">
{' '}
<a href="#" className="font-medium text-blue-600 hover:text-blue-500">
14
</a>
</p>
</div>
<form className="mt-8 space-y-6">
<div className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
<div className="space-y-2">
<Label htmlFor="phone"></Label>
<Input
id="phone"
name="phone"
type="tel"
placeholder="请输入手机号码"
autoComplete="tel"
required
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
</label>
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
/>
<div className="space-y-2">
<Label htmlFor="verificationCode"></Label>
<div className="flex space-x-2">
<Input
id="verificationCode"
name="verificationCode"
type="text"
placeholder="请输入验证码"
required
/>
<Button
type="button"
variant="outline"
className="whitespace-nowrap"
onClick={handleSendCode}
disabled={countdown > 0}
>
{countdown > 0 ? `${countdown}秒后重发` : '获取验证码'}
</Button>
</div>
</div>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center">
<input
id="remember-me"
name="remember-me"
type="checkbox"
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label htmlFor="remember-me" className="ml-2 block text-sm text-gray-900">
<div className="flex items-center space-x-2">
<Checkbox id="remember-me" name="remember-me" />
<label htmlFor="remember-me" className="text-sm text-gray-900">
</label>
</div>
<div className="text-sm">
<a href="#" className="font-medium text-blue-600 hover:text-blue-500">
</a>
</div>
</div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
</button>
<div className={`flex flex-col gap-2`}>
<Button type="submit" className="w-full">
/
</Button>
<p className="text-xs text-center text-gray-500">
<a href="#" className="text-blue-600 hover:text-blue-500"></a><a href="#" className="text-blue-600 hover:text-blue-500"></a>
</p>
</div>
</form>
</div>
</div>
</div>
</main>
)
}

View File

@@ -9,17 +9,17 @@ export default function Footer(props: FooterProps) {
<div className={`flex-auto overflow-hidden flex flex-wrap justify-between`}>
<div className="flex flex-col lg:items-center gap-2 lg:gap-6 max-lg:w-1/2">
<img src="/qrcode.svg" alt="logo" className="flex-none w-20 h-20 sm:w-44 sm:h-44 bg-gray-100"/>
<span className="text-sm font-light"></span>
<span className="text-sm "></span>
</div>
<div className={`flex flex-col gap-2 lg:gap-4 max-lg:w-1/2`}>
<h3></h3>
<p className={`text-sm text-gray-500 font-light`}>大客户经理:张经理</p>
<p className={`text-sm text-gray-500 font-light`}>/微信:18751847847</p>
<p className={`text-sm text-gray-500 font-light`}>QQ号:800180559</p>
<p className={`text-sm text-gray-500 `}>大客户经理:张经理</p>
<p className={`text-sm text-gray-500 `}>/微信:18751847847</p>
<p className={`text-sm text-gray-500 `}>QQ号:800180559</p>
<h3 className={`hidden sm:block`}></h3>
<p className={`text-sm text-gray-500 font-light hidden sm:block`}></p>
<p className={`text-sm text-gray-500 font-light 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>
<SiteNavList
@@ -79,14 +79,14 @@ export default function Footer(props: FooterProps) {
</div>
<div className={`flex-none mt-6 pt-6 border-t border-gray-300 flex flex-col items-center`}>
<p className={`text-sm font-light`}>
<p className={`text-sm `}>
HTTP仅提供代理IP服务使HTTP从事任何违法犯罪行为HTTP不承担任何法律责任
<a href="#"></a>
</p>
<p className={`text-sm mt-3 font-light`}>
<p className={`text-sm mt-3 `}>
577404-405</p>
<p className={`text-sm mt-3 font-light`}>
<p className={`text-sm mt-3 `}>
B1-11111111
ICP备111111111号-1
11111111111111
@@ -109,7 +109,7 @@ function SiteNavList(props: {
<h3>{props.title}</h3>
<ul
className={[
`mt-1 flex flex-wrap gap-2 font-light`,
`mt-1 flex flex-wrap gap-2 `,
`lg:mt-4 lg:h-[184px] lg:flex-col lg:gap-4 lg:gap-x-6`,
].join(' ')}>
{props.items.map((item, index) => (

View File

@@ -7,7 +7,7 @@ import SolutionMenu from './_client/solution'
import ProductMenu from './_client/product'
import HelpMenu from './_client/help'
import Wrap from '@/components/wrap'
import logo from '@/assets/logo.png'
import logo from '@/assets/logo.webp'
export type HeaderProps = {}
@@ -114,18 +114,22 @@ export default function Header(props: HeaderProps) {
</div>
{/* 登录 */}
<div className={`flex items-center`}>
<a
href="#"
className={`w-24 h-12 flex items-center justify-center lg:text-lg font-medium`}
<Link
href="/login"
className={`w-24 h-12 flex items-center justify-center lg:text-lg`}
>
<span></span>
</a>
<a
href="#"
className={`w-20 lg:w-24 h-10 lg:h-12 bg-gradient-to-r from-blue-500 to-cyan-400 rounded-sm flex items-center justify-center lg:text-lg font-medium text-white`}
</Link>
<Link
href="/login"
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`,
`transition-colors duration-200 ease-in-out`,
`from-blue-500 to-cyan-400 hover:from-blue-500 hover:to-cyan-300`,
].join(' ')}
>
<span></span>
</a>
</Link>
</div>
</Wrap>
</div>

View File

@@ -8,7 +8,7 @@ export type RootLayoutProps = {
export default function RootLayout(props: RootLayoutProps) {
return (
<div className={`overflow-auto flex flex-col items-stretch relative`}>
<div className={`overflow-auto bg-white flex flex-col items-stretch relative`}>
{/* 页头 */}
<Header/>

View File

@@ -9,28 +9,28 @@ export default function Home() {
{/* banner */}
<section className={`w-full bg-[url('/banner.webp')] bg-cover bg-[center_right_40%]`}>
<Wrap className={`pt-64 pb-48 max-md:pt-32 max-md:pb-24`}>
<h1 className={`text-4xl font-medium`}></h1>
<h1 className={`text-4xl`}></h1>
<p className={`mt-10 text-gray-500`}>IP代理服务</p>
<div className={`mt-24 max-md:mt-14 flex gap-8 max-md:flex-col`}>
<p className={`flex gap-4 items-center`}>
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
<span className={`lg:text-lg font-light`}>200+</span>
<span className={`lg:text-lg `}>200+</span>
</p>
<p className={`flex gap-4 items-center`}>
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
<span className={`lg:text-lg font-light`}>300+</span>
<span className={`lg:text-lg `}>300+</span>
</p>
<p className={`flex gap-4 items-center`}>
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
<span className={`lg:text-lg font-light`}>&</span>
<span className={`lg:text-lg `}>&</span>
</p>
</div>
<button
className={[
`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 font-medium`,
`bg-gradient-to-r from-blue-500 to-cyan-400 text-white text-xl lg:text-4xl`,
].join(' ')}>
</button>
@@ -129,7 +129,7 @@ export default function Home() {
<img src="/s3-main.webp" alt="tumb" className={`w-2/3 md:flex-1 md:w-0 object-cover max-md:self-center`}/>
<div className={`flex-2 flex flex-col justify-between gap-4`}>
<h3 className={`flex justify-between`}>
<span className={`text-xl font-medium`}></span>
<span className={`text-xl`}></span>
<sub className={`text-sm text-gray-500`}>2025-03-04</sub>
</h3>
<p className={`text-gray-400 md:leading-12`}>
@@ -161,7 +161,7 @@ function Section(props: {
return (
<section>
<div className={`max-w-[1232px] mx-auto px-4 flex flex-col items-stretch`}>
<h2 className={`text-center text-3xl font-medium mb-8 lg:mb-24`}>{props.title}</h2>
<h2 className={`text-center text-3xl mb-8 lg:mb-24`}>{props.title}</h2>
{props.children}
</div>
</section>
@@ -183,7 +183,7 @@ function Sec3Item(props: {
`max-md:items-center`,
].join(' ')}>
<img src={`/${props.icon}.webp`} alt={`s1-1`} aria-hidden className="w-44 h-44 object-cover"/>
<h3 className={`text-xl font-medium`}>{props.title}</h3>
<h3 className={`text-xl`}>{props.title}</h3>
<div className={`flex flex-col gap-3`}>
{props.terms.map((item, index) => {
return (
@@ -207,7 +207,7 @@ function Sec4Item(props: {
<li className={`flex gap-8 items-center p-4 lg:p-8 shadow-[4px_4px_20px_4px] shadow-blue-50 rounded-lg`}>
<img src={`/${props.icon}.webp`} alt={`s2-1-1`} aria-hidden className="w-24 h-24 object-contain"/>
<div className={`flex flex-col gap-3`}>
<h3 className={`text-xl font-medium`}>{props.title}</h3>
<h3 className={`text-xl`}>{props.title}</h3>
<p>{props.description}</p>
</div>
</li>

View File

@@ -3,8 +3,6 @@ import {Metadata} from 'next'
import './globals.css'
import localFont from 'next/font/local'
const font = localFont({src: './SourceHanSansSC-VF.otf.woff2'})
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
@@ -17,7 +15,7 @@ export default function RootLayout({
}>) {
return (
<html lang="zh-Cn">
<body className={`${font.className} bg-blue-50`}>
<body className={`bg-blue-50`}>
{children}
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

BIN
src/assets/logo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -49,7 +49,7 @@ export default function BreadCrumb({
</Link>
) : (
<span
className="text-gray-900 font-medium"
className="text-gray-900"
onClick={item.onClick}
>
{item.label}

View File

@@ -5,7 +5,12 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
"inline-flex items-center justify-center gap-2 " +
"whitespace-nowrap rounded-md text-sm transition-all " +
"disabled:pointer-events-none disabled:opacity-50 " +
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 " +
"outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] " +
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {

View File

@@ -0,0 +1,32 @@
"use client"
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { CheckIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function Checkbox({
className,
...props
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
return (
<CheckboxPrimitive.Root
data-slot="checkbox"
className={cn(
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
data-slot="checkbox-indicator"
className="flex items-center justify-center text-current transition-none"
>
<CheckIcon className="size-3.5" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
)
}
export { Checkbox }

View File

@@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
type={type}
data-slot="input"
className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className

View File

@@ -13,7 +13,7 @@ function Label({
<LabelPrimitive.Root
data-slot="label"
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
"flex items-center gap-2 text-sm leading-none select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className
)}
{...props}

View File

@@ -92,7 +92,7 @@ function SelectLabel({
return (
<SelectPrimitive.Label
data-slot="select-label"
className={cn("px-2 py-1.5 text-sm font-medium", className)}
className={cn("px-2 py-1.5 text-sm", className)}
{...props}
/>
)

View File

@@ -1 +0,0 @@