feat: reset web
parent
6e0d9cca5d
commit
12da5f60bd
|
@ -25,7 +25,7 @@
|
|||
"unocss": "^0.60.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.17.4",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/qs": "^6.9.15",
|
||||
"@types/react": "^18.0.33",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
|
|
|
@ -45,9 +45,9 @@ importers:
|
|||
specifier: ^0.60.4
|
||||
version: 0.60.4(postcss@8.4.38)(rollup@3.29.4)(vite@4.5.2(@types/node@20.14.1)(less@4.1.3)(lightningcss@1.22.1)(terser@5.31.0))
|
||||
devDependencies:
|
||||
'@types/lodash':
|
||||
specifier: ^4.17.4
|
||||
version: 4.17.4
|
||||
'@types/lodash-es':
|
||||
specifier: ^4.17.12
|
||||
version: 4.17.12
|
||||
'@types/qs':
|
||||
specifier: ^6.9.15
|
||||
version: 6.9.15
|
||||
|
@ -1319,6 +1319,9 @@ packages:
|
|||
'@types/json-schema@7.0.15':
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
|
||||
'@types/lodash-es@4.17.12':
|
||||
resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
|
||||
|
||||
'@types/lodash@4.17.4':
|
||||
resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==}
|
||||
|
||||
|
@ -8068,6 +8071,10 @@ snapshots:
|
|||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/lodash-es@4.17.12':
|
||||
dependencies:
|
||||
'@types/lodash': 4.17.4
|
||||
|
||||
'@types/lodash@4.17.4': {}
|
||||
|
||||
'@types/minimist@1.2.5': {}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { getSupderInfoApi } from '@/services/system/supderLogin';
|
||||
import { history } from '@umijs/max';
|
||||
import './styles/global.less';
|
||||
import './styles/tailwind.css';
|
||||
import { routes as appRouters, loopMenuItem, type MenuItem } from './utils/router';
|
||||
|
||||
export function patchClientRoutes({ routes }: any) {
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
import { addPackageAPI, editPackageAPI } from '@/services/system/package';
|
||||
import { ActionType, ProFormMoney, ProFormRadio, ProFormText } from '@ant-design/pro-components';
|
||||
import { App, Form, Modal, Spin } from 'antd';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
type PropTypes = {
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
tableRef: React.RefObject<EditRow<ActionType>>;
|
||||
editRow: Package.PackageItem | undefined;
|
||||
};
|
||||
|
||||
const CreateModel = (props: PropTypes) => {
|
||||
const { message } = App.useApp();
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const { visible, editRow, tableRef, onCancel } = props;
|
||||
const [title, setTitle] = useState<string>();
|
||||
|
||||
const handleOnCancel = () => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
};
|
||||
|
||||
const handleAddNewPackage = async () => {
|
||||
const values = await form.validateFields();
|
||||
const _values = cloneDeep(values);
|
||||
_values['day'] = parseInt(_values['day']);
|
||||
_values['materialSpace'] = parseInt(_values['materialSpace']);
|
||||
try {
|
||||
setLoading(true);
|
||||
const result = !editRow?.id ? await addPackageAPI(_values) : await editPackageAPI(_values);
|
||||
if (result.code === 200) {
|
||||
message.success(`套餐${editRow?.id ? '修改' : '添加'}成功`);
|
||||
handleOnCancel();
|
||||
tableRef.current?.reload();
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTitle(editRow?.id ? '修改套餐' : '添加套餐');
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
if (visible) {
|
||||
if (editRow?.id) {
|
||||
form.setFieldsValue(editRow);
|
||||
}
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
destroyOnClose
|
||||
forceRender
|
||||
title={title}
|
||||
width={800}
|
||||
open={visible}
|
||||
afterClose={handleOnCancel}
|
||||
onCancel={handleOnCancel}
|
||||
onOk={handleAddNewPackage}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
<Form form={form} labelCol={{ span: 5 }}>
|
||||
<ProFormText name="id" hidden></ProFormText>
|
||||
<ProFormText
|
||||
initialValue={''}
|
||||
width="md"
|
||||
name="name"
|
||||
required
|
||||
label="套餐名称"
|
||||
placeholder="请输入套餐名称"
|
||||
rules={[{ required: true, message: '请输入套餐名称' }]}
|
||||
/>
|
||||
<ProFormMoney
|
||||
label="套餐展示金额(元)"
|
||||
name="showMoney"
|
||||
locale="en-US"
|
||||
initialValue={0}
|
||||
min={0}
|
||||
placeholder="请输入套餐展示金额"
|
||||
rules={[{ required: true, message: '请输入套餐展示金额' }]}
|
||||
/>
|
||||
<ProFormMoney
|
||||
label="套餐支付金额(元)"
|
||||
name="payMoney"
|
||||
locale="en-US"
|
||||
initialValue={0}
|
||||
min={0}
|
||||
placeholder="请输入套餐支付金额"
|
||||
rules={[{ required: true, message: '请输入套餐支付金额' }]}
|
||||
/>
|
||||
<ProFormText
|
||||
initialValue={0}
|
||||
width="md"
|
||||
name="day"
|
||||
required
|
||||
label="套餐天数(天)"
|
||||
placeholder="请输入套餐天数"
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => {
|
||||
if (!/^[0-9]+$/.test(value)) {
|
||||
return Promise.reject(new Error('请输入套餐天数'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormRadio.Group
|
||||
initialValue={'GB'}
|
||||
name="type"
|
||||
label="存储类型"
|
||||
options={[
|
||||
{
|
||||
label: 'GB',
|
||||
value: 'GB',
|
||||
},
|
||||
{
|
||||
label: 'MB',
|
||||
value: 'MB',
|
||||
},
|
||||
]}
|
||||
></ProFormRadio.Group>
|
||||
<ProFormText
|
||||
initialValue={0}
|
||||
width="md"
|
||||
name="materialSpace"
|
||||
required
|
||||
label="套餐存储空间"
|
||||
placeholder="请输入套餐存储空间"
|
||||
/>
|
||||
</Form>
|
||||
</Spin>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateModel;
|
|
@ -0,0 +1,134 @@
|
|||
import { delPackageAPI, getPackageAPI } from '@/services/system/package';
|
||||
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { App, Button, message } from 'antd';
|
||||
import { useRef, useState } from 'react';
|
||||
import CreateModel from './createModule';
|
||||
|
||||
const Page = () => {
|
||||
const { modal } = App.useApp();
|
||||
const tableRef = useRef<ActionType>();
|
||||
|
||||
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
|
||||
const [editRow, setEditRow] = useState<EditRow<EditRow<Package.PackageItem>>>();
|
||||
|
||||
const columns: ProColumns[] = [
|
||||
{
|
||||
title: '套餐名称',
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '用户展示金额',
|
||||
dataIndex: 'showMoney',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
renderText: (_, record: Package.PackageItem) => record.showMoney + '元',
|
||||
},
|
||||
{
|
||||
title: '实际支付金额',
|
||||
dataIndex: 'payMoney',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
renderText: (_, record: Package.PackageItem) => record.payMoney + '元',
|
||||
},
|
||||
{
|
||||
title: '套餐天数',
|
||||
dataIndex: 'day',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
renderText: (_, record: Package.PackageItem) => record.day + '天',
|
||||
},
|
||||
{
|
||||
title: '套餐容量',
|
||||
dataIndex: 'materialSpace',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
renderText: (_, record: Package.PackageItem) => record.materialSpace + '' + record?.type,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'option',
|
||||
valueType: 'option',
|
||||
align: 'center',
|
||||
render: (_: React.ReactNode, record: Package.PackageItem) => (
|
||||
<div className="space-x-4">
|
||||
<Button
|
||||
key={'edit'}
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setEditRow(record);
|
||||
setCreateModalVisible(true);
|
||||
}}
|
||||
>
|
||||
修改套餐
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
danger
|
||||
key={'del'}
|
||||
onClick={() => {
|
||||
modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除该套餐?',
|
||||
async onOk() {
|
||||
const flag = await delPackageAPI(record.id);
|
||||
if (flag) {
|
||||
message.success('删除成功');
|
||||
tableRef.current?.reload();
|
||||
} else {
|
||||
message.error('删除失败');
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除套餐
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProTable
|
||||
actionRef={tableRef}
|
||||
columns={columns}
|
||||
rowKey={'id'}
|
||||
search={false}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
key="addSuperAdmin"
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setCreateModalVisible(true);
|
||||
}}
|
||||
>
|
||||
新建套餐
|
||||
</Button>,
|
||||
]}
|
||||
request={async () => {
|
||||
const { meta, code, data } = await getPackageAPI();
|
||||
return {
|
||||
data: data ?? [],
|
||||
total: meta.total,
|
||||
success: code === 200,
|
||||
};
|
||||
}}
|
||||
></ProTable>
|
||||
|
||||
<CreateModel
|
||||
visible={createModalVisible}
|
||||
onCancel={() => {
|
||||
setEditRow(undefined);
|
||||
setCreateModalVisible(false);
|
||||
}}
|
||||
tableRef={tableRef}
|
||||
editRow={editRow}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
|
@ -82,7 +82,6 @@ const Page = () => {
|
|||
render: (_: React.ReactNode, record: SuperAdmin.SuperAdminItem) => (
|
||||
<div className="space-x-4">
|
||||
<Button
|
||||
className="app-btn warning"
|
||||
key={'edit'}
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
import { addUxUserAPI } from '@/services/system/user';
|
||||
import { Reg } from '@/utils/reg';
|
||||
import { ActionType, ProFormText } from '@ant-design/pro-components';
|
||||
import { App, Form, Modal, Spin } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
type PropTypes = {
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
onShowPwd: (email: string, password: string) => void;
|
||||
tableRef: React.RefObject<EditRow<ActionType>>;
|
||||
editRow: EditRow<User.UserItem>;
|
||||
};
|
||||
|
||||
const CreateModel = (props: PropTypes) => {
|
||||
const { message } = App.useApp();
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const { visible, editRow, tableRef, onCancel, onShowPwd } = props;
|
||||
const [title, setTitle] = useState<string>();
|
||||
|
||||
const handleOnCancel = () => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
};
|
||||
|
||||
const handleAddNewUser = async () => {
|
||||
const values = await form.validateFields();
|
||||
try {
|
||||
setLoading(true);
|
||||
const dict = 'ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
|
||||
const password = Array.from({ length: 8 }, () => dict[Math.round(dict.length * Math.random())]).join('');
|
||||
const submitData = values;
|
||||
submitData['password'] = password;
|
||||
const result = await addUxUserAPI(submitData);
|
||||
if (result.code === 200) {
|
||||
message.success(`用户添加成功`);
|
||||
handleOnCancel();
|
||||
onShowPwd(values.email, password);
|
||||
tableRef.current?.reload();
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTitle(editRow?.id ? '修改用户' : '添加用户');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
if (visible) {
|
||||
if (editRow?.id) {
|
||||
form.setFieldsValue(editRow);
|
||||
}
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
destroyOnClose
|
||||
forceRender
|
||||
title={title}
|
||||
width={600}
|
||||
open={visible}
|
||||
afterClose={handleOnCancel}
|
||||
onCancel={handleOnCancel}
|
||||
onOk={handleAddNewUser}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
<Form form={form} labelCol={{ span: 5 }}>
|
||||
<ProFormText name="id" hidden></ProFormText>
|
||||
<ProFormText
|
||||
initialValue={''}
|
||||
width="md"
|
||||
name="email"
|
||||
required
|
||||
label="邮箱"
|
||||
placeholder="请输入邮箱"
|
||||
rules={[
|
||||
{ required: true, message: '请输入邮箱' },
|
||||
{ pattern: Reg.Email, message: '邮箱格式不正确' },
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
initialValue={''}
|
||||
width="md"
|
||||
name="userName"
|
||||
required
|
||||
label="用户名称"
|
||||
placeholder="请输入用户名称"
|
||||
rules={[{ required: true, message: '请输入用户名称' }]}
|
||||
/>
|
||||
{/* <Form.Item label="开通套餐" required>
|
||||
<Space size="middle">
|
||||
{packageOptions.map((item) => {
|
||||
return (
|
||||
<Card
|
||||
key={item.id}
|
||||
title={item.name}
|
||||
size="small"
|
||||
className={`cursor-pointer ${choosePackage === item.id ? 'border-[#1677ff] border-2' : ''}`}
|
||||
onClick={() => {
|
||||
setChoosePackage(item.id);
|
||||
}}
|
||||
>
|
||||
<div>显示金额:{item.showMoney}</div>
|
||||
<div>付款金额:{item.payMoney}</div>
|
||||
<div>
|
||||
存储空间:{item.materialSpace}
|
||||
{item.type}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
</Form.Item> */}
|
||||
</Form>
|
||||
</Spin>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateModel;
|
|
@ -0,0 +1,107 @@
|
|||
import { getPackageAPI } from '@/services/system/package';
|
||||
import { openPackageAPI } from '@/services/system/user';
|
||||
import { ActionType } from '@ant-design/pro-components';
|
||||
import { App, Card, Form, Modal, Space, Spin } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
type PropTypes = {
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
tableRef: React.RefObject<EditRow<ActionType>>;
|
||||
editRow: EditRow<User.UserItem>;
|
||||
};
|
||||
|
||||
const OpenPackageModel = (props: PropTypes) => {
|
||||
const { message } = App.useApp();
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [packageOptions, setPackage] = useState<Package.PackageItem[]>([]);
|
||||
const [choosePackage, setChoosePackage] = useState<string>();
|
||||
|
||||
const { visible, editRow, tableRef, onCancel } = props;
|
||||
const [title, setTitle] = useState<string>();
|
||||
|
||||
const handleOnCancel = () => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
};
|
||||
|
||||
const getPackageList = () => {
|
||||
getPackageAPI().then((result) => {
|
||||
setPackage(result.data);
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!choosePackage) {
|
||||
message.warning('请选择一个套餐');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setLoading(true);
|
||||
const result = await openPackageAPI({ id: editRow!.id, packageId: choosePackage });
|
||||
if (result.code === 200) {
|
||||
message.success(result.message);
|
||||
handleOnCancel();
|
||||
tableRef.current?.reload();
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getPackageList();
|
||||
setTitle('开通用户套餐');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setChoosePackage(undefined);
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
destroyOnClose
|
||||
forceRender
|
||||
title={title}
|
||||
width={700}
|
||||
open={visible}
|
||||
afterClose={handleOnCancel}
|
||||
onCancel={handleOnCancel}
|
||||
onOk={handleSubmit}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
<Form form={form} labelCol={{ span: 5 }}>
|
||||
<Form.Item label="开通套餐" required>
|
||||
<Space size="middle">
|
||||
{packageOptions.map((item) => {
|
||||
return (
|
||||
<Card
|
||||
key={item.id}
|
||||
title={item.name}
|
||||
size="small"
|
||||
className={`cursor-pointer ${choosePackage === item.id ? 'border-[#1677ff] border-2' : ''}`}
|
||||
onClick={() => {
|
||||
setChoosePackage(item.id);
|
||||
}}
|
||||
>
|
||||
<div>显示金额:{item.showMoney}</div>
|
||||
<div>付款金额:{item.payMoney}</div>
|
||||
<div>
|
||||
存储空间:{item.materialSpace}
|
||||
{item.type}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default OpenPackageModel;
|
|
@ -0,0 +1,120 @@
|
|||
import { getUserByIdAPI } from '@/services/system/user';
|
||||
import { ActionType } from '@ant-design/pro-components';
|
||||
import { Descriptions, Form, Modal, Spin, Tabs } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
type PropTypes = {
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
tableRef: React.RefObject<EditRow<ActionType>>;
|
||||
editRow: EditRow<User.UserItem>;
|
||||
};
|
||||
|
||||
const UserDetailsModal = (props: PropTypes) => {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [tabType, setTabType] = useState('details');
|
||||
const [userInfo, setUserInfo] = useState<User.UserItem>();
|
||||
const [projectCount, setProjectCount] = useState<User.UserItemByInfo['projectCount']>(0);
|
||||
const [materialsCount, setMaterialsCount] = useState<User.UserItemByInfo['materialsCount']>(0);
|
||||
const [materialsGroup, setMaterialsGroup] = useState<User.UserItemByInfo['materialsGroup']>([]);
|
||||
|
||||
const { visible, editRow, onCancel } = props;
|
||||
const [title, setTitle] = useState<string>();
|
||||
|
||||
const handleOnCancel = () => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
};
|
||||
|
||||
const handleGetUserInfo = () => {
|
||||
setLoading(true);
|
||||
getUserByIdAPI(editRow!.id)
|
||||
.then((result) => {
|
||||
if (result.code === 200) {
|
||||
setUserInfo(result.data.userInfo);
|
||||
setProjectCount(result.data.projectCount);
|
||||
setMaterialsCount(result.data.materialsCount);
|
||||
setMaterialsGroup(result.data.materialsGroup);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTitle('用户详情');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
handleGetUserInfo();
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
const RenderUserDetails = () => {
|
||||
if (!userInfo) return <></>;
|
||||
return (
|
||||
<Descriptions size="small" column={{ xs: 1, sm: 2, md: 3, lg: 3 }} bordered>
|
||||
<Descriptions.Item label="邮箱">{userInfo.email}</Descriptions.Item>
|
||||
<Descriptions.Item label="用户名称">{userInfo.userName}</Descriptions.Item>
|
||||
<Descriptions.Item label="套餐信息">
|
||||
{userInfo?.package?.name +
|
||||
'/' +
|
||||
userInfo?.package?.showMoney +
|
||||
'元' +
|
||||
'/' +
|
||||
userInfo?.package?.materialSpace +
|
||||
userInfo?.package?.type}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="总存储空间">
|
||||
{userInfo?.increasedStorageInKb && (userInfo.increasedStorageInKb / 1024).toFixed(2) + 'MB'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="已使用空间">
|
||||
{userInfo?.UserProfile && (userInfo.UserProfile.storageUsedInKb / 1024).toFixed(2) + 'MB'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="剩余存储空间">
|
||||
{((userInfo.increasedStorageInKb! - userInfo!.UserProfile!.storageUsedInKb) / 1024).toFixed(2) + 'MB'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="项目数量">{projectCount}</Descriptions.Item>
|
||||
<Descriptions.Item label="素材数量">{materialsCount}</Descriptions.Item>
|
||||
<Descriptions.Item label="素材分组">
|
||||
<div className="flex space-x-2">
|
||||
{materialsGroup.map((item, index) => {
|
||||
return (
|
||||
<span key={index} className="text-xs">
|
||||
{item.materialType}({item._count})
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
destroyOnClose
|
||||
forceRender
|
||||
title={title}
|
||||
width={1200}
|
||||
open={visible}
|
||||
afterClose={handleOnCancel}
|
||||
onCancel={handleOnCancel}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
<Tabs activeKey={tabType} onChange={(activeKey) => setTabType(activeKey)}>
|
||||
<Tabs.TabPane key={'details'} tab={'用户详情'}>
|
||||
<RenderUserDetails />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key={'handleLogs'} tab={'操作记录'}></Tabs.TabPane>
|
||||
<Tabs.TabPane key={'orderInfo'} tab={'订单信息'}></Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Spin>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserDetailsModal;
|
|
@ -1,5 +1,278 @@
|
|||
const UserPage = () => {
|
||||
return <>1231</>;
|
||||
import { changeStatusAPI, delUxUserAPI, getUserListAPI } from '@/services/system/user';
|
||||
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { App, Button, Space, TableProps, message } from 'antd';
|
||||
import { useRef, useState } from 'react';
|
||||
import CreateModel from './CreateModule';
|
||||
import OpenPackageModel from './OpenPackage';
|
||||
import UserDetailsModal from './UserDetails';
|
||||
|
||||
const Page = () => {
|
||||
const { modal } = App.useApp();
|
||||
const tableRef = useRef<ActionType>();
|
||||
|
||||
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
|
||||
const [openPackageVisible, setOpenPackageVisible] = useState<boolean>(false);
|
||||
const [userDetailsVisible, setUserDetailsVisible] = useState<boolean>(false);
|
||||
const [editRow, setEditRow] = useState<EditRow<User.UserItem>>();
|
||||
const [tableParams, setTableParams] = useState<TableParams>({
|
||||
pagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
});
|
||||
|
||||
const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter) => {
|
||||
setTableParams({
|
||||
pagination,
|
||||
filters,
|
||||
...sorter,
|
||||
});
|
||||
};
|
||||
|
||||
const handleChangeUserStatus = (record: User.UserItem) => {
|
||||
modal.confirm({
|
||||
title: '提示',
|
||||
content: `确定要将用户【${record.email}】${record.enabled ? '禁用' : '启用'}`,
|
||||
async onOk() {
|
||||
const result = await changeStatusAPI({ id: record.id, enabled: !record.enabled });
|
||||
if (result.code === 200) {
|
||||
message.success(result.message);
|
||||
tableRef.current?.reload();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const columns: ProColumns[] = [
|
||||
{
|
||||
title: '邮箱',
|
||||
dataIndex: 'email',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
render: (_, record: User.UserItem) => {
|
||||
return (
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => {
|
||||
setEditRow(record);
|
||||
setUserDetailsVisible(true);
|
||||
}}
|
||||
>
|
||||
{record.email}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '用户名称',
|
||||
dataIndex: 'userName',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '当前套餐',
|
||||
dataIndex: 'packageId',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
renderText: (_, record: User.UserItem) => {
|
||||
if (!record.package) {
|
||||
return '-';
|
||||
}
|
||||
return (
|
||||
record?.package?.name +
|
||||
'/' +
|
||||
record.package.showMoney +
|
||||
'元' +
|
||||
'/' +
|
||||
record?.package?.materialSpace +
|
||||
record?.package?.type
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '用户来源',
|
||||
dataIndex: 'userSource',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
render: (_, record: User.UserItem) => {
|
||||
// 用户来源 1. ux 注册 2. 后台创建
|
||||
if (record.userSource === 1) {
|
||||
return 'ux 注册';
|
||||
} else if (record.userSource === 2) {
|
||||
return '管理员创建';
|
||||
}
|
||||
return record.userSource;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabled',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
render: (_, record: User.UserItem) => {
|
||||
if (record.enabled === true) {
|
||||
return (
|
||||
<div className="flex items-center space-x-2 justify-center">
|
||||
<span className="w-[10px] h-[10px] rounded-full bg-green"></span>
|
||||
<span>启用</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center space-x-2 justify-center">
|
||||
<span className="w-[10px] h-[10px] rounded-full bg-gray"></span>
|
||||
<span>禁用</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '账户创建时间',
|
||||
dataIndex: 'createdDateTime',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '最后登录时间',
|
||||
dataIndex: 'lastLoginTime',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'option',
|
||||
valueType: 'option',
|
||||
align: 'center',
|
||||
render: (_: React.ReactNode, record: User.UserItem) => (
|
||||
<div className="space-x-4">
|
||||
{!record.packageId && (
|
||||
<Button
|
||||
key={'openPackage'}
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setEditRow(record);
|
||||
setOpenPackageVisible(true);
|
||||
}}
|
||||
>
|
||||
开通套餐
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
className="app-btn warning"
|
||||
key={'changeStatus'}
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
handleChangeUserStatus(record);
|
||||
}}
|
||||
>
|
||||
{record.enabled ? '禁用' : '启用'}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
danger
|
||||
key={'delUser'}
|
||||
onClick={() => {
|
||||
modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除该用户?',
|
||||
async onOk() {
|
||||
const flag = await delUxUserAPI(record.id);
|
||||
if (flag.code === 200) {
|
||||
tableRef.current?.reload();
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
删除用户
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProTable
|
||||
actionRef={tableRef}
|
||||
columns={columns}
|
||||
rowKey={'id'}
|
||||
pagination={tableParams.pagination}
|
||||
onChange={handleTableChange}
|
||||
search={{
|
||||
labelWidth: 140,
|
||||
span: { xs: 24, sm: 24, md: 12, lg: 12, xl: 6, xxl: 6 },
|
||||
}}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
key="addUxAdmin"
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setCreateModalVisible(true);
|
||||
}}
|
||||
>
|
||||
新建用户
|
||||
</Button>,
|
||||
]}
|
||||
request={async (params) => {
|
||||
const { current, pageSize } = params;
|
||||
const _params = { current, pageSize };
|
||||
const { meta, code, data } = await getUserListAPI(_params);
|
||||
return {
|
||||
data: data ?? [],
|
||||
total: meta.total,
|
||||
success: code === 200,
|
||||
};
|
||||
}}
|
||||
></ProTable>
|
||||
|
||||
<CreateModel
|
||||
visible={createModalVisible}
|
||||
onCancel={() => {
|
||||
setEditRow(undefined);
|
||||
setCreateModalVisible(false);
|
||||
}}
|
||||
onShowPwd={(email, password) => {
|
||||
modal.info({
|
||||
title: '账号创建成功',
|
||||
cancelButtonProps: {
|
||||
hidden: true,
|
||||
},
|
||||
content: (
|
||||
<>
|
||||
<Space direction="vertical" size="middle">
|
||||
<div>登录账号:{email}</div>
|
||||
<div>登录密码:{password}</div>
|
||||
</Space>
|
||||
</>
|
||||
),
|
||||
});
|
||||
}}
|
||||
tableRef={tableRef}
|
||||
editRow={editRow}
|
||||
/>
|
||||
|
||||
<OpenPackageModel
|
||||
visible={openPackageVisible}
|
||||
onCancel={() => {
|
||||
setEditRow(undefined);
|
||||
setOpenPackageVisible(false);
|
||||
}}
|
||||
tableRef={tableRef}
|
||||
editRow={editRow}
|
||||
/>
|
||||
|
||||
<UserDetailsModal
|
||||
visible={userDetailsVisible}
|
||||
onCancel={() => {
|
||||
setEditRow(undefined);
|
||||
setUserDetailsVisible(false);
|
||||
}}
|
||||
tableRef={tableRef}
|
||||
editRow={editRow}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserPage;
|
||||
export default Page;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import request from '@/utils/request';
|
||||
|
||||
export const getPackageAPI = (): Promise<API.ResponstList<Package.PackageItem[]>> => {
|
||||
return request.get(`/system/package`);
|
||||
};
|
||||
|
||||
export const addPackageAPI = (data: Record<string, any>): Promise<API.ResponstBody> => {
|
||||
return request.post(`/system/package`, data);
|
||||
};
|
||||
|
||||
export const editPackageAPI = (data: Record<string, any>): Promise<API.ResponstBody> => {
|
||||
return request.put(`/system/package`, data);
|
||||
};
|
||||
|
||||
export const delPackageAPI = (id: string): Promise<API.ResponstBody> => {
|
||||
return request.delete(`/system/package/${id}`);
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
declare namespace Package {
|
||||
type PackageItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
showMoney?: number;
|
||||
payMoney?: string;
|
||||
day?: number;
|
||||
type?: string;
|
||||
materialSpace?: number;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import request from '@/utils/request';
|
||||
import qs from 'qs';
|
||||
|
||||
export const getUserListAPI = (params: Record<string, any>): Promise<API.ResponstList<User.UserItem[]>> => {
|
||||
return request.get(`/system/user?` + qs.stringify(params));
|
||||
};
|
||||
|
||||
export const getUserByIdAPI = (id: string): Promise<API.ResponstList<User.UserItemByInfo>> => {
|
||||
return request.get(`/system/user/getUserById/` + id);
|
||||
};
|
||||
|
||||
export const addUxUserAPI = (params: User.UserItem): Promise<API.ResponstBody> => {
|
||||
return request.post('/system/user', params);
|
||||
};
|
||||
|
||||
export const changeStatusAPI = (params: User.UserItem): Promise<API.ResponstBody> => {
|
||||
return request.put('/system/user/changeStatus', params);
|
||||
};
|
||||
|
||||
export const delUxUserAPI = (id: string): Promise<API.ResponstBody> => {
|
||||
return request.delete('/system/user/' + id);
|
||||
};
|
||||
|
||||
export const openPackageAPI = (params: User.UserItem): Promise<API.ResponstBody> => {
|
||||
return request.post('/system/user/openPackage', params);
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
declare namespace User {
|
||||
type UserProfile = {
|
||||
id: string;
|
||||
storageUsedInKb: number;
|
||||
};
|
||||
|
||||
type UserItem = {
|
||||
id: string;
|
||||
userName?: string;
|
||||
email?: string;
|
||||
password?: string;
|
||||
passwordHash?: string;
|
||||
enabled?: boolean;
|
||||
|
||||
packageId?: string;
|
||||
|
||||
expired?: string;
|
||||
// 用户来源 1. ux 注册 2. 后台创建
|
||||
userSource?: number;
|
||||
// 用户总空间
|
||||
increasedStorageInKb?: number;
|
||||
|
||||
createdDateTime?: string;
|
||||
lastUpdateDateTime?: string;
|
||||
lastLoginTime?: string;
|
||||
packageId?: string;
|
||||
package?: Package.PackageItem;
|
||||
|
||||
UserProfile?: UserProfile;
|
||||
};
|
||||
|
||||
type UserItemByInfo = {
|
||||
userInfo: UserItem;
|
||||
materialsCount: number;
|
||||
projectCount: number;
|
||||
projectCount: number;
|
||||
materialsGroup: { _count: number; materialType: string }[];
|
||||
};
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
@import url('var.less');
|
||||
|
||||
.app-btn {
|
||||
&.warning {
|
||||
background-color: @btn-warning;
|
||||
|
||||
&:hover {
|
||||
background-color: @btn-warning__hover !important;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
@import 'tailwindcss/base';
|
||||
@import 'tailwindcss/components';
|
||||
@import 'tailwindcss/utilities';
|
|
@ -0,0 +1,2 @@
|
|||
@btn-warning: #f5b753;
|
||||
@btn-warning__hover: #f5b753;
|
|
@ -21,6 +21,11 @@ export const routes: any[] = [
|
|||
name: '系统管理',
|
||||
path: '/system',
|
||||
children: [
|
||||
{
|
||||
name: '套餐设置',
|
||||
path: '/system/package',
|
||||
component: '/System/Package',
|
||||
},
|
||||
{
|
||||
name: '管理员设置',
|
||||
path: '/system/superAdmin',
|
||||
|
|
|
@ -3,6 +3,13 @@ import '@umijs/max/typings';
|
|||
declare global {
|
||||
declare type EditRow<T> = T | undefined;
|
||||
|
||||
interface TableParams {
|
||||
pagination?: TablePaginationConfig;
|
||||
sortField?: string;
|
||||
sortOrder?: string;
|
||||
filters?: Parameters<GetProp<TableProps, 'onChange'>>[1];
|
||||
}
|
||||
|
||||
const API_URL: string;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue