page.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. "use client";
  2. import {fetchApi, fetchFile} from "@/app/_modules/func";
  3. import {DeleteOutlined, ExclamationCircleFilled, PlusOutlined, ReloadOutlined,} from "@ant-design/icons";
  4. import type {ActionType, ProColumns, ProFormInstance,} from "@ant-design/pro-components";
  5. import {
  6. ModalForm,
  7. PageContainer,
  8. ProForm,
  9. ProFormRadio,
  10. ProFormText,
  11. ProFormTextArea,
  12. ProTable,
  13. } from "@ant-design/pro-components";
  14. import {Button, Modal, Space, Tag} from "antd";
  15. import {useRouter} from "next/navigation";
  16. import {
  17. faCheck,
  18. faDownload,
  19. faPenToSquare,
  20. faRotate,
  21. faToggleOff,
  22. faToggleOn,
  23. faXmark,
  24. } from "@fortawesome/free-solid-svg-icons";
  25. import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
  26. import {useRef, useState} from "react";
  27. import globalMessage from "@/app/_modules/globalMessage";
  28. //查询表格数据API
  29. const queryAPI = "/api/system/dict/type/list";
  30. //新建数据API
  31. const newAPI = "/api/system/dict/type";
  32. //修改数据API
  33. const modifyAPI = "/api/system/dict/type";
  34. //查询详情数据API
  35. const queryDetailAPI = "/api/system/dict/type";
  36. //删除API
  37. const deleteAPI = "/api/system/dict/type";
  38. //导出API
  39. const exportAPI = "/api/system/dict/type/export";
  40. //导出文件前缀名
  41. const exportFilePrefix = "dict";
  42. //刷新缓存
  43. const refreshAPI = "/api/system/dict/type/refreshCache";
  44. export default function Dict() {
  45. const { push } = useRouter();
  46. // 添加用于控制删除确认模态框的状态
  47. const [deleteModalVisible, setDeleteModalVisible] = useState(false);
  48. const [deleteDictId, setDeleteDictId] = useState<string | number | null>(null);
  49. //表格列定义
  50. const columns: ProColumns[] = [
  51. {
  52. title: "字典编号",
  53. dataIndex: "dictId",
  54. search: false,
  55. },
  56. {
  57. title: "字典名称",
  58. fieldProps: {
  59. placeholder: "请输入字典名称",
  60. },
  61. dataIndex: "dictName",
  62. ellipsis: true,
  63. sorter: true,
  64. order: 4,
  65. },
  66. {
  67. title: "字典类型",
  68. fieldProps: {
  69. placeholder: "请输入字典类型",
  70. },
  71. dataIndex: "dictType",
  72. ellipsis: true,
  73. order: 3,
  74. render: (_, record) => {
  75. return (
  76. <a onClick={() => push(`/system/dict-data/index/${record.dictId}`)}>
  77. {record.dictType}
  78. </a>
  79. );
  80. },
  81. },
  82. {
  83. title: "状态",
  84. fieldProps: {
  85. placeholder: "请选择字典状态",
  86. },
  87. dataIndex: "status",
  88. valueType: "select",
  89. render: (_, record) => {
  90. return (
  91. <Space>
  92. <Tag
  93. color={record.status === "0" ? "green" : "red"}
  94. icon={
  95. record.status == 0 ? (
  96. <FontAwesomeIcon icon={faCheck} />
  97. ) : (
  98. <FontAwesomeIcon icon={faXmark} />
  99. )
  100. }
  101. >
  102. {_}
  103. </Tag>
  104. </Space>
  105. );
  106. },
  107. valueEnum: {
  108. 0: {
  109. text: "正常",
  110. status: "0",
  111. },
  112. 1: {
  113. text: "停用",
  114. status: "1",
  115. },
  116. },
  117. order: 2,
  118. },
  119. {
  120. title: "备注",
  121. dataIndex: "remark",
  122. ellipsis: true,
  123. search: false,
  124. },
  125. {
  126. title: "创建时间",
  127. dataIndex: "createTime",
  128. valueType: "dateTime",
  129. search: false,
  130. },
  131. {
  132. title: "创建时间",
  133. fieldProps: {
  134. placeholder: ["开始日期", "结束日期"],
  135. },
  136. dataIndex: "createTimeRange",
  137. valueType: "dateRange",
  138. hideInTable: true,
  139. order: 1,
  140. search: {
  141. transform: (value) => {
  142. return {
  143. "params[beginTime]": `${value[0]} 00:00:00`,
  144. "params[endTime]": `${value[1]} 23:59:59`,
  145. };
  146. },
  147. },
  148. },
  149. {
  150. title: "操作",
  151. key: "option",
  152. search: false,
  153. render: (_, record) => [
  154. <Button
  155. key="modifyBtn"
  156. type="link"
  157. icon={<FontAwesomeIcon icon={faPenToSquare} />}
  158. onClick={() => onClickShowRowModifyModal(record)}
  159. >
  160. 修改
  161. </Button>,
  162. <Button
  163. key="deleteBtn"
  164. type="link"
  165. danger
  166. icon={<DeleteOutlined />}
  167. onClick={() => onClickDeleteRow(record)}
  168. >
  169. 删除
  170. </Button>,
  171. ],
  172. },
  173. ];
  174. //0.查询表格数据
  175. const queryTableData = async (params: any, sorter: any, filter: any) => {
  176. const searchParams = {
  177. pageNum: params.current,
  178. ...params,
  179. };
  180. delete searchParams.current;
  181. const queryParams = new URLSearchParams(searchParams);
  182. Object.keys(sorter).forEach((key) => {
  183. queryParams.append("orderByColumn", key);
  184. if (sorter[key] === "ascend") {
  185. queryParams.append("isAsc", "ascending");
  186. } else {
  187. queryParams.append("isAsc", "descending");
  188. }
  189. });
  190. const body = await fetchApi(`${queryAPI}?${queryParams}`, push);
  191. return body;
  192. };
  193. //1.新建
  194. //确定新建数据
  195. const executeAddData = async (values: any) => {
  196. const body = await fetchApi(newAPI, push, {
  197. method: "POST",
  198. headers: {
  199. "Content-Type": "application/json",
  200. },
  201. body: JSON.stringify(values),
  202. });
  203. if (body != undefined) {
  204. if (body.code == 200) {
  205. globalMessage.success(body.msg);
  206. if (actionTableRef.current) {
  207. actionTableRef.current.reload();
  208. }
  209. return true;
  210. }
  211. globalMessage.error(body.msg);
  212. return false;
  213. }
  214. return false;
  215. };
  216. //2.修改
  217. //是否展示修改对话框
  218. const [isShowModifyDataModal, setIsShowModifyDataModal] = useState(false);
  219. //展示修改对话框
  220. const onClickShowRowModifyModal = (record?: any) => {
  221. queryRowData(record);
  222. setIsShowModifyDataModal(true);
  223. };
  224. //修改数据表单引用
  225. const modifyFormRef = useRef<ProFormInstance>(null);
  226. //操作当前数据的附加数据
  227. const [operatRowData, setOperateRowData] = useState<{
  228. [key: string]: any;
  229. }>({});
  230. //查询并加载待修改数据的详细信息
  231. const queryRowData = async (record?: any) => {
  232. const dictId = record !== undefined ? record.dictId : selectedRow.dictId;
  233. operatRowData["dictId"] = dictId;
  234. setOperateRowData(operatRowData);
  235. if (dictId !== undefined) {
  236. const body = await fetchApi(`${queryDetailAPI}/${dictId}`, push);
  237. if (body !== undefined) {
  238. if (body.code == 200) {
  239. modifyFormRef?.current?.setFieldsValue({
  240. //需要加载到修改表单中的数据
  241. dictName: body.data.dictName,
  242. dictType: body.data.dictType,
  243. status: body.data.status,
  244. remark: body.data.remark,
  245. });
  246. }
  247. }
  248. }
  249. };
  250. //确认修改数据
  251. const executeModifyData = async (values: any) => {
  252. values["dictId"] = operatRowData["dictId"];
  253. const body = await fetchApi(modifyAPI, push, {
  254. method: "PUT",
  255. headers: {
  256. "Content-Type": "application/json",
  257. },
  258. body: JSON.stringify(values),
  259. });
  260. if (body !== undefined) {
  261. if (body.code == 200) {
  262. globalMessage.success(body.msg);
  263. //刷新列表
  264. if (actionTableRef.current) {
  265. actionTableRef.current.reload();
  266. }
  267. setIsShowModifyDataModal(false);
  268. return true;
  269. }
  270. globalMessage.error(body.msg);
  271. return false;
  272. }
  273. };
  274. //3.删除
  275. //点击删除按钮,展示删除确认框
  276. const onClickDeleteRow = (record?: any) => {
  277. const dictId = record !== undefined ? record.dictId : selectedRowKeys.join(",");
  278. setDeleteDictId(dictId);
  279. setDeleteModalVisible(true);
  280. };
  281. //确定删除选中的数据
  282. const executeDeleteRow = async () => {
  283. if (deleteDictId === null) return;
  284. const body = await fetchApi(`${deleteAPI}/${deleteDictId}`, push, {
  285. method: "DELETE",
  286. });
  287. if (body !== undefined) {
  288. if (body.code == 200) {
  289. globalMessage.success("删除成功");
  290. //修改按钮变回不可点击
  291. setRowCanModify(false);
  292. //删除按钮变回不可点击
  293. setRowCanDelete(false);
  294. //选中行数据重置为空
  295. setSelectedRowKeys([]);
  296. //刷新列表
  297. if (actionTableRef.current) {
  298. actionTableRef.current.reload();
  299. }
  300. } else {
  301. globalMessage.error(body.msg);
  302. }
  303. }
  304. setDeleteModalVisible(false);
  305. setDeleteDictId(null);
  306. };
  307. //4.导出
  308. //导出表格数据
  309. const exportTable = async () => {
  310. if (searchTableFormRef.current) {
  311. const formData = new FormData();
  312. const data = {
  313. pageNum: page,
  314. pageSize: pageSize,
  315. ...searchTableFormRef.current.getFieldsValue(),
  316. };
  317. Object.keys(data).forEach((key) => {
  318. if (data[key] !== undefined) {
  319. formData.append(key, data[key]);
  320. }
  321. });
  322. await fetchFile(
  323. exportAPI,
  324. push,
  325. {
  326. method: "POST",
  327. body: formData,
  328. },
  329. `${exportFilePrefix}_${new Date().getTime()}.xlsx`
  330. );
  331. }
  332. };
  333. //5.选择行
  334. //选中行操作
  335. const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
  336. const [selectedRow, setSelectedRow] = useState(undefined as any);
  337. //修改按钮是否可用,选中行时才可用
  338. const [rowCanModify, setRowCanModify] = useState(false);
  339. //删除按钮是否可用,选中行时才可用
  340. const [rowCanDelete, setRowCanDelete] = useState(false);
  341. //ProTable rowSelection
  342. const rowSelection = {
  343. onChange: (newSelectedRowKeys: React.Key[], selectedRows: any[]) => {
  344. setSelectedRowKeys(newSelectedRowKeys);
  345. setRowCanDelete(newSelectedRowKeys && newSelectedRowKeys.length > 0);
  346. if (newSelectedRowKeys && newSelectedRowKeys.length == 1) {
  347. setSelectedRow(selectedRows[0]);
  348. setRowCanModify(true);
  349. } else {
  350. setRowCanModify(false);
  351. setSelectedRow(undefined);
  352. }
  353. },
  354. //复选框的额外禁用判断
  355. // getCheckboxProps: (record) => ({
  356. // disabled: record.userId == 1,
  357. // }),
  358. };
  359. //搜索栏显示状态
  360. const [showSearch, setShowSearch] = useState(true);
  361. //action对象引用
  362. const actionTableRef = useRef<ActionType>(null);
  363. //搜索表单对象引用
  364. const searchTableFormRef = useRef<ProFormInstance>(null!);
  365. //当前页数和每页条数
  366. const [page, setPage] = useState(1);
  367. const defaultPageSize = 10;
  368. const [pageSize, setPageSize] = useState(defaultPageSize);
  369. const pageChange = (page: number, pageSize: number) => {
  370. setPage(page);
  371. setPageSize(pageSize);
  372. };
  373. //刷新缓存
  374. const refreshCache = async () => {
  375. const body = await fetchApi(refreshAPI, push, {
  376. method: "DELETE",
  377. });
  378. if (body !== undefined) {
  379. if (body.code == 200) {
  380. globalMessage.success("刷新成功");
  381. if (actionTableRef.current) {
  382. actionTableRef.current.reload();
  383. }
  384. } else {
  385. globalMessage.error(body.msg);
  386. }
  387. }
  388. };
  389. return (
  390. <PageContainer title={false}>
  391. <ProTable
  392. formRef={searchTableFormRef}
  393. rowKey="dictId"
  394. rowSelection={{
  395. selectedRowKeys,
  396. ...rowSelection,
  397. }}
  398. columns={columns}
  399. request={async (params: any, sorter: any, filter: any) => {
  400. // 表单搜索项会从 params 传入,传递给后端接口。
  401. const data = await queryTableData(params, sorter, filter);
  402. if (data !== undefined) {
  403. return Promise.resolve({
  404. data: data.rows,
  405. success: true,
  406. total: data.total,
  407. });
  408. }
  409. return Promise.resolve({
  410. data: [],
  411. success: true,
  412. });
  413. }}
  414. pagination={{
  415. defaultPageSize: defaultPageSize,
  416. showQuickJumper: true,
  417. showSizeChanger: true,
  418. onChange: pageChange,
  419. }}
  420. search={
  421. showSearch
  422. ? {
  423. defaultCollapsed: false,
  424. searchText: "搜索",
  425. }
  426. : false
  427. }
  428. dateFormatter="string"
  429. actionRef={actionTableRef}
  430. toolbar={{
  431. actions: [
  432. <ModalForm
  433. key="addmodal"
  434. title="添加字典类型"
  435. trigger={
  436. <Button icon={<PlusOutlined />} type="primary">
  437. 新建
  438. </Button>
  439. }
  440. autoFocusFirstInput
  441. modalProps={{
  442. destroyOnHidden: true,
  443. }}
  444. submitTimeout={2000}
  445. onFinish={executeAddData}
  446. >
  447. <ProForm.Group>
  448. <ProFormText
  449. width="md"
  450. name="dictName"
  451. label="字典名称"
  452. placeholder="请输入字典名称"
  453. rules={[{ required: true, message: "请输入字典名称" }]}
  454. />
  455. </ProForm.Group>
  456. <ProForm.Group>
  457. <ProFormText
  458. width="md"
  459. name="dictType"
  460. label="字典类型"
  461. placeholder="请输入字典类型"
  462. rules={[{ required: true, message: "请输入字典类型" }]}
  463. />
  464. <ProFormRadio.Group
  465. name="status"
  466. width="sm"
  467. label="状态"
  468. initialValue="0"
  469. options={[
  470. {
  471. label: "正常",
  472. value: "0",
  473. },
  474. {
  475. label: "停用",
  476. value: "1",
  477. },
  478. ]}
  479. />
  480. </ProForm.Group>
  481. <ProFormTextArea
  482. name="remark"
  483. width={688}
  484. label="备注"
  485. placeholder="请输入内容"
  486. />
  487. </ModalForm>,
  488. <ModalForm
  489. key="modifymodal"
  490. title="修改字典类型"
  491. formRef={modifyFormRef}
  492. trigger={
  493. <Button
  494. icon={<FontAwesomeIcon icon={faPenToSquare} />}
  495. disabled={!rowCanModify}
  496. onClick={() => onClickShowRowModifyModal()}
  497. >
  498. 修改
  499. </Button>
  500. }
  501. open={isShowModifyDataModal}
  502. autoFocusFirstInput
  503. modalProps={{
  504. destroyOnHidden: true,
  505. onCancel: () => {
  506. setIsShowModifyDataModal(false);
  507. },
  508. }}
  509. submitTimeout={2000}
  510. onFinish={executeModifyData}
  511. >
  512. <ProForm.Group>
  513. <ProFormText
  514. width="md"
  515. name="dictName"
  516. label="字典名称"
  517. placeholder="请输入字典名称"
  518. rules={[{ required: true, message: "请输入字典名称" }]}
  519. />
  520. </ProForm.Group>
  521. <ProForm.Group>
  522. <ProFormText
  523. width="md"
  524. name="dictType"
  525. label="字典类型"
  526. placeholder="请输入字典类型"
  527. rules={[{ required: true, message: "请输入字典类型" }]}
  528. />
  529. <ProFormRadio.Group
  530. name="status"
  531. width="sm"
  532. label="状态"
  533. initialValue="0"
  534. options={[
  535. {
  536. label: "正常",
  537. value: "0",
  538. },
  539. {
  540. label: "停用",
  541. value: "1",
  542. },
  543. ]}
  544. />
  545. </ProForm.Group>
  546. <ProFormTextArea
  547. name="remark"
  548. width={688}
  549. label="备注"
  550. placeholder="请输入内容"
  551. />
  552. </ModalForm>,
  553. <Button
  554. key="danger"
  555. danger
  556. icon={<DeleteOutlined />}
  557. disabled={!rowCanDelete}
  558. onClick={() => onClickDeleteRow()}
  559. >
  560. 删除
  561. </Button>,
  562. <Button
  563. key="export"
  564. type="primary"
  565. icon={<FontAwesomeIcon icon={faDownload} />}
  566. onClick={exportTable}
  567. >
  568. 导出
  569. </Button>,
  570. <Button
  571. key="refresh"
  572. type="primary"
  573. icon={<FontAwesomeIcon icon={faRotate} />}
  574. onClick={refreshCache}
  575. >
  576. 刷新缓存
  577. </Button>,
  578. ],
  579. settings: [
  580. {
  581. key: "switch",
  582. icon: showSearch ? (
  583. <FontAwesomeIcon icon={faToggleOn} />
  584. ) : (
  585. <FontAwesomeIcon icon={faToggleOff} />
  586. ),
  587. tooltip: showSearch ? "隐藏搜索栏" : "显示搜索栏",
  588. onClick: (key: string | undefined) => {
  589. setShowSearch(!showSearch);
  590. },
  591. },
  592. {
  593. key: "refresh",
  594. tooltip: "刷新",
  595. icon: <ReloadOutlined />,
  596. onClick: (key: string | undefined) => {
  597. if (actionTableRef.current) {
  598. actionTableRef.current.reload();
  599. }
  600. },
  601. },
  602. ],
  603. }}
  604. />
  605. {/* 删除确认模态框 */}
  606. <Modal
  607. title={
  608. <div style={{ display: 'flex', alignItems: 'center' }}>
  609. <ExclamationCircleFilled style={{ color: '#faad14', marginRight: 8 }} />
  610. <span>系统提示</span>
  611. </div>
  612. }
  613. open={deleteModalVisible}
  614. onOk={executeDeleteRow}
  615. onCancel={() => {
  616. setDeleteModalVisible(false);
  617. setDeleteDictId(null);
  618. }}
  619. okText="确认"
  620. cancelText="取消"
  621. >
  622. <p>{`是否确认删除字典编号为“${deleteDictId}”的数据项?`}</p>
  623. </Modal>
  624. </PageContainer>
  625. );
  626. }