@@ -16,6 +16,7 @@ import './index.css' | |||||
import TabsLayout from './tabs-layout'; | import TabsLayout from './tabs-layout'; | ||||
import Content from './content'; | import Content from './content'; | ||||
import userService from '@/request/service/user'; | import userService from '@/request/service/user'; | ||||
import authService from '@/request/service/auth'; | |||||
import { useRequest } from "@/hooks/use-request"; | import { useRequest } from "@/hooks/use-request"; | ||||
// import menuData from '../../mock/menu.json' | // import menuData from '../../mock/menu.json' | ||||
@@ -37,7 +38,7 @@ const BasicLayout: React.FC = () => { | |||||
{ manual: true } | { manual: true } | ||||
); | ); | ||||
const { data: menuList, run: listMenus} = useRequest(userService.listMenus, { manual: true }); | |||||
const { data: menuList, run: listMenus} = useRequest(authService.listMenus, { manual: true }); | |||||
const formatMenus = ( | const formatMenus = ( | ||||
menus: Menu[], | menus: Menu[], | ||||
@@ -0,0 +1,16 @@ | |||||
export interface DepartmentVO { | |||||
id?: number | |||||
name: string | |||||
parentId: number | |||||
status: number | |||||
sort: number | |||||
leaderUserId: number | |||||
phone: string | |||||
email: string | |||||
createTime: Date | |||||
} | |||||
export interface DepartmentPageReqVO { | |||||
name?: string | |||||
status?: number | |||||
} |
@@ -2,6 +2,7 @@ export * from './user.data.ts' | |||||
export * from './product.data.ts' | export * from './product.data.ts' | ||||
export * from './role.data.ts' | export * from './role.data.ts' | ||||
export * from './position.data.ts' | export * from './position.data.ts' | ||||
export * from './department.data.ts' | |||||
export interface ResponseDTO<T>{ | export interface ResponseDTO<T>{ | ||||
code: number; | code: number; | ||||
@@ -1,3 +1,5 @@ | |||||
import { DepartmentVO } from "."; | |||||
export interface LoginDTO { | export interface LoginDTO { | ||||
captchaVerification: string; | captchaVerification: string; | ||||
username: string; | username: string; | ||||
@@ -79,7 +81,7 @@ export interface User { | |||||
id: number; | id: number; | ||||
username: string; | username: string; | ||||
nickname: string; | nickname: string; | ||||
deptId:number; | |||||
deptId: number; | |||||
postIds: number; | postIds: number; | ||||
mobile: string; | mobile: string; | ||||
email: string; | email: string; | ||||
@@ -89,3 +91,29 @@ export interface User { | |||||
avatar?: string; | avatar?: string; | ||||
status: number; | status: number; | ||||
} | } | ||||
export interface UserVO { | |||||
id: number | |||||
username: string | |||||
nickname: string | |||||
dept: DepartmentVO | |||||
deptId: number | |||||
postIds: string[] | |||||
email: string | |||||
mobile: string | |||||
sex: number | |||||
avatar: string | |||||
loginIp: string | |||||
status: number | |||||
remark: string | |||||
loginDate: number | |||||
createTime: number | |||||
} | |||||
export interface UserPageReqVO extends PageParam { | |||||
deptId?: number | |||||
username?: string | |||||
mobile?: string | |||||
status?: number | |||||
createTime?: Date[] | |||||
} |
@@ -0,0 +1,127 @@ | |||||
import React, { useEffect, useState } from 'react' | |||||
import { Form, Input, InputNumber, Radio, Modal, Switch } from 'antd'; | |||||
import positonService from '@/request/service/positon'; | |||||
import { useRequest } from '@/hooks/use-request'; | |||||
import type { DepartmentVO } from '@/models' | |||||
import { antdUtils } from '@/utils/antd'; | |||||
const layout = { | |||||
labelCol: { span: 4, }, | |||||
wrapperCol: { span: 16 }, | |||||
bordered: false, | |||||
}; | |||||
interface EditorProps { | |||||
visible: boolean; | |||||
onCancel: (flag?: boolean) => void; | |||||
onSave: (role: DepartmentVO) => void; | |||||
data?: DepartmentVO | null; | |||||
} | |||||
const DepartmentEditor: React.FC<EditorProps> = (props) => { | |||||
const { visible, onCancel, onSave, data } = props; | |||||
const { runAsync: updatePostApi } = useRequest(positonService.updatePostApi, { manual: true }); | |||||
const { runAsync: createPostApi } = useRequest(positonService.createPostApi, { manual: true }); | |||||
const isEdit = !!data; | |||||
const [saveLoading, setSaveLoading] = useState(false); | |||||
const [form] = Form.useForm(); | |||||
useEffect(() => { | |||||
if (visible) { | |||||
if (data) { | |||||
form.setFieldsValue(data); | |||||
} | |||||
} else { | |||||
form.resetFields(); | |||||
} | |||||
}, [visible]); | |||||
const save = async () => { | |||||
setSaveLoading(true); | |||||
const fieldValues = form.getFieldsValue(); | |||||
const newValue = isEdit ? { ...data, ...fieldValues } : fieldValues; | |||||
const [error, { msg, code }] = isEdit ? await updatePostApi(newValue) : await createPostApi(newValue); | |||||
if (!error && code === 0) { | |||||
onSave(newValue); | |||||
} else { | |||||
antdUtils.message?.open({ | |||||
type: 'error', | |||||
content: msg ?? '操作失败', | |||||
}); | |||||
} | |||||
setSaveLoading(false); | |||||
} | |||||
return ( | |||||
<> | |||||
<Modal | |||||
open={visible} | |||||
title="新建" | |||||
width={640} | |||||
onOk={save} | |||||
onCancel={() => onCancel()} | |||||
destroyOnClose | |||||
> | |||||
<Form | |||||
form={form} | |||||
{...layout} | |||||
onFinish={save} | |||||
labelCol={{ flex: '0 0 100px' }} | |||||
wrapperCol={{ span: 16 }} | |||||
> | |||||
<Form.Item name="name" label="部门名称" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入岗位名称', | |||||
}, | |||||
]} | |||||
> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="phone" label="联系电话" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入电话号码', | |||||
}, | |||||
]} | |||||
> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="email" label="邮箱" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入邮箱', | |||||
}, | |||||
]} | |||||
> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="sort" label="显示排序" > | |||||
<InputNumber min={1} max={100} /> | |||||
</Form.Item> | |||||
<Form.Item name="status" label="状态"> | |||||
<Radio.Group options={[ | |||||
{ value: 0, label: "开启" }, | |||||
{ value: 1, label: "关闭" } | |||||
]} optionType="default"> | |||||
</Radio.Group> | |||||
</Form.Item> | |||||
</Form> | |||||
</Modal> | |||||
</> | |||||
) | |||||
} | |||||
export default DepartmentEditor; |
@@ -0,0 +1,189 @@ | |||||
import { Space, Table, Button, Input, Select, Divider, Tag, Card, Badge, Form } from 'antd'; | |||||
import type { TableColumnsType } from 'antd'; | |||||
import { t } from '@/utils/i18n'; | |||||
import React, { useState, useEffect } from 'react'; | |||||
import { PlusOutlined, ExclamationCircleFilled, SearchOutlined, UndoOutlined } from '@ant-design/icons'; | |||||
import type { DepartmentVO } from '@/models' | |||||
import { antdUtils } from '@/utils/antd'; | |||||
import { useRequest } from '@/hooks/use-request'; | |||||
import departmentService from '@/request/service/deparatment'; | |||||
import { formatDate } from '@/utils/formatTime' | |||||
import DepartmentEditor from './create-department'; | |||||
export default () => { | |||||
const [editorVisable, seEditorVisable] = useState<boolean>(false); | |||||
const [editData, seEditData] = useState<DepartmentVO>(); | |||||
const [dataSource, setDataSource] = useState<DepartmentVO[]>([]); | |||||
const [searchFrom] = Form.useForm(); | |||||
const { runAsync: getPageApi } = useRequest(departmentService.getDeptPageApi, { manual: true }); | |||||
const { runAsync: deleteApi } = useRequest(departmentService.deleteDeptApi, { manual: true }); | |||||
const load = async () => { | |||||
const [error, { data }] = await getPageApi(searchFrom.getFieldsValue()); | |||||
if (!error) { | |||||
setDataSource(data); | |||||
} | |||||
}; | |||||
const showDeleteConfirm = (data: DepartmentVO) => { | |||||
antdUtils.modal?.confirm({ | |||||
title: '确认要将该菜单删除吗?', | |||||
icon: <ExclamationCircleFilled />, | |||||
content: '请注意删除以后不可恢复!', | |||||
okText: '删除', | |||||
okType: 'danger', | |||||
cancelText: '取消', | |||||
onOk() { | |||||
return new Promise(async (resolve) => { | |||||
const [error, { code, msg} ] = await deleteApi({ id: data.id }); | |||||
if(error || code !== 0) { | |||||
antdUtils.message?.open({ type: 'error', content: msg??'操作失败'}) | |||||
} else { | |||||
antdUtils.message?.open({ type: 'success', content: '删除成功'}) | |||||
} | |||||
await load(); | |||||
resolve('') | |||||
}).catch(() => antdUtils.message?.open({ | |||||
type: 'error', | |||||
content: '操作失败', | |||||
})); | |||||
}, | |||||
onCancel() { | |||||
}, | |||||
}); | |||||
}; | |||||
const columns: TableColumnsType<DepartmentVO> = [ | |||||
{ | |||||
title: '部门名称', | |||||
dataIndex: 'name', | |||||
key: 'name', | |||||
align: 'right', | |||||
width: 200, | |||||
}, | |||||
// { | |||||
// title: '负责人', | |||||
// dataIndex: 'id', | |||||
// key: 'id', | |||||
// align: 'center', | |||||
// width: 100, | |||||
// }, | |||||
{ | |||||
title: '联系电话', | |||||
dataIndex: 'phone', | |||||
key: 'phone', | |||||
align: 'center', | |||||
width: 150 | |||||
}, | |||||
{ | |||||
title: '显示排序', | |||||
key: 'sort', | |||||
dataIndex: 'sort', | |||||
align: 'center', | |||||
}, | |||||
{ | |||||
title: '状态', | |||||
key: 'status', | |||||
dataIndex: 'status', | |||||
width: 100, | |||||
align: 'center', | |||||
render: (value: number) => { | |||||
return (value === 0 ? <Badge status="success" text="已开启" /> : <Badge status="error" text="已关闭" />) | |||||
} | |||||
}, | |||||
{ | |||||
title: '创建时间', | |||||
key: 'createTime', | |||||
dataIndex: 'createTime', | |||||
width: 200, | |||||
render: (value: number) => { | |||||
return formatDate(new Date(value), "YYYY-mm-dd HH:MM:SS") | |||||
} | |||||
}, | |||||
{ | |||||
title: t("QkOmYwne" /* 操作 */), | |||||
key: 'action', | |||||
render: (value: DepartmentVO, record) => ( | |||||
<Space size="small" split={( <Divider type='vertical' />)}> | |||||
<a onClick={() => { | |||||
seEditData(value); | |||||
seEditorVisable(true); | |||||
}}> | |||||
编辑 | |||||
</a> | |||||
<a onClick={() => { | |||||
showDeleteConfirm(value) | |||||
}}> | |||||
删除 | |||||
</a> | |||||
</Space> | |||||
), | |||||
}, | |||||
]; | |||||
useEffect(() => { | |||||
load(); | |||||
}, []); | |||||
const onReset = () => { | |||||
searchFrom.resetFields() | |||||
load() | |||||
} | |||||
return ( | |||||
<> | |||||
<div> | |||||
<Card className='mt-[4px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]' bodyStyle={{ paddingTop: 4, paddingBottom: 4 }}> | |||||
<div className='flex justify-between content-center'> | |||||
<div className='flex justify-normal items-center'> | |||||
<Form layout='inline' form={searchFrom}> | |||||
<Form.Item name="name" label="部门名称"> | |||||
<Input className='w-[150px]' placeholder='请输入名称' allowClear /> | |||||
</Form.Item> | |||||
<Form.Item className='ml-2 w-[130px]' name="status" label="状态"> | |||||
<Select placeholder="请选择" allowClear > | |||||
<Select.Option value="">全部</Select.Option> | |||||
<Select.Option value="0">开启</Select.Option> | |||||
<Select.Option value="1">关闭</Select.Option> | |||||
</Select> | |||||
</Form.Item> | |||||
</Form> | |||||
<Space.Compact className="ml-5"> | |||||
<Button type='primary' size='large' icon={<SearchOutlined />} onClick={load}> 搜索 </Button> | |||||
<Button type='primary' size='large' icon={<UndoOutlined />} onClick={onReset}> 重置 </Button> | |||||
</Space.Compact> | |||||
</div> | |||||
<div className="py-[4px]"> | |||||
<Button className="ml-5" type='primary' size='large' icon={<PlusOutlined />} onClick={() => { | |||||
seEditData(undefined); | |||||
seEditorVisable(true); | |||||
}}> 新增部门 </Button> | |||||
</div> | |||||
</div> | |||||
</Card> | |||||
<Card className='mt-[4px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]'> | |||||
<Table rowKey="id" | |||||
scroll={{ x: true }} | |||||
columns={columns} | |||||
dataSource={dataSource} | |||||
className='bg-transparent' | |||||
pagination={false}/> | |||||
</Card> | |||||
</div> | |||||
<DepartmentEditor | |||||
onSave={() => { | |||||
load(); | |||||
seEditorVisable(false); | |||||
}} | |||||
onCancel={() => { seEditorVisable(false) }} | |||||
visible={editorVisable} | |||||
data={editData} /> | |||||
</> | |||||
); | |||||
}; | |||||
@@ -190,8 +190,7 @@ const MenuEditor: React.FC<MenuEditorProps> = (props) => { | |||||
<Radio.Group options={[ | <Radio.Group options={[ | ||||
{ value: 1, label: "目录" }, | { value: 1, label: "目录" }, | ||||
{ value: 2, label: "菜单" }, | { value: 2, label: "菜单" }, | ||||
{ value: 3, label: "按钮" }, | |||||
{ value: 4, label: "菜单组" } | |||||
{ value: 3, label: "按钮" } | |||||
]} optionType="button"> | ]} optionType="button"> | ||||
</Radio.Group> | </Radio.Group> | ||||
</Form.Item> | </Form.Item> | ||||
@@ -0,0 +1,211 @@ | |||||
import React, { useEffect, useState } from 'react' | |||||
import { Form, Input, InputNumber, Radio, Modal, Select, TreeSelect } from 'antd'; | |||||
import { EyeTwoTone, EyeInvisibleOutlined } from '@ant-design/icons'; | |||||
import positonService from '@/request/service/positon'; | |||||
import departmentService from '@/request/service/deparatment'; | |||||
import userService from '@/request/service/user'; | |||||
import { useRequest } from '@/hooks/use-request'; | |||||
import type { DepartmentVO, UserVO } from '@/models' | |||||
import { antdUtils } from '@/utils/antd'; | |||||
import type { DefaultOptionType } from 'antd/es/select'; | |||||
const layout = { | |||||
labelCol: { span: 4, }, | |||||
wrapperCol: { span: 16 }, | |||||
}; | |||||
interface EditorProps { | |||||
visible: boolean; | |||||
onCancel: (flag?: boolean) => void; | |||||
onSave: (role: UserVO) => void; | |||||
data?: UserVO | null; | |||||
} | |||||
const toDepartmentTree = (data: DepartmentVO[]) => { | |||||
const root: DepartmentVO[] = data.filter(item => { | |||||
return !data.some(it => it.id === item.parentId) | |||||
}); | |||||
const toDepartmentTreeItem = (item: DepartmentVO): DefaultOptionType => { | |||||
return { | |||||
label: item.name, | |||||
value: item.id, | |||||
children: data.filter(it => it.parentId === item.id).map(it => { | |||||
return toDepartmentTreeItem(it) | |||||
}) | |||||
} | |||||
} | |||||
return root.map(item => { | |||||
return toDepartmentTreeItem(item) | |||||
}) | |||||
} | |||||
const UserEditor: React.FC<EditorProps> = (props) => { | |||||
const { visible, onCancel, onSave, data } = props; | |||||
const { runAsync: updateUserApi } = useRequest(userService.updateUserApi, { manual: true }); | |||||
const { runAsync: createUserApi } = useRequest(userService.createUserApi, { manual: true }); | |||||
const { data: simpleDepartment, run: listSimpleDeptApi } = useRequest(departmentService.listSimpleDeptApi, { manual: true }); | |||||
const { data: simplePositions, run: listSimplePostsApi } = useRequest(positonService.listSimplePostsApi, { manual: true }); | |||||
const isEdit = !!data; | |||||
const [shouldUpdateEditPassword, setUpdateEditPassword] = useState(false); | |||||
const [saveLoading, setSaveLoading] = useState(false); | |||||
const [form] = Form.useForm(); | |||||
useEffect(() => { | |||||
if (visible) { | |||||
if (data) { | |||||
form.setFieldsValue(data); | |||||
} | |||||
setUpdateEditPassword(true) | |||||
} else { | |||||
form.resetFields(); | |||||
} | |||||
}, [visible]); | |||||
const save = async () => { | |||||
setSaveLoading(true); | |||||
const fieldValues = form.getFieldsValue(); | |||||
const newValue = isEdit ? { ...data, ...fieldValues } : fieldValues; | |||||
const [error, { msg, code }] = isEdit ? await updateUserApi(newValue) : await createUserApi(newValue); | |||||
if (!error && code === 0) { | |||||
onSave(newValue); | |||||
} else { | |||||
antdUtils.message?.open({ | |||||
type: 'error', | |||||
content: msg ?? '操作失败', | |||||
}); | |||||
} | |||||
setSaveLoading(false); | |||||
} | |||||
useEffect(() => { | |||||
listSimpleDeptApi(); | |||||
listSimplePostsApi(); | |||||
}, [listSimplePostsApi, listSimpleDeptApi]); | |||||
return ( | |||||
<> | |||||
<Modal | |||||
open={visible} | |||||
title= {<div className='h-50px'>{isEdit? "编辑用户": "添加用户"}</div>} | |||||
width={640} | |||||
onOk={save} | |||||
onCancel={() => onCancel()} | |||||
destroyOnClose | |||||
confirmLoading= {saveLoading} | |||||
> | |||||
<Form | |||||
form={form} | |||||
{...layout} | |||||
onFinish={save} | |||||
labelCol={{ flex: '0 0 100px' }} | |||||
wrapperCol={{ span: 16 }} | |||||
> | |||||
<Form.Item name="username" label="用户账号" | |||||
hidden={isEdit} | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入用户账号', | |||||
}, | |||||
]} | |||||
> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item noStyle | |||||
shouldUpdate={() => { | |||||
const shouldUpdate = shouldUpdateEditPassword; | |||||
if(shouldUpdateEditPassword) { | |||||
setUpdateEditPassword(false) | |||||
} | |||||
return shouldUpdate | |||||
}} | |||||
> | |||||
{() => { | |||||
return !isEdit ? ( | |||||
<Form.Item name="password" label="用户密码" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入用户密码', | |||||
}, | |||||
]} | |||||
> | |||||
<Input.Password iconRender={(visible) => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)} /> | |||||
</Form.Item> | |||||
) : null | |||||
}} | |||||
</Form.Item> | |||||
<Form.Item name="nickname" label="用户昵称" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入用户昵称', | |||||
}, | |||||
]} | |||||
> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="email" label="邮箱" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入邮箱', | |||||
}, | |||||
]} | |||||
> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="mobile" label="联系电话" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入电话号码', | |||||
}, | |||||
]} | |||||
> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item | |||||
name="deptId" | |||||
label="部门" | |||||
> | |||||
<TreeSelect treeData={toDepartmentTree(simpleDepartment?.data ?? [])} /> | |||||
</Form.Item> | |||||
<Form.Item name="postIds" label="岗位" > | |||||
<Select mode="multiple" placeholder="请选择用户岗位" options={simplePositions?.data.map(item => { | |||||
return { | |||||
label: item.name, | |||||
value: item.id, | |||||
} | |||||
})}> | |||||
</Select> | |||||
</Form.Item> | |||||
<Form.Item name="remark" label="备注"> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="status" label="状态"> | |||||
<Radio.Group options={[ | |||||
{ value: 0, label: "开启" }, | |||||
{ value: 1, label: "关闭" } | |||||
]} optionType="default"> | |||||
</Radio.Group> | |||||
</Form.Item> | |||||
</Form> | |||||
</Modal> | |||||
</> | |||||
) | |||||
} | |||||
export default UserEditor; |
@@ -1,9 +1,239 @@ | |||||
import { Empty } from 'antd'; | |||||
import { | |||||
ProFormRadio, | |||||
ProFormSwitch, | |||||
ProList, | |||||
} from '@ant-design/pro-components'; | |||||
import { Card, Tag, Form, Input, Space, Select, Button, Popover } from 'antd'; | |||||
import { useState, useEffect } from 'react'; | |||||
import { t } from '@/utils/i18n'; | |||||
import { | |||||
EditOutlined, | |||||
EllipsisOutlined, | |||||
SearchOutlined, | |||||
UndoOutlined, | |||||
PlusOutlined, | |||||
ExclamationCircleFilled, | |||||
DeleteOutlined | |||||
} from '@ant-design/icons'; | |||||
import { antdUtils } from '@/utils/antd'; | |||||
import { useRequest } from '@/hooks/use-request'; | |||||
import { formatDate } from '@/utils/formatTime' | |||||
import userService from '@/request/service/user'; | |||||
import { UserVO } from '@/models'; | |||||
import UserEditor from './create-user'; | |||||
const CustomMade = () => { | |||||
const renderUserCard = (user: UserVO) => { | |||||
return ( | return ( | ||||
<Empty /> | |||||
<div style={{ flex: 1, }}> | |||||
<div> | |||||
<div className=' text-gray-900 text-lg font-semibold leading-relaxed'>{user.username}</div> | |||||
<div className='text-zinc-400 text-xs font-normal leading-relaxed'>{user.remark}</div> | |||||
</div> | |||||
<div className='grid grid-cols-2 gap-4 mt-5' > | |||||
<div> | |||||
<div className='text-zinc-400 text-xs font-normal leading-relaxed'>用户账号</div> | |||||
<div className='text-gray-900 text-xs font-medium leading-relaxed'>{user.username}</div> | |||||
</div> | |||||
<div> | |||||
<div className='text-zinc-400 text-xs font-normal leading-relaxed'>手机号码</div> | |||||
<div className='text-gray-900 text-xs font-medium leading-relaxed'>{user.mobile}</div> | |||||
</div> | |||||
<div> | |||||
<div className='text-zinc-400 text-xs font-normal leading-relaxed'>用户邮箱</div> | |||||
<div className='text-gray-900 text-xs font-medium leading-relaxed'>{user.email}</div> | |||||
</div> | |||||
<div> | |||||
<div className='text-zinc-400 text-xs font-normal leading-relaxed'>最后登录时间</div> | |||||
<div className='text-gray-900 text-xs font-medium leading-relaxed'>{formatDate(new Date(user.loginDate), "YYYY-mm-dd HH:MM:SS")}</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
) | |||||
} | |||||
export default () => { | |||||
const [editorVisable, seEditorVisable] = useState<boolean>(false); | |||||
const [editData, seEditData] = useState<UserVO>(); | |||||
const [cardActionProps, setCardActionProps] = useState<'actions' | 'extra'>('actions'); | |||||
const [dataSource, setDataSource] = useState<any[]>([]); | |||||
const [searchFrom] = Form.useForm(); | |||||
const { runAsync: getUserList } = useRequest(userService.getUserPageApi, { manual: true }); | |||||
const { runAsync: deleteUser } = useRequest(userService.deleteUserApi, { manual: true }); | |||||
const [open, setOpen] = useState(false); | |||||
const handleOpenChange = (isOpen: boolean) => { | |||||
setOpen(isOpen); | |||||
} | |||||
const showDeleteConfirm = (role: UserVO) => { | |||||
antdUtils.modal?.confirm({ | |||||
title: '确认要将该角色删除吗?', | |||||
icon: <ExclamationCircleFilled />, | |||||
content: '请注意删除以后不可恢复!', | |||||
okText: '删除', | |||||
okType: 'danger', | |||||
cancelText: '取消', | |||||
onOk() { | |||||
return new Promise(async (resolve) => { | |||||
const [error, { code, msg }] = await deleteUser(role.id); | |||||
if (error || code !== 0) { | |||||
antdUtils.message?.open({ type: 'error', content: msg ?? '操作失败' }) | |||||
} else { | |||||
antdUtils.message?.open({ type: 'success', content: '删除成功' }) | |||||
} | |||||
await load(); | |||||
resolve('') | |||||
}).catch(() => antdUtils.message?.open({ | |||||
type: 'error', | |||||
content: '操作失败', | |||||
})); | |||||
} | |||||
}); | |||||
}; | |||||
const load = async () => { | |||||
const [error, { code, msg, data }] = await getUserList(searchFrom.getFieldsValue()); | |||||
if (!error) { | |||||
setDataSource(data.list.map((item) => { | |||||
return ({ | |||||
subTitle: <Tag color="#5BD8A6">{item.dept.name}</Tag>, | |||||
actions: [ | |||||
<EditOutlined className='my-1' onClick={() => { | |||||
seEditData(item); | |||||
seEditorVisable(true); | |||||
setOpen(false); | |||||
}} />, | |||||
<DeleteOutlined key='delete' onClick={() => { showDeleteConfirm(item) }} />, | |||||
// <a className='my-1' onClick={() => { showDeleteConfirm(item) }}>删除</a>, | |||||
// <EllipsisOutlined key="ellipsis" />, | |||||
// <a className='my-1'>重置密码</a>, | |||||
// <a className='my-1'>分配角色</a>, | |||||
<div> | |||||
<Popover | |||||
placement="bottom" | |||||
key="ellipsis" | |||||
// open={open} | |||||
// onOpenChange={handleOpenChange} | |||||
content={( | |||||
<div className='flex flex-col'> | |||||
{/* <a className='my-1' onClick={() => { | |||||
seEditData(item); | |||||
seEditorVisable(true); | |||||
setOpen(false); | |||||
}}>编辑</a> | |||||
<a className='my-1' onClick={() => { showDeleteConfirm(item) }}>删除</a> */} | |||||
<a className='my-1'>重置密码</a> | |||||
<a className='my-1'>分配角色</a> | |||||
</div> | |||||
)} trigger="hover"> | |||||
<a key="actions"> | |||||
<EllipsisOutlined /> | |||||
</a> | |||||
</Popover> | |||||
</div>, | |||||
], | |||||
avatar: item.avatar, | |||||
content: renderUserCard(item), | |||||
}) | |||||
})) | |||||
} | |||||
} | |||||
useEffect(() => { | |||||
load(); | |||||
}, []); | |||||
const onReset = () => { | |||||
searchFrom.resetFields() | |||||
load() | |||||
} | |||||
return ( | |||||
<> | |||||
<div> | |||||
<Card className='mt-[4px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]' bodyStyle={{ paddingTop: 4, paddingBottom: 4 }}> | |||||
<div className='flex justify-between content-center'> | |||||
<div className='flex justify-normal items-center'> | |||||
<Form layout='inline' form={searchFrom}> | |||||
<Form.Item name="username" label="用户账号"> | |||||
<Input className='w-[150px]' placeholder='请输入账号' allowClear /> | |||||
</Form.Item> | |||||
<Form.Item name="mobile" label="手机号码"> | |||||
<Input className='w-[150px]' placeholder='请输入号码' allowClear /> | |||||
</Form.Item> | |||||
<Form.Item className='ml-2 w-[130px]' name="status" label="状态"> | |||||
<Select placeholder="请选择" allowClear > | |||||
<Select.Option value="">全部</Select.Option> | |||||
<Select.Option value="0">开启</Select.Option> | |||||
<Select.Option value="1">关闭</Select.Option> | |||||
</Select> | |||||
</Form.Item> | |||||
</Form> | |||||
<Space.Compact className="ml-5"> | |||||
<Button type='primary' size='large' icon={<SearchOutlined />} onClick={load}> 搜索 </Button> | |||||
<Button type='primary' size='large' icon={<UndoOutlined />} onClick={onReset}> 重置 </Button> | |||||
</Space.Compact> | |||||
</div> | |||||
<div className="py-[4px]"> | |||||
<Button className="ml-5" type='primary' size='large' icon={<PlusOutlined />} onClick={() => { | |||||
seEditData(undefined); | |||||
seEditorVisable(true); | |||||
}}> 添加用户 </Button> | |||||
</div> | |||||
</div> | |||||
</Card> | |||||
<Card className='mt-[4px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]'> | |||||
<div style={{ margin: -24, padding: 24, }}> | |||||
<ProList<any> | |||||
className="pt-2" | |||||
pagination={{ | |||||
defaultPageSize: 12, | |||||
showSizeChanger: false, | |||||
}} | |||||
showActions="hover" | |||||
rowClassName="min-w-[360px]" | |||||
rowSelection={false} | |||||
grid={{ gutter: 16, column: 4 }} | |||||
onItem={(record: any) => { | |||||
return { | |||||
onMouseEnter: () => { | |||||
console.log(record); | |||||
}, | |||||
onClick: () => { | |||||
console.log(record); | |||||
}, | |||||
}; | |||||
}} | |||||
metas={{ | |||||
title: {}, | |||||
subTitle: {}, | |||||
type: {}, | |||||
avatar: {}, | |||||
content: {}, | |||||
actions: { | |||||
cardActionProps, | |||||
}, | |||||
}} | |||||
dataSource={dataSource} | |||||
/> | |||||
</div> | |||||
</Card> | |||||
</div> | |||||
<UserEditor | |||||
onSave={() => { | |||||
load(); | |||||
seEditorVisable(false); | |||||
}} | |||||
onCancel={() => { seEditorVisable(false) }} | |||||
visible={editorVisable} | |||||
data={editData} /> | |||||
</> | |||||
); | ); | ||||
}; | }; | ||||
export default CustomMade; |
@@ -1,5 +1,5 @@ | |||||
import request from '@/request'; | import request from '@/request'; | ||||
import { LoginDTO, TokenDTO } from '@/models' | |||||
import { LoginDTO, TokenDTO, Menu } from '@/models' | |||||
export default { | export default { | ||||
// 登录 | // 登录 | ||||
@@ -10,7 +10,13 @@ export default { | |||||
logout: () => { | logout: () => { | ||||
return request.post<any>('/admin-api/system/auth/logout'); | return request.post<any>('/admin-api/system/auth/logout'); | ||||
}, | }, | ||||
rerefshToken: (refreshToken: string) => { | rerefshToken: (refreshToken: string) => { | ||||
return request.post<TokenDTO>(`/admin-api/system/auth/refresh-token?refreshToken=${refreshToken}`); | return request.post<TokenDTO>(`/admin-api/system/auth/refresh-token?refreshToken=${refreshToken}`); | ||||
} | |||||
}, | |||||
listMenus: () => { | |||||
return request.get<Menu[]>(`/admin-api/system/auth/list-menus`); | |||||
}, | |||||
}; | }; |
@@ -0,0 +1,40 @@ | |||||
import request from '@/request'; | |||||
import { DepartmentPageReqVO, DepartmentVO } from '@/models'; | |||||
const BASE_URL = '/admin-api/system/dept'; | |||||
export default { | |||||
// 查询部门(精简)列表 | |||||
listSimpleDeptApi: () => { | |||||
return request.get<DepartmentVO[]>(`${BASE_URL}/list-all-simple`); | |||||
}, | |||||
// 查询部门列表 | |||||
getDeptPageApi: (params: DepartmentPageReqVO) => { | |||||
return request.get<DepartmentVO[]>(`${BASE_URL}/list`, { params }) | |||||
}, | |||||
// 查询部门详情 | |||||
getDeptApi: (id: number) => { | |||||
return request.get(`${BASE_URL}/get?id=${id}`); | |||||
}, | |||||
// 新增部门 | |||||
createDeptApi: (data: DepartmentVO) => { | |||||
return request.post(`${BASE_URL}/create`, data); | |||||
}, | |||||
// 修改部门 | |||||
updateDeptApi: (data: DepartmentVO) => { | |||||
return request.put(`${BASE_URL}/update`, data); | |||||
}, | |||||
// 删除部门 | |||||
deleteDeptApi: (id: number) => { | |||||
return request.delete(`${BASE_URL}/delete?id=${id}`); | |||||
}, | |||||
}; | |||||
@@ -12,7 +12,7 @@ export default { | |||||
// 获取岗位精简信息列表 | // 获取岗位精简信息列表 | ||||
listSimplePostsApi: () => { | listSimplePostsApi: () => { | ||||
return request.get<PageData<PositionVO>>(`${BASE_URL}/list-all-simple`); | |||||
return request.get<PositionVO[]>(`${BASE_URL}/list-all-simple`); | |||||
}, | }, | ||||
// 查询岗位详情 | // 查询岗位详情 | ||||
@@ -40,6 +40,3 @@ export default { | |||||
}, | }, | ||||
}; | }; | ||||
@@ -1,12 +1,70 @@ | |||||
import request from '@/request'; | import request from '@/request'; | ||||
import { Menu, User } from '@/models'; | |||||
import { User, UserVO, UserPageReqVO, PageData } from '@/models'; | |||||
const BASE_URL = '/admin-api/system/user'; | |||||
export default { | export default { | ||||
listMenus: () => { | |||||
return request.get<Menu[]>('/admin-api/system/auth/list-menus'); | |||||
}, | |||||
getProfile: () => { | getProfile: () => { | ||||
return request.get<User>('/admin-api/system/user/profile/get') | |||||
} | |||||
return request.get<User>(`${BASE_URL}/profile/get`) | |||||
}, | |||||
// 查询用户管理列表 | |||||
getUserPageApi: (params: UserPageReqVO) => { | |||||
return request.get<PageData<UserVO>>(`${BASE_URL}/page`, {params}) | |||||
}, | |||||
// 查询用户详情 | |||||
getUserApi: (id: number) => { | |||||
return request.get(`${BASE_URL}/get?id=` + id) | |||||
}, | |||||
// 新增用户 | |||||
createUserApi: (data: UserVO) => { | |||||
return request.post(`${BASE_URL}/create`, data) | |||||
}, | |||||
// 修改用户 | |||||
updateUserApi: (data: UserVO) => { | |||||
return request.put(`${BASE_URL}/update`, data) | |||||
}, | |||||
// 删除用户 | |||||
deleteUserApi: (id: number) => { | |||||
return request.delete(`${BASE_URL}/delete?id=` + id) | |||||
}, | |||||
// 导出用户 | |||||
// exportUserApi: (params: UserExportReqVO) => { | |||||
// return request.download(`${BASE_URL}/export`, params ) | |||||
// }, | |||||
// 下载用户导入模板 | |||||
// importUserTemplateApi: () => { | |||||
// return request.download(`${BASE_URL}/get-import-template` ) | |||||
// }, | |||||
// 用户密码重置 | |||||
resetUserPwdApi: (id: number, password: string) => { | |||||
const data = { | |||||
id, | |||||
password | |||||
} | |||||
return request.put(`${BASE_URL}/update-password`, data) | |||||
}, | |||||
// 用户状态修改 | |||||
updateUserStatusApi: (id: number, status: number) => { | |||||
const data = { | |||||
id, | |||||
status | |||||
} | |||||
return request.put(`${BASE_URL}/update-status`, data) | |||||
}, | |||||
// 获取用户精简信息列表 | |||||
getListSimpleUsersApi: () => { | |||||
return request.get(`${BASE_URL}/list-all-simple`) | |||||
}, | |||||
}; | }; |