- 添加系统管理相关的 API 接口和功能模块 - 实现组织机构管理、用户管理、角色管理等系统管理功能 - 新增系统管理相关的页面和组件 - 更新侧边栏菜单,增加系统管理选项 - 添加多语言支持main
parent
de7e203502
commit
9f8cec8233
@ -0,0 +1,89 @@ |
||||
import { |
||||
orgizationParams, |
||||
} from '@/types/system'; |
||||
import { DELETE, GET, POST,PUT } from '../index'; |
||||
//组织机构管理
|
||||
|
||||
//1.查询全部组织机构
|
||||
export const getAllOrg = (prpos: orgizationParams) => { |
||||
return GET<orgizationParams, any>(`/api/dept/list`,prpos); |
||||
}; |
||||
|
||||
//2.新增部门信息
|
||||
export const addAllOrg = (prpos:any) => { |
||||
return POST<any,[]>('/api/dept/create', prpos); |
||||
}; |
||||
|
||||
//2.修改部门信息
|
||||
// 导出一个函数addAllOrg,接收一个参数prpos
|
||||
export const updateAllOrg = (prpos:any) => { |
||||
// 调用POST函数,发送一个POST请求,请求地址为'/api/dept/create',参数为prpos,返回一个Promise对象
|
||||
return PUT<any,[]>('/api/dept/update', prpos); |
||||
}; |
||||
|
||||
//3.查询部门详情
|
||||
export const getDetailAllOrg = (id: number) => { |
||||
return GET<any, any>(`/api/dept/`+id); |
||||
}; |
||||
|
||||
//4.删除部门
|
||||
export const deleteAllOrg = (id: number) => { |
||||
return DELETE<any, any>(`/api/dept/`+id); |
||||
}; |
||||
|
||||
|
||||
// export const promptTemplateLoad = (props: PromptTemplateLoadProps) => {
|
||||
// return POST<PromptTemplateLoadProps, PromptTemplateLoadResponse>(
|
||||
// `/prompt/template/load?prompt_type=${props.prompt_type}&target=${props.target}`,
|
||||
// props,
|
||||
// );
|
||||
// };
|
||||
|
||||
// export const promptResponseVerify = (props: PromptResponseVerifyProps) => {
|
||||
// return POST<PromptResponseVerifyProps, Record<string, string>>('/prompt/response/verify', props);
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * 创建prompt
|
||||
// */
|
||||
// export const addPrompt = (data: OperatePromptParams) => {
|
||||
// return POST<OperatePromptParams, []>('/prompt/add', data);
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * 编辑prompt
|
||||
// */
|
||||
// export const updatePrompt = (data: OperatePromptParams) => {
|
||||
// return POST<OperatePromptParams, []>('/prompt/update', data);
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * 删除prompt
|
||||
// */
|
||||
// export const deletePrompt = (data: OperatePromptParams) => {
|
||||
// return POST<OperatePromptParams, null>('/prompt/delete', data);
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * prompt列表
|
||||
// */
|
||||
// export const getPromptList = (data: Record<string, any>) => {
|
||||
// return POST<Record<string, any>, PromptListResponse>(
|
||||
// `/prompt/query_page?page=${data.page}&page_size=${data.page_size}`,
|
||||
// data,
|
||||
// );
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * LLM测试
|
||||
// */
|
||||
// export const llmTest = (data: DebugParams) => {
|
||||
// return POST<DebugParams, Record<string, any>>('/prompt/template/debug', data);
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * llm输出验证
|
||||
// */
|
||||
// export const llmOutVerify = (data: LlmOutVerifyParams) => {
|
||||
// return POST<LlmOutVerifyParams, Record<string, any>>('/prompt/response/verify', data);
|
||||
// };
|
@ -0,0 +1,103 @@ |
||||
|
||||
import { createFromIconfontCN } from '@ant-design/icons'; |
||||
import { |
||||
MenuFoldOutlined, |
||||
UsergroupDeleteOutlined, |
||||
UserOutlined |
||||
} from '@ant-design/icons'; |
||||
import { ConfigProvider, Tabs } from 'antd'; |
||||
import { t } from 'i18next'; |
||||
import { useRouter } from 'next/router'; |
||||
import React from 'react'; |
||||
import './style.css'; |
||||
|
||||
const IconFont = createFromIconfontCN({ |
||||
scriptUrl: '//at.alicdn.com/t/c/font_4926951_si989wen79f.js', |
||||
}); |
||||
function SystemConstructLayout({ children }: { children: React.ReactNode }) { |
||||
const items = [ |
||||
//菜单
|
||||
{ |
||||
key: 'menu', |
||||
name: t('menu_management'), |
||||
path: '/menu', |
||||
icon: <MenuFoldOutlined /> |
||||
}, |
||||
//角色
|
||||
{ |
||||
key: 'role', |
||||
name: t('role_management'), |
||||
icon: <UsergroupDeleteOutlined />, |
||||
path: '/role', |
||||
}, |
||||
//机构
|
||||
{ |
||||
key: 'organization', |
||||
name: t('institutional_framework'), |
||||
path: '/organization', |
||||
icon: <IconFont type="icon-company" />, |
||||
}, |
||||
//用户
|
||||
{ |
||||
key: 'user', |
||||
name: t('user_information_management'), |
||||
icon: <UserOutlined />, |
||||
path: '/user', |
||||
}, |
||||
|
||||
]; |
||||
const router = useRouter(); |
||||
const activeKey = router.pathname.split('/')[2]; |
||||
// const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; // unused
|
||||
|
||||
return ( |
||||
<div className='flex flex-col h-full w-full dark:bg-gradient-dark bg-gradient-light bg-cover bg-center'> |
||||
<ConfigProvider |
||||
theme={{ |
||||
components: { |
||||
Button: { |
||||
// defaultBorderColor: 'white',
|
||||
}, |
||||
Segmented: { |
||||
itemSelectedBg: '#2867f5', |
||||
itemSelectedColor: 'white', |
||||
}, |
||||
}, |
||||
}} |
||||
> |
||||
<Tabs |
||||
// tabBarStyle={{
|
||||
// background: '#edf8fb',
|
||||
// border: 'none',
|
||||
// height: '3.5rem',
|
||||
// padding: '0 1.5rem',
|
||||
// color: !isDarkMode ? 'white' : 'black',
|
||||
// }}
|
||||
activeKey={activeKey} |
||||
items={items.map(items => { |
||||
return { |
||||
key: items.key, |
||||
label: items.name, |
||||
children: children, |
||||
icon: items.icon, |
||||
}; |
||||
})} |
||||
onTabClick={key => { |
||||
router.push(`/system/${key}`); |
||||
}} |
||||
// tabBarExtraContent={
|
||||
// <Button
|
||||
// className='border-none text-white bg-button-gradient h-full flex items-center'
|
||||
// icon={<PlusOutlined className='text-base' />}
|
||||
// // onClick={handleCreate}
|
||||
// >
|
||||
// {t('create_app')}
|
||||
// </Button>
|
||||
// }
|
||||
/> |
||||
</ConfigProvider> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
export default SystemConstructLayout; |
@ -0,0 +1,11 @@ |
||||
import SystemConstructConstruct from '@/new-components/layout/SystemConstruct'; |
||||
|
||||
function index() { |
||||
return ( |
||||
<SystemConstructConstruct> |
||||
<div>index</div> |
||||
</SystemConstructConstruct> |
||||
); |
||||
} |
||||
|
||||
export default index; |
@ -0,0 +1,15 @@ |
||||
.color-red { |
||||
color: red |
||||
} |
||||
|
||||
.text-center { |
||||
text-align: center |
||||
} |
||||
|
||||
.p-10 { |
||||
padding: 10px |
||||
} |
||||
|
||||
.construc-container table{ |
||||
display: table;; |
||||
} |
@ -0,0 +1,369 @@ |
||||
import { Space, Switch, Input, Table, Form, Button, Select, Popconfirm, App, Modal, InputNumber, TreeSelect } from 'antd'; |
||||
import { apiInterceptors, getAllOrg, addAllOrg, getDetailAllOrg, updateAllOrg, deleteAllOrg } from '@/client/api'; |
||||
import useUser from '@/hooks/use-user'; |
||||
import SystemConstructConstruct from '@/new-components/layout/SystemConstruct'; |
||||
import styles from './index.module.scss'; |
||||
import type { ColumnsType } from 'antd/es/table'; |
||||
import React, { useEffect, useState } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { IPrompt, orginzationResponse } from '@/types/prompt'; |
||||
import { useRequest } from 'ahooks'; |
||||
import { TFunction } from 'i18next'; |
||||
|
||||
|
||||
|
||||
const { Option } = Select; |
||||
type LayoutType = Parameters<typeof Form>[0]['layout']; |
||||
|
||||
|
||||
const Prompt = () => { |
||||
const [editId, setEditId] = useState<number | null>(null); |
||||
const [modalType, setModalType] = useState<'add' | 'edit'>('add'); |
||||
const [addForm] = Form.useForm(); |
||||
const { message } = App.useApp(); |
||||
const [addLoading, setAddLoading] = useState(false); |
||||
const { t } = useTranslation(); |
||||
const [formLayout] = useState<LayoutType>('inline'); |
||||
const [promptList, setPromptList] = useState<orginzationResponse>(); |
||||
|
||||
const DeleteBtn: React.FC<{ record: IPrompt; }> = ({ record }) => { |
||||
const userInfo = useUser(); |
||||
const { t } = useTranslation(); |
||||
|
||||
const { message } = App.useApp(); |
||||
|
||||
// 删除prompt
|
||||
const { run: deletePromptRun, loading: deleteLoading } = useRequest( |
||||
async record => { |
||||
await deleteAllOrg(record?.id); |
||||
}, |
||||
{ |
||||
manual: true, |
||||
onSuccess: async (res: any) => { |
||||
if (res?.success == true) { |
||||
message.success('删除成功'); |
||||
await getPrompts(); |
||||
}else{ |
||||
message.error('删除失败,请联系管理员'); |
||||
} |
||||
}, |
||||
onError: (err: any) => { |
||||
message.error(err?.message); |
||||
}, |
||||
}, |
||||
); |
||||
|
||||
// if (userInfo?.id !== record?.id) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
return ( |
||||
<Popconfirm title='确认删除吗?' onConfirm={async () => await deletePromptRun(record)}> |
||||
<Button loading={deleteLoading}>{t('Delete')}</Button> |
||||
</Popconfirm> |
||||
); |
||||
}; |
||||
const getColumns = (t: TFunction, handleEdit: (prompt: IPrompt) => void): ColumnsType<IPrompt> => [ |
||||
{ |
||||
title: t('department_name'), |
||||
dataIndex: 'name', |
||||
key: 'name', |
||||
width: '25%', |
||||
ellipsis: true, |
||||
showSorterTooltip: true, |
||||
}, |
||||
{ |
||||
title: t('principal'), |
||||
dataIndex: 'leader_user_id', |
||||
key: 'leader_user_id', |
||||
// width: '10%',
|
||||
}, |
||||
{ |
||||
title: t('sort'), |
||||
dataIndex: 'sort', |
||||
// width: '10%',
|
||||
key: 'sort', |
||||
}, { |
||||
title: t('Status'), |
||||
dataIndex: 'status', |
||||
// width: '10%',
|
||||
key: 'status', |
||||
}, { |
||||
title: t('create_time'), |
||||
dataIndex: 'create_time', |
||||
width: '20%', |
||||
key: 'create_time', |
||||
}, { |
||||
title: t('Operation'), |
||||
dataIndex: 'operate', |
||||
key: 'operate', |
||||
// width: '25%',
|
||||
render: (_, record) => ( |
||||
<Space align='center'> |
||||
<Button |
||||
onClick={() => { |
||||
handleEdit(record); |
||||
}} |
||||
type='primary' |
||||
> |
||||
{t('Edit')} |
||||
</Button> |
||||
<DeleteBtn record={record} /> |
||||
</Space> |
||||
), |
||||
}, |
||||
]; |
||||
const handleEditBtn = async (prompt: IPrompt) => { |
||||
setModalType('edit'); |
||||
setEditId(prompt.id); |
||||
try { |
||||
const [_, data] = await apiInterceptors(getDetailAllOrg(prompt.id)); |
||||
if (data) { |
||||
addForm.setFieldsValue({ |
||||
...data, |
||||
parent_id: data.parent_id || undefined, |
||||
status: data.status === 1 |
||||
}); |
||||
setModalVisible(true); |
||||
} |
||||
} catch (err) { |
||||
message.error('获取详情失败'); |
||||
} |
||||
}; |
||||
|
||||
const buildTree = (data: IPrompt[]): IPrompt[] => { |
||||
const map = new Map<number, IPrompt>(); |
||||
const tree: IPrompt[] = []; |
||||
|
||||
// 创建哈希映射时不自动添加children字段
|
||||
data.forEach(item => { |
||||
map.set(item.id, { ...item }); |
||||
}); |
||||
|
||||
data.forEach(item => { |
||||
const node = map.get(item.id)!; |
||||
if (item.parent_id !== 0) { |
||||
const parent = map.get(item.parent_id); |
||||
if (parent) { |
||||
// 只在需要时初始化children数组
|
||||
parent.children = parent.children || []; |
||||
parent.children.push(node); |
||||
} else { |
||||
tree.push(node); |
||||
} |
||||
} else { |
||||
tree.push(node); |
||||
} |
||||
}); |
||||
|
||||
// 后处理:移除空children
|
||||
const removeEmptyChildren = (nodes: IPrompt[]): IPrompt[] => { |
||||
return nodes.map(node => ({ |
||||
...node, |
||||
children: node.children?.length ? removeEmptyChildren(node.children) : undefined |
||||
})); |
||||
}; |
||||
|
||||
return removeEmptyChildren(tree); |
||||
}; |
||||
const { |
||||
run: getPrompts, loading: loadingOrgs |
||||
} = useRequest( |
||||
async (params?: { name?: string; status?: string }) => { // 改为对象参数
|
||||
const [_, data] = await apiInterceptors( |
||||
getAllOrg(params) // 直接传递参数对象
|
||||
); |
||||
return data; |
||||
}, |
||||
{ |
||||
manual: true, |
||||
onSuccess: data => { |
||||
const treeData = buildTree(data.dept_list); |
||||
setPromptList({ ...data, dept_list: treeData }); |
||||
}, |
||||
}, |
||||
); |
||||
const handleSearch = async () => { |
||||
try { |
||||
const values = await addForm.validateFields(); |
||||
await getPrompts({ |
||||
name: values.name || undefined, |
||||
status: values.status || undefined |
||||
}); |
||||
} catch (err) { |
||||
console.error('表单验证失败:', err); |
||||
} |
||||
}; |
||||
const handleReset = async () => { |
||||
addForm.resetFields(); // 清空表单
|
||||
await getPrompts(); // 重新获取全部数据
|
||||
}; |
||||
const [modalVisible, setModalVisible] = useState(false); |
||||
const [deptTree, setDeptTree] = useState<any[]>([]); |
||||
|
||||
// 转换部门列表为树形结构
|
||||
const convertToTree = (data: IPrompt[], parentId = 0, excludeId?: number): any[] => { |
||||
return data |
||||
.filter(item => !excludeId || item.id !== excludeId) // 仅在编辑时过滤
|
||||
.filter(item => item.parent_id === parentId) |
||||
.map(item => ({ |
||||
title: item.name, |
||||
value: item.id, |
||||
children: convertToTree(data, item.id, excludeId), |
||||
})); |
||||
}; |
||||
|
||||
useEffect(() => { |
||||
if (promptList?.dept_list) { |
||||
// 仅在编辑模式排除当前编辑ID
|
||||
const excludeId = modalType === 'edit' ? editId : undefined; |
||||
setDeptTree(convertToTree(promptList.dept_list, 0, excludeId)); |
||||
} |
||||
}, [promptList, modalType, editId]); |
||||
|
||||
const buttonItemLayout = |
||||
formLayout === 'inline' |
||||
? { |
||||
wrapperCol: { span: 14, offset: 6 }, |
||||
} |
||||
: null; |
||||
|
||||
useEffect(() => { |
||||
getPrompts(); |
||||
}, []); |
||||
|
||||
return ( |
||||
<SystemConstructConstruct> |
||||
<div className={`px-6 py-2 ${styles['construc-container']} md:p-6 h-[90vh] overflow-y-auto`}> |
||||
<Form className={`${styles['p-10']} ${styles['color-red ']}`} |
||||
onFinish={handleSearch} |
||||
layout={'inline'} |
||||
form={addForm} |
||||
initialValues={{ layout: formLayout }} |
||||
> |
||||
<Form.Item label="部门名称" name="name"> |
||||
<Input placeholder="请输入部门名称" /> |
||||
</Form.Item> |
||||
<Form.Item label="部门状态" name="status"> |
||||
<Select style={{ width: 100 }} placeholder="请选择部门状态"> |
||||
<Option value="0">开启</Option> |
||||
<Option value="1">关闭</Option> |
||||
</Select> |
||||
</Form.Item> |
||||
<Form.Item {...buttonItemLayout}> |
||||
<Button htmlType="submit" type="primary">搜索</Button> |
||||
</Form.Item> |
||||
<Form.Item {...buttonItemLayout}> |
||||
<Button onClick={handleReset} type="primary">重置</Button> |
||||
</Form.Item> |
||||
<Form.Item {...buttonItemLayout}> |
||||
<Button type="primary" onClick={() => { |
||||
setModalType('add'); |
||||
setModalVisible(true); |
||||
addForm.resetFields(); |
||||
}}>新增</Button> |
||||
</Form.Item> |
||||
</Form> |
||||
<Modal |
||||
title={modalType === 'add' ? '新增部门' : '编辑部门'} |
||||
open={modalVisible} |
||||
onCancel={() => { |
||||
setModalVisible(false); |
||||
addForm.resetFields(); |
||||
}} |
||||
footer={null} |
||||
> |
||||
<Form |
||||
form={addForm} |
||||
labelCol={{ span: 6 }} |
||||
wrapperCol={{ span: 16 }} |
||||
onFinish={async (values) => { |
||||
setAddLoading(true); |
||||
try { |
||||
let apiCall; |
||||
if (modalType === 'edit' && editId) { |
||||
apiCall = updateAllOrg({ ...values, status: values.status ? 1 : 0, id: editId }); |
||||
} else { |
||||
apiCall = addAllOrg({ |
||||
...values, |
||||
status: values.status ? 1 : 0, |
||||
}); |
||||
} |
||||
|
||||
const [_, data] = await apiInterceptors(apiCall); |
||||
if (data) { |
||||
message.success(modalType === 'edit' ? '编辑成功' : '新增成功'); |
||||
setModalVisible(false); |
||||
addForm.resetFields(); |
||||
await getPrompts(); |
||||
setEditId(null); |
||||
} |
||||
} catch (err) { |
||||
console.error('操作失败:', err); |
||||
} finally { |
||||
setAddLoading(false); |
||||
} |
||||
}} |
||||
> |
||||
<Form.Item label="部门名称" name="name" rules={[{ required: true }]}> |
||||
<Input /> |
||||
</Form.Item> |
||||
|
||||
<Form.Item label="上级部门" name="parent_id"> |
||||
<TreeSelect |
||||
treeData={deptTree} |
||||
placeholder="请选择上级部门" |
||||
treeDefaultExpandAll |
||||
/> |
||||
</Form.Item> |
||||
|
||||
<Form.Item label="排序" name="sort" rules={[{ required: true }]}> |
||||
<InputNumber min={0} style={{ width: '100%' }} /> |
||||
</Form.Item> |
||||
|
||||
<Form.Item label="负责人ID" name="leader_user_id" rules={[{ required: true }]}> |
||||
<InputNumber min={1} style={{ width: '100%' }} /> |
||||
</Form.Item> |
||||
|
||||
<Form.Item label="联系电话" name="phone"> |
||||
<Input /> |
||||
</Form.Item> |
||||
|
||||
<Form.Item label="邮箱" name="email"> |
||||
<Input /> |
||||
</Form.Item> |
||||
|
||||
<Form.Item label="状态" name="status" valuePropName="checked"> |
||||
<Switch checkedChildren="开启" unCheckedChildren="关闭" /> |
||||
</Form.Item> |
||||
|
||||
<Form.Item wrapperCol={{ offset: 8, span: 16 }}> |
||||
<Space> |
||||
<Button onClick={() => setModalVisible(false)}>取消</Button> |
||||
<Button type="primary" htmlType="submit" loading={addLoading}>提交</Button> |
||||
</Space> |
||||
</Form.Item> |
||||
</Form> |
||||
</Modal> |
||||
|
||||
<Table |
||||
columns={getColumns(t, handleEditBtn)} |
||||
dataSource={promptList?.dept_list || []} |
||||
rowKey={record => record.id} |
||||
loading={loadingOrgs} |
||||
childrenColumnName="children" |
||||
defaultExpandAllRows={true} |
||||
// pagination={{
|
||||
// pageSize: 10,
|
||||
// total: promptList?.total_count,
|
||||
// onChange: async (page, page_size) => {
|
||||
// await getPrompts(page, page_size);
|
||||
// },
|
||||
// }}
|
||||
/> |
||||
</div> |
||||
</SystemConstructConstruct> |
||||
); |
||||
}; |
||||
|
||||
export default Prompt; |
@ -0,0 +1,7 @@ |
||||
export interface orgizationParams { |
||||
params?:{ |
||||
name?: string; |
||||
status?: number;}; |
||||
name?: string; |
||||
status?: number; |
||||
} |
Loading…
Reference in new issue