umi.js学习(七)、简单列表和数据编辑(使用antd)

本文介绍了一个基于Umi.js搭建的用户管理系统案例,包括路由配置、API请求封装、数据仓库实现等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 效果展示

在这里插入图片描述

  • 创建user模块

在这里插入图片描述

  • 配置路由及配置代理
.umire.ts中


import { defineConfig } from 'umi';

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  routes: [
    { path: '/', component: '@/pages/users/index' },
  ],
  fastRefresh: {},
  proxy: {
    '/api': {
      'target': 'https://www.fastmock.site/mock/fed13828390069db6c28fe3f2da98a19/demo1/',
      // 'target': 'https://public-api-v1.aspirantzhang.com/',
      'changeOrigin': true,
      'pathRewrite': { '^/api': '' },
    },
  },
});
  • 封装网络请求request.ts
/** Request 网络请求工具 更详细的 api 文档: https://github.com/umijs/umi-request */
import { extend } from 'umi-request';
import { message } from 'antd';

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

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

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

// request拦截器, 改变url 或 options.
request.interceptors.request.use((url, options) => {
  console.log(url)
  if (options.data) {
    console.log(JSON.stringify(options.data))
  } else if (options.params && Object.keys(options.params).length > 0) {
    console.log(options.params)
  }
  return {
    url: url,
    options: options,
  };
});

// response拦截器, 处理response
request.interceptors.response.use(async response => {
  const data = await response.clone().json();
  if (data.code !== 200) {
    message.error(data.message);
    return data.message
  } else {
    return response;
  }
});
export default request;
  • 网络请求接口(service.ts中)

import { request as request1 } from 'umi';
import { message } from 'antd';

import request from '@/utils/request';

import { FormValues } from './data'

export async function getRemoteList1() {
    return request1('/api/users', {
        method: 'get'
    })
        .then(response => {
            return response
        })
        .catch(error => {
            console.log(error);
        });
}

export async function editRecord1({ id, value }: any) {
    return request1(`/api/edit`, {
        method: 'post',
        data: value
    })
        .then(response => {
            if (response.code != 200) {
                message.error(response.message);
            }
            return response
        })
        .catch(error => {
            message.error(error);
        });
}

// -----------------------------------使用封装后的request---------------------------------------------

type EditRecordParamsType = {
    id: number |undefined;
    value: FormValues;
};


export async function getRemoteList(): Promise<any> {
    return request('/api/users');
}

export async function editRecord(params: EditRecordParamsType): Promise<any> {
    return request('/api/edit', {
        method: 'POST',
        data: params,
    });
}

  • 提取公用数据类型(data.d.ts中)
/**
 * 这个文件是放 公共的 类型接口的
 */

/**
 * 封装后台返回的数据
 */
export type SingleUserType ={
    id: number,
    name?: string,
    email?: string,
    create_time?: string,
    update_time?: string,
    status: number
}

/**
 * Modal 框的确定按钮的类型
 */
export type FormValues ={
    [name: string]: any
}
  • 数据仓库(dva)model.ts中请求并处理数据
import { Effect, Reducer, Subscription } from 'umi';

//导入service远端数据请求
import { getRemoteList } from './service'

import { SingleUserType } from './data'

/**
 * 封装后台返回的数据
 */
export type UserState = {
    data: SingleUserType[],
    meta: {
        total: number,
        per_page: number,
        page: number
    }

}

interface UserModelType {
    namespace: string;//这里的命名空间就是展示页面得到数据的键
    state: UserState;//封装后台返回的数据
    effects: {
        getRemote: Effect;
    };
    reducers: {
        getList: Reducer<UserState>,
    };
    subscriptions: { setup: Subscription };
}

const UserModel: UserModelType = {
    namespace: 'usersData',//这里的命名空间就是展示页面得到数据的键

    state: {
        data: [],
        meta: {
            total: 0,
            per_page: 10,
            page: 1
        }
    },
    //异步
    effects: {
        //获取列表数据
        *getRemote({ payload }, { call, put }) {
            //从service中获取数据(service主要用于接口请求)
            const data = yield call(getRemoteList)
            if (data && data instanceof Object) {
                yield put({
                    type: 'getList',//这个是将数据给reducers中哪个方法
                    payload: data  //注意这里传递一个数组会出问题,原因待定
                })
            }
        },
    },
    //同步
    reducers: {
        getList(state, action) {
            return {
                ...state,
                ...action.payload,
            };
        },
    },
    //订阅
    subscriptions: {
        setup({ dispatch, history }) {
            return history.listen(({ pathname }) => {
                if (pathname === '/') {//当前页面的路径
                    dispatch({
                        type: 'getRemote',//调用effects中的方法
                    })
                }
            });
        }
    }
};

export default UserModel;
  • 首页index.tsx展示数据
import React, { useState, FC } from 'react';

