1.我们在列表数据展现定义columns数组时,预留了修改和查看的操作列代码,代码如下:

    1. {
    2. title: '操作',
    3. dataIndex: 'option',
    4. valueType: 'option',
    5. render: (_, record) => [
    6. <a
    7. key="config"
    8. onClick={() => {
    9. handleUpdateModalVisible(true);
    10. setCurrentRow(record);
    11. }}
    12. >
    13. 修改
    14. </a>,
    15. <a
    16. onClick={() => {
    17. setCurrentRow(record);
    18. setShowDetail(true);
    19. }}
    20. >
    21. 查看
    22. </a>
    23. ],
    24. },

    这里点某一行数据的修改操作链接时会调用handleUpdateModalVisible(true)方法,从字面意义上看应该是有个UpdateModel组件,让其可见,然后调用setCurrentRow方法把当前行数据record存入state

    1. const [currentRow, setCurrentRow] = useState();

    2.我们先找UpdateModel组件
    image.png
    发现有个UpdateForm,而且src/pages/loginfo/index.jsx中确实有UpdateForm组件,而
    handleUpdateModalVisible(true)只是个useState的设置,来设置UpdateForm组件的可见性
    image.png

    1. <UpdateForm
    2. onSubmit={async (value) => {
    3. const success = await handleUpdate(value, currentRow);
    4. if (success) {
    5. handleUpdateModalVisible(false);
    6. setCurrentRow(undefined);
    7. if (actionRef.current) {
    8. actionRef.current.reload();
    9. }
    10. }
    11. }}
    12. onCancel={() => {
    13. handleUpdateModalVisible(false);
    14. setCurrentRow(undefined);
    15. }}
    16. updateModalVisible={updateModalVisible}
    17. values={currentRow || {}}
    18. />
    1. updateModalVisible={updateModalVisible}

    3.从以上代码我们可以分析出,修改模块的核心组件是UpdateForm,而UpdateForm是一个单独引入的JSX文件,文件位置在src/pages/loginfo/components/UpdateForm.jsx

    4.我们打开UpdateForm.jsx,发现里面代码蛮多,而且还用了StepsForm
    image.png
    我们的表单没有这么复杂,我们把UpdateForm.jsx,全替换掉,替换后的的代码如下

    1. import React from 'react';
    2. import { Modal } from 'antd';
    3. import ProForm, { ModalForm, ProFormText, ProFormDateTimePicker } from '@ant-design/pro-form';
    4. const UpdateForm = (props) => {
    5. return (
    6. <ModalForm
    7. layout="horizontal"
    8. labelCol={{ span: 4 }}
    9. title="修改登录日志"
    10. width="600px"
    11. visible={props.updateModalVisible}
    12. modalProps={{
    13. destroyOnClose: true,
    14. onCancel: () => props.onCancel(),
    15. }}
    16. onFinish={props.onSubmit}
    17. initialValues={{
    18. infoId: props.values.infoId,
    19. ipaddr: props.values.ipaddr,
    20. loginLocation: props.values.loginLocation,
    21. browser: props.values.browser,
    22. loginTime: props.values.loginTime,
    23. msg: props.values.msg,
    24. }}
    25. >
    26. <ProFormText
    27. name="infoId"
    28. hidden="true"
    29. />
    30. <ProFormText
    31. rules={[
    32. {
    33. required: true,
    34. message: '登录IP地址不能为空',
    35. },
    36. {
    37. pattern: new RegExp(
    38. '^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',
    39. 'g',
    40. ),
    41. message: '登录IP地址格式错误',
    42. },
    43. ]}
    44. width="md"
    45. name="ipaddr"
    46. label="登录IP地址"
    47. placeholder="请输入登录IP地址"
    48. />
    49. <ProFormText
    50. rules={[
    51. {
    52. required: true,
    53. message: '登录地点为必填项',
    54. },
    55. ]}
    56. width="md"
    57. label="登录地点"
    58. placeholder="请输入登录地点"
    59. name="loginLocation"
    60. />
    61. <ProFormText
    62. rules={[
    63. {
    64. required: true,
    65. message: '浏览器类型为必填项',
    66. },
    67. ]}
    68. width="md"
    69. label="浏览器类型"
    70. placeholder="请输入浏览器类型"
    71. name="browser"
    72. />
    73. <ProFormDateTimePicker
    74. name="loginTime"
    75. label="访问时间"
    76. rules={[
    77. {
    78. required: true,
    79. message: '访问时间为必填项',
    80. },
    81. ]}
    82. />
    83. <ProFormText
    84. rules={[
    85. {
    86. required: true,
    87. message: '提示消息为必填项',
    88. },
    89. ]}
    90. width="md"
    91. label="提示消息"
    92. placeholder="请输入提示消息"
    93. name="msg"
    94. />
    95. </ModalForm>
    96. );
    97. };
    98. export default UpdateForm;

    这里的UpdateForm组件其实跟我们之前做新增时用的组件差不多,用的也是ModalForm,ModalForm里面用的组件和非空验证以及正则表达式验证也是一样的,其实之前在做新增登录日志时我们也说过,新增和修改其实可以共用一个jsx文件,有兴趣的朋友可以自行改造成共用一个jsx文件。

    5.UpdateForm表单中有几段代码稍作说明

    1. modalProps={{
    2. destroyOnClose: true,
    3. onCancel: () => props.onCancel(),
    4. }}

    通过props公开onCancel方法
    image.png

    1. onFinish={props.onSubmit}
    1. <UpdateForm
    2. onSubmit={async (value) => {
    3. const success = await handleUpdate(value, currentRow);
    4. if (success) {
    5. handleUpdateModalVisible(false);
    6. setCurrentRow(undefined);
    7. if (actionRef.current) {
    8. actionRef.current.reload();
    9. }
    10. }
    11. }}

    通过props公开onSubmit方法,即更新表单后,点确定时会触发onSubmit方法,onSubmit中会调用handleUpdate更新数据,更新成功后会隐藏更新表单,然后重新加载表格。

    1. initialValues={{
    2. infoId: props.values.infoId,
    3. ipaddr: props.values.ipaddr,
    4. loginLocation: props.values.loginLocation,
    5. browser: props.values.browser,
    6. loginTime: props.values.loginTime,
    7. msg: props.values.msg,
    8. }}

    initialValues设置编辑表单上各个组件的初始值,updateForm中有属性values,values的值即为当前行的数据对象,values会通过props传递给UpdateForm,我们可以在initialValues方法中拿到props.values,并且给各个组件设置初始值,如infoId:props.values.infoId,
    image.png

    1. <ProFormText
    2. name="infoId"
    3. hidden="true"
    4. />

    让主键字段infoId文本框隐藏,即实现效果,这里踩了好久的坑,在Antd Pro官网的ProFormText并未找到如何实现隐藏域的效果,官网地址:
    https://procomponents.ant.design/components/field-set#proformtext,在Antd 的Input组件属性中也未找到,官网地址:
    https://ant.design/components/input-cn/,最后自己反复猜测尝试,才发现可以用hidden=”true”实现隐藏域效果

    6.保存UpdateForm.jsx后,等编译后,打开登录日志列表页面,选择我们刚才录入的哪条数据,发现数据可以正常显示在更新页面上,且主键字段值不会显示出来,如下图:
    image.png

    7.下一步是写后台方法,之前在做新增时也说过,若依的登录日志的Contoller中是没有新增、修改的方法的,我们先加一下修改的后台方法,代码如下

    1. @PostMapping(value="edit")
    2. public AjaxResult edit(@Validated @RequestBody SysLogininfor sysLogininfor)
    3. {
    4. sysLogininfor.setUpdateBy(getUsername());
    5. sysLogininfor.setUserName(getUsername());
    6. sysLogininfor.setStatus("0");
    7. return toAjax(logininforService.updateSysLogininfor(sysLogininfor));
    8. }

    这是一个Post接口,传入参数为一个sysLoginfor实体对象的JSON数据,接口调用的url地址为/api/monitor/logininfor/edit,这里有个问题,若依的servcie中连updateSysLogininfor方法也未提供,我们需要手动增加updateSysLogininfor方法,若依后台使用的是mybatis,我们需要先从mapper文件开始进行修改
    8.我们先在mapper文件中增加更新logininfo的xml代码,找到ruoyi-system模块(若依是一个多模块的项目)下的resources/mapper/system/SysLogininforMapper.xml,找到insert代码
    image.png

    1. <insert id="insertLogininfor" parameterType="SysLogininfor">
    2. insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time)
    3. values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate())
    4. </insert>

    我们在insert后面增加update的mybatis xml代码如下:

    1. <update id="updateSysLogininfor" parameterType="SysLogininfor">
    2. update sys_logininfor
    3. <trim prefix="SET" suffixOverrides=",">
    4. <if test="userName != null">user_name = #{userName},</if>
    5. <if test="ipaddr != null">ipaddr = #{ipaddr},</if>
    6. <if test="loginLocation != null">login_location = #{loginLocation},</if>
    7. <if test="browser != null">browser = #{browser},</if>
    8. <if test="os != null">os = #{os},</if>
    9. <if test="status != null">status = #{status},</if>
    10. <if test="msg != null">msg = #{msg},</if>
    11. <if test="loginTime != null">login_time = #{loginTime},</if>
    12. </trim>
    13. where info_id = #{infoId}
    14. </update>

    然后保存xml文件
    9.找到ruoyi-system模块下的src/java/com/ruoyi/system/mapper/SysLogininforMapper.java,在
    public void insertLogininfor(SysLogininfor logininfor);后面加一行更新方法的代码如下

    1. public int updateSysLogininfor(SysLogininfor sysLogininfor);

    image.png
    10.找到ruoyi-system模块下的src/java/com/ruoyi/system/service/ISysLogininforService.java,在
    public void insertLogininfor(SysLogininfor logininfor);后面加一行更新方法的代码如下

    1. public int updateSysLogininfor(SysLogininfor sysLogininfor);

    11.找到ruoyi-system模块下的src/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java,在

    1. @Override
    2. public void insertLogininfor(SysLogininfor logininfor)
    3. {
    4. logininforMapper.insertLogininfor(logininfor);
    5. }

    后面加更新方法的代码如下

    1. @Override
    2. public int updateSysLogininfor(SysLogininfor sysLogininfor)
    3. {
    4. return logininforMapper.updateSysLogininfor(sysLogininfor);
    5. }

    这一步做完后contoller中的edit部分的代码应该不会报错了,我们重新maven reload一下
    image.png
    然后Rebuild Project
    image.png
    稍等几分钟,Rebuild 成功后,重启后台
    12.后台方法写好后,我们就应该修改前台调用的接口了,找到src/pages/loginfo/service.js,找到updateRule方法,代码如下:

    1. export async function updateRule(data, options) {
    2. return request('/api/rule', {
    3. data,
    4. method: 'PUT',
    5. ...(options || {}),
    6. });
    7. }

    修改为以下代码,主要修改了方法名和请求url地址,method改成后台对应的POST

    1. export async function updateLoginfo(data, options) {
    2. return request('/api/monitor/logininfor/edit', {
    3. data,
    4. method: 'POST',
    5. ...(options || {}),
    6. });
    7. }

    13.回到src/pages/loginfo/index.jsx,修改引入service的代码

    1. import { loginfo, addLoginfo, updateRule, removeRule } from './service';

    将updateRule替换成updateLoginfo,替换后代码如下

    1. import { loginfo, addLoginfo, updateLoginfo, removeRule } from './service';

    14.找到handleUpdate方法,代码如下:

    1. const handleUpdate = async (fields, currentRow) => {
    2. const hide = message.loading('正在配置');
    3. try {
    4. await updateRule({ ...currentRow, ...fields });
    5. hide();
    6. message.success('配置成功');
    7. return true;
    8. } catch (error) {
    9. hide();
    10. message.error('配置失败请重试!');
    11. return false;
    12. }
    13. };

    这是更新调用后台接口的主方法,查询表格里面已做好代码封装,我们只需将updateRule方法改为updateLoginfo即可,修改后代码如下:

    1. const handleUpdate = async (fields, currentRow) => {
    2. const hide = message.loading('正在更新');
    3. try {
    4. await updateLoginfo({ ...currentRow, ...fields });
    5. hide();
    6. message.success('更新成功');
    7. return true;
    8. } catch (error) {
    9. hide();
    10. message.error('更新失败请重试!');
    11. return false;
    12. }
    13. };
    14. /**

    15.保存前台代码,前台编译完成后,确保后台已启动,我们重新登录前台,打开登录日志,编辑我们之前录入的那条数据
    image.png
    修改成功后表格会刷新,会绑定更新后的数据,如下图所示
    image.png