page.tsx 17 KB

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