@@ -4,6 +4,7 @@ export * from './role.data.ts' | |||||
export * from './position.data.ts' | export * from './position.data.ts' | ||||
export * from './department.data.ts' | export * from './department.data.ts' | ||||
export * from './tenant.data.ts' | export * from './tenant.data.ts' | ||||
export * from './template.data.ts' | |||||
export interface ResponseDTO<T>{ | export interface ResponseDTO<T>{ | ||||
code: number; | code: number; | ||||
@@ -0,0 +1,16 @@ | |||||
//数据字典 | |||||
export interface DataDictVO{ | |||||
description: string; | |||||
id: number; | |||||
name: string; | |||||
} | |||||
//数据字典详情 | |||||
export interface DataDictDetailVO { | |||||
dictId: number; | |||||
dictSort: number; | |||||
id: number; | |||||
label: string; | |||||
value: string; | |||||
} |
@@ -0,0 +1,112 @@ | |||||
import React, { useEffect, useState } from 'react' | |||||
import { Form, Input, InputNumber, Modal } from 'antd'; | |||||
import dictService from '@/request/service/dict'; | |||||
import { useRequest } from '@/hooks/use-request'; | |||||
import type { DataDictDetailVO } from '@/models' | |||||
import { antdUtils } from '@/utils/antd'; | |||||
const layout = { | |||||
labelCol: { span: 4, }, | |||||
wrapperCol: { span: 16 }, | |||||
bordered: false, | |||||
}; | |||||
export default (props: { | |||||
visible: boolean; | |||||
onCancel: (flag?: boolean) => void; | |||||
onSave: (role: DataDictDetailVO) => void; | |||||
data?: DataDictDetailVO | null; | |||||
}) => { | |||||
const { visible, onCancel, onSave, data } = props; | |||||
const { runAsync: updateApi } = useRequest(dictService.updateDictDetailApi, { manual: true }); | |||||
const { runAsync: createApi } = useRequest(dictService.deleteDictDetailApi, { manual: true }); | |||||
const isEdit = !!data; | |||||
const [saveLoading, setSaveLoading] = useState(false); | |||||
const [form] = Form.useForm(); | |||||
useEffect(() => { | |||||
if (visible) { | |||||
if (data) { | |||||
form.setFieldsValue(data); | |||||
} | |||||
} else { | |||||
form.resetFields(); | |||||
} | |||||
}, [visible]); | |||||
const save = async () => { | |||||
setSaveLoading(true); | |||||
const fieldValues = form.getFieldsValue(); | |||||
const newValue = isEdit ? { ...data, ...fieldValues } : fieldValues; | |||||
const [error, { msg, code }] = isEdit ? await 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="label" label="字典标签" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输字典标签', | |||||
}, | |||||
]} | |||||
> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="value" label="字典值" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入字典值', | |||||
}, | |||||
]} | |||||
> | |||||
<Input.TextArea /> | |||||
</Form.Item> | |||||
<Form.Item name="dictSort" label="字典排序" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入字典排序', | |||||
}, | |||||
]} | |||||
> | |||||
<InputNumber min={1} /> | |||||
</Form.Item> | |||||
</Form> | |||||
</Modal> | |||||
</> | |||||
) | |||||
}; |
@@ -0,0 +1,101 @@ | |||||
import React, { useEffect, useState } from 'react' | |||||
import { Form, Input, Modal } from 'antd'; | |||||
import dictService from '@/request/service/dict'; | |||||
import { useRequest } from '@/hooks/use-request'; | |||||
import type { DataDictVO } from '@/models' | |||||
import { antdUtils } from '@/utils/antd'; | |||||
const layout = { | |||||
labelCol: { span: 4, }, | |||||
wrapperCol: { span: 16 }, | |||||
bordered: false, | |||||
}; | |||||
export default (props: { | |||||
visible: boolean; | |||||
onCancel: (flag?: boolean) => void; | |||||
onSave: (role: DataDictVO) => void; | |||||
data?: DataDictVO | null; | |||||
}) => { | |||||
const { visible, onCancel, onSave, data } = props; | |||||
const { runAsync: updateApi } = useRequest(dictService.updateDictApi, { manual: true }); | |||||
const { runAsync: createApi } = useRequest(dictService.createDictApi, { manual: true }); | |||||
const isEdit = !!data; | |||||
const [saveLoading, setSaveLoading] = useState(false); | |||||
const [form] = Form.useForm(); | |||||
useEffect(() => { | |||||
if (visible) { | |||||
if (data) { | |||||
form.setFieldsValue(data); | |||||
} | |||||
} else { | |||||
form.resetFields(); | |||||
} | |||||
}, [visible]); | |||||
const save = async () => { | |||||
setSaveLoading(true); | |||||
const fieldValues = form.getFieldsValue(); | |||||
const newValue = isEdit ? { ...data, ...fieldValues } : fieldValues; | |||||
const [error, { msg, code }] = isEdit ? await 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="description" label="字典描述" | |||||
rules={[ | |||||
{ | |||||
required: true, | |||||
message: '请输入字典描述', | |||||
}, | |||||
]} | |||||
> | |||||
<Input /> | |||||
</Form.Item> | |||||
</Form> | |||||
</Modal> | |||||
</> | |||||
) | |||||
}; |
@@ -1,9 +1,300 @@ | |||||
import { Empty } from 'antd'; | |||||
import React, { useState, useRef } from 'react'; | |||||
import { Space, Table, Button, Image, Divider, Card, Input } from 'antd'; | |||||
import type { InputRef } from 'antd'; | |||||
import type { ColumnType, ColumnsType } from 'antd/es/table'; | |||||
import type { FilterConfirmProps, TableRowSelection } from 'antd/es/table/interface'; | |||||
import { t } from '@/utils/i18n'; | |||||
import { PlusOutlined, ExclamationCircleFilled, DeleteOutlined, SearchOutlined } from '@ant-design/icons'; | |||||
import { antdUtils } from '@/utils/antd'; | |||||
import { DataDictVO, DataDictDetailVO } from '@/models'; | |||||
import DictEditor from './dict-editor'; | |||||
import DictDetailEditor from './dict-detail-editor'; | |||||
const dataDicts: DataDictVO[] = [ | |||||
{ | |||||
"id": 43, | |||||
"name": "爆款词", | |||||
"description": "爆款词" | |||||
}, | |||||
{ | |||||
"id": 42, | |||||
"name": "T-T", | |||||
"description": "t-shirt" | |||||
}, | |||||
{ | |||||
"id": 41, | |||||
"name": "T-shirt Background", | |||||
"description": "T桖背景" | |||||
} | |||||
] | |||||
const dictDetailsA: DataDictDetailVO[] = [ | |||||
{ | |||||
"id": 114, | |||||
"dictId": 43, | |||||
"label": "tshirt for women", | |||||
"value": "tshirt for women", | |||||
"dictSort": 1 | |||||
}, | |||||
{ | |||||
"id": 113, | |||||
"dictId": 43, | |||||
"label": "oversize t shirt black", | |||||
"value": "oversize t shirt black", | |||||
"dictSort": 1 | |||||
}, | |||||
{ | |||||
"id": 112, | |||||
"dictId": 43, | |||||
"label": "oversize t shirt", | |||||
"value": "oversize t shirt", | |||||
"dictSort": 1 | |||||
} | |||||
] | |||||
const dictDetailsB: DataDictDetailVO[] = [ | |||||
{ | |||||
"id": 107, | |||||
"dictId": 42, | |||||
"label": "three", | |||||
"value": "three", | |||||
"dictSort": 3 | |||||
}, | |||||
{ | |||||
"id": 106, | |||||
"dictId": 42, | |||||
"label": "two", | |||||
"value": "two", | |||||
"dictSort": 2 | |||||
}, | |||||
{ | |||||
"id": 105, | |||||
"dictId": 42, | |||||
"label": "one", | |||||
"value": "one", | |||||
"dictSort": 1 | |||||
} | |||||
] | |||||
const dictDetailsC: DataDictDetailVO[] = [ | |||||
{ | |||||
"id": 111, | |||||
"dictId": 41, | |||||
"label": "Soothing hot springs", | |||||
"value": "Hot spring photos, hot spring water, beautiful scenery, no one, film quality, 8K, meticulous", | |||||
"dictSort": 5 | |||||
}, | |||||
{ | |||||
"id": 110, | |||||
"dictId": 41, | |||||
"label": "Great Eiffel Tower", | |||||
"value": "Keywords Paris Tower, photo, high quality, 8K,", | |||||
"dictSort": 4 | |||||
}, | |||||
{ | |||||
"id": 109, | |||||
"dictId": 41, | |||||
"label": "Iconic Eiffel Tower", | |||||
"value": "Keywords Paris Tower, photo, high quality, 8K,", | |||||
"dictSort": 3 | |||||
}, | |||||
{ | |||||
"id": 108, | |||||
"dictId": 41, | |||||
"label": "Pleasant Tunisia", | |||||
"value": "Venice, photos, high quality, 8K, water, details, film quality, rendering, beautiful scenery.", | |||||
"dictSort": 2 | |||||
}, | |||||
{ | |||||
"id": 104, | |||||
"dictId": 41, | |||||
"label": "Trendy Tunisia", | |||||
"value": "Venice, photos, high quality, 8K, water, details, film quality, rendering, beautiful scenery.", | |||||
"dictSort": 1 | |||||
} | |||||
] | |||||
export default () => { | |||||
const [dictDetail, setDictDetail] = useState<DataDictDetailVO[]>(); | |||||
const [editorVisable, seEditorVisable] = useState<boolean>(false); | |||||
const [editData, seEditData] = useState<DataDictVO>(); | |||||
const [detailEditorVisable, seEdtailEditorVisable] = useState<boolean>(false); | |||||
const [editDetailData, seEditDetailData] = useState<DataDictDetailVO>(); | |||||
const showDeleteConfirm = (item: DataDictVO) => { | |||||
antdUtils.modal?.confirm({ | |||||
title: `确认删除名称为: ${item.name} 的字典吗?`, | |||||
icon: <ExclamationCircleFilled />, | |||||
content: '请注意删除以后不可恢复!', | |||||
okText: '删除', | |||||
okType: 'danger', | |||||
cancelText: '取消', | |||||
onOk() { | |||||
return new Promise((resolve, reject) => { | |||||
setTimeout(() => { | |||||
antdUtils.message?.open({ | |||||
type: 'success', | |||||
content: '删除成功', | |||||
}); | |||||
resolve(null) | |||||
}, 1000); | |||||
}).catch(() => antdUtils.message?.open({ | |||||
type: 'error', | |||||
content: '操作失败', | |||||
})); | |||||
}, | |||||
onCancel() { | |||||
}, | |||||
}); | |||||
}; | |||||
const columns: ColumnsType<DataDictDetailVO> = [ | |||||
{ | |||||
title: '字典标签', | |||||
dataIndex: 'label', | |||||
key: 'label', | |||||
align: 'center', | |||||
width: 150, | |||||
}, | |||||
{ | |||||
title: '字典值', | |||||
key: 'value', | |||||
dataIndex: 'value', | |||||
align: 'center', | |||||
}, | |||||
{ | |||||
title: '排序', | |||||
key: 'dictSort', | |||||
dataIndex: 'dictSort', | |||||
width: 100, | |||||
}, | |||||
{ | |||||
title: t("QkOmYwne" /* 操作 */), | |||||
key: 'action', | |||||
render: (_, record) => ( | |||||
<Space size="middle" split={( | |||||
<Divider type='vertical' /> | |||||
)}> | |||||
<a | |||||
onClick={() => { | |||||
seEditDetailData(record) | |||||
seEdtailEditorVisable(true); | |||||
}}> | |||||
编辑 | |||||
</a> | |||||
<a | |||||
onClick={() => { | |||||
}}> | |||||
删除 | |||||
</a> | |||||
</Space> | |||||
), | |||||
width: 150, | |||||
}, | |||||
]; | |||||
const updateDactDetail = (data: DataDictVO) => { | |||||
if (data.id == 43) { | |||||
setDictDetail(dictDetailsA) | |||||
} else if (data.id == 42) { | |||||
setDictDetail(dictDetailsB) | |||||
} else { | |||||
setDictDetail(dictDetailsC) | |||||
} | |||||
} | |||||
const dictColumns: ColumnsType<DataDictVO> = [ | |||||
{ | |||||
title: '名称', | |||||
dataIndex: 'name', | |||||
key: 'name', | |||||
align: 'center', | |||||
}, | |||||
{ | |||||
title: '描述', | |||||
key: 'description', | |||||
dataIndex: 'description', | |||||
align: 'center', | |||||
}, | |||||
{ | |||||
title: t("QkOmYwne" /* 操作 */), | |||||
key: 'action', | |||||
align: 'center', | |||||
render: (_, record) => ( | |||||
<Space size="middle" split={( | |||||
<Divider type='vertical' /> | |||||
)}> | |||||
<a | |||||
onClick={() => { | |||||
seEditData(record) | |||||
seEditorVisable(true); | |||||
}}> | |||||
编辑 | |||||
</a> | |||||
<a | |||||
onClick={() => { | |||||
showDeleteConfirm(record) | |||||
}}> | |||||
删除 | |||||
</a> | |||||
</Space> | |||||
), | |||||
width: 150, | |||||
}, | |||||
]; | |||||
const CustomMade = () => { | |||||
return ( | return ( | ||||
<Empty /> | |||||
<> | |||||
<div className='flex flex-row'> | |||||
<Card className='basis-2/5 w-[100px] mb-[10px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]'> | |||||
<Table rowKey="id" scroll={{ x: true }} columns={dictColumns} dataSource={dataDicts} className='bg-transparent' | |||||
onRow={(record) => { | |||||
return { | |||||
onClick: () => { | |||||
updateDactDetail(record) | |||||
} | |||||
}; | |||||
}} | |||||
pagination={{ position: ['bottomRight'] }} | |||||
/> | |||||
</Card> | |||||
<Card className='basis-3/5 mb-[10px] ml-[10px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]' bodyStyle={{ | |||||
paddingTop: 0, | |||||
paddingBottom: 0 | |||||
}}> | |||||
<div className="py-[8px] flex flex-row-reverse"> | |||||
<Button className="ml-5" type='primary' size='middle' icon={<PlusOutlined />}> 新增 </Button> | |||||
</div> | |||||
<Table rowKey="id" scroll={{ x: true }} columns={columns} dataSource={dictDetail} className='bg-transparent' | |||||
pagination={{ position: ['bottomRight'] }} | |||||
/> | |||||
</Card> | |||||
</div> | |||||
<DictEditor | |||||
onSave={() => { | |||||
}} | |||||
onCancel={() => { seEditorVisable(false) }} | |||||
visible={editorVisable} | |||||
data={editData} | |||||
/> | |||||
<DictDetailEditor | |||||
onSave={() => { | |||||
}} | |||||
onCancel={() => { seEdtailEditorVisable(false) }} | |||||
visible={detailEditorVisable} | |||||
data={editDetailData} | |||||
/> | |||||
</> | |||||
); | ); | ||||
}; | }; | ||||
export default CustomMade; |
@@ -0,0 +1,28 @@ | |||||
import request from '@/request'; | |||||
import { DataDictVO, DataDictDetailVO } from '@/models'; | |||||
const BASE_URL = '/admin-api/system/dict'; | |||||
export default { | |||||
createDictApi: (data: DataDictVO) => { | |||||
return request.post(`${BASE_URL}/create`, data); | |||||
}, | |||||
updateDictApi: (data: DataDictVO) => { | |||||
return request.put(`${BASE_URL}/update`, data); | |||||
}, | |||||
deleteDictApi: (id: number) => { | |||||
return request.delete(`${BASE_URL}/delete?id=${id}`); | |||||
}, | |||||
updateDictDetailApi: (data: DataDictDetailVO) => { | |||||
return request.put(`${BASE_URL}/update`, data); | |||||
}, | |||||
deleteDictDetailApi: (id: number) => { | |||||
return request.delete(`${BASE_URL}/delete?id=${id}`); | |||||
}, | |||||
}; |