page.tsx 17 KB

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