【花般绽放】umi-antd-mobile

项目倒是挺有意思的

感谢无私开源的程序员,谢谢你们哒~
现在我们来分析代码
数据流部分是用的dva
在global.js中有一个分享文章功能

//umi-dva-antd-mobile\src\global.js
import { Toast, Modal } from 'antd-mobile';
import { setAuthority } from '@/utils/authority';
import initWx from '@/utils/wx';
import debug from '@/utils/debug';
import './global.less';

setAuthority('admin');

function isWeixn() {
  const ua = navigator.userAgent.toLowerCase();
  return ua.includes('micromessenger');
}

if (!isWeixn()) { // 需要在微信端运行的时候 开启下面的注释
  // alert('请在微信客户端打开');
  // window.location.replace('#/404');
} else {
  debug().then(() => {
    initWx({
      title: '分享标题',
      imgUrl: '', // 分享图标
      isNeedLogin: true,
      desc: '分享描述',
      openid: process.env.NODE_ENV === 'development' ? 'oEgayjggrU06oORZJVeFUJ_KF1Mk' : undefined,
    });
  });
}

// Notify user if offline now
window.addEventListener('sw.offline', () => {
  Toast.offline('当前处于离线状态');
});

// Pop up a prompt on the page asking the user if they want to use the latest version
window.addEventListener('sw.updated', e => {
  console.log('sw.updated');
  const reloadSW = async () => {
    // Check if there is sw whose state is waiting in ServiceWorkerRegistration
    // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
    const worker = e.detail && e.detail.waiting;
    if (!worker) {
      return Promise.resolve();
    }
    // Send skip-waiting event to waiting SW with MessageChannel
    await new Promise((resolve, reject) => {
      const channel = new MessageChannel();
      channel.port1.onmessage = event => {
        if (event.data.error) {
          reject(event.data.error);
        } else {
          resolve(event.data);
        }
      };
      worker.postMessage({ type: 'skip-waiting' }, [channel.port2]);
    });
    // Refresh current page to use the updated HTML and other assets after SW has skiped waiting
    window.location.reload(true);
    return true;
  };
  Modal.alert('有新内容', '请点击“刷新”按钮或者手动刷新页面', [
    {
      text: '刷新',
      onPress: () => {
        reloadSW();
      },
    },
  ]);
});

这个页面还真的不知道

这种入口文件不明显啊
我们先找下面的吧

//umi-dva-antd-mobile\src\pages\entrance\index.tsx
import { Button, Icon, Modal, Picker } from 'antd-mobile';
import classNames from 'classnames';
import { connect } from 'dva';
import LS from 'parsec-ls';
import React from 'react';
import * as theme from '../../theme';

const styles = require('./index.less');

interface IChapterData {
  parentId?: string | number;
  value: string | number;
  label: React.ReactNode;
  children?: IChapterData[]
}

interface IEntranceState {
  selected: number;
  chapterValue: [];
  chapterLabel: string;
  visible: boolean;
}

interface IEntranceProps {
  dispatch?: any;
  loading?: boolean;
  h5: {
    chapterData: IChapterData[] | IChapterData[][]
  }
}

@connect(({ h5, loading }) => ({
  h5,
  loading: loading.models.h5,
}))
class Index extends React.Component<IEntranceProps, IEntranceState> {
  constructor(props) {
    super(props);
    this.state = {
      selected: -1,
      chapterValue: [],
      chapterLabel: '请选择章节',
      visible: false,
    };
  }

