This commit is contained in:
2026-01-16 16:47:12 +08:00
parent ee77541400
commit b2727a6b5f
27 changed files with 1736 additions and 135 deletions

View File

@@ -0,0 +1,12 @@
/**
* 管理员状态
*/
export const statusObj: any = {
1: '正常',
2: '禁用',
};
export const statusOptions = [
{ label: '正常', value: '1' },
{ label: '禁用', value: '2' },
];

View File

@@ -0,0 +1,10 @@
/** 状态 */
export const statusObj: any = {
1: '成功',
2: '失败',
};
export const statusOptions: any = [
{ label: '成功', value: '1' },
{ label: '失败', value: '2' },
];

View File

@@ -13,7 +13,7 @@ export const OSSBaseUrl = 'https://cdn.fzcfkj.com/';
export const headerTabNavHeight = 32;
export const DefaultERPName = (() => {
return '易宝赞普惠版后台系统';
return '易宝赞普惠版后台管理系统';
})();
export const Colors = {

View File

@@ -1,4 +1,4 @@
import { DashboardOutlined, UserOutlined } from '@ant-design/icons';
import { BarsOutlined, DashboardOutlined, TeamOutlined, UserOutlined } from '@ant-design/icons';
import type React from 'react';
/* cspell:disable */
@@ -19,20 +19,23 @@ interface MenuDataItem {
const userMenu: MenuDataItem = {
name: '用户管理',
icon: <UserOutlined style={iconStyle} />,
children: [
{ name: '用户管理', path: '/user/list', auth: '' },
// { name: '岗位角色', path: '/user/group', auth: 'SF_ERP_GROUP_VIEW' },
],
children: [{ name: '用户信息', path: '/user/list', auth: '' }], //SF_ERP_USER_VIEW
};
const staffMenu: MenuDataItem = {
name: '后台用户',
icon: <UserOutlined style={iconStyle} />,
const companyMenu: MenuDataItem = {
name: '企业管理',
icon: <TeamOutlined style={iconStyle} />,
children: [{ name: '企业信息', path: '/company/list', auth: '' }], //SF_ERP_COMPANY_VIEW
};
const authMenu: MenuDataItem = {
name: '权限管理',
icon: <BarsOutlined style={iconStyle} />,
children: [
{ name: '组织架构', path: '/staff/dep', auth: 'SF_ERP_DEPART_VIEW' },
{ name: '岗位角色', path: '/staff/group', auth: 'SF_ERP_GROUP_VIEW' },
{ name: '员工管理', path: '/staff/list', auth: 'SF_ERP_STAFF_VIEW' },
{ name: '我的权限', path: '/staff/my', auth: 'SF_MY_RIGHT_VIEW' },
// { name: '日志管理', path: '/system/record', auth: 'SF_ERP_LOG_VIEW' },
{ name: '管理员信息', path: '/staff/list', auth: '' }, //SF_ERP_ADMIN_VIEW
{ name: '组织架构', path: '/staff/dep', auth: '' }, //SF_ERP_DEPART_VIEW
{ name: '岗位角色', path: '/staff/group', auth: '' }, //SF_ERP_GROUP_VIEW
{ name: '我的权限', path: '/staff/my', auth: '' }, //SF_MY_RIGHT_VIEW
{ name: '登录日志', path: '/staff/login', auth: '' }, //SF_LOGIN_LOG_VIEW
{ name: '操作日志', path: '/staff/sys', auth: '' }, //SF_OPERATE_LOG_VIEW
],
};
@@ -44,7 +47,8 @@ const asideMenuConfig: MenuDataItem[] = [
children: [{ name: '系统主页', path: '/home/index', auth: '' }],
},
userMenu,
staffMenu,
companyMenu,
authMenu,
];
export { asideMenuConfig };

View File

@@ -1,10 +1,14 @@
import { lazy } from 'react';
import AppLayout from '@/layouts/AppLayout';
import EmptyLayout from '@/layouts/EmptyLayout';
import CompanyList from '@/pages/Company/List';
import ErrorPage from '@/pages/Error';
import Index from '@/pages/Index';
import Login from '@/pages/Record/Login';
import Sys from '@/pages/Record/Sys';
import Dep from '@/pages/Staff/dep';
import Group from '@/pages/Staff/group';
import StaffList from '@/pages/Staff/List';
import UserList from '@/pages/User/List';
import type { IRouteItem } from '@/router/types';
@@ -26,12 +30,23 @@ export const routes: IRouteItem[] = [
// { path: '/group', Component: lazy(() => import('@/pages/Staff/group')) },
],
},
{
path: '/company',
Layout: AppLayout,
children: [
{ path: '/list', Component: CompanyList },
// { path: '/group', Component: lazy(() => import('@/pages/Staff/group')) },
],
},
{
path: '/staff',
Layout: AppLayout,
children: [
{ path: '/list', Component: StaffList },
{ path: '/dep', Component: Dep },
{ path: '/group', Component: Group },
{ path: '/login', Component: Login },
{ path: '/sys', Component: Sys },
],
},
{

View File

@@ -0,0 +1,14 @@
/** 员工类型 */
export const staffType: any = {
1: '主账号',
2: '子账号',
3: '经销商主账号',
4: '经销商子账号',
};
export const staffTypeOption = [
{ label: '主账号', value: '1' },
{ label: '子账号', value: '2' },
{ label: '经销商主账号', value: '3' },
{ label: '经销商子账号', value: '4' },
];

View File

@@ -0,0 +1,23 @@
/** 性别 */
export const userSex: any = {
1: '男',
2: '女',
};
export const userSexOptions = [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
];
/**
* 用户状态
*/
export const userState: any = {
1: '正常',
0: '禁用',
};
export const stateOptions = [
{ label: '正常', value: '1' },
{ label: '禁用', value: '0' },
];

View File

@@ -1,8 +1,9 @@
import { AdminServices } from '@/services/AdminServices';
import { requestLite } from '@/utils/useRequest';
export const loginState = async () => {
return new Promise<any>((resolve, reject) => {
requestLite('/Users/loginState').then((res) => {
requestLite(AdminServices.loginState).then((res) => {
if (res?.err_code == 0) {
resolve(res);
} else {

View File

@@ -0,0 +1,168 @@
import { Button, Form, Input, notification, Select } from 'antd';
import TextArea from 'antd/lib/input/TextArea';
import { stringify } from 'qs';
import type React from 'react';
import { useImperativeHandle, useState } from 'react';
import ModalPlugin from '@/components/ModalPlugin';
import { stateOptions, userSexOptions } from '@/configs/usersConfig';
import { UserServices } from '@/services/UserServices';
import type { IRef } from '@/utils/type';
import { useRequest } from '@/utils/useRequest';
interface IProps extends IRef {
onCallback?: () => void;
}
export type IUserEditModalType = {
show: (data?: any) => void;
};
export const UserEditModal: React.FC<IProps> = (props) => {
const [open, setOpen] = useState(false);
const [title, setTitle] = useState('');
const [form] = Form.useForm();
const [data, setData] = useState<any>(null);
// 请求成功回调
const success = (res: any) => {
if (res.err_code === 0) {
notification.success({ message: '保存成功' });
props.onCallback?.();
setOpen(false);
form.resetFields(); // 提交成功重置表单
}
};
const { loading: addLoading, request: addRequest } = useRequest(UserServices.add, { onSuccess: success });
const { loading: editLoading, request: editRequest } = useRequest(UserServices.edit, { onSuccess: success });
const save = async () => {
try {
const values = await form.validateFields();
// 编辑场景带上主键
if (data?.user_id) {
values.user_id = data.user_id;
editRequest(stringify(values));
} else {
addRequest(stringify(values));
}
} catch (error) {
console.log('表单验证未通过', error);
}
};
useImperativeHandle(props.ref, () => ({
show: (data?: any) => {
setTitle(data ? `${data.login_name} 编辑` : '新增');
setOpen(true);
setData(data);
if (data) {
// 编辑场景初始化表单
form.setFieldsValue({
login_name: data.login_name,
user_phone: data.user_phone,
user_mail: data.user_mail,
nick_name: data.nick_name,
password: '', // 密码编辑可选
comments: data.comments,
user_sex: String(data.user_sex),
state: String(data.state),
});
} else {
form.resetFields();
form.setFieldsValue({
user_sex: '1',
state: '1',
});
}
},
}));
return (
<ModalPlugin
open={open}
onCancel={() => setOpen(false)}
title={title}
footer={[
<Button key='cancel' onClick={() => setOpen(false)}>
</Button>,
<Button key='save' type='primary' loading={addLoading || editLoading} onClick={save}>
</Button>,
]}
>
<Form form={form} labelCol={{ style: { width: window?.dfConfig?.language === 'zh-cn' ? 80 : 120 } }}>
<Form.Item
label='用户名'
name='login_name'
required
rules={[
{ required: true, message: '请输入用户名' },
{ min: 2, message: '最少2字符' },
{ max: 20, message: '最多20字符' },
]}
>
<Input allowClear placeholder='请填写用户名' maxLength={20} showCount />
</Form.Item>
<Form.Item
label='昵称'
name='nick_name'
rules={[
{ min: 2, message: '最少2字符' },
{ max: 20, message: '最多20字符' },
]}
>
<Input allowClear placeholder='请填写昵称' maxLength={20} showCount />
</Form.Item>
<Form.Item
label='手机号'
name='user_phone'
required
rules={[
{ required: true, message: '请输入手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' },
]}
>
<Input allowClear placeholder='请填写手机号' maxLength={11} showCount />
</Form.Item>
<Form.Item label='邮箱' name='user_mail' rules={[{ type: 'email', message: '邮箱格式不正确' }]}>
<Input allowClear placeholder='请填写邮箱' />
</Form.Item>
<Form.Item label='密码' name='password' rules={[{ min: 6, max: 18, message: '密码需6~18位' }]}>
<Input.Password allowClear placeholder={data ? '不修改请留空' : '请填写密码'} />
</Form.Item>
{!data?.user_id && (
<Form.Item
label='企业名'
name='company_name'
rules={[
{ min: 2, message: '最少2字符' },
{ max: 20, message: '最多20字符' },
]}
>
<Input allowClear placeholder='请填写企业名' />
</Form.Item>
)}
<Form.Item label={'性别'} name='user_sex'>
<Select options={userSexOptions} placeholder='请选择性别' />
</Form.Item>
<Form.Item label={'状态'} name='state'>
<Select options={stateOptions} placeholder='请选择状态' />
</Form.Item>
<Form.Item label='备注' name='comments'>
<TextArea allowClear maxLength={200} showCount />
</Form.Item>
</Form>
</ModalPlugin>
);
};

View File

@@ -0,0 +1,245 @@
import { Button, DatePicker, Input, Select } from 'antd';
import { stringify } from 'qs';
import { useEffect, useRef, useState } from 'react';
import { FormItemPlugin, FormPlugin } from '@/components/FormPlugin';
import { GapBox } from '@/components/GapBox';
import PageContainerPlugin from '@/components/PageContainer/PageContainerPlugin';
import { FooterPagination, HeaderPagination } from '@/components/PaginationPlugin';
import { MoreSearchButton, SearchButton } from '@/components/SearchButton';
import type { ColumnsTypeUltra } from '@/components/TableColumnsFilterPlugin';
import { TablePlugin } from '@/components/TablePlugin';
import { staffType } from '@/configs/staffsConfig';
import { stateOptions, userSex, userState } from '@/configs/usersConfig';
import type { IAjaxDataBase, IParamsBase } from '@/interfaces/common';
import { type IUserEditModalType, UserEditModal } from '@/pages/User/List/components/UserEditModal';
import { UserServices } from '@/services/UserServices';
import { useAuthStore } from '@/store/AuthStore';
import { tableFixedByPhone, toArray } from '@/utils/common';
import { useRequest } from '@/utils/useRequest';
interface IAjaxData extends IAjaxDataBase {
data: any[];
}
type IParams = IParamsBase & {
login_name?: string;
nick_name?: string;
user_phone?: string;
state?: number;
create_dateL?: string;
create_dateU?: string;
};
/** 企业列表页面 */
const CompanyListForm: React.FC = () => {
const auth = useAuthStore().auth;
const [params, setParams] = useState<IParams>({ curr_page: 1, page_count: 20 });
const [ajaxData, setAjaxData] = useState<IAjaxData>({ count: 0, data: [] });
const [showMoreSearch, setShowMoreSearch] = useState(false);
const UserEditModalRef = useRef<IUserEditModalType>(null);
const { loading: userLoading, request: userRequest } = useRequest(UserServices.getUserList, {
onSuccessCodeZero: (res) => {
setAjaxData({
count: res.count || 0,
data: toArray(res.data),
});
},
});
const columns: ColumnsTypeUltra<any> = [
{
title: '操作',
width: 50,
fixed: tableFixedByPhone('left'),
render: (_, item) => (
<GapBox>
<Button
type='primary'
onClick={() => {
UserEditModalRef.current?.show(item);
}}
>
</Button>
{auth.SF_ERP_USER_EDIT && (
<Button
type='primary'
onClick={() => {
UserEditModalRef.current?.show(item);
}}
>
</Button>
)}
</GapBox>
),
},
{ title: '用户名', dataIndex: 'login_name', width: 120 },
{ title: '企业名称', dataIndex: 'company_name', width: 120 },
{ title: '类型', dataIndex: 'staff_type', width: 60, render: (value) => staffType[value] },
{ title: '昵称', dataIndex: 'nick_name', width: 120 },
{ title: '手机', dataIndex: 'user_phone', width: 120 },
{ title: '性别', dataIndex: 'user_sex', width: 60, render: (value) => userSex[value] },
{ title: '状态', dataIndex: 'state', width: 60, ellipsis: true, render: (value) => userState[value] },
{
title: '备注',
dataIndex: 'comments',
width: 300,
},
{
title: '创建时间',
width: window.dfConfig.isPhone ? 160 : 160,
dataIndex: 'create_date',
ellipsis: true,
},
];
function page(curr: number) {
if (!userLoading) {
params.curr_page = curr;
console.log(params);
userRequest(stringify(params));
}
}
useEffect(() => {
page(1);
}, []);
return (
<>
<FormPlugin gutter={16}>
<FormItemPlugin label={'用户名'}>
<Input
allowClear
defaultValue={params.login_name}
placeholder='用户名'
onChange={(e) => {
params.login_name = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin label={'昵称'}>
<Input
allowClear
defaultValue={params.nick_name}
placeholder='昵称'
onChange={(e) => {
params.nick_name = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin label={'手机号'}>
<Input
allowClear
defaultValue={params.user_phone}
placeholder='手机号'
onChange={(e) => {
params.user_phone = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin>
<div
style={{
display: 'flex',
flexWrap: 'nowrap',
alignItems: 'center',
whiteSpace: 'nowrap',
gap: 8,
}}
>
<SearchButton
onClick={() => {
page(1);
}}
/>
<MoreSearchButton
show={showMoreSearch}
onClick={() => {
setShowMoreSearch((v) => !v);
}}
/>
</div>
</FormItemPlugin>
</FormPlugin>
<FormPlugin style={{ display: showMoreSearch ? 'flex' : 'none' }} gutter={16}>
<FormItemPlugin label={'状态'}>
<Select
allowClear
value={params.state}
options={stateOptions}
onChange={(value) => {
params.state = value || undefined;
setParams({ ...params });
}}
/>
</FormItemPlugin>
<FormItemPlugin label={'创建日期'}>
<DatePicker.RangePicker
style={{ width: '100%' }}
onChange={(_values, dates) => {
params.create_dateL = dates?.[0] || undefined;
params.create_dateU = dates?.[1] || undefined;
}}
/>
</FormItemPlugin>
</FormPlugin>
<GapBox style={{ marginBottom: 12, justifyContent: 'space-between' }}>
<GapBox>
<Button
type='primary'
onClick={() => {
UserEditModalRef.current?.show();
}}
>
</Button>
</GapBox>
<HeaderPagination
style={{ marginBottom: 0, marginRight: 12 }}
current={params.curr_page}
pageSize={params.page_count}
total={ajaxData.count}
onChange={page}
/>
</GapBox>
<TablePlugin
loading={userLoading}
onChange={(_p, _f, sort: any) => {
page(1);
}}
dataSource={ajaxData.data}
columns={columns}
scroll={{ x: true }}
rowKey={'user_id'}
/>
<FooterPagination
current={params.curr_page}
pageSize={params.page_count}
total={ajaxData.count}
onChange={(curr, pageSize) => {
params.page_count = pageSize;
page(curr);
}}
/>
<UserEditModal ref={UserEditModalRef} onCallback={() => page(1)} />
</>
);
};
const CompanyList = () => (
<PageContainerPlugin breadcrumb={['企业管理', '企业信息']}>
<CompanyListForm />
</PageContainerPlugin>
);
export default CompanyList;

View File

@@ -4,14 +4,22 @@ import { useState } from 'react';
import loginBg from '@/assets/loginBg.jpg';
import { FormItemPlugin, FormPlugin } from '@/components/FormPlugin';
import { DefaultERPName } from '@/configs/config';
import { AdminServices } from '@/services/AdminServices';
import { type IAuth, useAuthStore } from '@/store/AuthStore';
import { useUserStore } from '@/store/UserStore';
import { toObject } from '@/utils/common';
import { notificationEventBus } from '@/utils/EventBus';
import { useRequest } from '@/utils/useRequest';
const Login = () => {
const [userInfo, setUserInfo] = useState({ login_name: 'zhengw', password: '123456', login_type: 1 });
const { request, loading } = useRequest('Users/login', {
onSuccessCodeZero() {
const user = useUserStore();
const auth = useAuthStore();
const [userInfo, setUserInfo] = useState({ username: 'admin', password: '123456', login_type: 1 });
const { request, loading } = useRequest(AdminServices.login, {
onSuccessCodeZero: (res) => {
notificationEventBus.emit({ description: '登录成功' });
user.updateUser(res.user_info);
auth.updateAuth(toObject(res.auth) as IAuth);
location.href = '#/';
},
});
@@ -45,16 +53,20 @@ const Login = () => {
background: 'rgba(255,255,255,0.8)',
}}
>
<div style={{ marginBottom: 24, fontSize: 20 }}>{DefaultERPName}</div>
<div style={{ marginBottom: 24, fontSize: 20, display: 'flex', justifyContent: 'center' }}>
{DefaultERPName}
{document.title}
</div>
<FormPlugin>
<FormItemPlugin allCol={24}>
<Input
allowClear
prefix='账号'
size='large'
autoFocus
value={userInfo.login_name}
value={userInfo.username}
onChange={(e) => {
userInfo.login_name = e.target.value;
userInfo.username = e.target.value;
setUserInfo({ ...userInfo });
}}
onPressEnter={login}

View File

@@ -0,0 +1,195 @@
import { DatePicker, Input, Select } from 'antd';
import { stringify } from 'qs';
import { useEffect, useState } from 'react';
import { FormItemPlugin, FormPlugin } from '@/components/FormPlugin';
import PageContainerPlugin from '@/components/PageContainer/PageContainerPlugin';
import { FooterPagination, HeaderPagination } from '@/components/PaginationPlugin';
import { MoreSearchButton, SearchButton } from '@/components/SearchButton';
import type { ColumnsTypeUltra } from '@/components/TableColumnsFilterPlugin';
import { TablePlugin } from '@/components/TablePlugin';
import { statusObj, statusOptions } from '@/configs/adminLogConfig';
import type { IAjaxDataBase, IParamsBase } from '@/interfaces/common';
import { AdminLogServices } from '@/services/AdminLogServices';
import { useAuthStore } from '@/store/AuthStore';
import { tableFixedByPhone, toArray } from '@/utils/common';
import { useRequest } from '@/utils/useRequest';
interface IAjaxData extends IAjaxDataBase {
data: any[];
}
type IParams = IParamsBase & {
ip?: string;
username?: string;
status?: number;
login_dateL?: string;
login_dateU?: string;
};
/** 登录日志页面 */
const AdminLoginLogForm: React.FC = () => {
const auth = useAuthStore().auth;
const [params, setParams] = useState<IParams>({ curr_page: 1, page_count: 20 });
const [ajaxData, setAjaxData] = useState<IAjaxData>({ count: 0, data: [] });
const [showMoreSearch, setShowMoreSearch] = useState(false);
const { loading: listLoading, request: listRequest } = useRequest(AdminLogServices.getAdminLoginLogList, {
onSuccessCodeZero: (res) => {
setAjaxData({
count: res.count || 0,
data: toArray(res.data),
});
},
});
const columns: ColumnsTypeUltra<any> = [
{
title: '#',
width: 20,
fixed: tableFixedByPhone('left'),
align: 'center',
render(_value, _record, index) {
return index + 1;
},
},
{ title: '管理员账号', dataIndex: 'username', width: 120 },
{ title: '登录IP', dataIndex: 'ip', width: 120 },
{ title: '登录地址', dataIndex: 'ip_location', width: 120 },
{ title: '平台', dataIndex: 'os', width: 120 },
{ title: '浏览器', dataIndex: 'browser', width: 120 },
{ title: '登录时间', dataIndex: 'login_date', width: 120 },
{ title: '状态', dataIndex: 'status', width: 60, render: (value) => statusObj[value] },
{
title: '备注',
dataIndex: 'remark',
width: 120,
},
{
title: '创建时间',
width: window.dfConfig.isPhone ? 160 : 160,
dataIndex: 'create_date',
},
];
function page(curr: number) {
if (!listLoading) {
params.curr_page = curr;
listRequest(stringify(params));
}
}
useEffect(() => {
page(1);
}, []);
return (
<>
<FormPlugin gutter={16}>
<FormItemPlugin label={'管理员账号'}>
<Input
allowClear
defaultValue={params.username}
placeholder='管理员账号'
onChange={(e) => {
params.username = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin label={'登录IP'}>
<Input
allowClear
defaultValue={params.ip}
placeholder='登录IP'
onChange={(e) => {
params.ip = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin label={'状态'}>
<Select
allowClear
value={params.status}
options={statusOptions}
onChange={(value) => {
params.status = value || undefined;
setParams({ ...params });
}}
/>
</FormItemPlugin>
<FormItemPlugin>
<div
style={{
display: 'flex',
flexWrap: 'nowrap',
alignItems: 'center',
whiteSpace: 'nowrap',
gap: 8,
}}
>
<SearchButton
onClick={() => {
page(1);
}}
/>
<MoreSearchButton
show={showMoreSearch}
onClick={() => {
setShowMoreSearch((v) => !v);
}}
/>
</div>
</FormItemPlugin>
</FormPlugin>
<FormPlugin style={{ display: showMoreSearch ? 'flex' : 'none' }} gutter={16}>
<FormItemPlugin label={'登录日期'}>
<DatePicker.RangePicker
style={{ width: '100%' }}
onChange={(_values, dates) => {
params.login_dateL = dates?.[0] || undefined;
params.login_dateU = dates?.[1] || undefined;
}}
/>
</FormItemPlugin>
</FormPlugin>
<HeaderPagination
style={{ marginBottom: 12, marginRight: 12 }}
current={params.curr_page}
pageSize={params.page_count}
total={ajaxData.count}
onChange={page}
/>
<TablePlugin
loading={listLoading}
onChange={(_p, _f, sort: any) => {
page(1);
}}
dataSource={ajaxData.data}
columns={columns}
scroll={{ x: true }}
rowKey={'login_id'}
/>
<FooterPagination
current={params.curr_page}
pageSize={params.page_count}
total={ajaxData.count}
onChange={(curr, pageSize) => {
params.page_count = pageSize;
page(curr);
}}
/>
</>
);
};
/** 登录日志页面 */
const RecordLogin = () => (
<PageContainerPlugin breadcrumb={['权限管理', '登录日志']}>
<AdminLoginLogForm />
</PageContainerPlugin>
);
export default RecordLogin;

View File

@@ -0,0 +1,263 @@
import { DatePicker, Input, Select, TreeSelect } from 'antd';
import { stringify } from 'qs';
import { useEffect, useState } from 'react';
import { FormItemPlugin, FormPlugin } from '@/components/FormPlugin';
import PageContainerPlugin from '@/components/PageContainer/PageContainerPlugin';
import { FooterPagination, HeaderPagination } from '@/components/PaginationPlugin';
import { MoreSearchButton, SearchButton } from '@/components/SearchButton';
import type { ColumnsTypeUltra } from '@/components/TableColumnsFilterPlugin';
import { TablePlugin } from '@/components/TablePlugin';
import { statusObj, statusOptions } from '@/configs/adminLogConfig';
import type { IAjaxDataBase, IParamsBase } from '@/interfaces/common';
import { AdminLogServices } from '@/services/AdminLogServices';
import { useAuthStore } from '@/store/AuthStore';
import { tableFixedByPhone, toArray } from '@/utils/common';
import { useRequest } from '@/utils/useRequest';
interface IAjaxData extends IAjaxDataBase {
data: any[];
}
type IParams = IParamsBase & {
ip?: string;
username?: string;
status?: number;
menu_id?: number;
function_id?: number;
create_dateL?: string;
create_dateU?: string;
};
/** 操作日志页面 */
const AdminSysLogForm: React.FC = () => {
const auth = useAuthStore().auth;
const [params, setParams] = useState<IParams>({ curr_page: 1, page_count: 20 });
const [ajaxData, setAjaxData] = useState<IAjaxData>({ count: 0, data: [] });
const [showMoreSearch, setShowMoreSearch] = useState(false);
/** 菜单树 */
function buildTree(list: any[], parentId = 0): any[] {
return list
.filter((item) => item.parent_menu_id == parentId)
.map((item) => ({
title: item.menu_ch_name,
value: item.menu_id,
key: item.menu_id,
children: buildTree(list, item.menu_id),
}));
}
const [menuList, setMenuList] = useState<any[]>([]);
const { request: menuListRequest } = useRequest(AdminLogServices.getAdminMenuAjaxList, {
onSuccess: (res) => {
if (res.err_code === 0) {
const rawList = toArray(res?.data);
setMenuList(buildTree(toArray(rawList)));
}
},
});
/** 当前菜单下的功能 */
const [functionList, setFunctionList] = useState<any[]>([]);
const { request: functionListRequest } = useRequest(AdminLogServices.getAdminFunctionAjaxList, {
onSuccess: (res) => {
if (res.err_code === 0) {
const list: any[] = [];
toArray(res.data).forEach((el) => {
list.push({ label: el.function_ch_name, value: el.function_id });
});
setFunctionList(list);
}
},
});
const { loading: listLoading, request: listRequest } = useRequest(AdminLogServices.getAdminSysLogList, {
onSuccessCodeZero: (res) => {
setAjaxData({
count: res.count || 0,
data: toArray(res.data),
});
},
});
const columns: ColumnsTypeUltra<any> = [
{
title: '#',
width: 20,
fixed: tableFixedByPhone('left'),
align: 'center',
render(_value, _record, index) {
return index + 1;
},
},
{ title: '菜单', dataIndex: 'menu_ch_name', width: 80 },
{ title: '权限', dataIndex: 'function_ch_name', width: 60 },
{ title: '操作账号', dataIndex: 'username', width: 120 },
{ title: '操作IP', dataIndex: 'ip', width: 120 },
{ title: '操作时间', dataIndex: 'create_date', width: 120 },
{ title: '状态', dataIndex: 'status', width: 60, render: (value) => statusObj[value] },
];
function page(curr: number) {
if (!listLoading) {
params.curr_page = curr;
listRequest(stringify(params));
}
}
useEffect(() => {
page(1);
menuListRequest();
}, []);
return (
<>
<FormPlugin gutter={16}>
<FormItemPlugin label={'操作账号'}>
<Input
allowClear
defaultValue={params.username}
placeholder='操作账号'
onChange={(e) => {
params.username = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin label='菜单'>
<TreeSelect
treeDefaultExpandAll
showSearch
allowClear
placeholder='请选择菜单'
treeData={menuList}
value={params.menu_id}
onChange={(value) => {
setParams((prev) => ({
...prev,
menu_id: value || undefined,
function_id: undefined,
}));
setFunctionList([]);
if (value) {
functionListRequest(stringify({ menu_id: value }));
}
}}
/>
</FormItemPlugin>
<FormItemPlugin label='权限'>
<Select
showSearch
allowClear
placeholder='请选择权限'
disabled={!params.menu_id}
options={functionList}
value={params.function_id}
onChange={(value) => {
setParams((prev) => ({
...prev,
function_id: value || undefined,
}));
}}
/>
</FormItemPlugin>
<FormItemPlugin>
<div
style={{
display: 'flex',
flexWrap: 'nowrap',
alignItems: 'center',
whiteSpace: 'nowrap',
gap: 8,
}}
>
<SearchButton
onClick={() => {
page(1);
}}
/>
<MoreSearchButton
show={showMoreSearch}
onClick={() => {
setShowMoreSearch((v) => !v);
}}
/>
</div>
</FormItemPlugin>
</FormPlugin>
<FormPlugin style={{ display: showMoreSearch ? 'flex' : 'none' }} gutter={16}>
<FormItemPlugin label={'操作IP'}>
<Input
allowClear
defaultValue={params.ip}
placeholder='操作IP'
onChange={(e) => {
params.ip = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin label={'状态'}>
<Select
allowClear
value={params.status}
options={statusOptions}
onChange={(value) => {
params.status = value || undefined;
setParams({ ...params });
}}
/>
</FormItemPlugin>
<FormItemPlugin label={'操作日期'}>
<DatePicker.RangePicker
style={{ width: '100%' }}
onChange={(_values, dates) => {
params.create_dateL = dates?.[0] || undefined;
params.create_dateU = dates?.[1] || undefined;
}}
/>
</FormItemPlugin>
</FormPlugin>
<HeaderPagination
style={{ marginBottom: 12, marginRight: 12 }}
current={params.curr_page}
pageSize={params.page_count}
total={ajaxData.count}
onChange={page}
/>
<TablePlugin
loading={listLoading}
onChange={(_p, _f, sort: any) => {
page(1);
}}
dataSource={ajaxData.data}
columns={columns}
scroll={{ x: true }}
rowKey={'sys_id'}
/>
<FooterPagination
current={params.curr_page}
pageSize={params.page_count}
total={ajaxData.count}
onChange={(curr, pageSize) => {
params.page_count = pageSize;
page(curr);
}}
/>
</>
);
};
/** 操作日志页面 */
const RecordSys = () => (
<PageContainerPlugin breadcrumb={['权限管理', '操作日志']}>
<AdminSysLogForm />
</PageContainerPlugin>
);
export default RecordSys;

View File

@@ -0,0 +1,151 @@
import { Button, Form, Input, notification, Select } from 'antd';
import { stringify } from 'qs';
import type React from 'react';
import { useImperativeHandle, useState } from 'react';
import ModalPlugin from '@/components/ModalPlugin';
import { statusOptions } from '@/configs/adminConfig';
import { AdminServices } from '@/services/AdminServices';
import type { IRef } from '@/utils/type';
import { useRequest } from '@/utils/useRequest';
interface IProps extends IRef {
onCallback?: () => void;
}
export type IAdminEditModalType = {
show: (data?: any) => void;
};
export const AdminEditModal: React.FC<IProps> = (props) => {
const [open, setOpen] = useState(false);
const [title, setTitle] = useState('');
const [form] = Form.useForm();
const [data, setData] = useState<any>(null);
// 请求成功回调
const success = (res: any) => {
if (res.err_code === 0) {
notification.success({ message: '保存成功' });
props.onCallback?.();
setOpen(false);
form.resetFields(); // 提交成功重置表单
}
};
const { loading: addLoading, request: addRequest } = useRequest(AdminServices.add, { onSuccess: success });
const { loading: editLoading, request: editRequest } = useRequest(AdminServices.edit, { onSuccess: success });
const save = async () => {
try {
const values = await form.validateFields();
// 编辑场景带上主键
if (data?.admin_id) {
values.admin_id = data.admin_id;
editRequest(stringify(values));
} else {
addRequest(stringify(values));
}
} catch (error) {
console.log('表单验证未通过', error);
}
};
useImperativeHandle(props.ref, () => ({
show: (data?: any) => {
setTitle(data ? `${data.username} 编辑` : '新增');
setOpen(true);
setData(data);
if (data) {
// 编辑场景初始化表单
form.setFieldsValue({
username: data.username,
mobile: data.mobile,
email: data.email,
nickname: data.nickname,
password: '', // 密码编辑可选
status: String(data.status),
});
} else {
form.resetFields();
form.setFieldsValue({
status: '1',
});
}
},
}));
return (
<ModalPlugin
open={open}
onCancel={() => setOpen(false)}
title={title}
footer={[
<Button key='cancel' onClick={() => setOpen(false)}>
</Button>,
<Button key='save' type='primary' loading={addLoading || editLoading} onClick={save}>
</Button>,
]}
>
<Form form={form} labelCol={{ style: { width: window?.dfConfig?.language === 'zh-cn' ? 80 : 120 } }}>
<Form.Item
label='用户名'
name='username'
required
rules={[
{ required: true, message: '请输入用户名' },
{ min: 2, message: '最少2字符' },
{ max: 20, message: '最多20字符' },
]}
>
<Input allowClear placeholder='请填写用户名' maxLength={20} showCount />
</Form.Item>
<Form.Item
label='昵称'
name='nickname'
required
rules={[
{ required: true, message: '请输入昵称' },
{ min: 2, message: '最少2字符' },
{ max: 20, message: '最多20字符' },
]}
>
<Input allowClear placeholder='请填写昵称' maxLength={20} showCount />
</Form.Item>
<Form.Item
label='手机号'
name='mobile'
required
rules={[
{ required: true, message: '请输入手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' },
]}
>
<Input allowClear placeholder='请填写手机号' maxLength={11} showCount />
</Form.Item>
<Form.Item label='邮箱' name='email' rules={[{ type: 'email', message: '邮箱格式不正确' }]}>
<Input allowClear placeholder='请填写邮箱' />
</Form.Item>
<Form.Item
label='密码'
name='password'
tooltip={data ? undefined : '不填写则系统自动生成初始密码'}
//help={<div style={{ marginBottom: 6, fontSize: 12, color: '#999' }}>不填写则系统自动生成初始密码</div>}
rules={[{ min: 6, max: 18, message: '密码需6~18位' }]}
>
<Input.Password allowClear placeholder={data ? '不修改请留空' : '请填写密码'} />
</Form.Item>
<Form.Item label={'状态'} name='status'>
<Select options={statusOptions} placeholder='请选择状态' />
</Form.Item>
</Form>
</ModalPlugin>
);
};

View File

@@ -0,0 +1,229 @@
import { Button, DatePicker, Input, Select } from 'antd';
import { stringify } from 'qs';
import { useEffect, useRef, useState } from 'react';
import { FormItemPlugin, FormPlugin } from '@/components/FormPlugin';
import { GapBox } from '@/components/GapBox';
import PageContainerPlugin from '@/components/PageContainer/PageContainerPlugin';
import { FooterPagination, HeaderPagination } from '@/components/PaginationPlugin';
import { MoreSearchButton, SearchButton } from '@/components/SearchButton';
import type { ColumnsTypeUltra } from '@/components/TableColumnsFilterPlugin';
import { TablePlugin } from '@/components/TablePlugin';
import { statusObj, statusOptions } from '@/configs/adminConfig';
import type { IAjaxDataBase, IParamsBase } from '@/interfaces/common';
import { AdminServices } from '@/services/AdminServices';
import { useAuthStore } from '@/store/AuthStore';
import { tableFixedByPhone, toArray } from '@/utils/common';
import { useRequest } from '@/utils/useRequest';
import { AdminEditModal, type IAdminEditModalType } from './components/AdminEditModal';
interface IAjaxData extends IAjaxDataBase {
data: any[];
}
type IParams = IParamsBase & {
username?: string;
nickname?: string;
mobile?: string;
status?: number;
create_dateL?: string;
create_dateU?: string;
};
/** 管理员列表页面 */
const AdminListForm: React.FC = () => {
const auth = useAuthStore().auth;
const [params, setParams] = useState<IParams>({ curr_page: 1, page_count: 20 });
const [ajaxData, setAjaxData] = useState<IAjaxData>({ count: 0, data: [] });
const [showMoreSearch, setShowMoreSearch] = useState(false);
const AdminEditModalRef = useRef<IAdminEditModalType>(null);
const { loading: listLoading, request: listRequest } = useRequest(AdminServices.getAdminList, {
onSuccessCodeZero: (res) => {
setAjaxData({
count: res.count || 0,
data: toArray(res.data),
});
},
});
const columns: ColumnsTypeUltra<any> = [
{
title: '操作',
width: 50,
fixed: tableFixedByPhone('left'),
render: (_, item) => (
<GapBox>
{item?.admin_id != 1 && (
<Button
type='primary'
onClick={() => {
AdminEditModalRef.current?.show(item);
}}
>
</Button>
)}
</GapBox>
),
},
{ title: '管理员', dataIndex: 'username', width: 120 },
{ title: '昵称', dataIndex: 'nickname', width: 120 },
{ title: '手机', dataIndex: 'mobile', width: 120 },
{ title: '邮箱', dataIndex: 'email', width: 120 },
{ title: '状态', dataIndex: 'status', width: 60, ellipsis: true, render: (value) => statusObj[value] },
{
title: '创建时间',
width: window.dfConfig.isPhone ? 160 : 160,
dataIndex: 'create_date',
ellipsis: true,
},
];
function page(curr: number) {
if (!listLoading) {
params.curr_page = curr;
console.log(params);
listRequest(stringify(params));
}
}
useEffect(() => {
page(1);
}, []);
return (
<>
<FormPlugin gutter={16}>
<FormItemPlugin label={'管理员'}>
<Input
allowClear
defaultValue={params.username}
placeholder='管理员'
onChange={(e) => {
params.username = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin label={'昵称'}>
<Input
allowClear
defaultValue={params.nickname}
placeholder='昵称'
onChange={(e) => {
params.nickname = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin label={'手机'}>
<Input
allowClear
defaultValue={params.mobile}
placeholder='手机'
onChange={(e) => {
params.mobile = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin>
<div
style={{
display: 'flex',
flexWrap: 'nowrap',
alignItems: 'center',
whiteSpace: 'nowrap',
gap: 8,
}}
>
<SearchButton
onClick={() => {
page(1);
}}
/>
<MoreSearchButton
show={showMoreSearch}
onClick={() => {
setShowMoreSearch((v) => !v);
}}
/>
</div>
</FormItemPlugin>
</FormPlugin>
<FormPlugin style={{ display: showMoreSearch ? 'flex' : 'none' }} gutter={16}>
<FormItemPlugin label={'状态'}>
<Select
allowClear
value={params.status}
options={statusOptions}
onChange={(value) => {
params.status = value || undefined;
setParams({ ...params });
}}
/>
</FormItemPlugin>
<FormItemPlugin label={'创建日期'}>
<DatePicker.RangePicker
style={{ width: '100%' }}
onChange={(_values, dates) => {
params.create_dateL = dates?.[0] || undefined;
params.create_dateU = dates?.[1] || undefined;
}}
/>
</FormItemPlugin>
</FormPlugin>
<GapBox style={{ marginBottom: 12, justifyContent: 'space-between' }}>
<GapBox>
<Button
type='primary'
onClick={() => {
AdminEditModalRef.current?.show();
}}
>
</Button>
</GapBox>
<HeaderPagination
style={{ marginBottom: 0, marginRight: 12 }}
current={params.curr_page}
pageSize={params.page_count}
total={ajaxData.count}
onChange={page}
/>
</GapBox>
<TablePlugin
loading={listLoading}
onChange={(_p, _f, sort: any) => {
page(1);
}}
dataSource={ajaxData.data}
columns={columns}
scroll={{ x: true }}
rowKey={'admin_id'}
/>
<FooterPagination
current={params.curr_page}
pageSize={params.page_count}
total={ajaxData.count}
onChange={(curr, pageSize) => {
params.page_count = pageSize;
page(curr);
}}
/>
<AdminEditModal ref={AdminEditModalRef} onCallback={() => page(1)} />
</>
);
};
const StaffList = () => (
<PageContainerPlugin breadcrumb={['权限管理', '管理员信息']}>
<AdminListForm />
</PageContainerPlugin>
);
export default StaffList;

View File

@@ -1,26 +1,174 @@
import { Button, Form, Input, notification, Select } from 'antd';
import TextArea from 'antd/lib/input/TextArea';
import { stringify } from 'qs';
import type React from 'react';
import { useImperativeHandle, useState } from 'react';
import ModalPlugin from '@/components/ModalPlugin';
import { stateOptions, userSexOptions } from '@/configs/usersConfig';
import { UserServices } from '@/services/UserServices';
import type { IRef } from '@/utils/type';
import { useRequest } from '@/utils/useRequest';
interface IProps extends IRef {}
interface IProps extends IRef {
onCallback?: () => void;
}
export type IUserEditModalType = {
show: () => void;
show: (data?: any) => void;
};
export const UserEditModal: React.FC<IProps> = (props) => {
console.log(props.ref);
const [open, setOpen] = useState(false);
const [title, setTitle] = useState('');
const [form] = Form.useForm();
const [data, setData] = useState<any>(null);
// 请求成功回调
const success = (res: any) => {
if (res.err_code === 0) {
notification.success({ message: '保存成功' });
props.onCallback?.();
setOpen(false);
form.resetFields(); // 提交成功重置表单
}
};
const { loading: addLoading, request: addRequest } = useRequest(UserServices.add, { onSuccess: success });
const { loading: editLoading, request: editRequest } = useRequest(UserServices.edit, { onSuccess: success });
const save = async () => {
try {
const values = await form.validateFields();
// 编辑场景带上主键
if (data?.user_id) {
values.user_id = data.user_id;
editRequest(stringify(values));
} else {
addRequest(stringify(values));
}
} catch (error) {
console.log('表单验证未通过', error);
}
};
useImperativeHandle(props.ref, () => ({
show: () => {
show: (data?: any) => {
setTitle(data ? `${data.login_name} 编辑` : '新增');
setOpen(true);
setData(data);
if (data) {
// 编辑场景初始化表单
form.setFieldsValue({
login_name: data.login_name,
user_phone: data.user_phone,
user_mail: data.user_mail,
nick_name: data.nick_name,
password: '', // 密码编辑可选
comments: data.comments,
user_sex: String(data.user_sex),
state: String(data.state),
});
} else {
form.resetFields();
form.setFieldsValue({
user_sex: '1',
state: '1',
});
}
},
}));
return (
<ModalPlugin open={open} onCancel={() => setOpen(false)}>
11111
<ModalPlugin
open={open}
onCancel={() => setOpen(false)}
title={title}
footer={[
<Button key='cancel' onClick={() => setOpen(false)}>
</Button>,
<Button key='save' type='primary' loading={addLoading || editLoading} onClick={save}>
</Button>,
]}
>
<Form form={form} labelCol={{ style: { width: window?.dfConfig?.language === 'zh-cn' ? 80 : 120 } }}>
<Form.Item
label='用户名'
name='login_name'
required
rules={[
{ required: true, message: '请输入用户名' },
{ min: 2, message: '最少2字符' },
{ max: 20, message: '最多20字符' },
]}
>
<Input allowClear placeholder='请填写用户名' maxLength={20} showCount />
</Form.Item>
<Form.Item
label='昵称'
name='nick_name'
rules={[
{ min: 2, message: '最少2字符' },
{ max: 20, message: '最多20字符' },
]}
>
<Input allowClear placeholder='请填写昵称' maxLength={20} showCount />
</Form.Item>
<Form.Item
label='手机号'
name='user_phone'
required
rules={[
{ required: true, message: '请输入手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' },
]}
>
<Input allowClear placeholder='请填写手机号' maxLength={11} showCount />
</Form.Item>
<Form.Item label='邮箱' name='user_mail' rules={[{ type: 'email', message: '邮箱格式不正确' }]}>
<Input allowClear placeholder='请填写邮箱' />
</Form.Item>
<Form.Item
label='密码'
name='password'
tooltip={data ? undefined : '不填写则系统自动生成初始密码'}
//help={<div style={{ marginBottom: 6, fontSize: 12, color: '#999' }}>不填写则系统自动生成初始密码</div>}
rules={[{ min: 6, max: 18, message: '密码需6~18位' }]}
>
<Input.Password allowClear placeholder={data ? '不修改请留空' : '请填写密码'} />
</Form.Item>
{!data?.user_id && (
<Form.Item
label='企业名'
name='company_name'
rules={[
{ min: 2, message: '最少2字符' },
{ max: 20, message: '最多20字符' },
]}
>
<Input allowClear placeholder='请填写企业名' />
</Form.Item>
)}
<Form.Item label={'性别'} name='user_sex'>
<Select options={userSexOptions} placeholder='请选择性别' />
</Form.Item>
<Form.Item label={'状态'} name='state'>
<Select options={stateOptions} placeholder='请选择状态' />
</Form.Item>
<Form.Item label='备注' name='comments'>
<TextArea allowClear maxLength={200} showCount />
</Form.Item>
</Form>
</ModalPlugin>
);
};

View File

@@ -73,7 +73,7 @@ export const userListStateProxy = proxy<{
if (temp.order_step?.length) {
temp.order_step = temp.order_step.join(',');
}
const res: any = await requestLite(UserServices.list, temp);
const res: any = await requestLite(UserServices.getUserList, temp);
this.loading = false;
this.ajaxData = toArray(res?.data);
if (this.ajaxData.length == 0 && this.params.curr_page > 1) {

View File

@@ -1,116 +1,196 @@
import { Button } from 'antd';
import { useRef } from 'react';
import { useSnapshot } from 'valtio';
import { Button, DatePicker, Input, Select } from 'antd';
import { stringify } from 'qs';
import { useEffect, useRef, useState } from 'react';
import { FormItemPlugin, FormPlugin } from '@/components/FormPlugin';
import { GapBox } from '@/components/GapBox';
import PageContainerPlugin from '@/components/PageContainer/PageContainerPlugin';
import { FooterPagination, HeaderPagination } from '@/components/PaginationPlugin';
import { MoreSearchButton, ResetButton, SearchButton, SearchInputPlugin } from '@/components/SearchButton';
import type { ColumnsTypeUltra2 } from '@/components/TableColumnsFilterPlugin2';
import { formatTableSort, TablePlugin } from '@/components/TablePlugin';
import { tableFixedByPhone } from '@/utils/common';
import { userListStateProxy } from './components/state';
import { type IUserEditModalType, UserEditModal } from './components/UserEditModal';
import { MoreSearchButton, SearchButton } from '@/components/SearchButton';
import type { ColumnsTypeUltra } from '@/components/TableColumnsFilterPlugin';
import { TablePlugin } from '@/components/TablePlugin';
import { staffType } from '@/configs/staffsConfig';
import { stateOptions, userSex, userState } from '@/configs/usersConfig';
import type { IAjaxDataBase, IParamsBase } from '@/interfaces/common';
import { type IUserEditModalType, UserEditModal } from '@/pages/User/List/components/UserEditModal';
import { UserServices } from '@/services/UserServices';
import { useAuthStore } from '@/store/AuthStore';
import { tableFixedByPhone, toArray } from '@/utils/common';
import { useRequest } from '@/utils/useRequest';
const SearchBox = () => {
const snap = useSnapshot(userListStateProxy);
interface IAjaxData extends IAjaxDataBase {
data: any[];
}
type IParams = IParamsBase & {
login_name?: string;
nick_name?: string;
user_phone?: string;
state?: number;
create_dateL?: string;
create_dateU?: string;
};
/** 用户列表页面 */
const UserListForm: React.FC = () => {
const auth = useAuthStore().auth;
const [params, setParams] = useState<IParams>({ curr_page: 1, page_count: 20 });
const [ajaxData, setAjaxData] = useState<IAjaxData>({ count: 0, data: [] });
const [showMoreSearch, setShowMoreSearch] = useState(false);
const UserEditModalRef = useRef<IUserEditModalType>(null);
const { loading: userLoading, request: userRequest } = useRequest(UserServices.getUserList, {
onSuccessCodeZero: (res) => {
setAjaxData({
count: res.count || 0,
data: toArray(res.data),
});
},
});
const columns: ColumnsTypeUltra<any> = [
{
title: '操作',
width: 50,
fixed: tableFixedByPhone('left'),
render: (_, item) => (
<GapBox>
<Button
type='primary'
onClick={() => {
UserEditModalRef.current?.show(item);
}}
>
</Button>
{auth.SF_ERP_USER_EDIT && (
<Button
type='primary'
onClick={() => {
UserEditModalRef.current?.show(item);
}}
>
</Button>
)}
</GapBox>
),
},
{ title: '用户名', dataIndex: 'login_name', width: 120 },
{ title: '企业名称', dataIndex: 'company_name', width: 120 },
{ title: '类型', dataIndex: 'staff_type', width: 60, render: (value) => staffType[value] },
{ title: '昵称', dataIndex: 'nick_name', width: 120 },
{ title: '手机', dataIndex: 'user_phone', width: 120 },
{ title: '性别', dataIndex: 'user_sex', width: 60, render: (value) => userSex[value] },
{ title: '状态', dataIndex: 'state', width: 60, ellipsis: true, render: (value) => userState[value] },
{
title: '备注',
dataIndex: 'comments',
width: 300,
},
{
title: '创建时间',
width: window.dfConfig.isPhone ? 160 : 160,
dataIndex: 'create_date',
ellipsis: true,
},
];
function page(curr: number) {
if (!userLoading) {
params.curr_page = curr;
console.log(params);
userRequest(stringify(params));
}
}
useEffect(() => {
page(1);
}, []);
return (
<>
<FormPlugin gutter={16}>
<FormItemPlugin label='订单单号'>
<SearchInputPlugin
value={snap.params.order_no}
onPressEnter={() => {
userListStateProxy.page(1);
}}
onEnd={(v) => {
userListStateProxy.params.order_no = v;
<FormItemPlugin label={'用户名'}>
<Input
allowClear
defaultValue={params.login_name}
placeholder='用户名'
onChange={(e) => {
params.login_name = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin label='自定义单号'>
<SearchInputPlugin
value={snap.params.custom_order_no}
onPressEnter={() => {
userListStateProxy.page(1);
}}
onEnd={(v) => {
userListStateProxy.params.custom_order_no = v;
<FormItemPlugin label={'昵称'}>
<Input
allowClear
defaultValue={params.nick_name}
placeholder='昵称'
onChange={(e) => {
params.nick_name = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin label='经销商名称'>
<SearchInputPlugin
value={snap.params.custom_name}
onPressEnter={() => {
userListStateProxy.page(1);
}}
onEnd={(v) => {
userListStateProxy.params.custom_name = v;
<FormItemPlugin label={'手机号'}>
<Input
allowClear
defaultValue={params.user_phone}
placeholder='手机号'
onChange={(e) => {
params.user_phone = e.target.value.trim() || undefined;
}}
onPressEnter={() => page(1)}
/>
</FormItemPlugin>
<FormItemPlugin>
<GapBox>
<div
style={{
display: 'flex',
flexWrap: 'nowrap',
alignItems: 'center',
whiteSpace: 'nowrap',
gap: 8,
}}
>
<SearchButton
onClick={() => {
userListStateProxy.page(1);
}}
/>
<ResetButton
onClick={() => {
userListStateProxy.reset();
page(1);
}}
/>
<MoreSearchButton
show={snap.showMoreSearch}
show={showMoreSearch}
onClick={() => {
userListStateProxy.showMoreSearch = !userListStateProxy.showMoreSearch;
setShowMoreSearch((v) => !v);
}}
/>
</GapBox>
</div>
</FormItemPlugin>
</FormPlugin>
<FormPlugin style={{ display: snap.showMoreSearch ? 'flex' : 'none' }} gutter={16}>
<FormItemPlugin label='经销商手机'>
<SearchInputPlugin
value={snap.params.custom_phone}
onPressEnter={() => {
userListStateProxy.page(1);
<FormPlugin style={{ display: showMoreSearch ? 'flex' : 'none' }} gutter={16}>
<FormItemPlugin label={'状态'}>
<Select
allowClear
value={params.state}
options={stateOptions}
onChange={(value) => {
params.state = value || undefined;
setParams({ ...params });
}}
onEnd={(v) => {
userListStateProxy.params.custom_phone = v;
/>
</FormItemPlugin>
<FormItemPlugin label={'创建日期'}>
<DatePicker.RangePicker
style={{ width: '100%' }}
onChange={(_values, dates) => {
params.create_dateL = dates?.[0] || undefined;
params.create_dateU = dates?.[1] || undefined;
}}
/>
</FormItemPlugin>
</FormPlugin>
</>
);
};
const Content = () => {
const snap = useSnapshot(userListStateProxy);
const tableColumns: ColumnsTypeUltra2<any> = [
{
columnName: '操作',
title: '操作',
fixed: tableFixedByPhone('left'),
width: 52,
render: () => {
return <GapBox style={{ justifyContent: 'center', flexWrap: 'wrap' }}>{/* */}</GapBox>;
},
},
{ columnName: '创建时间', width: 150, title: '创建时间', dataIndex: 'create_date', sorter: true },
{ columnName: '备注', title: '备注', dataIndex: 'comments', width: 180 },
{ columnName: '' },
];
const UserEditModalRef = useRef<IUserEditModalType>(null);
return (
<>
<GapBox style={{ marginBottom: 12, justifyContent: 'space-between' }}>
<GapBox>
<Button
@@ -119,49 +199,47 @@ const Content = () => {
UserEditModalRef.current?.show();
}}
>
</Button>
</GapBox>
<HeaderPagination
current={snap.params.curr_page}
pageSize={snap.params.page_count}
total={snap.count}
onChange={(page: number) => {
userListStateProxy.page(page);
}}
style={{ marginBottom: 0, marginRight: 12 }}
current={params.curr_page}
pageSize={params.page_count}
total={ajaxData.count}
onChange={page}
/>
</GapBox>
<TablePlugin
sticky={{ offsetHeader: window.dfConfig.tableStickyOffsetHeader }}
scroll={{ x: 1000 }}
loading={snap.loading}
loading={userLoading}
onChange={(_p, _f, sort: any) => {
userListStateProxy.params.order = formatTableSort(sort);
userListStateProxy.page(1);
page(1);
}}
dataSource={snap.ajaxData}
dataSource={ajaxData.data}
columns={columns}
scroll={{ x: true }}
rowKey={'user_id'}
columns={tableColumns}
/>
<FooterPagination
current={snap.params.curr_page}
pageSize={snap.params.page_count}
total={snap.count}
onChange={(page: number, pageSize: number): void => {
userListStateProxy.page(page, pageSize);
current={params.curr_page}
pageSize={params.page_count}
total={ajaxData.count}
onChange={(curr, pageSize) => {
params.page_count = pageSize;
page(curr);
}}
/>
<UserEditModal ref={UserEditModalRef} />
<UserEditModal ref={UserEditModalRef} onCallback={() => page(1)} />
</>
);
};
const UserList = () => (
<PageContainerPlugin breadcrumb={['用户管理', '用户列表']}>
<SearchBox />
<Content />
<PageContainerPlugin breadcrumb={['用户管理', '用户信息']}>
<UserListForm />
</PageContainerPlugin>
);
export default UserList;

View File

@@ -0,0 +1,3 @@
export const AdminDepartmentServices = {
getAdminDepartmentList: '/AdminDepartment/getAdminDepartmentList',
} as const;

View File

@@ -0,0 +1,3 @@
export const AdminGroupServices = {
getAdminGroupList: '/AdminGroup/getAdminGroupList',
} as const;

View File

@@ -0,0 +1,10 @@
export const AdminLogServices = {
/** 登录日志 */
getAdminLoginLogList: '/AdminLog/getAdminLoginLogList',
/** 操作日志 */
getAdminSysLogList: '/AdminLog/getAdminSysLogList',
/** 菜单 */
getAdminMenuAjaxList: '/AdminMenu/getAdminMenuAjaxList',
/** 权限 */
getAdminFunctionAjaxList: '/AdminFunction/getAdminFunctionAjaxList',
} as const;

View File

@@ -0,0 +1,12 @@
export const AdminServices = {
/** 获取登录状态及信息 */
loginState: '/Admin/loginState',
/** 退出登录 */
logout: '/Admin/logout',
/** 管理员登录 */
login: '/Admin/login',
/** 管理员列表 */
getAdminList: '/Admin/getAdminList',
add: '/Admin/add',
edit: '/Admin/edit',
} as const;

View File

@@ -0,0 +1,3 @@
export const CompanyServices = {
getCompanyList: '/Company/getCompanyList',
} as const;

View File

@@ -1,3 +1,5 @@
export const UserServices = {
list: '/UserServices/list',
getUserList: '/User/getUserList',
add: '/User/add',
edit: '/User/edit',
} as const;

View File

@@ -37,9 +37,9 @@ export const pathAddApiString = (path: string): string => {
return path;
}
if (`${path}`.startsWith('/')) {
return `/api${path}`;
return `/api/adminrelas${path}`;
}
return `/api/${path}`;
return `/api/adminrelas/${path}`;
}
return path;
};

View File

@@ -37,7 +37,7 @@ export const useRequest = (url: string, options?: IOptions) => {
const res = await post(url, data, config);
if (res?.err_code != 0) {
if (res.err_code == 110000) {
if (res.err_code == 401) {
location.href = '#/login';
} else {
if (options?.hideNotice != true) {

View File

@@ -23,7 +23,7 @@ export default defineConfig({
host: '0.0.0.0',
proxy: {
'/api': {
target: 'http://192.168.1.138:93',
target: 'http://www.free.loc',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''), // 不可以省略rewrite
},