2

背景:PC端的CRM系统第一阶段已经开发完毕.使用技术为:JS + AntDesignPro.现在开始开发企业微信.需要使用的技术为:TS + AntDesignMobile.因为公司之前有人开发过企业微信版,所以直接拉的别人搭好的架子.
开发之前,反复浏览TS的官网,AntDesignMobile的官网.

1:项目目录:

image.png
image.png
与AntDesignPro目录是差不多的.简单说下.开发代码还是在src下面的pages里面.assets里面放的一般都是图标.components里面都是自己封装的组件.layouts里面是鉴权.interceptors.ts文件里面是获取token的地方.umirc.ts里面是连接后端服务器地址的.开发服务器,测试服务器,生产服务器等.

2:开发首页页面:

目录结构:
image.png

上代码:
services层:

import axios from 'axios';

/**
 * 首页列表
 */
export async function listHome(data : any) {
    return axios.post(`/${process.env.APIPREFIX}/ticket/listHome`, data);
  }

model层:

import * as services from './service';
export default {
    namespace: 'home',
    effects: {
       // 首页列表
    *listHome({ data, callback} : any, { put, call }: any) {
        const datas = yield call(services.listHome, data);
        yield put({
          type: 'save',
          payload:{listHome : datas.data} ,
        });
        if (datas.code === '0000') {
          callback && callback(datas.data);
        }
      },
    },
    reducers: {
        save(state : any, { payload } : any) {
          return {
            ...state,
            ...payload,
          };
        },
    }
}

index.tsx:

/**
 * title: '昆药CRM'
 */
import React from 'react';
import { List } from 'antd-mobile';
import router from 'umi/router';
import styles from './index.less';
import { connect } from 'dva';
import IconBlock from './components/IconBlock';
import { Flex } from 'antd-mobile';

const Item = List.Item;
const Brief = Item.Brief;
const namespace = 'home';
const mapStateToProps = (state: any) => {
  const states = state[namespace];
  return {
    ...states,
  };
};
const mapDispatchToProps = (dispatch: any) => ({
  // 首页列表
  listHome(data: any, callback: any) {
    dispatch({
      type: `${namespace}/listHome`,
      data,
      callback,
    });
  },
});

class Home extends React.Component {
  constructor(props: any) {
    super(props);
    this.state = {
      menu: [],
      order: {
        groupTitle: '订单相关查询',
        menuList: [],
      }, // 订单类
      invoiceFinancial: {
        groupTitle: '回款相关查询',
        menuList: [],
      }, // 发票财务类
      masterData: {
        groupTitle: '主数据查询',
        menuList: [],
      }, // 主数据类
    };
  }

  componentDidMount() {
    this.getList();
  }

  // 获取首页图标
  getList = () => {
    const { listHome }: any = this.props;
    listHome({}, (res: any) => {
      res.menus &&
        res.menus.map((val: any) => {
          val.children.map((value: any) => {
            if (value.menuName === '订单类') {
              const { order }: any = this.state;
              this.setState(
                {
                  order: {
                    ...order,
                    menuList: value.children,
                  },
                },
                () => {
                  const { order }: any = this.state;
                  this.setState({ menu: [order] });
                },
              );
            }
            if (value.menuName === '发票财务类') {
              const { invoiceFinancial }: any = this.state;
              this.setState(
                {
                  invoiceFinancial: {
                    ...invoiceFinancial,
                    menuList: value.children,
                  },
                },
                () => {
                  const { menu, invoiceFinancial }: any = this.state;
                  this.setState({ menu: [invoiceFinancial, ...menu] });
                },
              );
            }
            if (value.menuName === '主数据类') {
              const { masterData }: any = this.state;
              this.setState(
                {
                  masterData: {
                    ...masterData,
                    menuList: value.children,
                  },
                },
                () => {
                  const { menu, masterData }: any = this.state;
                  this.setState({ menu: [masterData, ...menu] });
                },
              );
            }
          });
        });
    });
  };

  // 跳转
  jump = (path: any) => {
    const { menuList, dispatch }: any = this.props;
    let key: any = '';
    let menuList1 = menuList.menuData.menuList;
    let query = (arr: any, index: number) => {
      if (arr.path === path) {
        key = index && index;
      } else if (arr.children.length > 0) {
        arr.children.map((i: any) => {
          query(i, index);
        });
      }
    };
    menuList1.map((item: any, index: number) => {
      query(item, index);
    });
    dispatch &&
      dispatch({
        type: 'global/changeLayoutCollapsed',
        payload: false,
      });
    localStorage.setItem('menuNav', key.toString());
    router.push({
      pathname: path,
    });
  };

  render() {
    const { menu }: any = this.state;
    return (
      <div style={{marginBottom:50}}>
        <img src={require('../../assets/icons/banner.png')} style={{width:'100%',marginBottom:20}}/>
        {menu &&
          menu.map((value: any) => (
            <div key={value.groupTitle}>
              <span className={styles.section_title}>{value.groupTitle}</span>
              <Flex  wrap="wrap">
                  {value.menuList.map((item: any, index: number) => (
                      <IconBlock
                        key={index}
                        titleName={item.menuName}
                        iconPath={item.icon}
                        method={() => this.jump(item.url)}
                      />
                  ))}
                 </Flex>
            </div>
          ))}
      </div>
    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Home);

components-IconBlock.tsx:

import React from 'react';
import styles from './style.less';

interface IconBlockProps {
  titleName: string,
  iconPath: string,
  method: (e: React.MouseEvent) => void;
}

const IconBlock: React.FC<IconBlockProps> = ({ titleName, iconPath, method }) => {
  return (
    <div className={styles.blockWrapper} onClick={method}>
      <div className={styles.icon}>
        <img src={iconPath}/>
      </div>
      <div className={styles.title}>{titleName}</div>
    </div>
  )
}
export default IconBlock;

3:实现效果:

头部:
image.png
底部:
image.png

4:说下遇到的BUG:

1:注入model层的时候,一直报错:
image.png.
实在是第一次用ts.真不会写..这个类型整死我了... 搞了几个小时...
换了好几种写法.. 最后才解决了...解决方法:把connect放在页面底部.export default connect(mapStateToProps, mapDispatchToProps)(Home);
2:ESlint 真的很严格.. 提交代码的时候.一直给我报错.. 一向急躁的我到处看ts写法问题... 最后发现... 是我的标签前面多打了个空格... 我真的要被自己气死了....
3:页面样式问题.AntDesignMobile和AntDesignPro的组件还是有很多不一样的.比如AntDesignPro有的Row和Col,Text等等都没有了.. 这个写起来真的好不习惯..AntDesignMobile有Flex布局.但是这个API用起来和原生的区别真的不大....image.png.
写代码几个小时后就容易疲劳.然后列表循环展示图标的时候.把Flex标签放循环里面了.样式就可想而知.一塌糊涂.. 这么浅显的BUG我竟然还是创造出来了... 写出来.警告自己.以后不可再犯.


Timor
37 声望20 粉丝