Ant Design of React

来自ling
跳转至: 导航搜索

常用代码

{JSON.stringify(values)}

常用链接

https://umijs.org/zh-CN/config#chunks

https://zh-hans.reactjs.org/docs/getting-started.html

https://ant.design/docs/react/introduce-cn

https://umijs.org/zh/

https://pro.ant.design/docs/getting-started-cn

umi-request

https://blog.csdn.net/weixin_41753520/article/details/98317567

https://antv-g6.gitee.io/zh/docs/manual/introduction

https://graphin.antv.vision/zh/docs/manual/getting-started/

https://www.yuque.com/antv/g6

节点编辑和tooltip

https://wiki.ling2.cn/index.php/Segmenter_user.dict

http://cloud.ling2.cn/#/filemanager/book?fileId=76ac6aa2-ec7f-4357-84fe-d291641ce12d&businessType=BOOK

各大 Form 大揭秘 -- antd Form

G6

https://graphin.antv.vision/zh/docs/manual/getting-started

内置的点

内置的边

Event

圣诞推文可视化

useEffect,useState

https://blog.csdn.net/gtLBTNq9mr3/article/details/100059135

// 声明一个叫 “count” 的 state 变量

 const [count, setCount] = useState(0);

既然我们知道了 useState 的作用,那么掌握 useEffect 就更容易,函数组件中没有生命周期,那么可以使用 useEffect 来替代。如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

demo

import React from 'react';
import { Spin } from 'antd';
import isEqual from 'lodash/isEqual';
import { isComponentClass } from './Secured';
// eslint-disable-next-line import/no-cycle

interface PromiseRenderProps<T, K> {
  ok: T;
  error: K;
  promise: Promise<boolean>;
}

interface PromiseRenderState {
  component: React.ComponentClass | React.FunctionComponent;
}

export default class PromiseRender<T, K> extends React.Component<
  PromiseRenderProps<T, K>,
  PromiseRenderState
> {
  state: PromiseRenderState = {
    component: () => null,
  };

  componentDidMount() {
    this.setRenderComponent(this.props);
  }

  shouldComponentUpdate = (nextProps: PromiseRenderProps<T, K>, nextState: PromiseRenderState) => {
    const { component } = this.state;
    if (!isEqual(nextProps, this.props)) {
      this.setRenderComponent(nextProps);
    }
    if (nextState.component !== component) return true;
    return false;
  };

  // set render Component : ok or error
  setRenderComponent(props: PromiseRenderProps<T, K>) {
    const ok = this.checkIsInstantiation(props.ok);
    const error = this.checkIsInstantiation(props.error);
    props.promise
      .then(() => {
        this.setState({
          component: ok,
        });
        return true;
      })
      .catch(() => {
        this.setState({
          component: error,
        });
      });
  }

  // Determine whether the incoming component has been instantiated
  // AuthorizedRoute is already instantiated
  // Authorized  render is already instantiated, children is no instantiated
  // Secured is not instantiated
  checkIsInstantiation = (
    target: React.ReactNode | React.ComponentClass,
  ): React.FunctionComponent => {
    if (isComponentClass(target)) {
      const Target = target as React.ComponentClass;
      return (props: any) => <Target {...props} />;
    }
    if (React.isValidElement(target)) {
      return (props: any) => React.cloneElement(target, props);
    }
    return () => target as React.ReactNode & null;
  };

  render() {
    const { component: Component } = this.state;
    const { ok, error, promise, ...rest } = this.props;

    return Component ? (
      <Component {...rest} />
    ) : (
      <div
        style={{
          width: '100%',
          height: '100%',
          margin: 'auto',
          paddingTop: 50,
          textAlign: 'center',
        }}
      >
        <Spin size="large" />
      </div>
    );
  }
}

常用命令

npm start#

运行这个脚本会启动服务,自动打开默认浏览器展示你的页面。当你重新编辑代码后,页面还会自动刷新。

npm run build#

运行这个脚本将会编译你的项目,你可以在项目中的 dist 目录中找到编译后的文件用于部署。

编译之后的文件经过压缩。如果你想知道更详细的信息可以查阅 编译。

如果你需要部署,可以查阅 部署。

image

