Files
audi-rednote/audi-content-portal/src/components/users/UserAccessManagement.tsx
2026-04-15 17:08:17 +08:00

278 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 =
| "source.sync"
| "content.edit"
| "content.approve"
| "content.publish"
| "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> = {
"source.sync": "官网车型库",
"content.edit": "内容编辑",
"content.approve": "内容审核",
"content.publish": "内容发布",
"settings.manage": "系统设置",
"users.manage": "用户与权限管理",
}
const INITIAL_ROLE_PERMISSIONS: RolePermission[] = [
{
roleName: "业务/市场负责人",
permissions: [
"content.approve",
"content.publish",
],
},
{
roleName: "内容运营专员",
permissions: [
"source.sync",
"content.edit",
"content.publish",
],
},
{
roleName: "项目与运维负责人",
permissions: [
"source.sync",
"settings.manage",
"users.manage",
],
},
]
const PERMISSION_COLUMNS: PermissionKey[] = [
"source.sync",
"content.edit",
"content.approve",
"content.publish",
"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>
)
}