page.tsx 16 KB

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