npm run analyze#

analyze 脚本做的事情与 build 的相同,但是他会打开一个页面来展示你的依赖信息。如果需要优化性能和包大小,你需要它。

npm run lint#

我们提供了一系列的 lint 脚本,包括 TypeScript,less,css,md 文件。你可以通过这个脚本来查看你的代码有哪些问题。在 commit 中我们自动运行相关 lint。

npm run lint:fix#

这个脚本会自动修复一些 lint 错误,如果你被 lint 搞的焦头烂额,试试它吧。

npm test#

这个脚本会执行一系列测试,包括 e2e 测试。详细信息可以看 测试。

npm run ui
npm run fetch:blocks#

这个脚本可以将所有的区块下载到当前项目中。你会得到与 https://preview.pro.ant.design/ 相同的界面。

如果速度缓慢,可以尝试升级 umi 版本到最新,并在 config.ts 设置 block.defaultGitUrl 为 'https://gitee.com/ant-design/pro-blocks'。

npm run i18n-remove#

常用链接

官网 ant-design-pro 12 步 30 分钟,完成用户管理的 CURD 应用 (react+dva+antd) dva-knowledgemap roadhog dva dva-cli dva 基础流程 api Concepts Redux 中文文档 antd-admin react-antd-admin1 react-antd-admin2 github-stars 编写 React 组件的最佳实践 部署 dva roadhog json-server roadhog-proxy JS函数式编程指南 js注释规范 model的继承和覆盖 React-Redux React Native 中 component 生命周期 React Native 中 component 生命周期 表单相关的几个问题(表单拆分、展示->可编辑状态的切换、Form.Item中的受控组件)

react native umi

ant-design-pro

  • model放在../models目录
  • model在src\common\router.js中使用
'/ling/builder/model': {
     component: dynamicWrapper(app, ['entity'], () => import('../ling/builder/model/routes/EntityIndex')),
 },
  • connet和loading
@connect(({ project, activities, chart, loading }) => ({
  project,
  activities,
  chart,
  projectLoading: loading.effects['project/fetchNotice'],
  activitiesLoading: loading.effects['activities/fetchList'],
}))
  • 获取数据
  render() {
    const {entity: {data}, loading} = this.props;
...

初始项目

npm i dva-cli -g
dva -v

然后创建应用:

dva new user-dashboard
cd user-dashboard
cnpm i antd --save
cnpm i babel-plugin-import --save-dev
cnpm i moment  --save-dev
cnpm i jsonp  --save-dev
cnpm i axios --save-dev

#antd没有样式

组件

QueryCommand

编码规范

Ling3编码规范#web端编码规范 dbtable

React

React 入门实例教程 超级好的react-demos

props 一般用于父组件向子组件通信,在组件之间通信使用。

state 一般用于组件内部的状态维护,更新组建内部的数据,状态,更新子组件的props等。

纯函数组件是和状态无关的,所需数据都来自父级组件

class App extends Component {
   constructor(){
     this.state = {
      height: 200,
      plotCfg: {
        margin: [60, 70, 40, 10],
      },
     }
  }

  render() {
       return  <Chart  {...this.state}/>
   }

}

const Chart = (props) => {
  const {height} = props
  return (
    <ECharts height ={height} />
  );
}

使用bindActionCreators整合多个model

如何在bindActionCreators中引入多个actionCreators模块

https://github.com/sorrycc/github-stars/blob/master/src/actions/index.js

https://github.com/sorrycc/github-stars/blob/master/src/models/user.js


import { createAction } from 'redux-actions';
export const userLogin = createAction('user/login');
import * as _actions from '../actions/index';


function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(_actions, dispatch),
  };
}

ref

组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。 但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性(查看 demo07 )。

推荐写法

  selectModal=null;

  render() {
    return (
      <div>
        <BaseIndex
          {...this.indexProps}
        />
        <Modal
          width={'95%'}
          maskClosable={false}
          visible={this.state.selectTemplateVisible}
          onOk={
            () => {
              this.setState({
                selectTemplateVisible: false
              })
              let selections=this.selectModal.getSelectRows()
              console.log(selections)
              this.setState({currentSelection:selections });
            }
          }
          onCancel={() => {
            this.setState({
              selectTemplateVisible: false
            })
          }}
        >
          <BaseIndex
            {...this.selectTemplateProps}
            ref={
            (select)=>{
              this.selectModal=select;
            }
          }
          />
        </Modal>
      </div>
    )
  }

