page.tsx 16 KB

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