umi.js学习(九)、antd pro中使用dva数据流

  • 效果展示

在这里插入图片描述

  • 创建模块

在这里插入图片描述

  • 配置路由及配置代理
在config/proxy.ts中

/**
 * 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
 * The agent cannot take effect in the production environment
 * so there is no configuration of the production environment
 * For details, please see
 * https://pro.ant.design/docs/deploy
 */
export default {
  dev: {
    '/api': {
      target: 'http://*****.cn:9036',
      changeOrigin: true,
      pathRewrite: { '^/api': '' },
    },
  },
};

  • 封装网络请求request.ts
/** Request 网络请求工具 更详细的 api 文档: https://github.com/umijs/umi-request */
import { message } from 'antd';
import { extend } from 'umi-request';
import { errorHandler, requestOpt, reLogin } from './requestopt';
import { setToken, getToken } from '@/utils/auth'

/** 配置request请求时的默认参数 */
const request = extend({
  errorHandler, // 默认错误处理
  credentials: 'include', // 默认请求是否带上cookie
});

// request拦截器, 改变url 或 options.
request.interceptors.request.use((url, options) => {
  return requestOpt(url, options)
});

// response拦截器, 处理response
request.interceptors.response.use(async response => {
  const data = await response.clone().json();
  const token = await response.headers.get('token')
  if (token && token != getToken()) {
    setToken(token)
  }
  if (process.env.NODE_ENV === 'development') {
    console.log(data)
  }
  if (data.code !== 200) {
    message.error(data.message);
    if (data.code === 300) {
      reLogin()
    }
    return data.message
  } else {
    return response;
  }
})
export default request

import { message } from 'antd';
import { RequestOptionsInit } from 'umi-request';
import { getToken } from '@/utils/auth'
import { getPageQuery } from '@/utils/utils';
import { history } from 'umi';
import { stringify } from 'querystring';
import { removeToken } from '@/utils/auth'

const codeMessage: { [status: string]: string } = {
    200: '服务器成功返回请求的数据。',
    201: '新建或修改数据成功。',
    202: '一个请求已经进入后台排队(异步任务)。',
    204: '删除数据成功。',
    400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
    401: '用户没有权限(令牌、用户名、密码错误)。',
    403: '用户得到授权,但是访问是被禁止的。',
    404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
    406: '请求的格式不可得。',
    410: '请求的资源被永久删除,且不会再得到的。',
    422: '当创建一个对象时,发生一个验证错误。',
    500: '服务器发生错误,请检查服务器。',
    502: '网关错误。',
    503: '服务不可用,服务器暂时过载或维护。',
    504: '网关超时。',
};

/** 异常处理程序 */
export const errorHandler = (error: { response: Response }): Response => {
    const { response } = error;
    console.log(response)
    if (response && response.status) {
        const errorText = codeMessage[response.status] || response.statusText;
        message.error(errorText);
    } else if (!response) {
        message.error('您的网络发生异常,无法连接服务器');
    }
    return response;
};


/**
 * request拦截器, 改变url 或 options.
 * @returns 
 */
export const requestOpt = (url: string, options: RequestOptionsInit): object => {
    if (process.env.NODE_ENV === 'development'){
        console.log("url==>", url)
        if (options.data) {
            console.log("options.data==>", JSON.stringify(options.data))
        } else if (options.params && Object.keys(options.params).length > 0) {
            console.log("options.params==>", options.params)
        }
    }
    if (process.env.NODE_ENV === 'development' && !options.prefix) {
        url = '/api' + url
    }
    const headers = {
        'token': getToken(),
    };
    return {
        url: url,
        options: { ...options, headers },
    };
}

export const reLogin = () => {
    const { redirect } = getPageQuery();
    // Note: There may be security issues, please note
    if (window.location.pathname !== '/user/login' && !redirect) {
        removeToken()
        history.replace({
            pathname: '/user/login',
            search: stringify({
                redirect: window.location.href,
            }),
        });
    }
}

  • 网络请求接口(service.ts中)
import request from '@/utils/request';
import type { ListParamsType } from '@/services/data'
import type { AccountInfoType } from './data'
/**
 * 列表数据
 * @param params 
 * @returns 
 */
