289 lines
9.5 KiB
TypeScript
289 lines
9.5 KiB
TypeScript
|
|
import * as React from "react"
|
|||
|
|
import { ShieldCheck, UserCog, Users } from "lucide-react"
|
|||
|
|
|
|||
|
|
import { Badge } from "@/components/ui/badge"
|
|||
|
|
import { Button } from "@/components/ui/button"
|
|||
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|||
|
|
import { Input } from "@/components/ui/input"
|
|||
|
|
import {
|
|||
|
|
Table,
|
|||
|
|
TableBody,
|
|||
|
|
TableCell,
|
|||
|
|
TableHead,
|
|||
|
|
TableHeader,
|
|||
|
|
TableRow,
|
|||
|
|
} from "@/components/ui/table"
|
|||
|
|
|
|||
|
|
type UserStatus = "enabled" | "disabled"
|
|||
|
|
|
|||
|
|
type UserRecord = {
|
|||
|
|
id: string
|
|||
|
|
name: string
|
|||
|
|
account: string
|
|||
|
|
role: "业务/市场负责人" | "内容运营专员" | "项目与运维负责人"
|
|||
|
|
status: UserStatus
|
|||
|
|
lastLogin: string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type PermissionKey =
|
|||
|
|
| "dashboard.view"
|
|||
|
|
| "source.sync"
|
|||
|
|
| "content.edit"
|
|||
|
|
| "content.approve"
|
|||
|
|
| "content.publish"
|
|||
|
|
| "leads.manage"
|
|||
|
|
| "settings.manage"
|
|||
|
|
| "users.manage"
|
|||
|
|
|
|||
|
|
type RolePermission = {
|
|||
|
|
roleName: "业务/市场负责人" | "内容运营专员" | "项目与运维负责人"
|
|||
|
|
permissions: PermissionKey[]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const INITIAL_USERS: UserRecord[] = [
|
|||
|
|
{
|
|||
|
|
id: "u1",
|
|||
|
|
name: "陈经理",
|
|||
|
|
account: "market.chen",
|
|||
|
|
role: "业务/市场负责人",
|
|||
|
|
status: "enabled",
|
|||
|
|
lastLogin: "2026-04-12 09:18",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "u2",
|
|||
|
|
name: "刘运营",
|
|||
|
|
account: "content.liu",
|
|||
|
|
role: "内容运营专员",
|
|||
|
|
status: "enabled",
|
|||
|
|
lastLogin: "2026-04-12 08:42",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "u3",
|
|||
|
|
name: "王运维",
|
|||
|
|
account: "ops.wang",
|
|||
|
|
role: "项目与运维负责人",
|
|||
|
|
status: "enabled",
|
|||
|
|
lastLogin: "2026-04-11 20:05",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "u4",
|
|||
|
|
name: "赵测试",
|
|||
|
|
account: "qa.zhao",
|
|||
|
|
role: "内容运营专员",
|
|||
|
|
status: "disabled",
|
|||
|
|
lastLogin: "2026-04-01 11:20",
|
|||
|
|
},
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
const PERMISSION_LABEL: Record<PermissionKey, string> = {
|
|||
|
|
"dashboard.view": "查看仪表盘",
|
|||
|
|
"source.sync": "车型同步",
|
|||
|
|
"content.edit": "内容编辑",
|
|||
|
|
"content.approve": "内容审核",
|
|||
|
|
"content.publish": "内容发布",
|
|||
|
|
"leads.manage": "潜客管理",
|
|||
|
|
"settings.manage": "系统设置",
|
|||
|
|
"users.manage": "用户与权限管理",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const INITIAL_ROLE_PERMISSIONS: RolePermission[] = [
|
|||
|
|
{
|
|||
|
|
roleName: "业务/市场负责人",
|
|||
|
|
permissions: [
|
|||
|
|
"dashboard.view",
|
|||
|
|
"content.approve",
|
|||
|
|
"content.publish",
|
|||
|
|
"leads.manage",
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
roleName: "内容运营专员",
|
|||
|
|
permissions: [
|
|||
|
|
"dashboard.view",
|
|||
|
|
"source.sync",
|
|||
|
|
"content.edit",
|
|||
|
|
"content.publish",
|
|||
|
|
"leads.manage",
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
roleName: "项目与运维负责人",
|
|||
|
|
permissions: [
|
|||
|
|
"dashboard.view",
|
|||
|
|
"source.sync",
|
|||
|
|
"settings.manage",
|
|||
|
|
"users.manage",
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
const PERMISSION_COLUMNS: PermissionKey[] = [
|
|||
|
|
"dashboard.view",
|
|||
|
|
"source.sync",
|
|||
|
|
"content.edit",
|
|||
|
|
"content.approve",
|
|||
|
|
"content.publish",
|
|||
|
|
"leads.manage",
|
|||
|
|
"settings.manage",
|
|||
|
|
"users.manage",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
export function UserAccessManagement() {
|
|||
|
|
const [query, setQuery] = React.useState("")
|
|||
|
|
const [users, setUsers] = React.useState<UserRecord[]>(INITIAL_USERS)
|
|||
|
|
const [rolePermissions, setRolePermissions] = React.useState<RolePermission[]>(INITIAL_ROLE_PERMISSIONS)
|
|||
|
|
|
|||
|
|
const filteredUsers = users.filter((user) => {
|
|||
|
|
const key = `${user.name} ${user.account} ${user.role}`.toLowerCase()
|
|||
|
|
return key.includes(query.toLowerCase())
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const toggleUserStatus = (id: string) => {
|
|||
|
|
setUsers((prev) =>
|
|||
|
|
prev.map((user) =>
|
|||
|
|
user.id === id
|
|||
|
|
? { ...user, status: user.status === "enabled" ? "disabled" : "enabled" }
|
|||
|
|
: user
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const togglePermission = (roleName: RolePermission["roleName"], permission: PermissionKey) => {
|
|||
|
|
setRolePermissions((prev) =>
|
|||
|
|
prev.map((role) => {
|
|||
|
|
if (role.roleName !== roleName) return role
|
|||
|
|
const has = role.permissions.includes(permission)
|
|||
|
|
return {
|
|||
|
|
...role,
|
|||
|
|
permissions: has
|
|||
|
|
? role.permissions.filter((item) => item !== permission)
|
|||
|
|
: [...role.permissions, permission],
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="flex flex-col gap-6 p-8">
|
|||
|
|
<div className="flex items-center justify-between">
|
|||
|
|
<div className="flex flex-col gap-1">
|
|||
|
|
<h1 className="text-3xl font-bold tracking-tight">用户与权限管理</h1>
|
|||
|
|
<p className="text-muted-foreground">集中维护账号状态、角色定义与权限边界。</p>
|
|||
|
|
</div>
|
|||
|
|
<Button className="bg-audi-black hover:bg-audi-dark-gray text-white px-6">新增用户</Button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<Card className="border-none shadow-sm bg-white">
|
|||
|
|
<CardHeader>
|
|||
|
|
<div className="flex items-center justify-between gap-3">
|
|||
|
|
<div>
|
|||
|
|
<CardTitle className="text-lg font-bold">用户管理</CardTitle>
|
|||
|
|
<CardDescription>管理账号启用状态与角色归属。</CardDescription>
|
|||
|
|
</div>
|
|||
|
|
<div className="w-full max-w-xs">
|
|||
|
|
<Input
|
|||
|
|
value={query}
|
|||
|
|
onChange={(e) => setQuery(e.target.value)}
|
|||
|
|
placeholder="搜索姓名、账号或角色..."
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</CardHeader>
|
|||
|
|
<CardContent>
|
|||
|
|
<Table>
|
|||
|
|
<TableHeader>
|
|||
|
|
<TableRow className="hover:bg-transparent border-b border-gray-100">
|
|||
|
|
<TableHead className="font-bold text-audi-black">姓名</TableHead>
|
|||
|
|
<TableHead className="font-bold text-audi-black">账号</TableHead>
|
|||
|
|
<TableHead className="font-bold text-audi-black">角色</TableHead>
|
|||
|
|
<TableHead className="font-bold text-audi-black">状态</TableHead>
|
|||
|
|
<TableHead className="font-bold text-audi-black">最近登录</TableHead>
|
|||
|
|
<TableHead className="text-right font-bold text-audi-black">操作</TableHead>
|
|||
|
|
</TableRow>
|
|||
|
|
</TableHeader>
|
|||
|
|
<TableBody>
|
|||
|
|
{filteredUsers.map((user) => (
|
|||
|
|
<TableRow key={user.id} className="hover:bg-gray-50/50 border-b border-gray-50 transition-colors">
|
|||
|
|
<TableCell className="font-medium">{user.name}</TableCell>
|
|||
|
|
<TableCell className="text-muted-foreground">{user.account}</TableCell>
|
|||
|
|
<TableCell>
|
|||
|
|
<Badge variant="outline">{user.role}</Badge>
|
|||
|
|
</TableCell>
|
|||
|
|
<TableCell>
|
|||
|
|
<Badge
|
|||
|
|
variant="outline"
|
|||
|
|
className={
|
|||
|
|
user.status === "enabled"
|
|||
|
|
? "bg-emerald-50 text-emerald-700 border-emerald-200"
|
|||
|
|
: "bg-zinc-100 text-zinc-700 border-zinc-200"
|
|||
|
|
}
|
|||
|
|
>
|
|||
|
|
{user.status === "enabled" ? "启用" : "停用"}
|
|||
|
|
</Badge>
|
|||
|
|
</TableCell>
|
|||
|
|
<TableCell className="text-xs text-muted-foreground">{user.lastLogin}</TableCell>
|
|||
|
|
<TableCell className="text-right">
|
|||
|
|
<div className="inline-flex gap-2">
|
|||
|
|
<Button size="sm" variant="outline">
|
|||
|
|
编辑角色
|
|||
|
|
</Button>
|
|||
|
|
<Button size="sm" variant="outline" onClick={() => toggleUserStatus(user.id)}>
|
|||
|
|
{user.status === "enabled" ? "停用" : "启用"}
|
|||
|
|
</Button>
|
|||
|
|
</div>
|
|||
|
|
</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
))}
|
|||
|
|
</TableBody>
|
|||
|
|
</Table>
|
|||
|
|
</CardContent>
|
|||
|
|
</Card>
|
|||
|
|
|
|||
|
|
<Card className="border-none shadow-sm bg-white">
|
|||
|
|
<CardHeader>
|
|||
|
|
<CardTitle className="text-lg font-bold">角色与权限管理</CardTitle>
|
|||
|
|
<CardDescription>按角色授予菜单、操作与审批权限(原型可直接勾选)。</CardDescription>
|
|||
|
|
</CardHeader>
|
|||
|
|
<CardContent>
|
|||
|
|
<div className="mb-3 flex items-center gap-2 text-sm text-muted-foreground">
|
|||
|
|
<Users className="h-4 w-4" />角色
|
|||
|
|
<UserCog className="h-4 w-4 ml-4" />权限
|
|||
|
|
<ShieldCheck className="h-4 w-4 ml-4 text-audi-red" />权限边界
|
|||
|
|
</div>
|
|||
|
|
<Table>
|
|||
|
|
<TableHeader>
|
|||
|
|
<TableRow className="hover:bg-transparent border-b border-gray-100">
|
|||
|
|
<TableHead className="font-bold text-audi-black">角色</TableHead>
|
|||
|
|
{PERMISSION_COLUMNS.map((permission) => (
|
|||
|
|
<TableHead key={permission} className="font-bold text-audi-black text-xs">
|
|||
|
|
{PERMISSION_LABEL[permission]}
|
|||
|
|
</TableHead>
|
|||
|
|
))}
|
|||
|
|
</TableRow>
|
|||
|
|
</TableHeader>
|
|||
|
|
<TableBody>
|
|||
|
|
{rolePermissions.map((role) => (
|
|||
|
|
<TableRow key={role.roleName} className="hover:bg-gray-50/50 border-b border-gray-50 transition-colors">
|
|||
|
|
<TableCell className="font-medium">{role.roleName}</TableCell>
|
|||
|
|
{PERMISSION_COLUMNS.map((permission) => {
|
|||
|
|
const checked = role.permissions.includes(permission)
|
|||
|
|
return (
|
|||
|
|
<TableCell key={`${role.roleName}-${permission}`}>
|
|||
|
|
<input
|
|||
|
|
type="checkbox"
|
|||
|
|
checked={checked}
|
|||
|
|
onChange={() => togglePermission(role.roleName, permission)}
|
|||
|
|
/>
|
|||
|
|
</TableCell>
|
|||
|
|
)
|
|||
|
|
})}
|
|||
|
|
</TableRow>
|
|||
|
|
))}
|
|||
|
|
</TableBody>
|
|||
|
|
</Table>
|
|||
|
|
</CardContent>
|
|||
|
|
</Card>
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|