迷新白的博客 迷新白的博客
首页
随笔
  • Vuepress
  • Springboot
  • 开发工具
  • 系统工具
读吧
  • 智能浇花系统 (opens new window)
  • 用户中心系统 (opens new window)
  • 关于
  • 友情链接
GitHub (opens new window)

迷新白

愿你平安
首页
随笔
  • Vuepress
  • Springboot
  • 开发工具
  • 系统工具
读吧
  • 智能浇花系统 (opens new window)
  • 用户中心系统 (opens new window)
  • 关于
  • 友情链接
GitHub (opens new window)
  • 用户中心系统

    • 前端初始化
    • 用户中心后端-1
    • 用户中心后端-2
    • 注册模块(后端)
    • 登录模块(后端)
    • 管理模块(后端)
    • Ant Design Pro前端初始化
    • 登录+注册模块(前端)
    • 管理模块+登录状态(前端)
      • 登录态管理(currrent)
        • 后端
        • 前端
        • 设置头像
      • 用户管理功能
        • 前端开发(一)
        • 前端开发(二)
    • 注销模块+校验模块(前后端)
    • 异常处理器(后端优化)
    • 请求响应处理器(前端)
  • 仿Deepseek官网AI聊天网站

  • 尤克里里音月-Flutter(需求分析阶段)

  • 项目
  • 用户中心系统
迷新白
2025-04-08
目录

管理模块+登录状态(前端)

# 10.管理模块+登录状态(前端)

用户管理功能➕登录态管理(currrent)

# 登录态管理(currrent)

# 后端

在UserController添加接口以获取当前登陆状态、信息

    @GetMapping("/current")
    public User getCurrentUser(HttpServletRequest request) {
        Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        User currentUser = (User) userObj;//(User)表示强制转换
        if (currentUser == null) {
            return null;
        }
        long userId = currentUser.getId();
        //todo校验用户是否合法
        User user = userService.getById(userId);
        return userService.getSafetyUser(user);
    }
1
2
3
4
5
6
7
8
9
10
11
12

在UserServiceImpl中的getSafetyUser加一个判断,用户是否为空

    @Override
    public User getSafetyUser(User originUser) {
        if (originUser == null){
            return null;
        }
        ....
        }
1
2
3
4
5
6
7

# 前端

  • app.tsx中点击queryCurrentUser进入api.ts,修改代码,点击CurrentUser查看其返回的数据,结合数据库字段设计代码
api.ts
/** 获取当前的用户 GET /api/currentUser */
export async function currentUser(options?: { [key: string]: any }) {
  return request<API.CurrentUser>('/api/user/currentUser', {
    method: 'GET',
    ...(options || {}),
  });
}

typings.d.ts
declare namespace API {
  type CurrentUser = {
    id: number;
    username: string;
    userAccount: string;
    avatarUrl?: string;
    gender: number;
    phone: string;
    email: string;
    userStatus: number;
    userRole: number;
    createTime: Data;
    };
    address?: string;
    phone?: string;
  };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  • 将 app.tsx 中之前定义的白名单的代码提到前面,修改变量名,修改引用的变量名
const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login';
/**
 * 无需用户登录态的页面
 */
const NO_NEED_LOGIN_WHITE_LIST = ['/user/register',loginPath];

......

// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
  return {      
      .....
      if (NO_NEED_LOGIN_WHITE_LIST.includes(location.pathname)){
        return;
      }
      ....
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 修改app.tsx 的下面代码
/**
 * @see  https://umijs.org/zh-CN/plugins/plugin-initial-state
 * */
.....
  const fetchUserInfo = async () => {
    try {
      return await queryCurrentUser();
    } catch (error) {
      history.push(loginPath);
    }
    return undefined;
  };
  // 如果不需要登录页面,bu执行
  if (NO_NEED_LOGIN_WHITE_LIST.includes(history.location.pathname)) {
    return {
      //@ts-ignore
      fetchUserInfo,
      settings: defaultSettings,
    };
  }
  const currentUser = await fetchUserInfo();
  return {
    //@ts-ignore
    fetchUserInfo,
    currentUser,
    settings: defaultSettings,
  };
}

// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
  return {
    rightContentRender: () => <RightContent />,
    disableContentMargin: false,
    waterMarkProps: {
      content: initialState?.currentUser?.username,
    },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  • 登陆测试,发现一直有一个问题,登陆后会显示登陆成功,但是有一个空白的错误
image-20250410202447030

今天知道了可以用开发者工具查看前端的控制台

image-20250410202528482

有很多错误,一个一个来

第一个就是前端的api.ts文件里,接口名字和后端的不匹配

/** 获取当前的用户 GET /api/currentUser */
改成
/** 获取当前的用户 GET /api/current */
1
2
3

改好后,请求成功了,发现登陆状态没有被记录

image-20250410211049671

一步步排查 首先是UserController中

/**
 * 用户态登录键
 */
 //private static final String USER_LOGIN_STATE = "userLoginState"; 注释掉,改成
String USER_LOGIN_STATE = "userLoginState";
1
2
3
4
5

UserConstant.java补充代码

    /**
     * 默认权限
     */
    int DEFAULT_ROLE = 0;

    /**
     * 管理员权限
     */
    int ADMIN_ROLE = 1;
}
1
2
3
4
5
6
7
8
9
10

