page.tsx 17 KB

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