老式写法

<!DOCTYPE html>
<html>
  <head>
    <script src="../build/react.js"></script>
    <script src="../build/react-dom.js"></script>
    <script src="../build/browser.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">
var MyComponent = React.createClass({
  handleClick: function() {
    this.refs.myTextInput.focus();
  },
  render: function() {
    return (
      <div>
        <input type="text" ref="myTextInput" />
        <input type="button" value="Focus the text input" onClick={this.handleClick} />
      </div>
    );
  }
});
ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);
    </script>
  </body>
</html>

上面代码中,组件 MyComponent 的子节点有一个文本输入框,用于获取用户的输入。这时就必须获取真实的 DOM 节点,虚拟 DOM 是拿不到用户输入的。为了做到这一点,文本输入框必须有一个 ref 属性,然后 this.refs.[refName] 就会返回这个真实的 DOM 节点。 需要注意的是,由于 this.refs.[refName] 属性获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会读取 this.refs.[refName] 属性。 React 组件支持很多事件,除了 Click 事件以外,还有 KeyDown 、Copy、Scroll 等,完整的事件清单请查看官方文档

Supported Events

https://facebook.github.io/react/docs/events.html#supported-events

[name]: value

 handleInputChange(event) {
   const target = event.target;
   const value = target.type === 'checkbox' ? target.checked : target.value;
   const name = target.name;
   this.setState({
     [name]: value
   });
 }

React.PropTypes

MyComponent.propTypes = {
  // You can declare that a prop is a specific JS primitive. By default, these
  // are all optional.
  optionalArray: React.PropTypes.array,
  optionalBool: React.PropTypes.bool,
  optionalFunc: React.PropTypes.func,
  optionalNumber: React.PropTypes.number,
  optionalObject: React.PropTypes.object,
  optionalString: React.PropTypes.string,
  optionalSymbol: React.PropTypes.symbol,

  // Anything that can be rendered: numbers, strings, elements or an array
  // (or fragment) containing these types.
  optionalNode: React.PropTypes.node,

  // A React element.
  optionalElement: React.PropTypes.element,

  // You can also declare that a prop is an instance of a class. This uses
  // JS's instanceof operator.
  optionalMessage: React.PropTypes.instanceOf(Message),

  // You can ensure that your prop is limited to specific values by treating
  // it as an enum.
  optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

  // An object that could be one of many types
  optionalUnion: React.PropTypes.oneOfType([
    React.PropTypes.string,
    React.PropTypes.number,
    React.PropTypes.instanceOf(Message)
  ]),

  // An array of a certain type
  optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

  // An object with property values of a certain type
  optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

  // An object taking on a particular shape
  optionalObjectWithShape: React.PropTypes.shape({
    color: React.PropTypes.string,
    fontSize: React.PropTypes.number
  }),

  // You can chain any of the above with `isRequired` to make sure a warning
  // is shown if the prop isn't provided.
  requiredFunc: React.PropTypes.func.isRequired,

  // A value of any data type
  requiredAny: React.PropTypes.any.isRequired,

  // You can also specify a custom validator. It should return an Error
  // object if the validation fails. Don't `console.warn` or throw, as this
  // won't work inside `oneOfType`.
  customProp: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },

  // You can also supply a custom validator to `arrayOf` and `objectOf`.
  // It should return an Error object if the validation fails. The validator
  // will be called for each key in the array or object. The first two
  // arguments of the validator are the array or object itself, and the
  // current item's key.
  customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })
};

Redux

https://zhuanlan.zhihu.com/p/22405838

学习函数式编程,必须掌握很多术语,否则根本看不懂文档。Reduce 和 Transduce 的含义

界面 => action => reducer => store => react => virtual dom => 界面

React Native component life.jpg

Dva-pattern.png

一种编码规范或者设计模式,或者一种约定

Effects

  • put 用于触发 action 。