用户态登录键是一个键名,safetyUser是键值

判断是/current请求没有任何信息,因为cookie值不一样了,表示在不同会话,并且后台发现/current的值是空image-20250410222509532

image-20250410222453335

image-20250410222426478

image-20250410222416274

先不管了 直接做下面的


在论坛里找到了别人遇到同样问题的解决办法用户管理项目关于两次请求sessioni - 编程导航 - 程序员编程学习交流社区 (opens new window)

在request里设置credentials=='include'就行 app.tsx:

image-20250411010524050
export const request: RequestConfig = {
  prefix: 'http://localhost:8080',
  timeout: 10000,
  credentials:'include',
};
1
2
3
4
5

根据这篇:credentials: 'include'跨域问题解决方案-CSDN博客 (opens new window)

问题在于前端设置: credentials: ‘include’,( 允许 cookie 共享,跨域问题,传Cookie是必须配置)

不传递Cookie时,不允许配置credentials

# 设置头像

src/components/RightContent/AvatarDropdown.tsx

image-20250411121634717

image-20250411122112425

# 用户管理功能

# 前端开发(一)

  1. 文件夹src/pages下新建Admin文件夹,复制同级目录user文件夹下的Register文件夹粘贴到Admin文件夹,更名为UserManage image-20250411122659132

  2. 到config/routes.ts文件中添加路由image-20250411122905647

  3. 访问这个地址,显示没有权限image-20250411122917769原因在于routes.ts文件中有一个属性是设置判断权限的image-20250411123044244路由组件是如何判断账号的权限呢?从某个地方可以全局获取


    • 首次访问页面(刷新页面),会进入app.tsx,执行getInitialState (opens new window)方法,其返回值会作为全局共享的数据
    • services/access.tx文件中判断用户的访问权限,canAdmin的值为true时会被认为是具有管理员权限
    • 进入文件中修改判断代码image-20250411123826095
  4. 修改好后,进入管理页面进行测试,显示的是预期的注册功能(因为pages是从注册文件夹复制过来的)image-20250411124035979

  5. 在路由配置中修改补充代码image-20250411124242886

# 前端开发(二)

