@@ -0,0 +1,202 @@ | |||||
import React, { useState, useEffect, useRef } from 'react'; | |||||
import { useSetState } from 'ahooks'; | |||||
import { Space, Table, Button, Divider, Card, Input } from 'antd'; | |||||
import type { InputRef } from 'antd'; | |||||
import type { ColumnsType, ColumnType, TableProps } from 'antd/es/table'; | |||||
import { t } from '@/utils/i18n'; | |||||
import { PlusOutlined, ExclamationCircleFilled, SearchOutlined } from '@ant-design/icons'; | |||||
import { antdUtils } from '@/utils/antd'; | |||||
import { useRequest } from '@/hooks/use-request'; | |||||
import { DataDictVO, DataDictPageReqVO } from '@/models'; | |||||
import DictEditor from './dict-editor'; | |||||
import templateDictService from '@/request/service/template-dict'; | |||||
export default (props: { | |||||
onSelectItem: (item: DataDictVO) => void; | |||||
}) => { | |||||
const { onSelectItem } = props; | |||||
const [dataDicts, setDataDicts] = useState<DataDictVO[]>(); | |||||
const [searchState, setSearchState] = useSetState<DataDictPageReqVO>({ | |||||
pageNo: 1, | |||||
pageSize: 10 | |||||
}); | |||||
const [total, setTotal] = useState(0) | |||||
const searchInput = useRef<InputRef>(null); | |||||
const [onSearching, setOnSearching] = useState(false); | |||||
const [editorVisable, seEditorVisable] = useState<boolean>(false); | |||||
const [editData, seEditData] = useState<DataDictVO>(); | |||||
//获取字典分页数据 | |||||
const { runAsync: getDictPageApi } = useRequest(templateDictService.getDictPageApi, { manual: true }); | |||||
//删除字典数据 | |||||
const { runAsync: deleteDictApi } = useRequest(templateDictService.deleteDictApi, { manual: true }); | |||||
const load = async () => { | |||||
setOnSearching(true); | |||||
const [error, { data }] = await getDictPageApi(searchState); | |||||
setOnSearching(false); | |||||
if (!error) { | |||||
setDataDicts(data.list); | |||||
setTotal(data.total); | |||||
} | |||||
} | |||||
const showDeleteConfirm = (item: DataDictVO) => { | |||||
antdUtils.modal?.confirm({ | |||||
title: `确认删除名称为: ${item.name} 的字典吗?`, | |||||
icon: <ExclamationCircleFilled />, | |||||
content: '请注意删除以后不可恢复!', | |||||
okText: '删除', | |||||
okType: 'danger', | |||||
cancelText: '取消', | |||||
onOk: async () => { | |||||
const [error, { code, msg }] = await deleteDictApi(item.id); | |||||
if (error || code !== 0) { | |||||
antdUtils.message?.open({ type: 'error', content: msg ?? '操作失败' }) | |||||
} else { | |||||
antdUtils.message?.open({ type: 'success', content: '删除成功' }) | |||||
} | |||||
await load(); | |||||
}, | |||||
onCancel() { | |||||
}, | |||||
}); | |||||
}; | |||||
const getColumnSearchProps = (placeholder: string): ColumnType<DataDictVO> => ({ | |||||
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters, close }) => ( | |||||
<div style={{ padding: 8 }} onKeyDown={(e) => e.stopPropagation()}> | |||||
<Input.Search | |||||
ref={searchInput} | |||||
placeholder={placeholder} | |||||
value={selectedKeys[0]} | |||||
onChange={(e) => { | |||||
setSelectedKeys(e.target.value && e.target.value !== '' ? [e.target.value] : []) | |||||
}} | |||||
onSearch={(value) => { | |||||
if (value === '' && clearFilters) { | |||||
clearFilters!!() | |||||
} | |||||
confirm(); | |||||
}} | |||||
onPressEnter={() => confirm()} | |||||
allowClear | |||||
style={{ marginBottom: 8, display: 'block' }} | |||||
enterButton="搜索" | |||||
size="middle" | |||||
loading={onSearching} | |||||
/> | |||||
</div> | |||||
), | |||||
filterIcon: (filtered: boolean) => ( | |||||
<SearchOutlined style={{ color: filtered ? 'primaryColor' : undefined }} /> | |||||
), | |||||
onFilterDropdownOpenChange: (visible) => { | |||||
if (visible) { | |||||
setTimeout(() => searchInput.current?.select(), 100); | |||||
} | |||||
}, | |||||
}); | |||||
const columns: ColumnsType<DataDictVO> = [ | |||||
{ | |||||
title: '名称', | |||||
dataIndex: 'name', | |||||
key: 'name', | |||||
align: 'center', | |||||
...getColumnSearchProps('请输入名称'), | |||||
}, | |||||
{ | |||||
title: '描述', | |||||
key: 'description', | |||||
dataIndex: 'description', | |||||
align: 'center', | |||||
// ...getColumnSearchProps('请输入描述'), | |||||
}, | |||||
{ | |||||
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, | |||||
}, | |||||
]; | |||||
useEffect(() => { | |||||
load(); | |||||
}, [searchState]); | |||||
const onChange: TableProps<DataDictVO>['onChange'] = (pagination, filters, sorter, extra) => { | |||||
const state: DataDictPageReqVO = { | |||||
name: filters.name ? filters.name[0] as string : undefined, | |||||
description: filters.description ? filters.description[0] as string : undefined, | |||||
pageNo: pagination.current, | |||||
pageSize: pagination.pageSize | |||||
} | |||||
setOnSearching(true); | |||||
setSearchState(state); | |||||
}; | |||||
return ( | |||||
<> | |||||
<div> | |||||
<Card className='mb-[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 />} | |||||
onClick={() => { | |||||
seEditData(undefined); | |||||
seEditorVisable(true); | |||||
}}> 新增 </Button> | |||||
</div> | |||||
<Table rowKey="id" scroll={{ x: true }} columns={columns} dataSource={dataDicts} className='bg-transparent' | |||||
onRow={(record) => { | |||||
return { | |||||
onClick: () => { | |||||
onSelectItem(record); | |||||
} | |||||
}; | |||||
}} | |||||
onChange={onChange} | |||||
pagination={{ | |||||
current: searchState.pageNo, | |||||
pageSize: searchState.pageSize, | |||||
total, | |||||
position: ['bottomRight'] | |||||
}} | |||||
/> | |||||
</Card> | |||||
</div> | |||||
<DictEditor | |||||
onSave={() => { | |||||
load(); | |||||
seEditorVisable(false); | |||||
}} | |||||
onCancel={() => { seEditorVisable(false) }} | |||||
visible={editorVisable} | |||||
data={editData} | |||||
/> | |||||
</> | |||||
); | |||||
}; |
@@ -120,7 +120,7 @@ export default forwardRef((props, ref) => { | |||||
return ( | return ( | ||||
<> | <> | ||||
<div> | <div> | ||||
<Card className='basis-3/5 mb-[10px] ml-[10px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]' bodyStyle={{ | |||||
<Card className='mb-[10px] ml-[10px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]' bodyStyle={{ | |||||
paddingTop: 0, | paddingTop: 0, | ||||
paddingBottom: 0 | paddingBottom: 0 | ||||
}}> | }}> | ||||
@@ -1,213 +1,26 @@ | |||||
import React, { useState, useRef, useEffect } 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 { useRequest } from '@/hooks/use-request'; | |||||
import { DataDictVO, DataDictDetailVO } from '@/models'; | |||||
import DictEditor from './dict-editor'; | |||||
import DictDetailEditor from './dict-detail-editor'; | |||||
import templateDictService from '@/request/service/template-dict'; | |||||
import React, { useRef } from 'react'; | |||||
import { DataDictVO } from '@/models'; | |||||
import DictDetail from './dict-detail'; | import DictDetail from './dict-detail'; | ||||
import DictData from './dict-data'; | |||||
import type { DictDetailRef } from './dict-detail' | import type { DictDetailRef } from './dict-detail' | ||||
export default () => { | export default () => { | ||||
const [dataDicts, setDataDicts] = useState<DataDictVO[]>(); | |||||
const [dictDetail, setDictDetail] = useState<DataDictDetailVO[]>(); | |||||
const [total, setTotal] = useState(0); | |||||
const [editorVisable, seEditorVisable] = useState<boolean>(false); | |||||
const [editData, seEditData] = useState<DataDictVO>(); | |||||
const [detailEditorVisable, seEdtailEditorVisable] = useState<boolean>(false); | |||||
const [editDetailData, seEditDetailData] = useState<DataDictDetailVO>(); | |||||
const detailRef = useRef<DictDetailRef>() | const detailRef = useRef<DictDetailRef>() | ||||
//获取字典分页数据 | |||||
const { runAsync: getDictPageApi } = useRequest(templateDictService.getDictPageApi, { manual: true }); | |||||
//删除字典数据 | |||||
const { runAsync: deleteDictApi } = useRequest(templateDictService.deleteDictApi, { manual: true }); | |||||
const load = async () => { | |||||
const [error, { data }] = await getDictPageApi(); | |||||
if (!error) { | |||||
setDataDicts(data.list); | |||||
setTotal(data.total); | |||||
} | |||||
} | |||||
useEffect(() => { | |||||
load(); | |||||
}, []); | |||||
const showDeleteConfirm = (item: DataDictVO) => { | |||||
antdUtils.modal?.confirm({ | |||||
title: `确认删除名称为: ${item.name} 的字典吗?`, | |||||
icon: <ExclamationCircleFilled />, | |||||
content: '请注意删除以后不可恢复!', | |||||
okText: '删除', | |||||
okType: 'danger', | |||||
cancelText: '取消', | |||||
onOk: async () => { | |||||
const [error, { code, msg }] = await deleteDictApi(item.id); | |||||
if (error || code !== 0) { | |||||
antdUtils.message?.open({ type: 'error', content: msg ?? '操作失败' }) | |||||
} else { | |||||
antdUtils.message?.open({ type: 'success', content: '删除成功' }) | |||||
} | |||||
await load(); | |||||
}, | |||||
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) => { | |||||
detailRef.current?.updateDictData(data) | |||||
} | |||||
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, | |||||
}, | |||||
]; | |||||
return ( | return ( | ||||
<> | <> | ||||
<div className='flex flex-row'> | <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]' bodyStyle={{ | |||||
paddingTop: 0, | |||||
paddingBottom: 0 | |||||
}}> | |||||
<div className="py-[8px] flex flex-row-reverse"> | |||||
<Button className="ml-5" type='primary' size='middle' icon={<PlusOutlined />} | |||||
onClick={() => { | |||||
seEditData(undefined); | |||||
seEditorVisable(true); | |||||
}}> 新增 </Button> | |||||
</div> | |||||
<Table rowKey="id" scroll={{ x: true }} columns={dictColumns} dataSource={dataDicts} className='bg-transparent' | |||||
onRow={(record) => { | |||||
return { | |||||
onClick: () => { | |||||
updateDactDetail(record) | |||||
} | |||||
}; | |||||
}} | |||||
pagination={{ position: ['bottomRight'] }} | |||||
/> | |||||
</Card> | |||||
<div className='basis-2/5 '> | |||||
<DictData | |||||
onSelectItem={(item: DataDictVO) => { | |||||
detailRef.current?.updateDictData(item); | |||||
}} /> | |||||
</div> | |||||
<div className='basis-3/5 '> | <div className='basis-3/5 '> | ||||
<DictDetail ref={detailRef}/> | |||||
<DictDetail ref={detailRef} /> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<DictEditor | |||||
onSave={() => { | |||||
load(); | |||||
seEditorVisable(false); | |||||
}} | |||||
onCancel={() => { seEditorVisable(false) }} | |||||
visible={editorVisable} | |||||
data={editData} | |||||
/> | |||||
<DictDetailEditor | |||||
onSave={() => { | |||||
}} | |||||
onCancel={() => { seEdtailEditorVisable(false) }} | |||||
visible={detailEditorVisable} | |||||
data={editDetailData} | |||||
/> | |||||
</> | </> | ||||
); | ); | ||||
}; | }; |