antd outofdate 表单_React 折腾记 - (10) UmiJS 2.x + antd 重写后台管理系统记录的问题及解决姿势...

本文记录了使用UmiJS 2.x和Antd 3.11.x重构后台管理系统时遇到的问题及解决方法,包括路由管理、dva封装、日期处理、组件封装、性能优化等方面。通过示例展示了如何处理moment日期格式、Antd日期组件的使用,以及使用React.Children和React.cloneElement改造组件。还提到了umi的鉴权、model规划、反向代理配置、代码预加载等实践。
摘要由CSDN通过智能技术生成

用的是 umi 2.x ,写起来挺舒服;顺带完善了上一版本后台的一些细节问题,功能等

umijs 类似 create-react-app , 也是一套方案的集合体,亮点很多.可以具体官网去看

声明式的路由( nuxtjs 既视感)

dva(基于redux+redux-saga的封装方案) :写起来有 vuex 的感觉;

主要记录我在过程中遇到的问题及解决的姿势,技术栈 antd 3.11.x + umi 2.x + react 16.7

问题汇总及解决姿势

moment的一些用法及antd 日期组件的细节

关于moment

为什么说另类..就是原生 日期API 结合 moment ,因为我们接口需要传递时间戳,而是不带毫秒级的;

而且时间必须为当天的凌晨 00:00:00 开始,结束时间到操作的此刻(直接 new Date().getTime() 就是此刻);

// 会直接返回你设置时间的时间戳

new Date().setHours(0, 0, 0, 0)

// 凌晨`00:00:00`

moment(new Date().setHours(0, 0, 0, 0))

// 近七天

moment(new Date().setHours(0, 0, 0, 0) - 7 * 24 * 3600000)

// 月初

moment().startOf('month')

复制代码

转成 unix stamp(服务器常用的时间戳规格) ,调用 moment().unix() 即可;

若是不控制到凌晨 00:00:00 这种,

日期可以直接用 moment 的 add 方法往后推导, subtract 往前推导,支持日/周/月/年

antd 的日期组件

置空用 null 是允许的,其他的话需要转成 moment 对象,控件获取的值默认就是 moment 对象

props.children 的改造,添加样式亦或者事件!

在封装一些组件的过程,我用了 React.Fragment(<>>: 简写) 来保证组件同级并列

有些必须需要 props.children 带上一些属性或者样式来保证我想要的效果.

一开始无解, 因为 Fragement简写的姿势 没法 props ,那也就是说没做写成高阶;

找了下官方文档,发现有这么两个 API :

React.Children : 提供了几个遍历子元素( React Element )的方法,与常规数组用法类似,只是参数不一样

React.cloneElement: 如名字所示,克隆子元素

这是上篇文章用到的部分内容,需要改造传递进来的按钮,给添加样式

// 构建

// 克隆子组件并且添加自己要添加的特性

const PropsBtn = React.Children.map(this.props.children, child =>

React.cloneElement(child, {

style: {

marginLeft: 8,

},

})

);

// 渲染

{PropsBtn ? <>{PropsBtn}> : null}

复制代码

用 memoize-one 来改善性能

可以缓存同样参数的结果集,非常适用于递归这类的函数处理,大大减少计算的压力;

也能用于 React 这类,是否有必要重新 setState , 第二个参数支持比较,官方推荐用 lodash 去深度比较

返回一个递归包裹的组件

最简单粗暴的方法就是用变量缓存,然后直接返回组件,比如我这边文章就用了;

umi 约定式基础鉴权

在 layouts 里面分别写对应的布局,然后由一个鉴权组件去判定是否允许进入,比如

/src/layout/index.js

import React from 'react';

import withRouter from 'umi/withRouter';

// 鉴权组件, 我写了webpack alias

import Authorized from 'components/Authorized';

// 布局组件

import EnranceLayout from './EntranceLayout';

import AdminLayout from './AdminLayout';

// 中文地区时间转换引入

import moment from 'moment';

import 'moment/locale/zh-cn';

// 路由动效

import { TransitionGroup, CSSTransition } from 'react-transition-group';

// 页面标题

import { Helmet } from 'react-helmet';

import { getDocumentTitle } from 'components/Sidebar/RouterTree';

moment.locale('zh-cn');

