page.tsx 17 KB

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