//UserState, SingleUserType 需要在model中导出
import { connect, Dispatch, Loading, UserState } from 'umi';
import { Table, Space, message } from 'antd';
import UserModal from './components/UserModal'
import { SingleUserType, FormValues } from './data'

import { editRecord } from './service'


/**
 * 声明下方 props 类型
 * const { users, dispatch, listLoading } = props
 */
type userPageProps = {
    users: UserState,
    dispatch: Dispatch,
    listLoading: boolean
}

const UserListPage: FC<userPageProps> = (props) => {
    //控制modal弹框
    const [visible, setVisible] = useState(false);
    const [confirmLoading, setConfirmLoading] = useState(false);
    const [record, setRecord] = useState<SingleUserType | undefined>(undefined);

    //获取从model中来的数据    
    const { users, dispatch, listLoading } = props

    //编辑
    const onClickEdit = (record: SingleUserType) => {
        setVisible(true)
        setRecord(record)
    };
    /**
     * 表单提交
     */
    const onFinish = async (value: FormValues) => {
        setConfirmLoading(true);
        const id = record?.id
        const result = await editRecord({ id, value })
        if (result && result instanceof Object) {
            message.success(result.data);
            setVisible(false);
            dispatch({
                type: 'usersData/getRemote'
            })
        }
        setConfirmLoading(false);
    };

    //关闭弹窗
    const handleCancel = () => {
        setVisible(false);
    };
    const columns = [
        {
            title: 'ID',
            dataIndex: 'id',
        },
        {
            title: 'Name',
            dataIndex: 'name',
            render: (text: string) => <a>{text}</a>,
        },
        {
            title: 'CreateTime',
            dataIndex: 'create_time',
        },
        {
            title: 'Action',
            render: (text: string, record: SingleUserType) => (
                <Space size="middle">
                    <a onClick={() => onClickEdit(record)}>编辑</a>
                    <a>删除</a>
                </Space>
            ),
        },
    ];


    return (
        <div className="list-table">
            <Table columns={columns} dataSource={users.data} rowKey={columns => columns.id} loading={listLoading} />
            <UserModal visible={visible} confirmLoading={confirmLoading}
                onFinish={onFinish} //关联子组件方法
                handleCancel={handleCancel}
                record={record} />
        </div>
    )
}
/**
 * 这是从model中获取数据
 * @param param0 
 * @returns 
 */
const mapStateToProps = ({ usersData, loading }: { usersData: UserState, loading: Loading }) => {
    return {
        users: usersData,//这里的usersData就是model中的namespace
        listLoading: loading.models.usersData
    }
}
/**
 *  mapStateToProps 简写
 */
/*
const mapStateToProps = ({ users }) => ({
    users
})
*/

export default connect(mapStateToProps)(UserListPage)

//最终简写
// export default connect(({ users }) => ({
//     users
// }))(UserListPage)
  • 弹窗UserModal.tsx
import React, { useEffect, FC } from 'react'
import { Modal, Form, Input } from 'antd';

import { SingleUserType, FormValues } from '../data'
/**
 * 声明下方 props 类型
 * const { visible, confirmLoading, record, handleCancel, onFinish } = props
 */
type userModalProps = {
    visible: boolean,
    confirmLoading: boolean,
    record: SingleUserType | undefined,
    handleCancel: () => void,
    onFinish: (values: FormValues) => void
}

const UserModal: FC<userModalProps> = (props) => {

    const { visible, confirmLoading, record, handleCancel, onFinish } = props
    const [form] = Form.useForm();

    //相当于componentDidMount
    useEffect(() => {
        form.setFieldsValue(record);
    }, [visible])

    //点击弹窗
    const handleOk = () => {
        form.submit()
    };

    /**
     * 表单失败
     */
    const onFinishFailed = (err: any) => {
        console.log(err)
    };
    return (
        <div>
            <Modal
                title="Title"
                forceRender
                visible={visible}
                okText='确定'
                cancelText="取消"
                onOk={handleOk}//传递父组件处理
                confirmLoading={confirmLoading}
                onCancel={handleCancel}//传递父组件处理
            >
                <Form
                    form={form}
                    name="basic"
                    onFinish={onFinish}
                    onFinishFailed={onFinishFailed}
                >
                    <Form.Item
                        label="name"
                        name="name"
                        rules={[{ required: true, message: '请输入name' }]}
                    >
                        <Input />
                    </Form.Item>
                    <Form.Item
                        label="CreateTime"
                        name="create_time"
                        rules={[{ required: true, message: '请输入create_time' }]}
                    >
                        <Input />
                    </Form.Item> <Form.Item
                        label="status"
                        name="status"
                        rules={[{ required: true, message: '请输入status' }]}
                    >
                        <Input />
                    </Form.Item>
                </Form>
            </Modal>
        </div>
    )
}

export default UserModal;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值