Umi3与Antd-Pro5中后台全栈项目实战(1)

简介: 0、前言使用umi3和antd pro5从零实现全栈中后台管理系统

0、前言

使用umi3和antd pro5从零实现全栈中后台管理系统

0-1、涉及技术栈

前端: TS 、 React、React Hooks、 umi3、antd-pro5后端:   express、mongodb、jwt**

0-2、实现的功能

  • 后端用户鉴权
  • 前端权限管理
  • 用户密码加密
  • 封装一套通用弹窗表单组件,实现新建、修改、详情功能
  • 用户登录注册(首次需要后端自己添加一条用户登录信息)

0-1、涉及技术栈

前端: TS 、 React、React Hooks、 umi3、antd-pro5后端:   express、mongodb、jwt**

0-2、实现的功能

  • 后端用户鉴权
  • 前端权限管理
  • 用户密码加密
  • 封装一套通用弹窗表单组件,实现新建、修改、详情功能
  • 用户登录注册(首次需要后端自己添加一条用户登录信息)

1、初始化前端项目

umi 官网coding网址

yarn create umi myapp
npm i 
npm run dev

1、设置config下的proxy代理

  dev: {
    '/api/': {
      target: "http://localhost:3000",
      changeOrigin: true,
      pathRewrite: { '^': '' },
    },
  },

2、登录

修改src/service/login.ts 接口改为/api/user/login

export async function fakeAccountLogin(params: LoginParamsType) {
  return request<API.LoginStateType>('/api/user/login', {
    method: 'POST',
    data: params,
  });
}

存储token pages/user/login/index.tsx

localStorage.setItem('token' , msg.token)

使用token services/user.ts