export default withRouter(props => {

const {

location: { pathname },

location,

} = props;

// 根据路由寻址,再结合鉴权来判定是否允许进入,根据您自身的业务进行调整

if (pathname.indexOf('/entrance') === -1) {

if (pathname.indexOf('/editor') !== -1) {

return (

{getDocumentTitle(pathname)}

{props.children}

);

}

return (

{getDocumentTitle(pathname)}

{props.children}

);

}

return (

{props.children}

);

});

复制代码

model的规划

全局的放在 src/models 目录,其他的 page 级别推荐直接 model.js ,官方说会自下往上寻找;

是根据 namespace 来区分的..不允许存在同名的 namespace ;

若是要开启 umi 的 model 动态引入, page 级别不允许调用其他 page 的 model ,不然会报错,初始化找不到的!!!

所以全局性放在全局更为合适,当然你不需要动态引入的话,页面间跨调是允许的..我目前是这么做;

pages 目录下的文件或者目录不自动生成对应可访问的 page

默认在 page 目录下,除了部分特殊的文件(比如官方自己过滤的 models ),都会自动产生可访问的页面,

也就是说文件会被当做路由组件;

屏蔽的话, 打开项目的配置文件 .umirc.js

const path = require('path');

// ref: https://umijs.org/config/

export default {

plugins: [

// ref: https://umijs.org/plugin/umi-plugin-react.html

[

'umi-plugin-react',

{

antd: true, // 默认引入antd

dva: { // 启用引入dva

immer: true,

dynamicImport: false, // models 动态引入关闭

hmr: true,

},

dynamicImport: false, // 组件切割动态引入

title: '声兮后台管理系统',

dll: true,

routes: { // 此处用正则忽略不想产生路径的文件或者目录!!!

exclude: [/model\.js/, /models\//, /services(\/|\.js)?/, /components\//],

},

hardSource: true,

locale: {},

},

],

],

};

复制代码

umi配置开发的反向代理及目录的alias

const path = require('path');

// ref: https://umijs.org/config/

export default {

plugins: [

alias: {

'@': path.resolve(__dirname, './src'),

models: path.resolve(__dirname, './src/models'),

components: path.resolve(__dirname, './src/components'),

utils: path.resolve(__dirname, './src/utils'),

services: path.resolve(__dirname, './src/services'),

assets: path.resolve(__dirname, './src/assets'),

},

proxy: {

'/api/web': {

target: 'http://stagapi.xxxx.com',

changeOrigin: true,

secure: false,

// pathRewrite: { '^/api': '/' },

},

},

};

复制代码

如何在umi这种加入 preloading

就是react代码没加载之前,显示的区域块,

目前的做法就是自定义模板文件,放在react渲染块内部,在解析代码渲染完毕会被替换掉

效果如下

src/pages/document.ejs

xx管理后台

.preloadLoading{

position:fixed;

left:0;

top:0;

width:100%;

height:100%;

display:flex;

justify-content:center;

align-items:center;

}

@-webkit-keyframes square-animation {

0% {

left: 0;

top: 0;

}

10.5% {

left: 0;

top: 0;

}

12.5% {

left: 32px;

top: 0;

}

23% {

left: 32px;

top: 0;

}

25% {

left: 64px;

top: 0;

}

35.5% {

left: 64px;

top: 0;

}

37.5% {

left: 64px;

top: 32px;

}

48% {

left: 64px;

top: 32px;

}

50% {

left: 32px;

top: 32px;

}

60.5% {

left: 32px;

top: 32px;

}

62.5% {

left: 32px;

top: 64px;

}

73% {

left: 32px;

top: 64px;

}

75% {

left: 0;

top: 64px;

}

85.5% {

left: 0;

top: 64px;

}

87.5% {

left: 0;

top: 32px;

}

98% {

left: 0;

top: 32px;

}

100% {

left: 0;

top: 0;

}

}

@keyframes square-animation {

0% {

left: 0;

top: 0;

}

10.5% {

left: 0;

top: 0;

}

12.5% {

left: 32px;

top: 0;

}

23% {

left: 32px;

top: 0;

}

25% {

left: 64px;

top: 0;

}

35.5% {

left: 64px;

top: 0;

}

37.5% {

left: 64px;

top: 32px;

}

48% {

left: 64px;

top: 32px;

}

50% {

left: 32px;

top: 32px;

}

60.5% {

left: 32px;

top: 32px;

}

62.5% {

left: 32px;

top: 64px;

}

73% {

left: 32px;

top: 64px;

}

75% {

left: 0;

top: 64px;

}

85.5% {

left: 0;

top: 64px;

}

87.5% {

left: 0;

top: 32px;

}

98% {

left: 0;

top: 32px;

}

100% {

left: 0;

top: 0;

}

}

@-webkit-keyframes hue-rotate {

0% {

-webkit-filter: hue-rotate(0deg);

filter: hue-rotate(0deg);

}

100% {

-webkit-filter: hue-rotate(360deg);

filter: hue-rotate(360deg);

}

}

@keyframes hue-rotate {

0% {

-webkit-filter: hue-rotate(0deg);

filter: hue-rotate(0deg);

}

100% {

-webkit-filter: hue-rotate(360deg);

filter: hue-rotate(360deg);

}

}

.loading {

position: relative;

width: 96px;

height: 96px;

-webkit-transform: rotate(45deg);

transform: rotate(45deg);

-webkit-animation: hue-rotate 10s linear infinite both;

animation: hue-rotate 10s linear infinite both;

}

.loading__square {

position: absolute;

top: 0;

left: 0;

width: 28px;

height: 28px;

margin: 2px;

border-radius: 2px;

background: #07a;

background-image: -webkit-linear-gradient(45deg, #fa0 40%, #0c9 60%);

background-image: linear-gradient(45deg, #fa0 40%, #0c9 60%);

background-image: -moz-linear-gradient(#fa0, #fa0);

background-size: cover;

background-position: center;

background-attachment: fixed;

-webkit-animation: square-animation 10s ease-in-out infinite both;

animation: square-animation 10s ease-in-out infinite both;

}

.loading__square:nth-of-type(0) {

-webkit-animation-delay: 0s;

animation-delay: 0s;

}

.loading__square:nth-of-type(1) {

-webkit-animation-delay: -1.42857s;

animation-delay: -1.42857s;

}

.loading__square:nth-of-type(2) {

-webkit-animation-delay: -2.85714s;

animation-delay: -2.85714s;

}

.loading__square:nth-of-type(3) {

-webkit-animation-delay: -4.28571s;

animation-delay: -4.28571s;

}

.loading__square:nth-of-type(4) {

-webkit-animation-delay: -5.71429s;

animation-delay: -5.71429s;

}

.loading__square:nth-of-type(5) {

-webkit-animation-delay: -7.14286s;

animation-delay: -7.14286s;

}

.loading__square:nth-of-type(6) {

-webkit-animation-delay: -8.57143s;

animation-delay: -8.57143s;

}

.loading__square:nth-of-type(7) {

-webkit-animation-delay: -10s;

animation-delay: -10s;

}

复制代码

标题如何自动随着路由表信息改变

首先得自己维护一份静态路由表,类似 vue 或者 react-router@3 那种,

结合 @withRouter 拿到 pathname 传入到静态路由表遍历

(这里就可以用到上面说的memoize-one来提高性能),

效果如下

姿势如下

用 react-helmet 来实现 title 的替换,这货不仅仅可以替换 title 还能替换 meta 这些

参考上面的问题 ==> umi 约定式基础鉴权 ,这里就有用到

antd 菜单栏随着宽度自适应及风格变化

就是缩小的时候隐藏部分子菜单,这个问题在我做侧边栏变水平的时候遇到.我缩小到 ipad 的尺寸

会溢出,用了常规的法子,就正常了,就是 style 那里设置一个最大宽度或者宽度

至于风格变化是因为 antd 内置了两套风格

style={{ maxWidth: '100%', flex: 1 }}

subMenuOpenDelay={0.3}

theme={theme ? 'dark' : 'light'}

mode={mode ? 'horizontal' : 'inline'}

openKeys={openKeys}

selectedKeys={selectedKeys}

onOpenChange={this.onOpenChange}

>

复制代码

当然 Logo 组件这些肯定是你自己拿了状态去变化的,还有包裹的父级区域的样式

目前不做配置保存,想做保存的,写在 localStorage 不失为一个好法子,没必要写到数据库,都是自己人用

效果如下

项目没有用到 antd pro 这个模板(太臃肿),自己写比较实在

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值