diff --git a/SuperAdmin/app/dashboard/admins/page.tsx b/SuperAdmin/app/dashboard/admins/page.tsx
index af272b9a..51e80f71 100644
--- a/SuperAdmin/app/dashboard/admins/page.tsx
+++ b/SuperAdmin/app/dashboard/admins/page.tsx
@@ -21,46 +21,6 @@ import {
AlertDialogTitle,
} from "@/components/ui/alert-dialog"
-// 保留原始示例数据,作为加载失败时的备用数据
-const adminsData = [
- {
- id: "1",
- account: "admin_zhang",
- username: "张管理",
- role: "超级管理员",
- permissions: ["项目管理", "客户池", "管理员权限"],
- createdAt: "2023-05-01",
- lastLogin: "2023-06-28 09:15",
- },
- {
- id: "2",
- account: "admin_li",
- username: "李管理",
- role: "项目管理员",
- permissions: ["项目管理", "客户池"],
- createdAt: "2023-05-10",
- lastLogin: "2023-06-27 14:30",
- },
- {
- id: "3",
- account: "admin_wang",
- username: "王管理",
- role: "客户管理员",
- permissions: ["客户池"],
- createdAt: "2023-05-15",
- lastLogin: "2023-06-28 11:45",
- },
- {
- id: "4",
- account: "admin_zhao",
- username: "赵管理",
- role: "项目管理员",
- permissions: ["项目管理"],
- createdAt: "2023-05-20",
- lastLogin: "2023-06-26 16:20",
- },
-]
-
export default function AdminsPage() {
const [searchTerm, setSearchTerm] = useState("")
const [isLoading, setIsLoading] = useState(true)
@@ -95,14 +55,8 @@ export default function AdminsPage() {
description: response.msg || "请稍后重试",
variant: "destructive",
})
- // 加载失败时显示示例数据
- setAdministrators(adminsData.map(admin => ({
- ...admin,
- id: Number(admin.id),
- name: admin.username,
- status: 1
- })))
- setTotalCount(adminsData.length)
+ setAdministrators([])
+ setTotalCount(0)
}
} catch (error) {
console.error("获取管理员列表出错:", error)
@@ -111,14 +65,8 @@ export default function AdminsPage() {
description: "请检查网络连接后重试",
variant: "destructive",
})
- // 加载失败时显示示例数据
- setAdministrators(adminsData.map(admin => ({
- ...admin,
- id: Number(admin.id),
- name: admin.username,
- status: 1
- })))
- setTotalCount(adminsData.length)
+ setAdministrators([])
+ setTotalCount(0)
} finally {
setIsLoading(false)
}
diff --git a/SuperAdmin/app/dashboard/customers/page.tsx b/SuperAdmin/app/dashboard/customers/page.tsx
index ee613e8f..0fe327bd 100644
--- a/SuperAdmin/app/dashboard/customers/page.tsx
+++ b/SuperAdmin/app/dashboard/customers/page.tsx
@@ -20,94 +20,6 @@ import { getTrafficPoolList } from "@/lib/traffic-pool-api"
import { Customer } from "@/lib/traffic-pool-api"
import { PaginationControls } from "@/components/ui/pagination-controls"
-// Sample customer data
-const customersData = [
- {
- id: "1",
- name: "张三",
- avatar: "/placeholder.svg?height=40&width=40",
- wechatId: "zhangsan123",
- gender: "男",
- region: "北京",
- source: "微信搜索",
- tags: ["潜在客户", "高消费"],
- projectName: "电商平台项目",
- addedDate: "2023-06-10",
- },
- {
- id: "2",
- name: "李四",
- avatar: "/placeholder.svg?height=40&width=40",
- wechatId: "lisi456",
- gender: "男",
- region: "上海",
- source: "朋友推荐",
- tags: ["活跃用户"],
- projectName: "社交媒体营销",
- addedDate: "2023-06-12",
- },
- {
- id: "3",
- name: "王五",
- avatar: "/placeholder.svg?height=40&width=40",
- wechatId: "wangwu789",
- gender: "男",
- region: "广州",
- source: "广告点击",
- tags: ["新用户"],
- projectName: "企业官网推广",
- addedDate: "2023-06-15",
- },
- {
- id: "4",
- name: "赵六",
- avatar: "/placeholder.svg?height=40&width=40",
- wechatId: "zhaoliu321",
- gender: "男",
- region: "深圳",
- source: "线下活动",
- tags: ["高消费", "忠诚客户"],
- projectName: "教育平台项目",
- addedDate: "2023-06-18",
- },
- {
- id: "5",
- name: "钱七",
- avatar: "/placeholder.svg?height=40&width=40",
- wechatId: "qianqi654",
- gender: "女",
- region: "成都",
- source: "微信群",
- tags: ["潜在客户"],
- projectName: "金融服务推广",
- addedDate: "2023-06-20",
- },
- {
- id: "6",
- name: "孙八",
- avatar: "/placeholder.svg?height=40&width=40",
- wechatId: "sunba987",
- gender: "女",
- region: "武汉",
- source: "微信搜索",
- tags: ["活跃用户", "高消费"],
- projectName: "电商平台项目",
- addedDate: "2023-06-22",
- },
- {
- id: "7",
- name: "周九",
- avatar: "/placeholder.svg?height=40&width=40",
- wechatId: "zhoujiu135",
- gender: "女",
- region: "杭州",
- source: "朋友推荐",
- tags: ["新用户"],
- projectName: "社交媒体营销",
- addedDate: "2023-06-25",
- },
-]
-
export default function CustomersPage() {
const [searchTerm, setSearchTerm] = useState("")
const [selectedRegion, setSelectedRegion] = useState("")
@@ -162,25 +74,6 @@ export default function CustomersPage() {
setCurrentPage(1); // Reset to first page when page size changes
};
- // Filter customers based on search and filters (兼容示例数据)
- const filteredCustomers = customersData.filter((customer) => {
- const matchesSearch =
- customer.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
- customer.wechatId.toLowerCase().includes(searchTerm.toLowerCase())
-
- const matchesRegion = selectedRegion ? customer.region === selectedRegion : true
- const matchesGender = selectedGender ? customer.gender === selectedGender : true
- const matchesSource = selectedSource ? customer.source === selectedSource : true
- const matchesProject = selectedProject ? customer.projectName === selectedProject : true
-
- return matchesSearch && matchesRegion && matchesGender && matchesSource && matchesProject
- })
-
- // Get unique values for filters
- const regions = [...new Set(customersData.map((c) => c.region))]
- const sources = [...new Set(customersData.map((c) => c.source))]
- const projects = [...new Set(customersData.map((c) => c.projectName))]
-
return (
@@ -217,11 +110,7 @@ export default function CustomersPage() {
所有地区
- {regions.map((region) => (
-
- {region}
-
- ))}
+ {/* 从API获取到的regions数据应在此处映射 */}
@@ -248,28 +137,20 @@ export default function CustomersPage() {
所有来源
- {sources.map((source) => (
-
- {source}
-
- ))}
+ {/* 从API获取到的sources数据应在此处映射 */}
-
所属项目
+
项目
@@ -277,16 +158,16 @@ export default function CustomersPage() {
-
+
- 客户昵称
- 微信ID
+ 姓名
性别
地区
来源
- 公司名称
+ 标签
+ 所属项目
添加时间
操作
@@ -294,45 +175,45 @@ export default function CustomersPage() {
{isLoading ? (
-
- 加载中...
-
+ 加载中...
) : error ? (
-
- {error}
-
+ {error}
- ) : customers.length > 0 ? (
+ ) : customers.length === 0 ? (
+
+ 没有符合条件的客户
+
+ ) : (
customers.map((customer) => (
-
-
- {
- // 图片加载失败时使用默认图片
- const target = e.target as HTMLImageElement;
- target.src = "/placeholder.svg?height=40&width=40";
- }}
- />
- {(customer.nickname || "未知").slice(0, 2)}
+
+
+
+ {customer.nickname ? customer.nickname.substring(0, 1) : "?"}
-
{customer.nickname || "未知"}
-
{customer.gender}
+
{customer.nickname}
+
{customer.wechatId}
- {customer.wechatId}
{customer.gender}
{customer.region}
{customer.source}
- {customer.projectName}
- {customer.addTime}
+
+
+ {customer.tags && customer.tags.map((tag) => (
+
+ {tag}
+
+ ))}
+
+
+ {customer.companyName}
+ {customer.createTime}
@@ -342,39 +223,41 @@ export default function CustomersPage() {
-
-
- 查看详情
+
+
+
+ 查看详情
-
- 分发客户
-
+
+ 分配客服
+ 添加标签
))
- ) : (
-
-
- 未找到客户
-
-
)}
-
- {/* 使用新的分页组件 */}
-
+
+ {/* 分页控制 */}
+
+
+ 共 {totalItems} 条记录,第 {currentPage} 页,共 {totalPages} 页
+
+
+
)
diff --git a/SuperAdmin/app/dashboard/layout.tsx b/SuperAdmin/app/dashboard/layout.tsx
index aa73fed2..af35df0a 100644
--- a/SuperAdmin/app/dashboard/layout.tsx
+++ b/SuperAdmin/app/dashboard/layout.tsx
@@ -1,14 +1,41 @@
"use client"
import type React from "react"
-import { useState, useEffect } from "react"
-import { useRouter } from "next/navigation"
+import { useState, useEffect, createContext, useContext } from "react"
+import { useRouter, usePathname } from "next/navigation"
import { Button } from "@/components/ui/button"
import { Menu, X } from "lucide-react"
import { Sidebar } from "@/components/layout/sidebar"
import { Header } from "@/components/layout/header"
import { getAdminInfo } from "@/lib/utils"
+// 全局标签页管理上下文
+interface TabData {
+ id: string
+ label: string
+ path: string
+ closable: boolean
+}
+
+interface TabContextType {
+ tabs: TabData[]
+ activeTab: string
+ addTab: (tab: Omit) => string
+ closeTab: (tabId: string) => void
+ setActiveTab: (tabId: string) => void
+ findTabByPath: (path: string) => TabData | undefined
+}
+
+const TabContext = createContext(undefined);
+
+export function useTabContext() {
+ const context = useContext(TabContext);
+ if (!context) {
+ throw new Error("useTabContext must be used within a TabProvider");
+ }
+ return context;
+}
+
export default function DashboardLayout({
children,
}: {
@@ -16,6 +43,122 @@ export default function DashboardLayout({
}) {
const [sidebarOpen, setSidebarOpen] = useState(true)
const router = useRouter()
+ const pathname = usePathname()
+
+ // 标签页状态管理
+ const [tabs, setTabs] = useState([
+ { id: "dashboard", label: "仪表盘", path: "/dashboard", closable: false }
+ ])
+ const [activeTab, setActiveTab] = useState("dashboard")
+
+ // 添加标签页
+ const addTab = (tabData: Omit) => {
+ const id = `tab-${Date.now()}`
+ const newTab = { id, ...tabData }
+
+ // 检查是否已存在类似标签(基于路径)
+ const existingTab = findTabByPath(tabData.path)
+
+ if (existingTab) {
+ // 如果已存在,激活它
+ setActiveTab(existingTab.id)
+ // 确保导航到该路径(即使路径匹配也强制导航)
+ router.push(tabData.path)
+ return existingTab.id
+ } else {
+ // 如果不存在,添加新标签
+ setTabs(prev => [...prev, newTab])
+ setActiveTab(id)
+ // 确保导航到该路径(即使路径匹配也强制导航)
+ router.push(tabData.path)
+ return id
+ }
+ }
+
+ // 设置激活标签(并导航到对应路径)
+ const setActiveTabAndNavigate = (tabId: string) => {
+ setActiveTab(tabId)
+ // 找到标签对应的路径并导航
+ const tab = tabs.find(tab => tab.id === tabId)
+ if (tab) {
+ router.push(tab.path)
+ }
+ }
+
+ // 关闭标签页
+ const closeTab = (tabId: string) => {
+ // 找到要关闭的标签的索引
+ const tabIndex = tabs.findIndex(tab => tab.id === tabId)
+
+ // 如果标签不存在或者是不可关闭的标签,直接返回
+ if (tabIndex === -1 || !tabs[tabIndex].closable) return
+
+ // 创建新的标签数组,移除要关闭的标签
+ const newTabs = tabs.filter(tab => tab.id !== tabId)
+ setTabs(newTabs)
+
+ // 如果关闭的是当前活动标签,需要激活另一个标签
+ if (activeTab === tabId) {
+ // 优先激活关闭标签左侧的标签,如果没有则激活默认的仪表盘标签
+ const newActiveTab = newTabs[tabIndex - 1]?.id || "dashboard"
+ setActiveTab(newActiveTab)
+
+ // 路由跳转到新激活的标签对应的路径
+ const newActivePath = newTabs.find(tab => tab.id === newActiveTab)?.path || "/dashboard"
+ router.push(newActivePath)
+ }
+ }
+
+ // 根据路径查找标签
+ const findTabByPath = (path: string): TabData | undefined => {
+ return tabs.find(tab => tab.path === path)
+ }
+
+ // 监听路径变化,自动添加标签
+ useEffect(() => {
+ // 不触发/dashboard路径,已有默认标签
+ if (pathname === "/dashboard") {
+ setActiveTab("dashboard")
+ return
+ }
+
+ // 检查当前路径是否已有对应标签
+ const existingTab = findTabByPath(pathname)
+
+ if (existingTab) {
+ // 如果存在,激活它
+ setActiveTab(existingTab.id)
+ } else {
+ // 如果不存在,添加新标签
+ // 生成标签标题
+ let label = "新标签"
+
+ // 根据路径生成更友好的标签名
+ if (pathname.includes("/projects")) {
+ if (pathname === "/dashboard/projects") {
+ label = "项目列表"
+ } else if (pathname.includes("/new")) {
+ label = "新建项目"
+ } else if (pathname.includes("/edit")) {
+ label = "编辑项目"
+ } else {
+ label = "项目详情"
+ }
+ } else if (pathname.includes("/admins")) {
+ label = "管理员"
+ } else if (pathname.includes("/customers")) {
+ label = "客户池"
+ } else if (pathname.includes("/settings")) {
+ label = "系统设置"
+ }
+
+ addTab({
+ label,
+ path: pathname,
+ closable: true
+ })
+ }
+ }, [pathname])
// 认证检查
useEffect(() => {
@@ -31,29 +174,72 @@ export default function DashboardLayout({
}, [router])
return (
-
- {/* Mobile sidebar toggle */}
-
-
-
+
+
+ {/* Mobile sidebar toggle */}
+
+
+
- {/* Sidebar */}
-
-
-
+ {/* Sidebar */}
+
+
+
- {/* Main content */}
-
-
-
{children}
+ {/* Main content */}
+
+
+
+ {/* 标签栏 */}
+
+
+ {tabs.map(tab => (
+
{
+ setActiveTabAndNavigate(tab.id)
+ }}
+ >
+ {tab.label}
+ {tab.closable && (
+
+ )}
+
+ ))}
+
+
+
+ {/* 内容区域 */}
+
+ {children}
+
+
-
+
)
}
diff --git a/SuperAdmin/app/dashboard/projects/[id]/edit/page.tsx b/SuperAdmin/app/dashboard/projects/[id]/edit/page.tsx
index 051e2ec7..9c7f266e 100644
--- a/SuperAdmin/app/dashboard/projects/[id]/edit/page.tsx
+++ b/SuperAdmin/app/dashboard/projects/[id]/edit/page.tsx
@@ -1,16 +1,24 @@
"use client"
-import type React from "react"
-import { useState, useEffect, use } from "react"
+import * as React from "react"
+import { useState, useEffect } from "react"
import { useRouter } from "next/navigation"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Label } from "@/components/ui/label"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
-import { ArrowLeft, Plus, Trash } from "lucide-react"
+import { ArrowLeft, Plus, Trash, X } from "lucide-react"
import Link from "next/link"
import { toast, Toaster } from "sonner"
+import Image from "next/image"
+import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog"
+
+// 为React.use添加类型声明
+declare module 'react' {
+ function use
(promise: Promise): T;
+ function use(value: T): T;
+}
interface Device {
id: number
@@ -28,6 +36,10 @@ interface Project {
deviceCount: number
friendCount: number
userCount: number
+ username: string
+ status: number
+ s2_accountId?: number
+ devices?: Device[]
}
export default function EditProjectPage({ params }: { params: { id: string } }) {
@@ -37,7 +49,10 @@ export default function EditProjectPage({ params }: { params: { id: string } })
const [project, setProject] = useState(null)
const [password, setPassword] = useState("")
const [confirmPassword, setConfirmPassword] = useState("")
- const { id } = use(params)
+ const [isModalOpen, setIsModalOpen] = useState(false)
+ const [qrCodeData, setQrCodeData] = useState("")
+ const [isAddingDevice, setIsAddingDevice] = useState(false)
+ const { id } = React.use(params)
useEffect(() => {
const fetchProject = async () => {
@@ -82,8 +97,8 @@ export default function EditProjectPage({ params }: { params: { id: string } })
account: project?.account,
memo: project?.memo,
phone: project?.phone,
- username: nickname,
- status: parseInt(status),
+ username: project?.username,
+ status: project?.status,
...(password && { password })
}),
})
@@ -103,8 +118,42 @@ export default function EditProjectPage({ params }: { params: { id: string } })
}
}
- const handleAddDevice = () => {
- router.push(`/dashboard/projects/${id}/devices/new`)
+ const handleAddDevice = async () => {
+ if (!project?.s2_accountId) {
+ toast.error("无法添加设备,未找到账号ID")
+ return
+ }
+
+ setIsAddingDevice(true)
+ try {
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/api/device/add`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ accountId: project.s2_accountId
+ }),
+ })
+
+ const data = await response.json()
+
+ if (data.code === 200 && data.data?.qrCode) {
+ setQrCodeData(data.data.qrCode)
+ setIsModalOpen(true)
+ } else {
+ toast.error(data.msg || "获取设备二维码失败")
+ }
+ } catch (error) {
+ toast.error("网络错误,请稍后重试")
+ } finally {
+ setIsAddingDevice(false)
+ }
+ }
+
+ const closeModal = () => {
+ setIsModalOpen(false)
+ setQrCodeData("")
}
if (isLoading) {
@@ -215,7 +264,7 @@ export default function EditProjectPage({ params }: { params: { id: string } })
- {project?.devices.length > 0 && project.devices.map((device) => (
+ {project && project.devices && project.devices.length > 0 && project.devices.map((device) => (
))}
-
@@ -245,11 +304,43 @@ export default function EditProjectPage({ params }: { params: { id: string } })
取消
- {isSubmitting ? "保存中..." : "保存修改"}
+ {isSubmitting ? "提交中..." : "保存修改"}
+
+ {/* 使用Dialog组件替代自定义模态框 */}
+
)
}
diff --git a/SuperAdmin/app/dashboard/projects/[id]/page.tsx b/SuperAdmin/app/dashboard/projects/[id]/page.tsx
index 0fe60923..d0ac9f7c 100644
--- a/SuperAdmin/app/dashboard/projects/[id]/page.tsx
+++ b/SuperAdmin/app/dashboard/projects/[id]/page.tsx
@@ -1,5 +1,6 @@
"use client"
+import React from "react"
import { useState, useEffect } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
@@ -11,6 +12,7 @@ import { ArrowLeft, Edit } from "lucide-react"
import { toast } from "sonner"
import { use } from "react"
import Image from "next/image"
+import { Badge } from "@/components/ui/badge"
interface ProjectProfile {
id: number
@@ -50,7 +52,14 @@ interface SubUser {
typeId: number
}
-export default function ProjectDetailPage({ params }: { params: { id: string } }) {
+interface ProjectDetailPageProps {
+ params: {
+ id: string
+ }
+}
+
+export default function ProjectDetailPage({ params }: ProjectDetailPageProps) {
+ const { id } = use(params)
const router = useRouter()
const [isLoading, setIsLoading] = useState(true)
const [isDevicesLoading, setIsDevicesLoading] = useState(false)
@@ -59,7 +68,6 @@ export default function ProjectDetailPage({ params }: { params: { id: string } }
const [devices, setDevices] = useState([])
const [subUsers, setSubUsers] = useState([])
const [activeTab, setActiveTab] = useState("overview")
- const { id } = use(params)
useEffect(() => {
const fetchProjectProfile = async () => {
@@ -112,15 +120,7 @@ export default function ProjectDetailPage({ params }: { params: { id: string } }
if (activeTab === "accounts") {
setIsSubUsersLoading(true)
try {
- const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/subusers`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- companyId: parseInt(id)
- })
- })
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/subusers?companyId=${id}`)
const data = await response.json()
if (data.code === 200) {
@@ -243,32 +243,53 @@ export default function ProjectDetailPage({ params }: { params: { id: string } }
) : devices.length === 0 ? (
暂无数据
) : (
-
-
-
- 设备名称
- 设备型号
- 品牌
- IMEI
- 设备状态
- 微信状态
- 微信好友数量
-
-
-
- {devices.map((device) => (
-
- {device.memo}
- {device.model}
- {device.brand}
- {device.imei}
- {device.alive === 1 ? "在线" : "离线"}
- {device.wAlive === 1 ? "在线" : device.wAlive === 0 ? "离线" : "未登录微信"}
- {device.friendCount || 0}
+ <>
+
+
+
+ 设备名称
+ 设备型号
+ 品牌
+ IMEI
+ 设备状态
+ 微信状态
+ 微信好友数量
- ))}
-
-
+
+
+ {devices.map((device) => (
+
+ {device.memo}
+ {device.model}
+ {device.brand}
+ {device.imei}
+
+
+ {device.alive === 1 ? "在线" : "离线"}
+
+
+
+
+ {device.wAlive === 1 ? "已登录" : device.wAlive === 0 ? "已登出" : "未登录微信"}
+
+
+ {device.friendCount || 0}
+
+ ))}
+
+
+
+ 共 {devices.length} 条数据
+
+ >
)}
@@ -286,42 +307,51 @@ export default function ProjectDetailPage({ params }: { params: { id: string } }
) : subUsers.length === 0 ? (
暂无数据
) : (
-
-
-
- 头像
- 账号ID
- 登录账号
- 昵称
- 手机号
- 状态
- 账号类型
- 创建时间
-
-
-
- {subUsers.map((user) => (
-
-
-
-
- {user.id}
- {user.account}
- {user.username}
- {user.phone}
- {user.status === 1 ? "登录" : "禁用"}
- {user.typeId === 1 ? "操盘手" : "门店顾问"}
- {user.createTime}
+ <>
+
+
+
+ 头像
+ 账号ID
+ 登录账号
+ 昵称
+ 手机号
+ 状态
+ 账号类型
+ 创建时间
- ))}
-
-
+
+
+ {subUsers.map((user) => (
+
+
+
+
+ {user.id}
+ {user.account}
+ {user.username}
+ {user.phone}
+
+
+ {user.status === 1 ? "启用" : "禁用"}
+
+
+ {user.typeId === 1 ? "操盘手" : "门店顾问"}
+ {user.createTime}
+
+ ))}
+
+
+
+ 共 {subUsers.length} 条数据
+
+ >
)}
diff --git a/SuperAdmin/app/dashboard/projects/page.tsx b/SuperAdmin/app/dashboard/projects/page.tsx
index 609c3680..4b93d9eb 100644
--- a/SuperAdmin/app/dashboard/projects/page.tsx
+++ b/SuperAdmin/app/dashboard/projects/page.tsx
@@ -1,7 +1,7 @@
"use client"
import { useState, useEffect } from "react"
-import Link from "next/link"
+import { useSearchParams, useRouter, usePathname } from "next/navigation"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
@@ -18,6 +18,7 @@ import {
} from "@/components/ui/dialog"
import { Badge } from "@/components/ui/badge"
import { PaginationControls } from "@/components/ui/pagination-controls"
+import { useTabContext } from "@/app/dashboard/layout"
interface Project {
id: number
@@ -32,17 +33,41 @@ interface Project {
}
export default function ProjectsPage() {
+ const searchParams = useSearchParams()
+ const router = useRouter()
+ const pathname = usePathname()
+ const { addTab } = useTabContext()
+
const [searchTerm, setSearchTerm] = useState("")
const [projects, setProjects] = useState([])
const [isLoading, setIsLoading] = useState(true)
- const [currentPage, setCurrentPage] = useState(1)
+ const [currentPage, setCurrentPage] = useState(parseInt(searchParams.get("page") || "1"))
const [totalPages, setTotalPages] = useState(1)
- const [pageSize, setPageSize] = useState(10)
+ const [pageSize, setPageSize] = useState(parseInt(searchParams.get("pageSize") || "10"))
const [totalItems, setTotalItems] = useState(0)
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
const [deletingProjectId, setDeletingProjectId] = useState(null)
const [isDeleting, setIsDeleting] = useState(false)
+ // 从URL更新状态
+ useEffect(() => {
+ const page = parseInt(searchParams.get("page") || "1")
+ const size = parseInt(searchParams.get("pageSize") || "10")
+ setCurrentPage(page)
+ setPageSize(size)
+ }, [searchParams])
+
+ // 更新URL查询参数
+ const updateUrlParams = (page: number, size: number) => {
+ const params = new URLSearchParams()
+ params.set("page", page.toString())
+ params.set("pageSize", size.toString())
+ if (searchTerm) {
+ params.set("search", searchTerm)
+ }
+ router.replace(`${pathname}?${params.toString()}`)
+ }
+
// 获取项目列表
useEffect(() => {
const fetchProjects = async () => {
@@ -72,12 +97,21 @@ export default function ProjectsPage() {
}
fetchProjects()
- }, [currentPage, pageSize])
+ // 更新URL参数
+ updateUrlParams(currentPage, pageSize)
+ }, [currentPage, pageSize, pathname])
// 处理页面大小变化
const handlePageSizeChange = (newSize: number) => {
setPageSize(newSize)
setCurrentPage(1)
+ updateUrlParams(1, newSize)
+ }
+
+ // 处理页面变化
+ const handlePageChange = (newPage: number) => {
+ setCurrentPage(newPage)
+ updateUrlParams(newPage, pageSize)
}
const handleDeleteClick = (projectId: number) => {
@@ -104,6 +138,7 @@ export default function ProjectsPage() {
if (data.code === 200) {
toast.success("删除成功")
+
// Fetch projects again after delete
const fetchProjects = async () => {
setIsLoading(true)
@@ -136,14 +171,39 @@ export default function ProjectsPage() {
}
}
+ // 打开项目详情
+ const handleViewProject = (project: Project) => {
+ addTab({
+ label: `项目 #${project.id} - 详情`,
+ path: `/dashboard/projects/${project.id}`,
+ closable: true
+ });
+ }
+
+ // 打开编辑项目
+ const handleEditProject = (project: Project) => {
+ addTab({
+ label: `项目 #${project.id} - 编辑`,
+ path: `/dashboard/projects/${project.id}/edit`,
+ closable: true
+ });
+ }
+
+ // 打开新建项目
+ const handleNewProject = () => {
+ addTab({
+ label: "新建项目",
+ path: "/dashboard/projects/new",
+ closable: true
+ });
+ }
+
return (
项目列表
-
-
- 新建项目
-
+
+ 新建项目
@@ -175,7 +235,7 @@ export default function ProjectsPage() {
{isLoading ? (
-
+
加载中...
@@ -200,15 +260,11 @@ export default function ProjectsPage() {
-
-
- 查看详情
-
+ handleViewProject(project)}>
+ 查看详情
-
-
- 编辑项目
-
+ handleEditProject(project)}>
+ 编辑项目
-
+
未找到项目
@@ -237,7 +293,7 @@ export default function ProjectsPage() {
totalPages={totalPages}
pageSize={pageSize}
totalItems={totalItems}
- onPageChange={setCurrentPage}
+ onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
/>
diff --git a/SuperAdmin/app/page.tsx b/SuperAdmin/app/page.tsx
new file mode 100644
index 00000000..e8fb4476
--- /dev/null
+++ b/SuperAdmin/app/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from "next/navigation"
+
+export default function Home() {
+ redirect("/dashboard")
+}
\ No newline at end of file
diff --git a/SuperAdmin/components/layout/sidebar.tsx b/SuperAdmin/components/layout/sidebar.tsx
index 73993682..1297ff6b 100644
--- a/SuperAdmin/components/layout/sidebar.tsx
+++ b/SuperAdmin/components/layout/sidebar.tsx
@@ -1,37 +1,178 @@
"use client"
-import { useEffect, useState } from "react"
-import Link from "next/link"
+import { useState, useEffect } from "react"
import { usePathname } from "next/navigation"
-import { getMenus, type MenuItem } from "@/lib/menu-api"
import * as LucideIcons from "lucide-react"
-import { ChevronDown, ChevronRight } from "lucide-react"
+import { cn } from "@/lib/utils"
+import { useTabContext } from "@/app/dashboard/layout"
+import { getMenus } from "@/lib/menu-api"
+
+// 适配后端返回的菜单项格式
+interface MenuItem {
+ id: number
+ parentId?: number | null
+ parent_id?: number // 后端返回的字段
+ name?: string
+ title?: string // 后端返回的字段
+ path: string
+ icon?: string
+ order?: number
+ sort?: number // 后端返回的字段
+ status?: number
+ children?: MenuItem[]
+}
export function Sidebar() {
const pathname = usePathname()
const [menus, setMenus] = useState