export async function queryCurrent() {
  return request<API.CurrentUser>('/api/currentUser',  headers: {
      Authorization :  'Bearer ' + `${localStorage.getItem('token')
      }`
    }
}

每次请求都带上token src/app.tsx

export const request: RequestConfig = {
  errorHandler,
  headers: { 
    Authorization :  'Bearer ' + `${localStorage.getItem('token')}`
  }
};

退出 RightContent/AvatarDropdown.tsx

 localStorage.removeItem('token')

3、pro5参考文档

procomponents.ant.design/components/…

4、实现一个用户管理

5、列表页

pages/ListTableList/index.tsx

import { PlusOutlined } from '@ant-design/icons';
import { Button, Divider, message, Avatar } from 'antd';
import React, { useState, useRef } from 'react';
import { PageContainer, FooterToolbar } from '@ant-design/pro-layout';
import ProTable, { ProColumns, ActionType } from '@ant-design/pro-table';
import HandleForm from './components/HandleForm';
import { TableListItem } from './data.d';
import { queryRule, updateRule, addRule, removeRule } from './service';
import moment from 'moment';
/**
 * 操作提交
 * @param fields
 */
const handleSubmit = async (_id?: string, fields?: TableListItem) => {
  let title = _id ? '修改' : '新增';
  const hide = message.loading(`正在${title}`);
  try {
    if (_id) {
      await updateRule({
        _id,
        ...fields,
      });
    } else {
      await addRule({ ...fields });
    }
    hide();
    message.success(`${title}成功`);
    return true;
  } catch (error) {
    hide();
    message.error(`${title}失败`);
    return false;
  }
};
/**
 *  删除节点
 * @param selectedRows
 */
const handleRemove = async (selectedRows: string[], _id: string) => {
  // console.log(selectedRows,_id,'selectedRows>>>>')
  const hide = message.loading('正在删除');
  // return
  try {
    await removeRule({
      _id: _id ? _id : selectedRows,
    });
    hide();
    message.success('删除成功');
    return true;
  } catch (error) {
    hide();
    message.error('删除失败');
    return false;
  }
};
const TableList: React.FC<{}> = () => {
  const [modalVisible, handleModalVisible] = useState<boolean>(false);
  const [currentInfo, handleSaveCurrentInfo] = useState<TableListItem | null>(null);
  const [isDetail, setDetail] = useState<boolean>(false);
  const actionRef = useRef<ActionType>();
  const [selectedRowsState, setSelectedRows] = useState<any>([]);
  const columns: ProColumns<TableListItem>[] = [
    {
      title: '用户名',
      dataIndex: 'username',
    },
    {
      title: '密码',
      dataIndex: 'password',
      hideInDescriptions: true, //详情页不显示
      hideInTable: true,
    },
    {
      title: '角色',
      dataIndex: 'access',
      search: false,
      filters: [
        { text: '普通用户', value: 'user' },
        { text: '管理员', value: 'admin' },
      ],
      valueEnum: {
        user: { text: '普通用户' },
        admin: { text: '管理员' },
      },
    },
    {
      title: '_id',
      dataIndex: '_id',
      sorter: true,
      hideInForm: true,
      search: false,
    },
    {
      title: '头像',
      dataIndex: 'avatar',
      search: false,
      hideInForm: true,
      render: (dom, entity) => {
        return <Avatar src={entity.avatar} alt="" />;
      },
    },
    {
      title: '邮箱',
      dataIndex: 'email',
    },
    {
      title: '更新时间',
      dataIndex: 'updatedAt',
      sorter: true,
      hideInForm: true,
      search: false,
      renderText: (val: string) => {
        if (!val) return '';
        return moment(val).fromNow(); // 绝对时间转化成相对时间
      },
    },
    {
      title: '创建时间',
      dataIndex: 'createdAt',
      sorter: true,
      hideInForm: true,
      search: false,
      valueType: 'dateTime',
    },
    {
      title: '操作',
      dataIndex: 'option',
      valueType: 'option',
      render: (_, record) => (
        <>
          <a
            href="javascript:;"
            onClick={() => {
              handleModalVisible(true), handleSaveCurrentInfo(record);
            }}
          >
            修改
          </a>
          <Divider type="vertical" />
          <a
            href="javascript:;"
            onClick={() => {
              handleModalVisible(true), handleSaveCurrentInfo(record), setDetail(true);
            }}
          >
            详情
          </a>
          <Divider type="vertical" />
          <a
            href="javascript:;"
            onClick={async () => {
              await handleRemove([], record._id as 'string');
              // 刷新
              actionRef.current?.reloadAndRest?.();
            }}
          >
            删除
          </a>
        </>
      ),
    },
  ];
  return (
    <PageContainer>
      <ProTable<TableListItem>
        headerTitle="用户管理"
        actionRef={actionRef}
        rowKey="_id"
        search={{
          labelWidth: 120,
        }}
        toolBarRender={() => [
          <Button type="primary" onClick={() => handleModalVisible(true)}>
            <PlusOutlined />
            新增
          </Button>,
        ]}
        request={(params, sorter, filter) => queryRule({ ...params, sorter, filter })}
        columns={columns}
        form={{
          submitter: false,
        }}
        pagination={{ defaultPageSize: 5 }}
        rowSelection={{
          onChange: (selected, selectedRows) => {
            setSelectedRows(selected);
          },
        }}
      />
      <HandleForm
        onCancel={() => {
          handleModalVisible(false), handleSaveCurrentInfo({}), setDetail(false);
        }}
        modalVisible={modalVisible}
        values={currentInfo}
        isDetail={isDetail}
        onSubmit={async (values) => {
          const success = await handleSubmit(currentInfo?._id, values);
          if (success) {
            handleModalVisible(false);
            if (actionRef.current) {
              actionRef.current.reload();
            }
          }
        }}
      ></HandleForm>
      {selectedRowsState?.length > 0 && (
        <FooterToolbar
          extra={
            <div>
              已选择
              <a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a> 项
            </div>
          }
        >
          <Button
            onClick={async () => {
              await handleRemove(selectedRowsState, '');
              setSelectedRows([]);
              actionRef.current?.reloadAndRest?.();
            }}
          >
            批量删除
          </Button>
        </FooterToolbar>
      )}
    </PageContainer>
  );
};
export default TableList;

pages/ListTableList/data.d.ts

export interface TableListItem {
  _id?: string;
  username?: string;
  password?: string;
  avatar?: string;
  access?: string;
  email?: string;
} 
export interface TableListPagination {
  total: number;
  pageSize: number;
  current: number;
}
export interface TableListData {
  list: TableListItem[];
  pagination: Partial<TableListPagination>;
}
export interface TableListParams {
  _id?: string;
  username?: string;
  password?: string;
  avatar?: string;
  access?: string;
  email?: string;
  pageSize?: number;
  currentPage?: number;
  filter?: { [key: string]: any[] };
  sorter?: { [key: string]: any };
}

pages/ListTableList/service.ts

import { request } from 'umi';
import { TableListParams } from './data.d';
export async function queryRule(params?: TableListParams) {
  return request('/api/user/account', {
    params,
  });
}
export async function removeRule(params: { _id: string|string[] }) {
  return request('/api/user/account', {
    method: 'DELETE',
    data: params
  });
}
export async function addRule(params: TableListParams) {
  return request('/api/user/account', {
    method: 'POST',
    data: {
      ...params
    },
  });
}
export async function updateRule(params: TableListParams) {
  return request(`/api/user/account?_id=${params._id}`, {
    method: 'PUT',
    data: {
      ...params
    },
  });
}

相关文章
|
JavaScript 前端开发 小程序
mpvue踩坑-微信开发工具无法支持vue文件
mpvue踩坑-微信开发工具无法支持vue文件
89 0
|
2月前
|
JavaScript 前端开发 索引
Vue3 + Vite项目实战:常见问题与解决方案全解析
Vue3 + Vite项目实战:常见问题与解决方案全解析
109 0
|
6月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的课程辅导网站附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的课程辅导网站附带文章和源代码部署视频讲解等
170 8
|
Web App开发 JSON 前端开发
使用 React 和 NodeJS 创建一个全栈项目
在本文中,我将使用 React 和 NodeJS 创建一个全栈项目。介绍下如何让 Node.js 作为 web 服务器来加载静态资源,如何让 React 程序可以直接调用 Node API。
799 0
|
小程序 前端开发 开发工具
微信小程序wepy框架入门教程-查看并运行wepy源码(二)
微信小程序wepy框架入门教程-查看并运行wepy源码(二)
138 0
|
前端开发 NoSQL MongoDB
Umi3与Antd-Pro5中后台全栈项目实战(2)
6、弹窗表单组件 pages/ListTableList/components/HandleForm.d.ts
275 0
|
JavaScript 前端开发
【Vue 开发实战】实战篇 # 26:Ant Design Pro介绍
【Vue 开发实战】实战篇 # 26:Ant Design Pro介绍
229 0
【Vue 开发实战】实战篇 # 26:Ant Design Pro介绍
|
监控 JavaScript 前端开发
技术分享 | 测试平台开发-前端开发之Vue.js 框架的使用
技术分享 | 测试平台开发-前端开发之Vue.js 框架的使用
|
JavaScript 小程序 开发者
1.5【微信小程序全栈开发课程】安装sublime3代码编辑器以及vue插件
从第二章开始我们就要正式开始敲代码了,微信开发者工具里面的代码编辑器不太好用,我们需要自己安装一个sublime代码编辑器。
158 0
1.5【微信小程序全栈开发课程】安装sublime3代码编辑器以及vue插件
|
前端开发
react项目实战学习笔记-学习7-ant 引入和测试
react项目实战学习笔记-学习7-ant 引入和测试
149 0

相关实验场景

更多