page.tsx 17 KB

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