page.tsx 19 KB

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