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
    },
  });
}

相关文章
|
26天前
|
前端开发 JavaScript 关系型数据库
前端的全栈之路:基于 Vue3 + Nest.js 全栈开发的后台应用
这篇文章介绍了一个名为Vue3Admin的全栈后台应用,前端基于SoybeanAdmin二次开发,后端基于Nest.js。主要使用了Vue3.5、AntDesignVue、UnoCSS、Pinia等前端技术栈,以及Nest.js、PostgreSQL、Prisma等后端技术栈。文章详细描述了系统的功能设计,包括动态国际化语言配置、登录用户操作日志、用户和角色权限映射、动态路由菜单、消息公告发布及前端业务功能等。同时,也提供了项目运行所需的环境和依赖,以及如何拉取代码、安装依赖和启动项目的方法。最后,文章展示了项目的演示图,并对项目进行了总结,指出项目未经严格测试,仅供学习交流使用。
前端的全栈之路:基于 Vue3 + Nest.js 全栈开发的后台应用
|
14天前
|
JavaScript 前端开发 索引
Vue3 + Vite项目实战:常见问题与解决方案全解析
Vue3 + Vite项目实战:常见问题与解决方案全解析
34 0
|
6月前
|
前端开发 NoSQL JavaScript
基于 React + Nest 全栈开发的后台系统
这篇文章介绍了一个基于React+Nest全栈开发的后台系统XmwAdmin。项目包括前端和后端技术栈,线上预览地址和登录信息。作者推荐使用pnpm包管理工具和特定的环境依赖。文章提供了项目的运行和编译代码,以及各个功能模块的介绍。还包括演示图和项目活动以及总结部分。数据库下载链接也提供了,该项目已完成后台的核心功能。
基于 React + Nest 全栈开发的后台系统
|
11月前
|
JavaScript API 开发者
零基础快速开发全栈后台管理系统(Vue3+ElementPlus+Koa2)—项目概述篇(一)
零基础快速开发全栈后台管理系统(Vue3+ElementPlus+Koa2)—项目概述篇(一)
|
Web App开发 JSON 前端开发
使用 React 和 NodeJS 创建一个全栈项目
在本文中,我将使用 React 和 NodeJS 创建一个全栈项目。介绍下如何让 Node.js 作为 web 服务器来加载静态资源,如何让 React 程序可以直接调用 Node API。
778 0
|
前端开发 NoSQL MongoDB
Umi3与Antd-Pro5中后台全栈项目实战(2)
6、弹窗表单组件 pages/ListTableList/components/HandleForm.d.ts
264 0
|
前端开发 JavaScript 小程序
前端uni-app框架之实战主要技术栈day_3
前端uni-app框架之实战主要技术栈day_3
前端uni-app框架之实战主要技术栈day_3
|
JavaScript 小程序 开发者
1.5【微信小程序全栈开发课程】安装sublime3代码编辑器以及vue插件
从第二章开始我们就要正式开始敲代码了,微信开发者工具里面的代码编辑器不太好用,我们需要自己安装一个sublime代码编辑器。
154 0
1.5【微信小程序全栈开发课程】安装sublime3代码编辑器以及vue插件
|
JavaScript 前端开发 API
「免费开源」基于Vue和Quasar的crudapi前端SPA项目实战之模块管理(十四)
通过配置的方式可以零代码实现表单管理功能,但是所有表单都没有分类,如果表单数量很多的情况下不方便查找,因此本文主要介绍表单模块管理相关内容。属于同一类型的表单可以添加到同一个模块,比如字典相关的表建立“字典”模块,用户相关的表建立“用户管理”模块,模块在首页直接展示,用户可以快速的操作对应的表单。
245 0
「免费开源」基于Vue和Quasar的crudapi前端SPA项目实战之模块管理(十四)
|
Web App开发 前端开发 JavaScript
「免费开源」基于Vue和Quasar的crudapi前端SPA项目实战之文件上传(十)
本文主要介绍文件上传相关内容。crudapi支持附件字段,表字段里面保存的是文件url字符串。附件可以通过其它文件管理系统比如阿里云的OSS进行上传,或者使用系统自带的文件管理API进行上传,包括普通文件上传和大文件切片上传两种方式。
363 0
「免费开源」基于Vue和Quasar的crudapi前端SPA项目实战之文件上传(十)