diff --git a/frontend/src/models/admin.js b/frontend/src/models/admin.js
index 336e987..31794c2 100644
--- a/frontend/src/models/admin.js
+++ b/frontend/src/models/admin.js
@@ -246,6 +246,30 @@ const Admin = {
return false;
});
},
+ depts: async () => {
+ return await fetch(`${API_BASE}/dept/list`, {
+ method: "GET",
+ headers: baseHeaders(),
+ })
+ .then((res) => res.json())
+ .then((res) => res?.depts || [])
+ .catch((e) => {
+ console.error(e);
+ return [];
+ });
+ },
+ addDepts: async (data) => {
+ return await fetch(`${API_BASE}/dept/add`, {
+ method: "POST",
+ headers: baseHeaders(),
+ body: JSON.stringify(data),
+ })
+ .then((res) => res.json())
+ .catch((e) => {
+ console.error(e);
+ return { depts: null, error: e.message };
+ });
+ },
};
export default Admin;
diff --git a/frontend/src/pages/Admin/Section/index.jsx b/frontend/src/pages/Admin/Section/index.jsx
index f7f5640..f3a474f 100644
--- a/frontend/src/pages/Admin/Section/index.jsx
+++ b/frontend/src/pages/Admin/Section/index.jsx
@@ -1,18 +1,18 @@
-import { useEffect, useState } from "react";
+import React, { useEffect, useState } from "react";
import Sidebar from "@/components/SettingsSidebar";
import { isMobile } from "react-device-detect";
import * as Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css";
-import { UserPlus } from "@phosphor-icons/react";
+import { Folder, FolderOpen, Pencil, Trash } from "@phosphor-icons/react";
import Admin from "@/models/admin";
-import UserRow from "../Users/UserRow";
import useUser from "@/hooks/useUser";
-import NewUserModal from "../Users/NewUserModal";
import { useModal } from "@/hooks/useModal";
import ModalWrapper from "@/components/ModalWrapper";
import CTAButton from "@/components/lib/CTAButton";
+import { TreeSelect } from "antd"; // 使用 antd 的 TreeSelect 组件
+import "antd/dist/reset.css"; // 引入 antd 样式
-export default function AdminUsers() {
+export default function AdminDepartments() {
const { isOpen, openModal, closeModal } = useModal();
return (
@@ -26,11 +26,11 @@ export default function AdminUsers() {
- 部门树状结构
+ 管理所有部门及其层级结构。删除部门将同时删除其子部门。
@@ -38,33 +38,33 @@ export default function AdminUsers() {
onClick={openModal}
className="mt-3 mr-0 mb-4 md:-mb-6 z-10"
>
- Add user
+ 添加部门
-
+
-
+
);
}
-function UsersContainer() {
+function DepartmentsContainer() {
const { user: currUser } = useUser();
const [loading, setLoading] = useState(true);
- const [users, setUsers] = useState([]);
+ const [departments, setDepartments] = useState([]);
useEffect(() => {
- async function fetchUsers() {
- const _users = await Admin.users();
- setUsers(_users);
+ async function fetchDepartments() {
+ const _departments = await Admin.depts();
+ setDepartments(buildTree(_departments)); // 将部门列表转换为树状结构
setLoading(false);
}
- fetchUsers();
+ fetchDepartments();
}, []);
if (loading) {
@@ -84,114 +84,202 @@ function UsersContainer() {
return (
-
-
- Username
-
-
- Role
-
-
- Date Added
-
-
- {" "}
-
-
+
+
+ 部门名称
+
+
+ 排序
+
+
+ 状态
+
+
+ 创建时间
+
+
+ 操作
+
+
- {users.map((user) => (
-
- ))}
+ {departments.map((dept) => (
+
+ ))}
);
}
-const ROLE_HINT = {
- default: [
- "Can only send chats with workspaces they are added to by admin or managers.",
- "Cannot modify any settings at all.",
- ],
- manager: [
- "Can view, create, and delete any workspaces and modify workspace-specific settings.",
- "Can create, update and invite new users to the instance.",
- "Cannot modify LLM, vectorDB, embedding, or other connections.",
- ],
- admin: [
- "Highest user level privilege.",
- "Can see and do everything across the system.",
- ],
-};
-
-export function RoleHintDisplay({ role }) {
+function DepartmentRow({ dept }) {
+ const [expanded, setExpanded] = useState(false);
+
+ const toggleExpand = () => {
+ setExpanded(!expanded);
+ };
+
return (
-
-
Permissions
-
- {ROLE_HINT[role ?? "default"].map((hints, i) => {
- return (
-
- {hints}
-
- );
- })}
-
-
+ <>
+
+
+
+ {dept.children && dept.children.length > 0 ? (
+
+ {expanded ? (
+
+ ) : (
+
+ )}
+
+ ) : (
+
+ )}
+ {dept.deptName}
+
+
+ {dept.orderNum}
+
+
+ {dept.status === 0 ? "启用" : "停用"}
+
+
+
+ {new Date(dept.createdAt).toLocaleDateString()}
+
+
+
+
+
+ {expanded &&
+ dept.children &&
+ dept.children.map((child) => (
+
+ ))}
+ >
);
}
-export function MessageLimitInput({ enabled, limit, updateState, role }) {
- if (role === "admin") return null;
+function NewDepartmentModal({ closeModal }) {
+ const [formData, setFormData] = useState({
+ deptName: "",
+ parentId: null, // 上级部门 ID
+ orderNum: 0,
+ status: 0,
+ });
+
+ const [departments, setDepartments] = useState([]); // 部门数据
+ const [treeData, setTreeData] = useState([]); // 树状结构数据
+
+ // 获取部门数据并转换为树状结构
+ useEffect(() => {
+ async function fetchDepartments() {
+ const _departments = await Admin.depts();
+ setDepartments(_departments);
+ setTreeData(buildTree(_departments)); // 将部门数据转换为树状结构
+ }
+ fetchDepartments();
+ }, []);
+
+ // 将部门数据转换为树状结构
+ function buildTree(depts, parentId = 0) {
+ return depts
+ .filter((dept) => dept.parentId === parentId)
+ .map((dept) => ({
+ title: dept.deptName, // 显示部门名称
+ value: dept.deptId, // 部门 ID
+ children: buildTree(depts, dept.deptId), // 递归查找子部门
+ }));
+ }
+
+ // 处理表单提交
+ const handleSubmit = async () => {
+ await Admin.addDepts(formData);
+ closeModal();
+ };
+
return (
-
-
-
-
- Limit messages per day
-
-
- {
- updateState((prev) => ({
- ...prev,
- enabled: e.target.checked,
- }));
- }}
- className="peer sr-only"
- />
-
+
+
+ 添加部门
+
+
+ {/* 上级部门选择器 */}
+
+
+ 上级部门
+
+ setFormData({ ...formData, parentId: value })
+ }
+ placeholder="请选择上级部门"
+ className="w-full"
+ dropdownStyle={{ maxHeight: 400, overflow: "auto" }}
+ />
-
- Restrict this user to a number of successful queries or chats within a
- 24 hour window.
-
+
+ {/* 部门名称输入框 */}
+
+ setFormData({ ...formData, deptName: e.target.value })
+ }
+ className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
+ />
+
+ {/* 排序输入框 */}
+
+ setFormData({ ...formData, orderNum: Number(e.target.value) })
+ }
+ className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
+ />
+
+ {/* 状态选择器 */}
+
+ setFormData({ ...formData, status: Number(e.target.value) })
+ }
+ className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
+ >
+ 启用
+ 停用
+
+
+ {/* 保存按钮 */}
+
保存
- {enabled && (
-
-
- Message limit per day
-
-
- e.target.blur()}
- onChange={(e) => {
- updateState({
- enabled: true,
- limit: Number(e?.target?.value || 0),
- });
- }}
- value={limit}
- min={1}
- className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
- />
-
-
- )}
);
}
+
+// 将部门列表转换为树状结构
+function buildTree(departments, parentId = 0) {
+ return departments
+ .filter((dept) => dept.parentId === parentId)
+ .map((dept) => ({
+ ...dept,
+ children: buildTree(departments, dept.deptId),
+ }));
+}
diff --git a/package-lock.json b/package-lock.json
index ef89d17..8aa8aeb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -272,7 +272,7 @@
},
"node_modules/antd": {
"version": "5.24.2",
- "resolved": "https://registry.npmjs.org/antd/-/antd-5.24.2.tgz",
+ "resolved": "https://registry.npmmirror.com/antd/-/antd-5.24.2.tgz",
"integrity": "sha512-7Z9HsE3ZIK3sE/WuUqii3w7Gl1IJuRL21sDUTtkN95JS5KhRYP8ISv7m/HxsJ3Mn/yxgojBCgLPJ212+Dn+aPw==",
"dependencies": {
"@ant-design/colors": "^7.2.0",
@@ -413,7 +413,7 @@
},
"node_modules/moment": {
"version": "2.30.1",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+ "resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
diff --git a/server/endpoints/admin.js b/server/endpoints/admin.js
index 80bda1d..5be2fcc 100644
--- a/server/endpoints/admin.js
+++ b/server/endpoints/admin.js
@@ -34,6 +34,7 @@ function adminEndpoints(app) {
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
async (_request, response) => {
try {
+ console.log("调用了调用了admin/users");
const users = await User.where();
response.status(200).json({ users });
} catch (e) {