export async function getUserList(params: ListParamsType): Promise<any> {
    return request('/Admin/UserList', {
        method: 'POST',
        data: { ...params },
        requestType: 'form'
    });
}

/**
 * 獲取账户信息
 * @param params 
 * @returns 
 */
export async function getUserInfo(params: { id: string }): Promise<any> {
    return request('/Admin/UserAdd', {
        method: 'GET',
        params: { ...params },
        requestType: 'form'
    });
}
/**
 * 用户添加
 * @param params 
 * @returns 
 */
export async function userAdd(params: AccountInfoType): Promise<any> {
    return request('/Admin/UserAdd', {
        method: 'POST',
        data: { ...params },
        requestType: 'form'
    });
}
/**
 * 冻结
 * @param params 
 * @returns 
 */
export async function userFrozen(params: { id: string }): Promise<any> {
    return request('/Admin/UserFroze', {
        method: 'POST',
        data: { ...params },
        requestType: 'form'
    });
}

  • 取公用数据类型(data.d.ts中)

/**
 * 封装后台返回的数据
 */
export type SingleUserListType = {
    id: number,
    level?: number,
    account?: string,
    password?: string,
    contact_name?: string,
    contact_mobile?: string,
    remark?: string,
    role_id?: number,
    is_enable?: number,
    ctime?: string,
    uptime?: string,
    role_name?: string,
    ctime_str: string,
}

/**
 * 添加编辑账户
 */
export type UserAddType = {
    user_id?: number,
    account?: string,
    account_password?: string,
    contact_name?: string,
    contact_mobile?: number,
    role_id?: number,
};


/**
 * 獲取账户信息
 */
export type AccountInfoType = {
    user_id?: number,
    account?: string,
    password?: string,
    contact_name?: string,
    contact_mobile?: number,
    role_id?: number,
    id?: number
};

/**
 * 獲取账户信息列表
 */
export type AccountRoleListType = {
    ctime?: number,
    ctime_str?: string,
    id?: number,
    is_enable?: number,
    menu_ids?: string,
    role_name?: string,
    uptime?: number,
    value?:number,
    label?:string,
    disabled?:boolean
};
  • model.ts中dva数据获取
import { Effect, Reducer } from 'umi';

//导入service远端数据请求
import { getUserList, getUserInfo, userAdd, userFrozen } from './service'

import { SingleUserListType, AccountInfoType, AccountRoleListType } from './data'
import { message } from 'antd';


export type AccountListState = {
    rows: SingleUserListType[];
    total: number;
    info: AccountInfoType;
    role_list: AccountRoleListType[]
}

interface AccountListModelType {
    namespace: string
    state: AccountListState;//封装后台返回的数据
    effects: {
        getRemoteUserList: Effect;
        getRemoteUserInfoData: Effect;
        postRemoteUserAdd: Effect;
        postRemoteUserFrozen: Effect;
    };
    reducers: {
        getUserList: Reducer<AccountListState>,
        getUserInfoData: Reducer<AccountListState>,
    };
}

const AccountListModel: AccountListModelType = {
    namespace: 'accountListData',
    state: {
        rows: [],
        total: 0,
        info: {},
        role_list: []
    },
    effects: {
        *getRemoteUserList({ payload }, { call, put }) {
            //从service中获取数据(service主要用于接口请求)
            const response = yield call(getUserList, { ...payload })
            if (response && response instanceof Object) {
                yield put({
                    type: 'getUserList',//这个是将数据给reducers中哪个方法
                    payload: response.data  //注意这里传递一个数组会出问题,原因待定
                })
            }
        },
        //獲取账户信息
        *getRemoteUserInfoData({ payload }, { call, put }) {
            const response = yield call(getUserInfo, { ...payload })
            if (response && response instanceof Object) {
                let { role_list } = response.data
                role_list.forEach((element: AccountRoleListType) => {
                    element.value = element.id,
                        element.label = element.role_name,
                        element.disabled = element.is_enable == 0
                })
                response.data.role_list = role_list
                response.data.info = { ...response.data.info, role_id: (response.data.info.role_id == 0 ? '' : response.data.info.role_id) }
                yield put({
                    type: 'getUserInfoData',
                    payload: response.data
                })
            }
        },
        //用户添加
        *postRemoteUserAdd({ payload }, { call, put }) {
            const response = yield call(userAdd, { ...payload })
            if (response && response instanceof Object) {
                message.success(response.message)
            }
        },
        //冻结
        *postRemoteUserFrozen({ payload }, { call, put }) {
            const response = yield call(userFrozen, { ...payload })
            if (response && response instanceof Object) {
                message.success(response.message)
            }
        }
    },
    //同步
    reducers: {
        getUserList(state, action) {
            return {
                ...state,
                ...action.payload,
            };
        },
        getUserInfoData(state, action) {
            return {
                ...state,
                ...action.payload,
            };
        }
    },
}

