page.tsx 19 KB

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