page.tsx 17 KB

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