export default AccountListModel;
  • 首页index.tsx展示数据
import React, { useRef, FC } from 'react';
import type { ProColumns, ActionType } from '@ant-design/pro-table';
import { PageContainer, WaterMark } from '@ant-design/pro-layout';
import { Card, Space } from 'antd';
import { connect, Dispatch, AccountListState } from 'umi';
import type { ListParamsType } from '@/services/data'
import { SingleUserListType, UserAddType } from './data'
import ProTable from '@ant-design/pro-table'
import './index.less'
import AddAccountModal from './components/addaccountmodal'
/**
 * 声明下方 props 类型
 */
type accountListPageProps = {
  listData: AccountListState,
  dispatch: Dispatch
}

const AccountListPage: FC<accountListPageProps> = (props) => {

  //获取从model中来的数据    
  const { listData, dispatch } = props
  //配置ProTable
  const ref = useRef<ActionType>();

  /**
    * ProTable的网络请求 request
    */
  const requestHandler = async (params: ListParamsType) => {
    await dispatch({
      type: 'accountListData/getRemoteUserList',
      payload: params = { ...params }
    })
    return {}
  }

  //提交数据
  const onCreateFinish = async (values: UserAddType) => {
    await dispatch({
      type: 'accountListData/postRemoteUserAdd',
      payload: { ...values }
    })
    ref.current?.reload();
    return true
  }
  /**
    * 启用或停用
    * @param id 
    */
  const onAccountTypeClick = async (id: number) => {
    await dispatch({
      type: 'accountListData/postRemoteUserFrozen',
      payload: { id: id }
    })
    ref.current?.reload();
  }

  const columns: ProColumns<SingleUserListType>[] = [
    {
      title: 'ID',
      dataIndex: 'id',
      width: 128,
      search: false,
      align: 'left'
    },
    {
      title: '账号名称',
      dataIndex: 'account',
      align: 'left',
    },
    {
      title: '状态',
      dataIndex: 'is_enable',
      search: false,
      align: 'left',
      render: (text) => <span>{text == 1 ? `启用` : `停用`}</span>,
    },
    {
      title: '联系人姓名',
      dataIndex: 'contact_name',
      hideInTable: true,
    },
    {
      title: '创建时间',
      dataIndex: 'ctime_str',
      search: false,
      align: 'left'
    },
    {
      title: '操作',
      align: 'center',
      render: (text, record) => (
        <Space size="middle">
          <AddAccountModal
            userId={record.id}
            itemInfo={listData.info}
            roleList={listData.role_list}
            dispatch={dispatch}
            showType={1}
            onCreateFinish={onCreateFinish} />
          {
            record.is_enable == 0 ? <a className="start-using" onClick={() => onAccountTypeClick(record.id)}>启用</a> :
              <a className="stop-using" onClick={() => onAccountTypeClick(record.id)}>停用</a>
          }
        </Space>
      ),
    },
  ];
  return (
    <PageContainer title="账号列表">
      <Card>
        <WaterMark content="地环院内部管理系统">
          <ProTable
            actionRef={ref}
            request={requestHandler}
            columns={columns}
            dataSource={listData.rows}
            rowKey="id"
            search={{
              labelWidth: 'auto',
            }}
            pagination={{
              showQuickJumper: true,
              pageSize: 10,
              total: listData.total
            }}
            form={{
              span: 6
            }}
            toolBarRender={() => [
              <AddAccountModal
                userId={0}
                itemInfo={listData.info}
                roleList={listData.role_list}
                dispatch={dispatch}
                showType={0}
                onCreateFinish={onCreateFinish} />
            ]}
          />
        </WaterMark>
      </Card>
    </PageContainer>
  )
}