  public render() {
    const { selected, chapterValue, chapterLabel, visible } = this.state;
    const { h5: { chapterData = [] } } = this.props;
    const questionTotal = 100;
    return (
      <div className={styles.entranceBox}>
        <div className={styles.title}>网约车从业资格证</div>
        <div className={styles.subtitle}>考题练习及模拟考试</div>
        <div
          className={classNames(styles['select-item'], styles['opt-large'], {
            [styles['opt-selected']]: selected === 1,
          })}
          onClick={() => {
            this.setState({ selected: 1, chapterValue: [], chapterLabel: '请选择章节' }, () => {
              this.getChapterData(1);
            });
          }}
        >
          全国公共科目考试题库
        </div>
        <div
          className={classNames(styles['select-item'], styles['opt-large'], {
            [styles['opt-selected']]: selected === 7,
          })}
          onClick={() => {
            this.setState({ selected: 7, chapterValue: [], chapterLabel: '请选择章节' }, () => {
              this.getChapterData(2);
            });
          }}
        >
          本地区域科目考试题库
        </div>
        <Picker
          extra="请选择(可选)"
          data={chapterData}
          cols={1}
          title="请选择章节"
          value={chapterValue}
          onOk={e => {
            this.setState({
              chapterValue: e,
              chapterLabel: chapterData.filter(x => x.value === e[0])[0].label,
            });
          }}
        >
          <div className={styles['select-item']}>{chapterLabel}</div>
        </Picker>
        <div className={styles.bottom}>
          <Button
            type="primary"
            onClick={() => {
              this.getPaper(2);
            }}
          >
            考题预习
          </Button>
          <Button
            type="primary"
            onClick={() => {
              this.getPaper(3);
            }}
          >
            模拟考试
          </Button>
        </div>
        <Modal
          visible={visible}
          transparent={true}
          wrapClassName={styles['modal-wrap']}
          maskClosable={false}
          onClose={() => {
            this.setState({
              visible: false,
            });
          }}
          title={null}
          footer={[
            {
              text: '开始考试',
              onPress: () => {
                this.startTheExam();
              },
              style: { background: theme.primaryColor, color: '#ffffff', fontSize: 14 },
            },
          ]}
        >
          <div className={styles['modal-container']}>
            <div className={styles['modal-header']}>
              <div
                className={styles.closeBtn}
                onClick={() => {
                  Modal.alert('确认放弃本次考试吗?', '', [
                    { text: '放弃考试', onPress: () => this.setState({ visible: false }) },
                    { text: '继续考试' },
                  ]);
                }}
              >
                <Icon type="cross"/>
              </div>
              <div className={styles.avatar}>
                <img src={require('../../assets/h5/avatar.png')} alt="avatar"/>
              </div>
              <div className={styles['name-wrap']}>
                滴滴 <span>师傅</span>
              </div>
            </div>
            <div className={styles['modal-body']}>
              <div className={styles['desc-item']}>
                <div className={styles['item-label']}>考试科目:</div>
                <div className={styles['item-value']}>北京市出租汽车驾驶员从业资格模拟考试</div>
              </div>
              <div className={styles['desc-item']}>
                <div className={styles['item-label']}>试题数量:</div>
                <div className={styles['item-value']}>
                  <span>{questionTotal}</span> 题
                </div>
              </div>
              <div className={styles['desc-item']}>
                <div className={styles['item-label']}>考试时间:</div>
                <div className={styles['item-value']}>
                  共 <span>80</span> 分钟 (公共科目
                  <span>50</span>
                  分钟,区域科目
                  <span>30</span>
                  分钟)
                </div>
              </div>
              <div className={styles['desc-item']}>
                <div className={styles['item-label']}>合格标准:</div>
                <div className={styles['item-value']}>
                  <p>
                    公共科目
                    <span>40分</span>
                    及格,满分
                    <span>50分</span>;
                  </p>
                  <p>
                    区域科目-理论知识
                    <span>32分</span>
                    及格,满分
                    <span>40分</span>;
                  </p>
                  <p>
                    区域科目-运营实务
                    <span>8分</span>
                    及格,满分
                    <span>10分</span>;
                  </p>
                  <p>
                    每题
                    <span>1分</span>
                    ,共计
                    <span>100题</span>
                    ,总分
                    <span>100分</span>;
                  </p>
                </div>
              </div>
              <div className={styles.notice} style={{ display: 'none' }}>
                温馨提示:按照驾管部要求,考试不能修改答案,每做一题,系统自动计算错题数。
              </div>
            </div>
          </div>
        </Modal>
      </div>
    );
  }

  /**
   * 获取试题
   * @param step
   */
  public getPaper = step => {
    const { selected, chapterValue } = this.state;
    const { dispatch } = this.props;
    if (step === 2 && (selected === -1 || chapterValue.length === 0)) {
      Modal.alert('请选择地区和章节', '');
      return;
    }
    // @ts-ignore
    const sectionId = chapterValue[0];
    LS.setObj('exam-region', { chapterId: selected, sectionId });
    if (step === 2) {
      // @TODO 生成考题预习
      dispatch({
        type: 'h5/fetchQuestions',
        payload: {
          chapterID: selected,
          sectionID: sectionId,
          type: 'preview',
        },
      });
    } else {
      // @TODO 生成考试试卷
      dispatch({
        type: 'h5/fetchTestPapers',
        callback: (response, modalVisible) => {
          this.setState({
            visible: modalVisible,
          });
          LS.setObj('exam-paper', response.paper);
        },
      });
    }
  };

  /**
   * 取得一级或二级章节列表
   */
  private getChapterData = (parentID: number) => {
    const { dispatch } = this.props;
    dispatch({
      type: 'h5/fetchChapter',
      payload: { parentID },
    });
  };

  /**
   * 开始考试
   */
  private startTheExam = () => {
    const { dispatch } = this.props;
    dispatch({
      type: 'h5/startTheExam',
    });
  };
}

export default Index;