yield put({ type: 'todos/add', payload: 'Learn Dva' });
  • call 用于调用异步逻辑,支持 promise 。
const result = yield call(fetch, '/todos');
  • select 用于从 state 里获取数据。
const todos = yield select(state => state.todos);
  • yield 把异步逻辑通过同步的方式组织起来。
  • async用来申明里面包裹的内容可以进行同步的方式执行,
  • await await则是进行执行顺序控制,每次执行一个await,程序都会暂停等待await返回值,然后再执行之后的await
  • await只能用在async函数之中,用在普通函数中会报错
  • await和yield都是表示暂停,外面包裹一层async表示里面的代码可以采用同步的方式进行处理
export async function fetchUser(username, password) {
  return await fetch('https://api.github.com/user', auth({}, username, password))
    .then(res => res.json());
}

  effects: {
    *login(action, { put }) {
      yield put({
        type: 'login/start',
      });

      const { username, password } = action.payload;
      const userInfo = yield GithubAPI.fetchUser(username, password);

      if (userInfo.message) {
        yield put({
          type: 'login/error',
          payload: userInfo.message,
        });
      } else {
        yield put({
          type: 'login/success',
          payload: {username, password, userInfo},
        });
        yield put({
          type: 'stars/sync',
        });
      }

      yield put({
        type: 'login/end',
      });
    },
  },

export async function fetchStars(url, username, password) {
  console.log('fetchStars', url);
  let links;
  const result = await fetch(url, auth({type: 'json'}, username, password)).then(res => {
    links = parseLink(res.headers.get('Link'));
    return res.json();
  });
  return {
    result: result.map(selectStar),
    links,
  };
}

React-Redux

  • 实际项目中,你应该权衡一下,是直接使用 Redux,还是使用 React-Redux。后者虽然提供了便利,但是需要掌握额外的 API,并且要遵守它的组件拆分规范
  • React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)

一、UI 组件

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 没有状态(即不使用this.state这个变量)
  • 所有数据都由参数(this.props)提供
  • 不使用任何 Redux 的 API

二、容器组件

  • 负责管理数据和业务逻辑,不负责 UI 的呈现
  • 带有内部状态
  • 使用 Redux 的 API

分工

UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。 你可能会问,如果一个组件既有 UI 又有业务逻辑,那怎么办?回答是,将它拆分成下面的结构:外面是一个容器组件,里面包了一个UI 组件。前者负责与外部的通信,将数据传给后者,由后者渲染出视图。 React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它

connect()

const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps)(TodoList)

四、mapStateToProps()

  • 输入逻辑:将state映射到 UI 组件的参数(props),
  • 是一个函数它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系
  • 作为函数,mapStateToProps执行后应该返回一个对象,里面的每一个键值对就是一个映射
  • mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
  • mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。
  • 使用ownProps作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。
  • connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。
const mapStateToProps = (state, ownProps) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  }
}

五、mapDispatchToProps()

  • 输出逻辑:将用户对 UI 组件的操作映射成 Action
  • 它定义了哪些用户的操作应该当作 Action,传给 Store。
  • 它可以是一个函数,也可以是一个对象。
  • mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数
const mapDispatchToProps = (
  dispatch,
  ownProps
) => {
  return {
    onClick: () => {
      dispatch({
        type: 'SET_VISIBILITY_FILTER',
        filter: ownProps.filter
      });
    }
  };
}
  • 如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出
const mapDispatchToProps = {
  onClick: (filter) => {
    type: 'SET_VISIBILITY_FILTER',
    filter: filter
  };
}

六、<Provider> 组件

  • 全局 state React-Router 路由库有相同功能,github-star项目的全局actions也有这个功能

下面代码中,Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了 store放在了上下文对象context上面。然后,子组件就可以从context拿到store

import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp);

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

class VisibleTodoList extends Component {
  componentDidMount() {
    const { store } = this.context;
    this.unsubscribe = store.subscribe(() =>
      this.forceUpdate()
    );
  }

  render() {
    const props = this.props;
    const { store } = this.context;
    const state = store.getState();
    // ...
  }
}

VisibleTodoList.contextTypes = {
  store: React.PropTypes.object
}


