Ant Design Pro V4.5 从服务器请求菜单(typescript版)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ant Design Pro V4.5 从服务器请求菜单(typescript版)相关的知识,希望对你有一定的参考价值。

​Ant Design Pro V4.5 从服务器请求菜单(typescript版)

第一章、初识菜单

        因为我使用的ant design pro用的不是js脚本,而是ts脚本,项目菜单项在config文件夹下的rooutes.ts中

export default [
  {
    path: \'/\',
    component: \'../layouts/BlankLayout\',
    routes: [
      {
        path: \'/user\',
        component: \'../layouts/UserLayout\',
        routes: [
          {
            name: \'login\',
            path: \'/user/login\',
            component: \'./user/login\',
          },
        ],
      },
      此处省略n行...
      },
    ],
  },
  {
    component: \'./404\',
  },
];

我们想用后端生成,查看官方资料,没弄懂官方的方法

官方的讲解

https://pro.ant.design/zh-CN/docs/advanced-menu

Github前端源码

https://github.com/ant-design/ant-design-pro

由于才疏识浅没弄懂官方的办法,那么就用自己的办法来实现

第二章、了解项目中的dva和Umi用法

        antd pro 默认采用了Umi和dva组件,因此想要在基础上添加功能必须了解数据请求和数据流向,在dva中主要分3层,models,services,components,其中models是最重要概念,这里放的是各种数据,与数据交互的应该都是在这里。services是请求后台接口的方法。components是我们编写的组件。

        service 层仅仅是封装了用request请求服务端的api的逻辑,这里不涉及业务逻辑,比较简单,例如下面代码:    

import request from \'@/utils/request\';

export interface LoginParamsType {
  userName: string;
  password: string;
  mobile: string;
  captcha: string;
}

export function fakeAccountLogin(params: LoginParamsType) {
  return request(\'/api/login/account\', {
    method: \'POST\',
    data: params,
  });
}

export async function getFakeCaptcha(mobile: string) {
  return request(`/api/login/captcha?mobile=${mobile}`);
}

        models层是核心业务逻辑的所在

import { Effect, Reducer } from \'umi\';

import { queryCurrent, query as queryUsers } from \'@/services/user\';

export interface CurrentUser {
    avatar?: string;
    name?: string;
    title?: string;
    group?: string;
    signature?: string;
    tags?: {
        key: string;
        label: string;
    }[];
    userid?: string;
    unreadCount?: number;
}

export interface UserModelState {
    currentUser?: CurrentUser;
}

//定义接口
export interface UserModelType {
    namespace: \'user\';
    state: UserModelState;
    effects: {
        fetch: Effect;
    };
    reducers: {
        saveCurrentUser: Reducer<UserModelState>;
    };
}

const UserModel: UserModelType = {
    //命名空间,component层调用路由方法的时候需要根据命名空间匹配
    namespace: \'user\',

    //存放值的地方
    state: {
        currentUser: {},
    },

    // 与后台交互,处理数据逻辑的地方
    effects: {
        //业务方法,这里的_是不需要参数,如果需要参数,这里改成传递进来的参数对象即可
        //这里支持call,put,select等几种方法,用call调用service层的方法
        //const m = yield select((state) => state.test.num) //select就是用来选择上面state里的,这里没用上
        *fetch(_, { call, put }) {
            //queryUsers是引入service层那个function的一个名字
            const response = yield call(queryUsers);
            yield put({
                // 这就是reducer中save方法, put就是用来触发reducer的方法,payload里就是传过去的参数。 同时它也能触发同等级effects中其他方法。
                type: \'saveCurrentUser\',
                payload: response,
            });
        },
    },

    // 能改变界面的action应该放这里,这里按官方意思不应该做数据处理,只是用来return state 从而改变界面
    reducers: {
        //可以理解为一个方法
        saveCurrentUser(state, action) {
            //return新的state,这样页面就会更新 es6语法,就是把state全部展开,然后把currentUser重新赋值
            return {
                ...state,
                currentUser: action.payload || {},
            };
        },
    },
};

export default UserModel;

        component组件中调用model层,在某一个方法里用dispatch即可调用,这里注意一下type请求的方法路由即可,前面是命名空间,后面是方法名

clickHandler = () => {
    dispatch({
        type: "user/fetch",// 这里就会触发models层里面effects中fetch方法(也可以直接触发reducer中方法,看具体情况) ,user是命名空间名字
        payload: {

        },
    })
}