const mapStateToProps = ({ accountListData }: { accountListData: AccountListState }) => {
  return {
    listData: accountListData,//这里的usersData就是model中的namespace
  }
}
export default connect(mapStateToProps)(AccountListPage)
  • 弹框 addaccountmodal.tsx
import React, { useEffect, FC } from 'react'
import { Button, Form } from 'antd';
import {
    ModalForm,
    ProFormText,
    ProFormSelect,
} from '@ant-design/pro-form';
import { PlusOutlined } from '@ant-design/icons';
import type { UserAddType } from '../data'
import { Dispatch } from 'umi';

type AddAccountModalProps = {
    showType: number
    onCreateFinish: (values: UserAddType) => void
    dispatch: Dispatch
    userId: number
    itemInfo: any
    roleList: any
}

const AddAccountModal: FC<AddAccountModalProps> = (props) => {

    const { showType, onCreateFinish, dispatch, userId, itemInfo, roleList } = props

    useEffect(() => {
        form.setFieldsValue({
            ...itemInfo,
        })
    }, [itemInfo])

    const [form] = Form.useForm();
    const layout = {
        labelCol: { span: 5 },
    };

    //ModalForm状态改变
    const onVisibleChange = async (value: boolean) => {
        if (value) {
            await dispatch({
                type: 'accountListData/getRemoteUserInfoData',
                payload: { id: userId }
            })
        }
    }

    return (
        <ModalForm<{
            account: string;
            account_password: string;
            contact_name: string;
            contact_mobile: number;
            role_id: number;
            user_id: number
        }>
            width="35%"
            {...layout}
            form={form}
            title={showType === 0 ? `添加账户` : `编辑账户`}
            trigger={
                showType === 0 ? (
                    <Button type="primary">
                        <PlusOutlined />
                        添加账户
                    </Button>
                ) : (
                    <a>编辑</a>
                )
            }
            onFinish={async (values) => {
                return onCreateFinish(values = { ...values, user_id: userId });
            }}
            onVisibleChange={onVisibleChange}
        >
            <ProFormText name="account"
                label="账户名称:"
                placeholder="请填写账户名称"
                disabled={showType !== 0}
                rules={[{ required: true, message: '您还没填写账户名称' }]}
            />
            <ProFormText name="account_password"
                label="账户密码:"
                placeholder={userId == 0 ? `请填写账户密码` : `修改则填写新密码,不修改则不填`}
                rules={[{
                    required: true, validator: (rule, value = '', callback) => {
                        if (Object.keys(value).length == 0) {
                            if (userId) {
                                return Promise.resolve()
                            } else {
                                return Promise.reject('您还没填写账户密码');
                            }
                        } else if (!new RegExp(/^(?!^(\d+|[a-zA-Z]+|[~!@#$%^&*?]+)$)^[\w~!@#$%^&*?]{6,20}$/).test(value)) {
                            return Promise.reject('必须含有数字、字母、特殊符号三项中间的两项,6到20位')
                        } else {
                            return Promise.resolve()
                        }
                    }
                }]}
            />
            <ProFormText name="contact_name"
                label="姓名:"
                placeholder="请填写姓名"
                rules={[{ required: true, message: '您还没填写姓名' }]}
            />
            <ProFormText name="contact_mobile"
                label="手机号:"
                placeholder="请填写手机号"
                rules={[{ required: true, message: '您还没填写手机号' },
                {
                    pattern: /^((13[0-9])|(14[5,7])|(15[0-3,5-9])|166|(17[0,3,5-8])|(18[0-9])|19[1,8,9]|(147))([0-9]{8})+$/,
                    message: '手机号格式不正确',
                },]}
            />
            <ProFormSelect
                options={roleList}
                width="md"
                name="role_id"
                label="绑定角色:"
                placeholder="请选择绑定角色"
                rules={[{ required: true, message: '您还没选择绑定角色' }]}
            />
        </ModalForm>
    );
};

export default AddAccountModal;
相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页