const Root = ({ store }) => (
  <Provider store={store}>
    <Router>
      <Route path="/" component={App} />
    </Router>
  </Provider>
);

roadhog

roadhog github

roadhog 是一个 cli 工具,提供 server、 build 和 test 三个命令,分别用于本地调试和构建。命令行体验和 create-react-app 一致,配置略有不同,比如默认开启 css modules,然后还提供了 JSON 格式的配置方式。

由于 create-react-app 的默认配置不能满足需求,而他又不提供定制的功能,于是基于他实现了一个可配置版。所以如果既要 create-react-app 的优雅体验,又想定制配置,那么可以试试 roadhog

build:dll

  • .roadhogrc.js中新增
 dllPlugin: {
   exclude: ["babel-runtime", "roadhog", "cross-env"],
   include: ["dva/router", "dva/saga", "dva/fetch"]
 }
  • package.json 中scripts配置
"scripts": {
   "start": "roadhog server",
   "dev": "cross-env BROWSER=none HOST=0.0.0.0 roadhog server",
   "build": "roadhog build",
   "precommit": "npm run lint",
   "lint": "eslint --fix --ext .js src",
   "build:dll": "roadhog buildDll",
   "build:new": "node version && roadhog build"
 },
  • 运行
npm run build:dll

dva

dva 是一个基于 react 和 redux 的轻量应用框架,概念来自 elm,支持 side effects、热替换、动态加载、react-native、SSR 等,已在生产环境广泛应用

等价写法

export default connect(({app, loading}) => ({app, loading}))(function(props) {
  return (
    <div className={styles.normal}>
      {console.log(props)}
    </div>
  );
});
===
// const IndexPage = (props) => {
//   const {children, location, dispatch, app, loading}=props
//   return (
//     <div className={styles.normal}>
//       {console.log(props)}
//       {console.log(app)}
//     </div>
//   );
// }

const IndexPage = ({children, location, dispatch, app, loading,routes}) => {
  return (
    <div className={styles.normal}>
      {console.log(routes)}
    </div>
  );
}

IndexPage.propTypes = {};

export default connect(({app, loading}) => ({app, loading}))(IndexPage)

dva cil

CLI for dva .

dva-cli use roadhog for build and server, view roadhog#Configuration (中文版) for details.

安装dva

npm i dva-cli -g

新建app

dva new user-dashboard
cd user-dashboard 

添加antd和babel

npm i antd --save
npm i babel-plugin-import --save-dev

Dva-plus.PNG

npm i dva-loading --save

修改 src/index.js 加载插件,在合适的地方加入下面两句

import createLoading from 'dva-loading';
app.use(createLoading());


新建功能命令

dva g route product-list
dva g model products
dva g component Editor
dva g component Users/UserModal
dva g component Header --no-css

组件

定义组件

components\users\list.js

function list ({
  loading,
  dataSource,
  pagination,
  onPageChange,
  onDeleteItem,
  onEditItem
}) {
  const columns = [
........
  ]

  return (
    <div>
      <Table
        className={styles.table}
        bordered
        scroll={{ x: 1200 }}
        columns={columns}
        dataSource={dataSource}
        loading={loading}
        onChange={onPageChange}
        pagination={pagination}
        simple
        rowKey={record => record.id}
      />
    </div>
  )
}

list.propTypes = {
  onPageChange: PropTypes.func,
  onDeleteItem: PropTypes.func,
  onEditItem: PropTypes.func,
  dataSource: PropTypes.array,
  loading: PropTypes.any,
  pagination: PropTypes.any
}

export default list

路由-使用组件

  • import UserList from '../components/users/list'
  • 定义参数
const userListProps = {
    dataSource: list,
    loading,
    pagination: pagination,
    onPageChange (page) {
      const query = location.query
      dispatch(routerRedux.push({
        pathname: '/users',
        query: {
          ...query,
          page: page.current,
          pageSize: page.pageSize
        }
      }))
    },
    onDeleteItem (id) {
      dispatch({
        type: 'users/delete',
        payload: id
      })
    },
    onEditItem (item) {
      dispatch({
        type: 'users/showModal',
        payload: {
          modalType: 'update',
          currentItem: item
        }
      })
    }
  }

路由-使用组件