第三章、修改代码使用后台传生成的菜单数据

        首先需要在组件接口中定义字段,类型为model中定义的类型,因为要获取model中字段这个值,肯定是同一种类型的,修改BasicLayout.tsx文件

        然后在组件的实现中定义变量,从props中获取值

        在组件代码下方的connect方法中将变量赋值,只有这里赋值了,组件中的常量才可以从props中获取到state中的值

        这里connect方法的参数其实就是各个model中的state,dispatch后,这里便可以获取到修改后的值tips:因为使用的是typescript,所以这里的参数需要在ConnectState中先定义,文件名connect.d.ts,在src->models文件夹下: 

 

        引入import { MenuModelState } from \'@/.umi/plugin-dva/connect\';

        在src/services文件夹下新增文件 menu.ts

import request from \'@/utils/request\';
import routesTest from \'../../config/routesTest\'
export async function queryTest(): Promise<any> { 
  return routesTest;
}

        先使用routes.ts中的数据模拟服务器返回的数据,在config目录下创建routesTest.ts文件,内容如下

export default [
          {
            path: \'/\',
            redirect: \'/list\',
          },
          {
            name: \'offline-statistic\',
            path: \'/list\',
            component: \'./Statistic\',
          },
          {
            name: \'host\',
            path: \'/host\',
            component: \'./Hosts\',
          }
        ]

        在src/models文件夹下新增文件 menu.ts

         代码如下

import { Effect, Reducer } from \'umi\';
import { MenuDataItem } from \'@ant-design/pro-layout\';
import { query } from \'@/services/menu\';

export interface MenuModelState {
    menuData: MenuDataItem[];
}

export interface MenuModelType {
    namespace: \'menu\';
    state: MenuModelState;
    effects: {
        getMenuData: Effect;
    };
    reducers: {
        saveMenuData: Reducer<MenuModelState>;
    };
}

//这里做了个转换,可能服务端返回的接口格式和前端的路由格式并不一致,可以在这个方法里进行调整,这里的方法仅作参考,根据自己实际情况进行调整即可
const menuFormatter = (response: any) => {
    if (response === null)
        return [];

    var re = response.map((item: { name: string; route: string; children: any; }) => {
        const result = {
            children: {},
            name: item.name,
            path: item.route === null ? \'/\' : item.route,
        };

        if (item.children) {
            result.children = menuFormatter(item.children);
        }

        return result;
    })

    return re;
}

const MenuModel: MenuModelType = {
    namespace: \'menu\',
    state: {
        menuData: [],
    },

    effects: {
        *getMenuData(_, { put, call }) {
            const response = yield call(query);
            yield put({
                type: \'saveMenuData\',
                payload: menuFormatter(response.data.viewMenu),
            });
        },
    },

    reducers: {
        saveMenuData(state, action) {
            return {
                ...state,
                menuData: action.payload || [],
            };
        },
    },
};
export default MenuModel;

        antd pro的菜单调整在 src/layouts/BasicLayout.tsx 文件中

        这里menuDataRender 默认指向一个方法,我这里不要原来的方式,我们要调整成自己的数据,改成如下menuDataRender={menuData},如果不行改成menuDataRender={()=>menuData}

        添加初始加载

        保存运行,菜单成功使用routesTest中的数据进行加载,如果图标没有显示请查看我的另一篇文章

        测试过程发现了一个问题,退出再登陆菜单正常显示,刷新后菜单消失了,刷新后菜单不显示,菜单数据还加载了,解决办法就是重新加载菜单或页面后,执行一次history.push(\'/\');因为我发现菜单消失后,点击菜单的头,菜单就会加载出来了,好神奇不是

        修改代码如下

useEffect(() => {
    if (dispatch) {
      dispatch({
        type: \'user/fetchCurrent\',
      });
      dispatch({
        type: \'menu/getMenuData\',
        callback(){
          history.push(\'/\');
        },
      });
    }
  }, []);

    namespace: \'menu\',
    state: {
        menuData: [],
    },

    effects: {
        *getMenuData(input, { put, call }) {
            const {callback} = input;
            const response = yield call(queryTest);
            yield put({                type: \'saveMenuData\',                payload: menuFormatter(response),            });

            callback();
        },
    },

        然后就可以尝试使用后端传过来的数据进行菜单加载了

export async function query(): Promise<any> { 
  return request(\'/api/services/app/UserMenuService/GetUserMenuData\');
}

        完美 

以上是关于Ant Design Pro V4.5 从服务器请求菜单(typescript版)的主要内容,如果未能解决你的问题,请参考以下文章

Ant Design Pro V4.5 从服务器请求菜单图标不显示解决

Ant Design Pro V4.5 从服务器请求数据的坑(typescript版)

Ant Design Pro V4.5 从服务器请求数据的坑(typescript版)#导入MD文档

Ant Design Pro V4.5 从服务器请求菜单不显示,刷新后菜单不显示解决办法(typescript版)

Ant Design Pro V4.5 登录成功后用户名的显示问题(typescript版)

Ant Design Pro V4.5 获取和设置ProFormSelect选项(typescript版)