修复了登录页面还存在顶部导航栏的问题
This commit is contained in:
@@ -12,18 +12,23 @@ const centralConfigData = {
|
||||
icon: "🏢",
|
||||
items: [
|
||||
{
|
||||
title: "租户创建管理",
|
||||
url: "/central-config/tenant-management/tenant-creation",
|
||||
title: "企业审核",
|
||||
url: "/central-config/tenant-management/enterprise-audit",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "租户配置管理",
|
||||
url: "/central-config/tenant-management/tenant-configuration",
|
||||
title: "审核历史",
|
||||
url: "/central-config/tenant-management/audit-history",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "租户授权管理",
|
||||
url: "/central-config/tenant-management/tenant-authorization",
|
||||
title: "企业信息",
|
||||
url: "/central-config/tenant-management/enterprise-info",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "平台用户管理",
|
||||
url: "/central-config/tenant-management/platform-user-management",
|
||||
isActive: false
|
||||
}
|
||||
]
|
||||
@@ -34,18 +39,23 @@ const centralConfigData = {
|
||||
icon: "👥",
|
||||
items: [
|
||||
{
|
||||
title: "用户账号管理",
|
||||
url: "/central-config/user-management/user-account-management",
|
||||
title: "员工管理",
|
||||
url: "/central-config/user-management/employee-management",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "角色权限管理",
|
||||
url: "/central-config/user-management/role-permission-management",
|
||||
title: "角色管理",
|
||||
url: "/central-config/user-management/role-management",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "用户行为跟踪",
|
||||
url: "/central-config/user-management/user-behavior-tracking",
|
||||
title: "菜单管理",
|
||||
url: "/central-config/user-management/menu-management",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "权限配置管理",
|
||||
url: "/central-config/user-management/permission-config",
|
||||
isActive: false
|
||||
}
|
||||
]
|
||||
@@ -56,40 +66,45 @@ const centralConfigData = {
|
||||
icon: "🔧",
|
||||
items: [
|
||||
{
|
||||
title: "基础配置管理",
|
||||
url: "/central-config/system-parameters/basic-configuration",
|
||||
title: "系统设置",
|
||||
url: "/central-config/system-parameters/system-settings",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "业务规则设置",
|
||||
url: "/central-config/system-parameters/business-rule-settings",
|
||||
title: "分类字典",
|
||||
url: "/central-config/system-parameters/category-dictionary",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "接口配置管理",
|
||||
url: "/central-config/system-parameters/interface-configuration",
|
||||
title: "数据字典",
|
||||
url: "/central-config/system-parameters/data-dictionary",
|
||||
isActive: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "系统监控",
|
||||
url: "/central-config/system-monitoring",
|
||||
url: "/central-config/system-monitor",
|
||||
icon: "📈",
|
||||
items: [
|
||||
{
|
||||
title: "性能监控管理",
|
||||
url: "/central-config/system-monitoring/performance-monitoring",
|
||||
title: "登录日志",
|
||||
url: "/central-config/system-monitor/login-log",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "日志管理",
|
||||
url: "/central-config/system-monitoring/log-management",
|
||||
title: "操作日志",
|
||||
url: "/central-config/system-monitor/operation-log",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "异常处理管理",
|
||||
url: "/central-config/system-monitoring/exception-handling",
|
||||
title: "性能监控",
|
||||
url: "/central-config/system-monitor/performance-monitor",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "网络日志",
|
||||
url: "/central-config/system-monitor/network-log",
|
||||
isActive: false
|
||||
}
|
||||
]
|
||||
@@ -99,24 +114,19 @@ const centralConfigData = {
|
||||
url: "/central-config/message-center",
|
||||
icon: "📨",
|
||||
items: [
|
||||
{
|
||||
title: "消息推送管理",
|
||||
url: "/central-config/message-center/message-push-management",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "消息发送",
|
||||
url: "/central-config/message-center/message-send",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "通知设置管理",
|
||||
url: "/central-config/message-center/notification-settings",
|
||||
title: "消息模版",
|
||||
url: "/central-config/message-center/message-template",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "反馈管理",
|
||||
url: "/central-config/message-center/feedback-management",
|
||||
title: "消息日志",
|
||||
url: "/central-config/message-center/message-log",
|
||||
isActive: false
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function MessageLogPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">消息日志</h1>
|
||||
<p>消息日志管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function MessageTemplatePage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">消息模版</h1>
|
||||
<p>消息模版管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export default function SystemMonitorLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<div className="system-monitor-layout">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function LoginLogPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">登录日志</h1>
|
||||
<p>登录日志管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function NetworkLogPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">网络日志</h1>
|
||||
<p>网络日志管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function OperationLogPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">操作日志</h1>
|
||||
<p>操作日志管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function SystemMonitorPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">系统监控</h1>
|
||||
<p>系统监控主页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function PerformanceMonitorPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">性能监控</h1>
|
||||
<p>性能监控管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function CategoryDictionaryPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">分类字典</h1>
|
||||
<p>分类字典管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function DataDictionaryPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">数据字典</h1>
|
||||
<p>数据字典管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function SystemSettingsPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">系统设置</h1>
|
||||
<p>系统设置管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function AuditHistoryPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">审核历史</h1>
|
||||
<p>审核历史记录管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
'use client'
|
||||
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Enterprise, AuditStatus } from '@/types/user-management'
|
||||
|
||||
interface AuditStatsCardProps {
|
||||
enterprises: Enterprise[]
|
||||
}
|
||||
|
||||
export function AuditStatsCard({ enterprises }: AuditStatsCardProps) {
|
||||
const stats = [
|
||||
{
|
||||
label: '待审核',
|
||||
value: enterprises.filter(e => e.auditStatus === 'pending').length,
|
||||
color: 'text-yellow-600',
|
||||
bg: 'bg-yellow-100',
|
||||
},
|
||||
{
|
||||
label: '已通过',
|
||||
value: enterprises.filter(e => e.auditStatus === 'approved').length,
|
||||
color: 'text-green-600',
|
||||
bg: 'bg-green-100',
|
||||
},
|
||||
{
|
||||
label: '已驳回',
|
||||
value: enterprises.filter(e => e.auditStatus === 'rejected').length,
|
||||
color: 'text-red-600',
|
||||
bg: 'bg-red-100',
|
||||
},
|
||||
{
|
||||
label: '总企业数',
|
||||
value: enterprises.length,
|
||||
color: 'text-blue-600',
|
||||
bg: 'bg-blue-100',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
{stats.map((stat, index) => (
|
||||
<Card key={index} className="p-4">
|
||||
<div className="text-sm text-muted-foreground">{stat.label}</div>
|
||||
<div className={`mt-2 ${stat.color}`}>{stat.value}</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
'use client'
|
||||
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Image as ImageIcon } from 'lucide-react'
|
||||
import { Enterprise } from '@/types/user-management'
|
||||
|
||||
interface EnterpriseBankInfoProps {
|
||||
enterprise: Enterprise
|
||||
}
|
||||
|
||||
export function EnterpriseBankInfo({ enterprise }: EnterpriseBankInfoProps) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<div>
|
||||
<Label>银行账号</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.bankAccount ? (
|
||||
<code className="text-sm font-mono">
|
||||
{enterprise.bankAccount}
|
||||
</code>
|
||||
) : '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>开户行</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.bankName || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Label>开户行全称</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.bankFullName || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Label>开户行地址</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.bankAddress || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Label>开户许可证</Label>
|
||||
<div className="mt-2">
|
||||
{enterprise.bankLicense ? (
|
||||
<div className="border rounded-lg p-2 inline-block">
|
||||
<img
|
||||
src={enterprise.bankLicense}
|
||||
alt="开户许可证"
|
||||
className="w-64 h-auto"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 text-muted-foreground p-4 border-2 border-dashed rounded-lg">
|
||||
<ImageIcon className="w-6 h-6" />
|
||||
<span>未上传</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
'use client'
|
||||
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Enterprise } from '@/types/user-management'
|
||||
|
||||
interface EnterpriseBasicInfoProps {
|
||||
enterprise: Enterprise
|
||||
}
|
||||
|
||||
export function EnterpriseBasicInfo({ enterprise }: EnterpriseBasicInfoProps) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<div>
|
||||
<Label>企业名称</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">{enterprise.name}</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>企业类型</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">{enterprise.type}</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>所在地区</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.province} {enterprise.city} {enterprise.district}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>详细地址</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">{enterprise.address}</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>登记人</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">{enterprise.registrant}</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>联系电话</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">{enterprise.contactPhone}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Building, FileText, CreditCard, User, CheckCircle, XCircle } from 'lucide-react'
|
||||
import { Enterprise, AuditStatus } from '@/types/user-management'
|
||||
import { EnterpriseBasicInfo } from './EnterpriseBasicInfo'
|
||||
import { EnterpriseOtherInfo } from './EnterpriseOtherInfo'
|
||||
import { EnterpriseBankInfo } from './EnterpriseBankInfo'
|
||||
import { EnterpriseLegalInfo } from './EnterpriseLegalInfo'
|
||||
|
||||
interface EnterpriseDetailDialogProps {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
enterprise: Enterprise | null
|
||||
onApprove: (auditReason: string) => void
|
||||
onReject: (auditReason: string) => void
|
||||
}
|
||||
|
||||
export function EnterpriseDetailDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
enterprise,
|
||||
onApprove,
|
||||
onReject
|
||||
}: EnterpriseDetailDialogProps) {
|
||||
const [auditReason, setAuditReason] = useState('')
|
||||
|
||||
const getAuditStatusBadge = (status: AuditStatus) => {
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
return <Badge className="bg-yellow-100 text-yellow-700">待审核</Badge>
|
||||
case 'approved':
|
||||
return <Badge className="bg-green-100 text-green-700">已通过</Badge>
|
||||
case 'rejected':
|
||||
return <Badge className="bg-red-100 text-red-700">已驳回</Badge>
|
||||
default:
|
||||
return <Badge>{status}</Badge>
|
||||
}
|
||||
}
|
||||
|
||||
const handleApprove = () => {
|
||||
onApprove(auditReason)
|
||||
setAuditReason('')
|
||||
}
|
||||
|
||||
const handleReject = () => {
|
||||
onReject(auditReason)
|
||||
setAuditReason('')
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
onOpenChange(false)
|
||||
setAuditReason('')
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-5xl max-h-[90vh]">
|
||||
<DialogHeader>
|
||||
<div className="flex items-center justify-between pr-8">
|
||||
<DialogTitle>企业详情审核</DialogTitle>
|
||||
{enterprise && getAuditStatusBadge(enterprise.auditStatus)}
|
||||
</div>
|
||||
<DialogDescription className="sr-only">
|
||||
查看企业的详细信息和审核状态
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{enterprise && (
|
||||
<ScrollArea className="max-h-[calc(90vh-200px)]">
|
||||
<Tabs defaultValue="basic" className="space-y-4">
|
||||
<TabsList className="grid grid-cols-4 w-full">
|
||||
<TabsTrigger value="basic">
|
||||
<Building className="w-4 h-4 mr-2" />
|
||||
基本信息
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="other">
|
||||
<FileText className="w-4 h-4 mr-2" />
|
||||
其他信息
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="bank">
|
||||
<CreditCard className="w-4 h-4 mr-2" />
|
||||
开户信息
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="legal">
|
||||
<User className="w-4 h-4 mr-2" />
|
||||
法人信息
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="basic">
|
||||
<EnterpriseBasicInfo enterprise={enterprise} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="other">
|
||||
<EnterpriseOtherInfo enterprise={enterprise} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="bank">
|
||||
<EnterpriseBankInfo enterprise={enterprise} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="legal">
|
||||
<EnterpriseLegalInfo enterprise={enterprise} />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
{/* 审核信息 */}
|
||||
<div className="mt-6 pt-6 border-t">
|
||||
<h4 className="mb-4 font-bold">审核信息</h4>
|
||||
<Card className="p-6 bg-gray-50 border">
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
|
||||
<div>
|
||||
<Label className="text-xs">提交时间</Label>
|
||||
<div className="mt-1.5 text-base">
|
||||
{new Date(enterprise.createdAt).toLocaleString('zh-CN')}
|
||||
</div>
|
||||
</div>
|
||||
{enterprise.auditTime && (
|
||||
<div>
|
||||
<Label className="text-xs">审核时间</Label>
|
||||
<div className="mt-1.5 text-base">
|
||||
{new Date(enterprise.auditTime).toLocaleString('zh-CN')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{enterprise.auditor && (
|
||||
<div>
|
||||
<Label className="text-xs">审核人</Label>
|
||||
<div className="mt-1.5 text-base">
|
||||
{enterprise.auditor}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{enterprise.auditReason && (
|
||||
<div className="col-span-2 pt-4 mt-2 border-t">
|
||||
<Label className="text-xs">审核意见</Label>
|
||||
<div className="mt-1.5 text-base">
|
||||
{enterprise.auditReason}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 审核操作区 - 仅待审核状态显示 */}
|
||||
{enterprise.auditStatus === 'pending' && (
|
||||
<div className="mt-6">
|
||||
<Label>审核意见</Label>
|
||||
<Textarea
|
||||
value={auditReason}
|
||||
onChange={(e) => setAuditReason(e.target.value)}
|
||||
rows={3}
|
||||
placeholder="请填写审核意见(驳回时必填)..."
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
)}
|
||||
<DialogFooter className="border-t pt-4">
|
||||
<Button variant="outline" onClick={handleClose}>
|
||||
关闭
|
||||
</Button>
|
||||
{enterprise?.auditStatus === 'pending' && (
|
||||
<>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleReject}
|
||||
>
|
||||
<XCircle className="w-4 h-4 mr-2" />
|
||||
驳回
|
||||
</Button>
|
||||
<Button onClick={handleApprove}>
|
||||
<CheckCircle className="w-4 h-4 mr-2" />
|
||||
通过
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
'use client'
|
||||
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Image as ImageIcon } from 'lucide-react'
|
||||
import { Enterprise } from '@/types/user-management'
|
||||
|
||||
interface EnterpriseLegalInfoProps {
|
||||
enterprise: Enterprise
|
||||
}
|
||||
|
||||
export function EnterpriseLegalInfo({ enterprise }: EnterpriseLegalInfoProps) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
<div>
|
||||
<Label>法人名称</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.legalPerson || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>身份证正面</Label>
|
||||
<div className="mt-2">
|
||||
{enterprise.idCardFront ? (
|
||||
<div className="border rounded-lg p-2 inline-block">
|
||||
<img
|
||||
src={enterprise.idCardFront}
|
||||
alt="身份证正面"
|
||||
className="w-80 h-auto"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 text-muted-foreground p-4 border-2 border-dashed rounded-lg">
|
||||
<ImageIcon className="w-6 h-6" />
|
||||
<span>未上传</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>身份证反面</Label>
|
||||
<div className="mt-2">
|
||||
{enterprise.idCardBack ? (
|
||||
<div className="border rounded-lg p-2 inline-block">
|
||||
<img
|
||||
src={enterprise.idCardBack}
|
||||
alt="身份证反面"
|
||||
className="w-80 h-auto"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 text-muted-foreground p-4 border-2 border-dashed rounded-lg">
|
||||
<ImageIcon className="w-6 h-6" />
|
||||
<span>未上传</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
'use client'
|
||||
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Image as ImageIcon } from 'lucide-react'
|
||||
import { Enterprise } from '@/types/user-management'
|
||||
|
||||
interface EnterpriseOtherInfoProps {
|
||||
enterprise: Enterprise
|
||||
}
|
||||
|
||||
export function EnterpriseOtherInfo({ enterprise }: EnterpriseOtherInfoProps) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<div>
|
||||
<Label>公司规模</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.companySize || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>注册资本</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.registeredCapital || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>成立时间</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.establishmentDate || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>发票类型</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.invoiceType || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Label>社会信用代码</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
<code className="text-sm font-mono">
|
||||
{enterprise.socialCreditCode}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Label>经营范围</Label>
|
||||
<div className="field-value mt-1 p-2 bg-gray-50 rounded">
|
||||
{enterprise.businessScope || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Label>营业执照</Label>
|
||||
<div className="mt-2">
|
||||
{enterprise.businessLicense ? (
|
||||
<div className="border rounded-lg p-2 inline-block">
|
||||
<img
|
||||
src={enterprise.businessLicense}
|
||||
alt="营业执照"
|
||||
className="w-64 h-auto"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 text-muted-foreground p-4 border-2 border-dashed rounded-lg">
|
||||
<ImageIcon className="w-6 h-6" />
|
||||
<span>未上传</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
'use client'
|
||||
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
import { Eye } from 'lucide-react'
|
||||
import { Enterprise, AuditStatus } from '@/types/user-management'
|
||||
|
||||
interface EnterpriseTableProps {
|
||||
enterprises: Enterprise[]
|
||||
onViewDetail: (enterprise: Enterprise) => void
|
||||
}
|
||||
|
||||
export function EnterpriseTable({ enterprises, onViewDetail }: EnterpriseTableProps) {
|
||||
const getAuditStatusBadge = (status: AuditStatus) => {
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
return <Badge className="bg-yellow-100 text-yellow-700">待审核</Badge>
|
||||
case 'approved':
|
||||
return <Badge className="bg-green-100 text-green-700">已通过</Badge>
|
||||
case 'rejected':
|
||||
return <Badge className="bg-red-100 text-red-700">已驳回</Badge>
|
||||
default:
|
||||
return <Badge>{status}</Badge>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>企业名称</TableHead>
|
||||
<TableHead>企业类型</TableHead>
|
||||
<TableHead>社会信用代码</TableHead>
|
||||
<TableHead>法人</TableHead>
|
||||
<TableHead>所在地区</TableHead>
|
||||
<TableHead>审核状态</TableHead>
|
||||
<TableHead>提交时间</TableHead>
|
||||
<TableHead>操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{enterprises.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} className="text-center text-muted-foreground py-8">
|
||||
暂无数据
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
enterprises.map((enterprise) => (
|
||||
<TableRow key={enterprise.id}>
|
||||
<TableCell>{enterprise.name}</TableCell>
|
||||
<TableCell className="text-muted-foreground">{enterprise.type}</TableCell>
|
||||
<TableCell className="text-muted-foreground">
|
||||
<code className="text-xs">{enterprise.socialCreditCode}</code>
|
||||
</TableCell>
|
||||
<TableCell>{enterprise.legalPerson || '-'}</TableCell>
|
||||
<TableCell>{`${enterprise.province} ${enterprise.city}`}</TableCell>
|
||||
<TableCell>{getAuditStatusBadge(enterprise.auditStatus)}</TableCell>
|
||||
<TableCell className="text-muted-foreground">
|
||||
{new Date(enterprise.createdAt).toLocaleDateString('zh-CN')}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onViewDetail(enterprise)}
|
||||
>
|
||||
<Eye className="w-4 h-4 mr-1" />
|
||||
查看
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client'
|
||||
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Search } from 'lucide-react'
|
||||
|
||||
interface SearchAndFilterProps {
|
||||
searchKeyword: string
|
||||
onSearchChange: (value: string) => void
|
||||
statusFilter: string
|
||||
onStatusFilterChange: (value: string) => void
|
||||
}
|
||||
|
||||
export function SearchAndFilter({
|
||||
searchKeyword,
|
||||
onSearchChange,
|
||||
statusFilter,
|
||||
onStatusFilterChange
|
||||
}: SearchAndFilterProps) {
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="搜索企业名称、信用代码、登记人..."
|
||||
value={searchKeyword}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Select value={statusFilter} onValueChange={onStatusFilterChange}>
|
||||
<SelectTrigger className="w-40">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">全部状态</SelectItem>
|
||||
<SelectItem value="pending">待审核</SelectItem>
|
||||
<SelectItem value="approved">已通过</SelectItem>
|
||||
<SelectItem value="rejected">已驳回</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export { AuditStatsCard } from './AuditStatsCard'
|
||||
export { SearchAndFilter } from './SearchAndFilter'
|
||||
export { EnterpriseTable } from './EnterpriseTable'
|
||||
export { EnterpriseDetailDialog } from './EnterpriseDetailDialog'
|
||||
export { EnterpriseBasicInfo } from './EnterpriseBasicInfo'
|
||||
export { EnterpriseOtherInfo } from './EnterpriseOtherInfo'
|
||||
export { EnterpriseBankInfo } from './EnterpriseBankInfo'
|
||||
export { EnterpriseLegalInfo } from './EnterpriseLegalInfo'
|
||||
@@ -0,0 +1,255 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
AuditStatsCard,
|
||||
SearchAndFilter,
|
||||
EnterpriseTable,
|
||||
EnterpriseDetailDialog
|
||||
} from './components'
|
||||
import { Enterprise, AuditStatus } from '@/types/user-management'
|
||||
|
||||
export default function EnterpriseAuditPage() {
|
||||
const [enterprises, setEnterprises] = useState<Enterprise[]>([])
|
||||
const [searchKeyword, setSearchKeyword] = useState('')
|
||||
const [statusFilter, setStatusFilter] = useState<string>('all')
|
||||
const [showDetailDialog, setShowDetailDialog] = useState(false)
|
||||
const [selectedEnterprise, setSelectedEnterprise] = useState<Enterprise | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
loadEnterprises()
|
||||
}, [])
|
||||
|
||||
const loadEnterprises = () => {
|
||||
const data = localStorage.getItem('smart_agriculture_enterprises')
|
||||
if (data) {
|
||||
setEnterprises(JSON.parse(data))
|
||||
} else {
|
||||
// 初始化示例数据
|
||||
const mockEnterprises: Enterprise[] = [
|
||||
{
|
||||
id: 'ent-1',
|
||||
name: '绿野农业科技有限公司',
|
||||
type: '有限责任公司',
|
||||
province: '北京市',
|
||||
city: '海淀区',
|
||||
district: '中关村街道',
|
||||
companySize: '50-200人',
|
||||
registeredCapital: '1000万元',
|
||||
establishmentDate: '2020-03-15',
|
||||
invoiceType: '增值税专用发票',
|
||||
socialCreditCode: '91110000123456789X',
|
||||
businessScope: '农业技术开发、技术咨询、技术服务;销售机械设备、电子产品。',
|
||||
bankAccount: '1234567890123456789',
|
||||
bankName: '中国工商银行',
|
||||
bankFullName: '中国工商银行股份有限公司北京中关村支行',
|
||||
bankAddress: '北京市海淀区中关村大街1号',
|
||||
legalPerson: '张伟',
|
||||
registrant: '张经理',
|
||||
contactPhone: '13800138001',
|
||||
address: '北京市海淀区中关村大街1号科技大厦',
|
||||
status: 'active',
|
||||
auditStatus: 'pending',
|
||||
createdAt: '2024-10-10T08:00:00',
|
||||
updatedAt: '2024-10-10T08:00:00',
|
||||
},
|
||||
{
|
||||
id: 'ent-2',
|
||||
name: '丰收现代农业集团',
|
||||
type: '股份有限公司',
|
||||
province: '江苏省',
|
||||
city: '南京市',
|
||||
district: '江宁区',
|
||||
companySize: '200-500人',
|
||||
registeredCapital: '5000万元',
|
||||
establishmentDate: '2018-06-20',
|
||||
invoiceType: '增值税专用发票',
|
||||
socialCreditCode: '91320000987654321Y',
|
||||
businessScope: '现代农业种植、农产品加工与销售、农业技术推广服务。',
|
||||
bankAccount: '9876543210987654321',
|
||||
bankName: '中国农业银行',
|
||||
bankFullName: '中国农业银行股份有限公司南京江宁支行',
|
||||
bankAddress: '江苏省南京市江宁区农业大道88号',
|
||||
legalPerson: '李明',
|
||||
registrant: '李总',
|
||||
contactPhone: '13900139002',
|
||||
address: '江苏省南京市江宁区农业大道88号',
|
||||
status: 'active',
|
||||
auditStatus: 'approved',
|
||||
auditTime: '2024-10-08T14:30:00',
|
||||
auditor: '系统管理员',
|
||||
createdAt: '2024-10-05T10:00:00',
|
||||
updatedAt: '2024-10-08T14:30:00',
|
||||
},
|
||||
{
|
||||
id: 'ent-3',
|
||||
name: '金穗农机服务中心',
|
||||
type: '个人独资企业',
|
||||
province: '山东省',
|
||||
city: '济南市',
|
||||
district: '历城区',
|
||||
companySize: '1-50人',
|
||||
registeredCapital: '200万元',
|
||||
establishmentDate: '2021-09-10',
|
||||
invoiceType: '增值税普通发票',
|
||||
socialCreditCode: '91370000456789012Z',
|
||||
businessScope: '农业机械租赁、维修服务、农机作业服务。',
|
||||
bankAccount: '5555666677778888',
|
||||
bankName: '中国建设银行',
|
||||
bankFullName: '中国建设银行股份有限公司济南历城支行',
|
||||
bankAddress: '山东省济南市历城区农机路66号',
|
||||
legalPerson: '王刚',
|
||||
registrant: '王主任',
|
||||
contactPhone: '13700137003',
|
||||
address: '山东省济南市历城区农机路66号',
|
||||
status: 'inactive',
|
||||
auditStatus: 'rejected',
|
||||
auditReason: '资质材料不完整,请补充营业执照副本和法人身份证复印件',
|
||||
auditTime: '2024-10-09T16:00:00',
|
||||
auditor: '系统管理员',
|
||||
createdAt: '2024-10-06T09:00:00',
|
||||
updatedAt: '2024-10-09T16:00:00',
|
||||
},
|
||||
];
|
||||
localStorage.setItem('smart_agriculture_enterprises', JSON.stringify(mockEnterprises));
|
||||
setEnterprises(mockEnterprises);
|
||||
}
|
||||
};
|
||||
|
||||
const filteredEnterprises = enterprises.filter(ent => {
|
||||
const matchKeyword = !searchKeyword ||
|
||||
ent.name.includes(searchKeyword) ||
|
||||
ent.socialCreditCode.includes(searchKeyword) ||
|
||||
ent.registrant.includes(searchKeyword);
|
||||
|
||||
const matchStatus = statusFilter === 'all' || ent.auditStatus === statusFilter;
|
||||
|
||||
return matchKeyword && matchStatus;
|
||||
});
|
||||
|
||||
const handleViewDetail = (enterprise: Enterprise) => {
|
||||
setSelectedEnterprise(enterprise);
|
||||
setShowDetailDialog(true);
|
||||
};
|
||||
|
||||
const handleApprove = (auditReason: string) => {
|
||||
if (!selectedEnterprise) return;
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const updated = enterprises.map(ent =>
|
||||
ent.id === selectedEnterprise.id
|
||||
? {
|
||||
...ent,
|
||||
auditStatus: 'approved' as AuditStatus,
|
||||
status: 'active' as const,
|
||||
auditTime: now,
|
||||
auditor: '系统管理员',
|
||||
auditReason: auditReason || undefined,
|
||||
updatedAt: now,
|
||||
}
|
||||
: ent
|
||||
);
|
||||
|
||||
// 创建审核历史记录
|
||||
const auditRecords = JSON.parse(localStorage.getItem('smart_agriculture_audit_records') || '[]');
|
||||
const newRecord = {
|
||||
id: `audit-${Date.now()}`,
|
||||
enterpriseId: selectedEnterprise.id,
|
||||
enterpriseName: selectedEnterprise.name,
|
||||
auditType: 'register',
|
||||
submitTime: selectedEnterprise.createdAt,
|
||||
auditTime: now,
|
||||
auditor: '系统管理员',
|
||||
result: 'approved',
|
||||
remarks: auditReason || '审核通过',
|
||||
};
|
||||
auditRecords.push(newRecord);
|
||||
localStorage.setItem('smart_agriculture_audit_records', JSON.stringify(auditRecords));
|
||||
|
||||
setEnterprises(updated);
|
||||
localStorage.setItem('smart_agriculture_enterprises', JSON.stringify(updated));
|
||||
setShowDetailDialog(false);
|
||||
toast.success('审核通过');
|
||||
};
|
||||
|
||||
const handleReject = (auditReason: string) => {
|
||||
if (!selectedEnterprise) return;
|
||||
if (!auditReason.trim()) {
|
||||
toast.error('请填写驳回原因');
|
||||
return;
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const updated = enterprises.map(ent =>
|
||||
ent.id === selectedEnterprise.id
|
||||
? {
|
||||
...ent,
|
||||
auditStatus: 'rejected' as AuditStatus,
|
||||
status: 'inactive' as const,
|
||||
auditTime: now,
|
||||
auditor: '系统管理员',
|
||||
auditReason: auditReason,
|
||||
updatedAt: now,
|
||||
}
|
||||
: ent
|
||||
);
|
||||
|
||||
// 创建审核历史记录
|
||||
const auditRecords = JSON.parse(localStorage.getItem('smart_agriculture_audit_records') || '[]');
|
||||
const newRecord = {
|
||||
id: `audit-${Date.now()}`,
|
||||
enterpriseId: selectedEnterprise.id,
|
||||
enterpriseName: selectedEnterprise.name,
|
||||
auditType: 'register',
|
||||
submitTime: selectedEnterprise.createdAt,
|
||||
auditTime: now,
|
||||
auditor: '系统管理员',
|
||||
result: 'rejected',
|
||||
reason: auditReason,
|
||||
remarks: '审核驳回',
|
||||
};
|
||||
auditRecords.push(newRecord);
|
||||
localStorage.setItem('smart_agriculture_audit_records', JSON.stringify(auditRecords));
|
||||
|
||||
setEnterprises(updated);
|
||||
localStorage.setItem('smart_agriculture_enterprises', JSON.stringify(updated));
|
||||
setShowDetailDialog(false);
|
||||
toast.success('已驳回');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6">
|
||||
<div>
|
||||
<h2 className="text-green-800">企业审核</h2>
|
||||
<p className="text-muted-foreground">管理企业注册与变更审核流程</p>
|
||||
</div>
|
||||
|
||||
{/* 统计卡片 */}
|
||||
<AuditStatsCard enterprises={enterprises} />
|
||||
|
||||
{/* 搜索和筛选 */}
|
||||
<SearchAndFilter
|
||||
searchKeyword={searchKeyword}
|
||||
onSearchChange={setSearchKeyword}
|
||||
statusFilter={statusFilter}
|
||||
onStatusFilterChange={setStatusFilter}
|
||||
/>
|
||||
|
||||
{/* 企业列表 */}
|
||||
<EnterpriseTable
|
||||
enterprises={filteredEnterprises}
|
||||
onViewDetail={handleViewDetail}
|
||||
/>
|
||||
|
||||
{/* 详情审核对话框 */}
|
||||
<EnterpriseDetailDialog
|
||||
open={showDetailDialog}
|
||||
onOpenChange={setShowDetailDialog}
|
||||
enterprise={selectedEnterprise}
|
||||
onApprove={handleApprove}
|
||||
onReject={handleReject}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function EnterpriseInfoPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">企业信息</h1>
|
||||
<p>企业信息管理页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function PlatformUserManagementPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">平台用户管理</h1>
|
||||
<p>平台用户管理功能页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function EmployeeManagementPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">员工管理</h1>
|
||||
<p>员工管理功能页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function MenuManagementPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">菜单管理</h1>
|
||||
<p>菜单管理功能页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function PermissionConfigPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">权限配置管理</h1>
|
||||
<p>权限配置管理功能页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function RoleManagementPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">角色管理</h1>
|
||||
<p>角色管理功能页面</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
16
crop-x/src/app/(app)/layout.tsx
Normal file
16
crop-x/src/app/(app)/layout.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import {Navbar} from "@/components/layouts/Navbar"
|
||||
import '@/styles/globals.css'
|
||||
export default function DashboardLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<Navbar></Navbar>
|
||||
{/* 布局 UI */}
|
||||
{/* 将 children 放在您希望渲染页面或嵌套布局的位置 */}
|
||||
<main>{children}</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Navbar} from "../components/layouts/Navbar"
|
||||
|
||||
import '@/styles/globals.css'
|
||||
export default function DashboardLayout({
|
||||
children,
|
||||
@@ -8,10 +8,9 @@ export default function DashboardLayout({
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<Navbar></Navbar>
|
||||
{/* 布局 UI */}
|
||||
{/* 将 children 放在您希望渲染页面或嵌套布局的位置 */}
|
||||
<main>{children}</main>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user