...
return (
    <div className='content-inner'>
      <UserSearch {...userSearchProps} />
      <UserList {...userListProps} />
      <UserModalGen />
    </div>
  )
...

路由-构造函数和connect

function Users ({ location, dispatch, users }) {//users 为 model
  const { loading, list, pagination, currentItem, modalVisible, modalType } = users// 取 model中的值绑定到页面
  const { field, keyword } = location.query

...


Users.propTypes = {
  users: PropTypes.object,
  location: PropTypes.object,
  dispatch: PropTypes.func
}

function mapStateToProps ({ users }) {
  return { users }
}

export default connect(mapStateToProps)(Users)

组件属性获取与传递/设置

  • 设置参数
  const demoSearchProps = {
    record:{},
    onSearch (params) {
      dispatch({
        type: `demo/Search`,
        payload: params
      })
    },
    onReset () {
      dispatch({
        type: 'demo/Reset'
      })
    },
    beforeSearch(params){
      console.log(params);
    }
  }
  • 传递参数,不管哪种获取参数类型,使用方式都一致
<SearchTemplate {...demoSearchProps} />
<DemoList {...demoListProps} />
  • 获取参数
    • 通过props获取参数,适用于class SearchTemplateBase extends Component类型 const {beforeSearch, onSearch, afterSearch} = this.props;
  handleSearch = (e) => {
    const {beforeSearch, onSearch, afterSearch} = this.props;
    this.props.form.validateFields((err, values) => {
      if (!err) {
        if (beforeSearch) {
          if (beforeSearch(values)) {
            // 还是要交给上层组件处理, 因为要触发table组件的状态变化...
            if (onSearch) {
              if (onSearch(values) && afterSearch) {
                afterSearch(values);
              }
            }
          }
        }
      }
    });
  };
    • 获取参数 在构造函数中获取 适用于非class类型 export default list
function list ({ loading, dataSource, pagination, onPageChange, onDeleteItem, onEditItem, isMotion, location }) {
  const handleMenuClick = (record, e) => {
    if (e.key === '1') {
      onEditItem(record)
    } else if (e.key === '2') {
      confirm({
        title: '您确定要删除这条记录吗?',
        onOk () {
          onDeleteItem(record.id)
        }
      })
    }
  }

子组件与父组件

常用代码

合并对象

   let tableConfig;
   try {
     const tmp = require(`../../schema/${tableName}.config.js`);  // 个性化配置加载失败也没关系
     tableConfig = Object.assign({}, globalConfig.DBTable.default, tmp);  // 注意合并默认配置
   } catch (e) {
     logger.warn('can not find config for table %s, use default instead', tableName);
     tableConfig = Object.assign({}, globalConfig.DBTable.default);
   }

类型判断与异常

 let ids_ = ;
 if (value instanceof Array) {
   ids_ = keys.join(',');
 } else if (value instanceof String) {
   ids_ = keys
 } else {
   throw new Error("不支持的主键类型");
 }

条件渲染

       {
         this.state.current===0
         &&
         <InnerPage
           {...this.programProps}
         >
         </InnerPage>
       }
           {tableConfig.showExport ?
             <Button onClick={this.handleExport}><Icon type="export"/>导出</Button> : }
           {tableConfig.showImport ?
             <Upload {...uploadProps}><Button><Icon type="upload"/>导入</Button></Upload> : }

table filter

      {
        title: '代码名称',
        dataIndex: 'className',
        filters: [
          {
            text: status[0],
            value: 0,
          },
          {
            text: status[1],
            value: 1,
          },
          {
            text: status[2],
            value: 2,
          },
          {
            text: status[3],
            value: 3,
          },
        ],
        render(val) {
          return <Badge status={statusMap[val]} text={status[val]}/>;
        },
      }

日期显示

render: val => {moment(val).format('YYYY-MM-DD HH:mm:ss')},

table不可勾选

    const rowSelection = {
      selectedRowKeys,
      onChange: this.handleRowSelectChange,
      getCheckboxProps: record => ({
        disabled: record.disabled,
      }),
    };

Divider

<Divider style=模板:Margin: '40px 0 24px'/>

基础知识

preventDefault

合并DBTable

Merge DBTable.PNG Merge DBTable2.PNG

部署

nginx

try_files

server
	{
		listen       666;
		server_name 47.92.30.98;
		root  /home/www/antd-admin/dist;

		location /api {
			 proxy_pass http://localhost:8000/api;
		}

		location / {
				index  index.html;
				try_files $uri $uri/ /index.html;
		}
	}

apache

.htaccess RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.html [QSA,L]

常见问题

浏览器路径问题

// 1. Initialize
const app = dva({
  // ...createLoading({
  //   effects: true,
  // }),
  // history: browserHistory,
  onError (error) {
    message.error(error.message)
  },
})

中注释掉history: browserHistory, 路径中就带#,可以直接浏览器运行

antd没有样式

babel-plugin-import 用于按需引入 antd 的 JavaScript 和 CSS,这样打包出来的文件不至于太大。

npm i antd --save
npm i babel-plugin-import --save-dev

修改 .roadhogrc.js,在 "extraBabelPlugins" 里加上:

["import", { "libraryName": "antd", "style": "css" }]

const path = require('path')

export default {
  entry: 'src/index.js',
  // "theme": "./theme.config.js",
  // 接口代理示例
  "proxy": {

  },
  "env": {
      "development": {
        "extraBabelPlugins": [
          "dva-hmr",
          "transform-runtime",
  		    ["import", { "libraryName": "antd", "style": true }]
        ]
      },
      "production": {
        "extraBabelPlugins": [
          "transform-runtime",
  		    ["import", { "libraryName": "antd", "style": true}]
        ]
      }
  }
}

TypeScript

// tsconfig.json

{
 "compilerOptions": {
   "moduleResolution": "node",
   "jsx": "preserve",
   "allowSyntheticDefaultImports": true
 }
}

setState后获取到的thisstate没变,还是初始state

this.setState 是在 render 时, state 才会改变调用的, 也就是说, setState 是异步的. 组件在还没有渲染之前, this.setState 还没有被调用.这么做的目的是为了提升性能, 在批量执行 State 转变时让 DOM 渲染更快.

void setState(
 function|object nextState,
 [function callback]
)

所以正确做法是

this.setState(
   Object.assign({}, { data }),
   () => console.log(this.state)
)

正确显示头行结构的面包屑

  • id: string, 唯一id
  • bpid: string, 面包屑导航的父id
  • mpid: string, 菜单的父id,缺省时为一级菜单,为-1时在菜单中不显示
  • name: 显示名称
  • route: 匹配路由,缺省时不做跳转
  • icon: 在名称前显示的图标
  {
    id: i,
    name: 'project',
    router: '/project',
  },
  {
    id: i+1,
    mpid: -1,
    bpid: i,
    name: 'project Detail',
    router: '/project/:id',
  },



,{
          path: 'project',
          getComponent (nextState, cb) {
            require.ensure([], require => {
              registerModel(app, require('./builder/models/Project'))
              cb(null, require('./builder/pages/ProjectIndex'))
            }, 'project')
          }
        }, {
          path: 'project/:id',
          getComponent (nextState, cb) {
            require.ensure([], require => {
              registerModel(app, require('./builder/models/ProjectDetail'))
              cb(null, require('./builder/pages/ProjectDetail'))
            }, 'project-detail')
          }
        }

Modal内容不清空

<Modal /> 组件有标准的 React 生命周期,关闭后状态不会自动清空。 如果希望每次打开都是新内容,需要自行手动清空旧的状态。或者打开时给 Modal 设置一个全新的 key, React 会渲染出一个全新的对话框 <Modal key={this.state.newKey} visible={this.state.visible} />

modal有时显示,有时不显示

2个modal,使用了相同的key,导致有时候显示有时候不显示,相互影响

table datasouce明明有多条数据却只显示一条

设置的rowKey={record => record.id}当id都为空时只显示出一条

menu可以打开,刷新打开报错

.roadhogrc中的配置和router.js中的配置冲突

Failed to execute 'pushState' on 'History' error when using window.history.pushState function

history.pushState(data, null, newUrl);==>   history.pushState(null, null, newUrl);

data的值过大

tab显示

Antd-admin-tabs.PNG

参考

Client-inherit.png