@@ -3,6 +3,7 @@ export * from './product.data.ts' | |||
export * from './role.data.ts' | |||
export * from './position.data.ts' | |||
export * from './department.data.ts' | |||
export * from './tenant.data.ts' | |||
export interface ResponseDTO<T>{ | |||
code: number; | |||
@@ -0,0 +1,49 @@ | |||
export interface TenantVO { | |||
id: number | |||
name: string | |||
contactName: string | |||
contactMobile: string | |||
status: number | |||
domain: string | |||
packageId: number | |||
username: string | |||
password: string | |||
expireTime: Date | |||
accountCount: number | |||
createTime: Date | |||
} | |||
export interface TenantPageReqVO extends PageParam { | |||
name?: string | |||
contactName?: string | |||
contactMobile?: string | |||
status?: number | |||
createTime?: Date[] | |||
} | |||
export interface TenantExportReqVO { | |||
name?: string | |||
contactName?: string | |||
contactMobile?: string | |||
status?: number | |||
createTime?: Date[] | |||
} | |||
export interface TenantPackageVO { | |||
id: number | |||
name: string | |||
status: number | |||
remark: string | |||
creator: string | |||
updater: string | |||
updateTime: string | |||
menuIds: number[] | |||
createTime: Date | |||
} | |||
export interface TenantPackagePageReqVO extends PageParam { | |||
name?: string | |||
status?: number | |||
remark?: string | |||
createTime?: Date[] | |||
} |
@@ -66,6 +66,7 @@ const DepartmentEditor: React.FC<EditorProps> = (props) => { | |||
width={640} | |||
onOk={save} | |||
onCancel={() => onCancel()} | |||
confirmLoading= {saveLoading} | |||
destroyOnClose | |||
> | |||
<Form | |||
@@ -162,6 +162,7 @@ const MenuEditor: React.FC<MenuEditorProps> = (props) => { | |||
width={640} | |||
onOk={save} | |||
onCancel={() => onCancel()} | |||
confirmLoading= {saveLoading} | |||
destroyOnClose | |||
> | |||
<Form | |||
@@ -66,6 +66,7 @@ const PositionEditor: React.FC<EditorProps> = (props) => { | |||
width={640} | |||
onOk={save} | |||
onCancel={() => onCancel()} | |||
confirmLoading= {saveLoading} | |||
destroyOnClose | |||
> | |||
<Form | |||
@@ -67,6 +67,7 @@ const RoleEditor: React.FC<RoleEditorProps> = (props) => { | |||
width={640} | |||
onOk={save} | |||
onCancel={() => onCancel()} | |||
confirmLoading= {saveLoading} | |||
destroyOnClose | |||
> | |||
<Form | |||
@@ -0,0 +1,235 @@ | |||
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 { TenantVO } from '@/models' | |||
import { antdUtils } from '@/utils/antd'; | |||
import { useRequest } from '@/hooks/use-request'; | |||
import tenantService from '@/request/service/tenant'; | |||
import { formatDate } from '@/utils/formatTime' | |||
import TenantEditor from './tenant-editor'; | |||
export default () => { | |||
const [editorVisable, seEditorVisable] = useState<boolean>(false); | |||
const [editData, seEditData] = useState<TenantVO>(); | |||
const [dataSource, setDataSource] = useState<TenantVO[]>([]); | |||
const [searchFrom] = Form.useForm(); | |||
const { runAsync: getPageApi } = useRequest(tenantService.getTenantPageApi, { manual: true }); | |||
const { runAsync: deleteApi } = useRequest(tenantService.deleteTenantApi, { manual: true }); | |||
const load = async () => { | |||
const [error, { data }] = await getPageApi(searchFrom.getFieldsValue()); | |||
if (!error) { | |||
setDataSource(data.list); | |||
} | |||
}; | |||
const showDeleteConfirm = (data: TenantVO) => { | |||
antdUtils.modal?.confirm({ | |||
title: '确认要将该菜单删除吗?', | |||
icon: <ExclamationCircleFilled />, | |||
content: '请注意删除以后不可恢复!', | |||
okText: '删除', | |||
okType: 'danger', | |||
cancelText: '取消', | |||
onOk() { | |||
return new Promise(async (resolve) => { | |||
const [error, { code, msg }] = await deleteApi(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<TenantVO> = [ | |||
{ | |||
title: '租户编号', | |||
dataIndex: 'id', | |||
key: 'id', | |||
align: 'right', | |||
width: 100, | |||
}, | |||
{ | |||
title: '租户名称', | |||
dataIndex: 'name', | |||
key: 'name', | |||
align: 'center', | |||
width: 150, | |||
}, | |||
{ | |||
title: '租户套餐', | |||
dataIndex: 'packageId', | |||
key: 'packageId', | |||
align: 'center', | |||
width: 100, | |||
}, | |||
{ | |||
title: '联系人', | |||
dataIndex: 'contactName', | |||
key: 'contactName', | |||
align: 'center', | |||
width: 150 | |||
}, | |||
{ | |||
title: '联系手机', | |||
dataIndex: 'contactMobile', | |||
key: 'contactMobile', | |||
align: 'center', | |||
width: 150 | |||
}, | |||
{ | |||
title: '账号额度', | |||
dataIndex: 'accountCount', | |||
key: 'accountCount', | |||
align: 'center', | |||
width: 150 | |||
}, | |||
{ | |||
title: '过期时间', | |||
key: 'expireTime', | |||
dataIndex: 'expireTime', | |||
width: 200, | |||
align: 'center', | |||
render: (value: number) => { | |||
return formatDate(new Date(value), "YYYY-mm-dd HH:MM:SS") | |||
} | |||
}, | |||
{ | |||
title: '绑定域名', | |||
dataIndex: 'domain', | |||
key: 'domain', | |||
align: 'center', | |||
width: 200 | |||
}, | |||
{ | |||
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: TenantVO, 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 name="contactName" label="租户名称"> | |||
<Input className='w-[150px]' placeholder='请输入联系人' allowClear /> | |||
</Form.Item> | |||
<Form.Item name="contactMobile" label="联系人手机"> | |||
<Input className='w-[160px]' 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> | |||
<TenantEditor | |||
onSave={() => { | |||
load(); | |||
seEditorVisable(false); | |||
}} | |||
onCancel={() => { seEditorVisable(false) }} | |||
visible={editorVisable} | |||
data={editData} /> | |||
</> | |||
); | |||
}; | |||
@@ -0,0 +1,194 @@ | |||
import React, { useEffect, useState } from 'react' | |||
import { Form, Input, InputNumber, Radio, Modal, Select, DatePicker } from 'antd'; | |||
import tenantService from '@/request/service/tenant'; | |||
import tenantPkgService from '@/request/service/tenant-package'; | |||
import { useRequest } from '@/hooks/use-request'; | |||
import type { TenantVO } from '@/models' | |||
import { antdUtils } from '@/utils/antd'; | |||
import customParseFormat from 'dayjs/plugin/customParseFormat'; | |||
import dayjs from 'dayjs'; | |||
dayjs.extend(customParseFormat); | |||
const layout = { | |||
labelCol: { span: 4, }, | |||
wrapperCol: { span: 16 } | |||
}; | |||
interface EditorProps { | |||
visible: boolean; | |||
onCancel: (flag?: boolean) => void; | |||
onSave: (role: TenantVO) => void; | |||
data?: TenantVO | null; | |||
} | |||
const TenantEditor: React.FC<EditorProps> = (props) => { | |||
const { visible, onCancel, onSave, data } = props; | |||
const { runAsync: updateApi } = useRequest(tenantService.updateTenantApi, { manual: true }); | |||
const { runAsync: createApi } = useRequest(tenantService.createTenantApi, { manual: true }); | |||
const { data: tenanPackages, run: getTenantPackageList } = useRequest(tenantPkgService.getTenantPackageList, { manual: true }); | |||
const isEdit = !!data; | |||
const [saveLoading, setSaveLoading] = useState(false); | |||
const [form] = Form.useForm(); | |||
useEffect(() => { | |||
if (visible) { | |||
if (data) { | |||
form.setFieldsValue(data); | |||
form.setFieldValue('expireTime', dayjs(data.expireTime)) | |||
} | |||
} else { | |||
form.resetFields(); | |||
} | |||
}, [visible]); | |||
useEffect(() => { | |||
getTenantPackageList() | |||
}, [getTenantPackageList]) | |||
const save = async () => { | |||
setSaveLoading(true); | |||
const fieldValues = form.getFieldsValue(); | |||
const expireTime = dayjs(form.getFieldValue('expireTime')).toDate().getTime() | |||
const newValue = isEdit ? { ...data, ...fieldValues, expireTime } : {...fieldValues, expireTime}; | |||
const [error, { msg, code }] = isEdit ? await updateApi(newValue) : await createApi(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()} | |||
confirmLoading= {saveLoading} | |||
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="packageId" label="租户套餐" | |||
rules={[ | |||
{ | |||
required: true, | |||
message: '请选择租户套餐', | |||
}, | |||
]} | |||
> | |||
<Select placeholder="请选择租户套餐" options={tenanPackages?.data.map(item => { | |||
return { | |||
label: item.name, | |||
value: item.id, | |||
} | |||
})}> | |||
</Select> | |||
</Form.Item> | |||
<Form.Item name="contactName" label="联系人" | |||
rules={[ | |||
{ | |||
required: true, | |||
message: '请输入联系人', | |||
}, | |||
]} | |||
> | |||
<Input /> | |||
</Form.Item> | |||
<Form.Item name="contactMobile" label="联系电话" | |||
rules={[ | |||
{ | |||
required: true, | |||
message: '请输入电话号码', | |||
}, | |||
]} | |||
> | |||
<Input /> | |||
</Form.Item> | |||
<Form.Item name="username" label="用户名称"> | |||
<Input /> | |||
</Form.Item> | |||
<Form.Item name="password" label="用户密码"> | |||
<Input /> | |||
</Form.Item> | |||
<Form.Item name="accountCount" label="账号额度" > | |||
<InputNumber min={0} /> | |||
</Form.Item> | |||
<Form.Item name="expireTime" label="过期时间" | |||
rules={[ | |||
{ | |||
required: true, | |||
message: '请设置过期时间', | |||
}, | |||
]}> | |||
<DatePicker showTime format="YYYY-MM-DD HH:mm:ss" /> | |||
</Form.Item> | |||
<Form.Item name="domain" label="绑定域名" | |||
rules={[ | |||
{ | |||
required: true, | |||
message: '请输入绑定域名', | |||
}, | |||
]} | |||
> | |||
<Input /> | |||
</Form.Item> | |||
<Form.Item name="status" label="租户状态" rules={[ | |||
{ | |||
required: true, | |||
message: '请选择租户状态', | |||
}, | |||
]}> | |||
<Radio.Group options={[ | |||
{ value: 0, label: "开启" }, | |||
{ value: 1, label: "关闭" } | |||
]} optionType="default"> | |||
</Radio.Group> | |||
</Form.Item> | |||
</Form> | |||
</Modal> | |||
</> | |||
) | |||
} | |||
export default TenantEditor; |
@@ -0,0 +1,184 @@ | |||
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 { TenantPackageVO } from '@/models' | |||
import { antdUtils } from '@/utils/antd'; | |||
import { useRequest } from '@/hooks/use-request'; | |||
import tenantPkgService from '@/request/service/tenant-package'; | |||
import { formatDate } from '@/utils/formatTime' | |||
import TenantPackageEditor from './tenant-pkg-editor'; | |||
export default () => { | |||
const [editorVisable, seEditorVisable] = useState<boolean>(false); | |||
const [editData, seEditData] = useState<TenantPackageVO>(); | |||
const [dataSource, setDataSource] = useState<TenantPackageVO[]>([]); | |||
const [searchFrom] = Form.useForm(); | |||
const { runAsync: getPageApi } = useRequest(tenantPkgService.getTenantPackageTypePageApi, { manual: true }); | |||
const { runAsync: deleteApi } = useRequest(tenantPkgService.deleteTenantPackageTypeApi, { manual: true }); | |||
const load = async () => { | |||
const [error, { data }] = await getPageApi(searchFrom.getFieldsValue()); | |||
if (!error) { | |||
setDataSource(data.list); | |||
} | |||
}; | |||
const showDeleteConfirm = (data: TenantPackageVO) => { | |||
antdUtils.modal?.confirm({ | |||
title: '确认要将该菜单删除吗?', | |||
icon: <ExclamationCircleFilled />, | |||
content: '请注意删除以后不可恢复!', | |||
okText: '删除', | |||
okType: 'danger', | |||
cancelText: '取消', | |||
onOk() { | |||
return new Promise(async (resolve) => { | |||
const [error, { code, msg} ] = await deleteApi(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<TenantPackageVO> = [ | |||
{ | |||
title: '套餐编号', | |||
dataIndex: 'id', | |||
key: 'id', | |||
align: 'right', | |||
width: 200, | |||
}, | |||
{ | |||
title: '套餐名称', | |||
dataIndex: 'name', | |||
key: 'name', | |||
align: 'center', | |||
width: 150 | |||
}, | |||
{ | |||
title: '备注', | |||
key: 'remark', | |||
dataIndex: 'remark', | |||
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: TenantPackageVO, 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 name="remark" 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> | |||
<TenantPackageEditor | |||
onSave={() => { | |||
load(); | |||
seEditorVisable(false); | |||
}} | |||
onCancel={() => { seEditorVisable(false) }} | |||
visible={editorVisable} | |||
data={editData} /> | |||
</> | |||
); | |||
}; | |||
@@ -0,0 +1,143 @@ | |||
import React, { useEffect, useState } from 'react' | |||
import { Form, Input, InputNumber, Radio, Modal, TreeSelect } from 'antd'; | |||
import tenantPkgService from '@/request/service/tenant-package'; | |||
import menuService from '@/request/service/menu'; | |||
import { useRequest } from '@/hooks/use-request'; | |||
import type { DefaultOptionType } from 'antd/es/select'; | |||
import type { Menu, TenantPackageVO } 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: TenantPackageVO) => void; | |||
data?: TenantPackageVO | null; | |||
} | |||
const toMenuTree = (data: Menu[]) => { | |||
const root: Menu[] = data.filter(item => { | |||
return !data.some(it => it.id === item.parentId) | |||
}); | |||
const toMenuTreeItem = (item: Menu): DefaultOptionType => { | |||
return { | |||
label: item.name, | |||
value: item.id, | |||
children: data.filter(it => it.parentId === item.id).map(it => { | |||
return toMenuTreeItem(it) | |||
}) | |||
} | |||
} | |||
return root.map(item => { | |||
return toMenuTreeItem(item) | |||
}) | |||
} | |||
const TenantPackageEditor: React.FC<EditorProps> = (props) => { | |||
const { visible, onCancel, onSave, data } = props; | |||
const { runAsync: updateApi } = useRequest(tenantPkgService.updateTenantPackageTypeApi, { manual: true }); | |||
const { runAsync: createApi } = useRequest(tenantPkgService.createTenantPackageTypeApi, { manual: true }); | |||
const { data: menus, run: getMenuList } = useRequest(menuService.listSimpleMenusApi, { manual: true }); | |||
// const [value, setValue] = useState(['0-0-0']); | |||
// const onChange = (newValue: string[]) => { | |||
// console.log('onChange ', value); | |||
// setValue(newValue); | |||
// }; | |||
const isEdit = !!data; | |||
const [saveLoading, setSaveLoading] = useState(false); | |||
const [form] = Form.useForm(); | |||
useEffect(() => { | |||
if (visible) { | |||
if (data) { | |||
form.setFieldsValue(data); | |||
} | |||
} else { | |||
form.resetFields(); | |||
} | |||
}, [visible]); | |||
useEffect(() => { | |||
getMenuList(); | |||
}, [getMenuList]); | |||
const save = async () => { | |||
setSaveLoading(true); | |||
const fieldValues = form.getFieldsValue(); | |||
const newValue = isEdit ? { ...data, ...fieldValues } : fieldValues; | |||
const [error, { msg, code }] = isEdit ? await updateApi(newValue) : await createApi(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} | |||
confirmLoading={saveLoading} | |||
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="menuIds" label="菜单权限" > | |||
<TreeSelect | |||
multiple | |||
treeCheckable | |||
showCheckedStrategy= {TreeSelect.SHOW_PARENT} | |||
treeData={toMenuTree(menus?.data ?? [])} /> | |||
</Form.Item> | |||
<Form.Item name="remark" label="备注" > | |||
<Input.TextArea showCount maxLength={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 TenantPackageEditor; |
@@ -7,9 +7,10 @@ export default { | |||
getMenuList: (params?: { name?: string, status?: number }) => { | |||
return request.get<Menu[]>(`${BASE_URL}/list`, { params }); | |||
}, | |||
// 获取菜单详情 | |||
getMenu: (id: number) => { | |||
return request.get(`${BASE_URL}/get`, { params: { id }}) | |||
return request.get(`${BASE_URL}/get`, { params: { id } }) | |||
}, | |||
// 新增菜单 | |||
@@ -24,5 +25,11 @@ export default { | |||
deleteMenu: (params: { id: number }) => { | |||
return request.delete(`${BASE_URL}/delete`, { params }) | |||
}, | |||
// 查询菜单(精简)列表 | |||
listSimpleMenusApi: () => { | |||
return request.get<Menu[]>(`${BASE_URL}/list-all-simple`); | |||
} | |||
}; |
@@ -0,0 +1,38 @@ | |||
import request from '@/request'; | |||
import { PageData, TenantPackagePageReqVO, TenantPackageVO } from '@/models'; | |||
const BASE_URL = '/admin-api/system/tenant-package'; | |||
export default { | |||
// 查询租户套餐列表 | |||
getTenantPackageTypePageApi: (params: TenantPackagePageReqVO) => { | |||
return request.get<PageData<TenantPackageVO>>(`${BASE_URL}/page`, { params }); | |||
}, | |||
// 获得租户 | |||
getTenantPackageApi: (id: number) => { | |||
return request.get(`${BASE_URL}/get?id=${id}`); | |||
}, | |||
// 新增租户套餐 | |||
createTenantPackageTypeApi: (data: TenantPackageVO) => { | |||
return request.post(`${BASE_URL}/create`, data ); | |||
}, | |||
// 修改租户套餐 | |||
updateTenantPackageTypeApi: (data: TenantPackageVO) => { | |||
return request.put(`${BASE_URL}/update`, data ); | |||
}, | |||
// 删除租户套餐 | |||
deleteTenantPackageTypeApi: (id: number) => { | |||
return request.delete(`${BASE_URL}/delete?id=${id}`); | |||
}, | |||
// 获取租户套餐精简信息列表 | |||
getTenantPackageList: () => { | |||
return request.get<TenantPackageVO[]>(`${BASE_URL}/get-simple-list`); | |||
} | |||
} | |||
@@ -0,0 +1,39 @@ | |||
import request from '@/request'; | |||
import { TenantPageReqVO, TenantVO, TenantExportReqVO, PageData } from '@/models'; | |||
const BASE_URL = '/admin-api/system/tenant'; | |||
export default { | |||
// 查询租户列表 | |||
getTenantPageApi: (params: TenantPageReqVO) => { | |||
return request.get<PageData<TenantVO>>(`${BASE_URL}/page`, { params }) | |||
}, | |||
// 查询租户详情 | |||
getTenantApi: (id: number) => { | |||
return request.get(`${BASE_URL}/get?id=${id}`) | |||
}, | |||
// 新增租户 | |||
createTenantApi: (data: TenantVO) => { | |||
return request.post(`${BASE_URL}/create`, data) | |||
}, | |||
// 修改租户 | |||
updateTenantApi: (data: TenantVO) => { | |||
return request.put(`${BASE_URL}/update`, data) | |||
}, | |||
// 删除租户 | |||
deleteTenantApi: (id: number) => { | |||
return request.delete(`${BASE_URL}/delete?id=${id}`) | |||
}, | |||
// 导出租户 | |||
exportTenantApi: (params: TenantExportReqVO) => { | |||
return request.download(`${BASE_URL}/export-excel`, params) | |||
}, | |||
} | |||