使用ProComponents高级表单

  • u
    • 是
    • 是
    • 是
  1. 删除src/pages/user/Register/index.tsx的代码,只保留初始化的部分image-20250411124720669


    补充代码

    import React from "react";
    
    const Register: React.FC = () => {
      return (
        <div id="userManage">
        </div>
      );
    };
    export default Register;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  2. 到ProTable - 高级表格 - ProComponents (opens new window)找到合适的表格复制代码(这里复制第一个)image-20250411125138499

  3. 删除不需要用到的组件代码,修改我们需要的属性

    import { EllipsisOutlined, PlusOutlined } from '@ant-design/icons';
    import type { ActionType, ProColumns } from '@ant-design/pro-components';
    import { ProTable, TableDropdown } from '@ant-design/pro-components';
    import {Button, Dropdown, Image, Space, Tag} from 'antd';
    import { useRef } from 'react';
    import request from 'umi-request';
    import CurrentUser = API.CurrentUser;
    export const waitTimePromise = async (time: number = 100) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(true);
        }, time);
      });
    };
    
    export const waitTime = async (time: number = 100) => {
      await waitTimePromise(time);
    };
    
    const columns: ProColumns<API.CurrentUser>[] = [
      {
        dataIndex: 'id',
        valueType: 'indexBorder',
        width: 48,
      },
      {
        title: '用户名',
        dataIndex: 'username',
        copyable: true,
      },
      {
        title: '用户账户',
        dataIndex: 'userAccount',
        copyable: true,
      },
      {
        title: '头像',
        dataIndex: 'avatarUrl',
        render: (_,record) => (
          <div>
            <Image src={record.avatarUrl} width={50} height={50}/>  //高度限制规格
          </div>
        ),
      },
      {
        title: '性别',
        dataIndex: 'gender',
      },
      {
        title: '电话',
        dataIndex: 'phone',
        copyable: true,
      },
      {
        title: '邮件',
        dataIndex: 'email',
        copyable: true,
      },
      {
        title: '状态',
        dataIndex: 'userStatus',
      },
      {
        title: '角色',
        dataIndex: 'userRole',
        valueType: 'select',
        valueEnum: {
          0: { text: '普通用户', status:'Default', },
          1: {
            text: '管理员',
            status: 'Success',
          },
        },
      },
      {
        title: '创建时间',
        key: 'createTime',
        valueType: 'dateTime',
      },
      {
        title: '操作',
        valueType: 'option',
        key: 'option',
        render: (text, record, _, action) => [
          <a
            key="editable"
            onClick={() => {
              action?.startEditable?.(record.id);
            }}
          >
            编辑
          </a>,
          <a href={record.url} target="_blank" rel="noopener noreferrer" key="view">
            查看
          </a>,
          <TableDropdown
            key="actionGroup"
            onSelect={() => action?.reload()}
            menus={[
              { key: 'copy', name: '复制' },
              { key: 'delete', name: '删除' },
            ]}
          />,
        ],
      },
    ];
    
    export default () => {
      const actionRef = useRef<ActionType>();
      return (
        <ProTable<API.CurrentUser>
          columns={columns}
          actionRef={actionRef}
          cardBordered
          request={async (params, sort, filter) => {
            console.log(sort, filter);
            await waitTime(2000);
            return request<{
              data: CurrentUser[];
            }>('https://proapi.azurewebsites.net/github/issues', {
              params,
            });
          }}
          editable={{
            type: 'multiple',
          }}
          columnsState={{
            persistenceKey: 'pro-table-singe-demos',
            persistenceType: 'localStorage',
            defaultValue: {
              option: { fixed: 'right', disable: true },
            },
            onChange(value) {
              console.log('value: ', value);
            },
          }}
          rowKey="id"
          search={{
            labelWidth: 'auto',
          }}
          options={{
            setting: {
              listsHeight: 400,
            },
          }}
          form={{
            // 由于配置了 transform,提交的参数与定义的不同这里需要转化一下
            syncToUrl: (values, type) => {
              if (type === 'get') {
                return {
                  ...values,
                  created_at: [values.startTime, values.endTime],
                };
              }
              return values;
            },
          }}
          pagination={{
            pageSize: 5,
            onChange: (page) => console.log(page),
          }}
          dateFormatter="string"
          headerTitle="高级表格"
          toolBarRender={() => [
            /*
            新建与选择菜单组件
             */
            <Button
              key="button"
              icon={<PlusOutlined />}
              onClick={() => {
                actionRef.current?.reload();
              }}
              type="primary"
            >
              新建
            </Button>,
            <Dropdown
              key="menu"
              menu={{
                items: [
                  {
                    label: '1st item',
                    key: '1',
                  },
                  {
                    label: '2nd item',
                    key: '2',
                  },
                  {
                    label: '3rd item',
                    key: '3',
                  },
                ],
              }}
            >
              <Button>
                <EllipsisOutlined />
              </Button>
            </Dropdown>,
          ]}
        />
      );
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
  4. 此时前端页面显示如下image-20250411130833432


    修改src/pages/Admin.tsx文件

    import { PageHeaderWrapper } from '@ant-design/pro-components';
    import React from 'react';
    
    const Admin: React.FC = (props) => {
      const {children} = props;
      return (
        <PageHeaderWrapper>
          {children}
        </PageHeaderWrapper>
      );
    };
    export default Admin;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    显示效果如下image-20250411131131329

  5. api.ts文件里定义一个searchUsers接口(复制notices接口,进行修改)

/** 搜索用户 GET /api/search */
export async function searchUsers(options?: { [key: string]: any }) {
  return request<API.NoticeIconList>('/api/user/search', {
    method: 'GET',
    ...(options || {}),
  });
}
1
2
3
4
5
6
7

发现函数没有被使用image-20250411144506419

原因就在于这里,要改成自己的api,原地址是一个测试文件image-20250411145027614

}>('http://localhost:8080/api/user/search', {
1

改完发现还是有问题的,因为没有传递管理页相关的参数,后端始终报错

image-20250411182850347

在编程导航网站里找到了其他朋友写好的代码跟着鱼皮直播(终)做到用户管理的时候,引 - 编程导航 - 程序员编程学习交流社区 (opens new window)

这一段的代码应该改成下面的

      request={async (params, sort, filter) => {
        console.log(sort, filter);
        await waitTime(2000);
        const userList = await searchUsers();
        return {
          data: userListt
        }
      }}
1
2
3
4
5
6
7
8

这样就显示出来了image-20250411184819539

但还可以发现,创建时间是错误的,更新时间加上去之后也是错误的

对比数据库里的数据

image-20250411184911866

对比前端发送请求得到响应的数据

image-20250411185000988

后端在把数据发送给前端时,数据被转化成了 ISO 8601 格式(2025-03-28T10:56:52.000+00:00)

Protable支持的属性:通用配置 - ProComponents (opens new window)

无语了(ˉ▽ˉ;)... 原因在于 key应该改为dataIndex

  {
    title: '创建时间',
    key: 'createTime',
    valueType: 'dateTime',
  },
1
2
3
4
5

文字写于:广东

#用户中心系统
更新时间: 2025/4/25 20:22:48
登录+注册模块(前端)
注销模块+校验模块(前后端)

← 登录+注册模块(前端) 注销模块+校验模块(前后端)→

最近更新
01
JavaScript计时器
08-09
02
CSS-动画效果
08-09
03
JavaScript文档流
08-09
更多文章>
Theme by Vdoing | Copyright © 2022-2025 迷新白 | 的博客
sitemap icon by Icons8
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式