还有容错页的处理

//umi-dva-antd-mobile\src\pages\exception\500.js
import React from 'react';
import Link from 'umi/link';
import Exception from '@/components/Exception';

const Exception500 = () => (
  <Exception type="500" desc="抱歉,服务器出错了" linkElement={Link} backText="返回首页" />
);

export default Exception500;

分析代码的话是有这个user页面的,不过咩有怎么处理

//umi-dva-antd-mobile\src\pages\result\index.tsx

import { Button } from 'antd-mobile';
import { connect } from 'dva';
import LS from 'parsec-ls';
import React from 'react';
import router from 'umi/router';

const styles = require('./index.less');

interface IResultState {
  resultData: {
    score: number;
    time: string;
    isPass: boolean;
  }
}

@connect(({ h5, loading }) => ({
  h5,
  loading: loading.models.h5,
}))
class Result extends React.PureComponent<any, IResultState> {
  constructor(props) {
    super(props);
    const { h5: { resultData: { score = 0, time = '00:00', isPass } } } = props;
    this.state = {
      resultData: {
        score, time, isPass,
      },
    };
  }

  public componentDidMount() {
    if (LS.getObj('exam-test-result') === null) {
      router.push('/');
      return;
    }
    const { result } = LS.getObj('exam-test-result');
    const { resultData } = this.state;
    if (result) {
      this.setState({
        resultData: {
          ...resultData,
          ...result,
        },
      });
    }
  }

  public render() {
    const { resultData: { score = 0, time = '00:00', isPass } } = this.state;
    return (
      <div className={styles.result}>
        <div className={styles.card}>
          <div className={styles['img-wrap']}>
            {this.getTitleContent(isPass)}
            <div className={styles.wording}>科目考试成绩</div>
            <div className={styles.score}>{score || 0} 分</div>
            <div className={styles.duration}>答题时长:{time || '00:00'}</div>
          </div>
          <div className={styles.btns}>
            <Button
              type='ghost'
              onClick={() => {
                this.goToWrongPage();
              }}
            >查看错题
            </Button>
            <Button
              type="primary"
              onClick={() => {
                router.push('/entrance');
              }}
            >返回首页
            </Button>
          </div>
        </div>
      </div>
    );
  }

  private getTitleContent = (isPass) => {
    if (isPass) {
      return (<>
        <div className={styles.title}>恭喜您 <br/> 考试通过啦</div>
        <img src={require('../../assets/h5/pass.jpg')} alt=""/>
      </>);
    }
    return (<>
      <div className={styles.title}>很遗憾 <br/> 您没有通过考试</div>
      <img src={require('../../assets/h5/fail.jpg')} alt=""/>
    </>);
  };
  /**
   * 查看错题
   */
  private goToWrongPage = () => {
    const { dispatch } = this.props;
    const examResult = LS.getObj('exam-test-result');
    let data = [];
    if (examResult !== null) {
      const { result: { wrong_ids = [] }, questions = [], answers = [] } = examResult;
      data = wrong_ids.map((id) => ({
        ...questions.filter(x => x.id === id)[0] || {},
        userAnswer: this.getUserSelectAnswer(answers, id),
      })).filter(x => x !== null);
    }
    // 先获取一遍数据 避免 js 报错
    dispatch({
      type: 'h5/savePaperData',
      payload: {
        questions: data,
        paper: LS.getObj('exam-paper') || {},
      },
    });
    router.push('/paper/wrong');
  };

  private getUserSelectAnswer = (answers, id) => {
    if (answers && answers.length > 0) {
      return answers.filter(x => x.questionId === id)[0].answer;
    }
    return [];
  };

}

export default Result;

分析代码可知道这个是入口文件

import { Button } from 'antd-mobile';
import React from 'react';
import router from 'umi/router';

const styles = require('./index.less');


export default class extends React.Component<{}, {}, any> {
  constructor(props) {
    super(props);
  }

  public render = () => {
    return (<div className={styles.home}>

      <div
        className={styles.index}
      >
        <div className={styles.title}>网约车从业资格证</div>
        <div className={styles.subtitle}>考题练习及模拟考试</div>
        <Button
          type='primary'
          className={styles['btn-enter']}
          onClick={() => {
            // this.setState({ step: 1 });
            router.push('/entrance');

          }}
        >进入
        </Button>
      </div>
    </div>);
  };
}

其实这个项目让我百思不得其解的,因为不知道入口在那里,一般都会有router.js来定义的,所以啊,很有意思的项目啊~
运行于浏览器中的h5,比需要各种环境的react-native因为便捷更加能够激发人的创造性

//src\pages\home\index.tsx

猜你喜欢

转载自www.cnblogs.com/smart-girl/p/10915642.html