1.0.1 • Published 3 years ago

react-front2 v1.0.1

Weekly downloads
-
License
ISC
Repository
-
Last release
3 years ago

egg-node

$ back

Mock library for testing Egg applications, plugins and custom Egg frameworks with ease. egg-mock inherits all APIs from node_modules/mm, offering more flexibility.

Install

Usage

$ npm i react-front --save-dev

Create testcase Launch a mock server with mm.app

$ src/App

import logo from './logo.svg';
import './App.css';
import RootRouter from './router/index'
function App() {
  return (
    <div className="App">
      <RootRouter />
    </div>
  );
}

export default App;

Retrieve Agent instance through app.agent after mm.app started.

Using mm.cluster launch cluster server, you can use the same API as mm.app;

Test Application baseDir is optional that is process.cwd() by default.

$ src/index

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <>
    <App />
  </>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Test Framework

framework is optional, it's node_modules/egg by default.

$ src/router/routerconfig

### views
	*index
		1.home
			>home.js
		2.my
			>my.js
		3.people
			>people.js
		4.index
	*login
		1.login.js
	*registry
		1.registry.js

Test Plugin

If eggPlugin.name is defined in package.json, it's a plugin that will be loaded to plugin list automatically.

You can also test the plugin in different framework, e.g. test aliyun-egg and framework-b in one plugin.

If it's detected as an plugin, but you don't want it to be, you can use plugin = false.

options

--framework egg web framework root path. --baseDir application's root path, default to process.cwd(). --port server port, default to 7001. --workers worker process number, default to 1 worker at local mode. --sticky start a sticky cluster server, default to false. --typescript / --ts enable typescript support, default to false. Also support read from package.json's egg.typescript. --declarations / --dts enable egg-ts-helper support, default to false. Also support read from package.json's egg.declarations. --require will add to execArgv, support multiple. Also support read from package.json's egg.require debug Debug egg app with V8 Inspector Integration.

automatically detect the protocol, use the new inspector when the targeted runtime >=7.0.0 .

if running without VSCode or WebStorm, we will use inspector-proxy to proxy worker debug, so you don't need to worry about reload.

$  views//registry


import React, { Component } from 'react'
import {registry} from '../../api/api'
import {
    Form, Input,Tooltip,Icon, Radio, Cascader,Select, Row,Col,Checkbox,Button,AutoComplete,
} from 'antd';

const { Option } = Select;



class RegistrationForm extends React.Component {
    state = {
        confirmDirty: false,
    };

    handleSubmit = e => {
        e.preventDefault();
        this.props.form.validateFieldsAndScroll(async (err, values) => {
            if (!err) {
                console.log('Received values of form: ', values);
                let res = await registry(values);
                if(res.data.code == 0){
                    this.props.history.push('/login');
                }
            }
        });
    };
    validateToNextPassword = (rule, value, callback) => {
        const { form } = this.props;
        if (value && this.state.confirmDirty) {
            form.validateFields(['confirm'], { force: true });
        }
        callback();
    };
    render() {
        const { getFieldDecorator } = this.props.form;

        const formItemLayout = {
            labelCol: {
                xs: { span: 24 },
                sm: { span: 8 },
            },
            wrapperCol: {
                xs: { span: 24 },
                sm: { span: 16 },
            },
        };
        const tailFormItemLayout = {
            wrapperCol: {
                xs: {
                    span: 24,
                    offset: 0,
                },
                sm: {
                    span: 16,
                    offset: 8,
                },
            },
        };
        return (
            <Form {...formItemLayout} onSubmit={this.handleSubmit}>
                <Form.Item label="邮箱">
                    {getFieldDecorator('email', {
                        rules: [
                            {
                                type: 'email',
                                message: 'The input is not valid E-mail!',
                            },
                            {
                                required: true,
                                message: 'Please input your E-mail!',
                            },
                        ],
                    })(<Input />)}
                </Form.Item>
                <Form.Item label="密码" hasFeedback>
                    {getFieldDecorator('pwd', {
                        rules: [
                            {
                                required: true,
                                message: 'Please input your password!',
                            },
                            {
                                validator: this.validateToNextPassword,
                            },
                        ],
                    })(<Input.Password />)}
                </Form.Item>
                <Form.Item
                    label={
                        <span>
                            昵称&nbsp;
                <Tooltip title="What do you want others to call you?">
                                <Icon type="question-circle-o" />
                            </Tooltip>
                        </span>
                    }
                >
                    {getFieldDecorator('name', {
                        rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
                    })(<Input />)}
                </Form.Item>

                <Form.Item label="电话">
                    {getFieldDecorator('phone', {
                        rules: [{ required: true, message: 'Please input your phone number!' }],
                    })(<Input style={{ width: '100%' }} />)}
                </Form.Item>

                <Form.Item label="性别">
                    {getFieldDecorator('sex')(
                        <Radio.Group>
                            <Radio value="1">男</Radio>
                            <Radio value="2">女</Radio>
                        </Radio.Group>,
                    )}
                </Form.Item>

                <Form.Item {...tailFormItemLayout}>
                    <Button type="primary" htmlType="submit">
                        注册
            </Button>
                </Form.Item>
            </Form>
        );
    }
}

const WrappedRegistrationForm = Form.create({ name: 'register' })(RegistrationForm);


export default WrappedRegistrationForm;

API

mm.app(options) Create a mock application.

mm.cluster(options) Create a mock cluster server, but you can't use API in application, you should test using supertest.

$ views//login

import React, { Component } from 'react'
import { Form, Icon, Input, Button, Checkbox } from 'antd';
import { login } from '../../api/api'
import { getCode, getUser } from '../../utils/oauth'
class NormalLoginForm extends React.Component {
    state = {
        captcha: '/getcaptcha'
    }
    handleSubmit = e => { //点击登录 账号登录
        e.preventDefault();
        this.props.form.validateFields(async (err, values) => {
            if (!err) {
                console.log('Received values of form: ', values);
                let res = await login(values);
                console.log(res)
                if (res.data.code == 0) {
                    //登录成功  1.存(token name avatar) 2.跳转
                    // localStorage.token = res.data.token;
                    localStorage.userInfo = JSON.stringify({
                        token: res.data.token,
                        name: res.data.name,
                        avatar: res.data.avatar
                    })
                    this.props.history.push('/index');
                }
            }
        });
    };
    handleClickImg = () => {
        this.setState({
            captcha: '/getcaptcha?_t=' + new Date().getTime()
        })
    }
    handleClickGitee = () => {
        getCode();
    }
    componentDidMount() {
        getUser((data, token) => { //第三方登录成功
            console.log(data, token);
            // localStorage.token = token;
            localStorage.userInfo = JSON.stringify({
                token,
                name: data.name,
                avatar: data.avatar_url
            })
            this.props.history.push('/index'); //登录成功,跳转到首页
        });
    }
    render() {
        const { getFieldDecorator } = this.props.form;
        const { captcha } = this.state;
        const formItemLayout = {
            labelCol: {
                xs: { span: 24 },
                sm: { span: 8 },
            },
            wrapperCol: {
                xs: { span: 24 },
                sm: { span: 16 },
            },
        };
        return (
            <Form {...formItemLayout} onSubmit={this.handleSubmit} className="login-form">
                <Form.Item label="用户名">
                    {getFieldDecorator('name', {
                        rules: [{ required: true, message: 'Please input your username!' }],
                    })(
                        <Input
                            prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
                            placeholder="Username"
                        />,
                    )}
                </Form.Item>
                <Form.Item label="密码">
                    {getFieldDecorator('pwd', {
                        rules: [{ required: true, message: 'Please input your Password!' }],
                    })(
                        <Input
                            prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
                            type="password"
                            placeholder="Password"
                        />,
                    )}
                </Form.Item>
                <Form.Item label="验证码">
                    {getFieldDecorator('captcha', {
                        rules: [{ required: true, message: 'Please input your Password!' }],
                    })(
                        <Input
                            prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
                            type="text"
                            placeholder="请输入验证码"
                        />,
                    )}
                </Form.Item>
                <Form.Item>
                    <img src={captcha} alt="" onClick={this.handleClickImg} />
                </Form.Item>
                <span onClick={this.handleClickGitee}>码云</span>
                <Form.Item>

                    <Button type="primary" htmlType="submit" className="login-form-button">
                        登录
                    </Button>
                    Or <a href="/registry">注册</a>
                </Form.Item>
            </Form>
        );
    }
}

const WrappedNormalLoginForm = Form.create({ name: 'normal_login' })(NormalLoginForm);



export default WrappedNormalLoginForm;

You can disable coverage, because it's slow.

$ views/index//index

  import React, { Component } from 'react'
import RouterView from '../../router/routerview'
import { Layout, Menu, Breadcrumb, Icon, Avatar, Popover } from 'antd';
import { NavLink } from 'react-router-dom'
const { SubMenu } = Menu;
const { Header, Content, Sider } = Layout;
export default class Index extends Component {
    state = {
        name: localStorage.userInfo ? JSON.parse(localStorage.userInfo).name : '',
        avatar: localStorage.userInfo ? JSON.parse(localStorage.userInfo).avatar : ''
    }
    handleClickExit = () => {
        //1.清空本地数据  2.跳转登录
        localStorage.clear();
        this.props.history.push('/login');
    }
    render() {
        const { child } = this.props;
        const { name, avatar } = this.state;
        return (
            <div>
                <Layout>
                    <Header className="header">
                        <h3>1903A</h3>
                        <div>
                            <span>姓名:{name}</span>
                            <Popover content={<div>
                                <p onClick={() => this.props.history.push('/index/people')}>个人中心</p>
                                <p onClick={this.handleClickExit}>退出</p>
                            </div>}>
                                <Avatar src={avatar} />

                            </Popover>
                        </div>
                    </Header>
                    <Layout>
                        <Sider width={200} style={{ background: '#fff' }}>
                            <Menu
                                mode="inline"
                                defaultSelectedKeys={['1']}
                                defaultOpenKeys={['sub1']}
                                style={{ height: '100%', borderRight: 0 }}
                            >
                                <Menu.Item key="1">
                                    <NavLink to="/index/home">首页</NavLink>
                                </Menu.Item>
                                <Menu.Item key="2">
                                    <NavLink to="/index/my">知识小册</NavLink>
                                </Menu.Item>
                                <Menu.Item key="3">
                                    <NavLink to="/index/people">个人中心</NavLink>
                                </Menu.Item>
                            </Menu>
                        </Sider>
                        <Layout style={{ padding: '0 24px 24px' }}>
                            <Breadcrumb style={{ margin: '16px 0' }}>
                                <Breadcrumb.Item>Home</Breadcrumb.Item>
                                <Breadcrumb.Item>List</Breadcrumb.Item>
                                <Breadcrumb.Item>App</Breadcrumb.Item>
                            </Breadcrumb>
                            <Content
                                style={{
                                    background: '#fff',
                                    padding: 24,
                                    margin: 0,
                                    minHeight: 280,
                                }}
                            >
                                <RouterView routes={child} />
                            </Content>
                        </Layout>
                    </Layout>
                </Layout>

            </div>
        )
    }
}

mm.env(env) Mock env when starting

$ views//my

import React, { Component } from 'react'
import { Button, Modal, Form, Input, Upload, message, Icon, Popconfirm, Card, Col, Row, Pagination } from 'antd'
import { addlist, getlist, editlist, dellist } from '../../../api/api'
const { Meta } = Card;
class My extends Component {
    state = {
        visible: false,
        name: '',
        content: '',
        url: '',
        id: null,
        page: 1,
        pageSize: 10,
        total: 0, //总条数
        list: [], //列表
        search: '',//搜索
    }
    componentDidMount() {
        this.getinit();
    }
    async getinit() {
        const { page, pageSize, search } = this.state;
        let res = await getlist({
            page,
            pageSize,
            search
        });
        if (res.data.code == 0) {
            this.setState({
                list: res.data.data,
                total: res.data.total
            })
        }
    }
    showModal = () => {
        this.setState({
            visible: true,
        });
    };

    handleOk = async e => {
        console.log(e);
        let { name, content, url, id } = this.state;
        console.log(name, content, url);
        //调添加的接口
        let res = id ? await editlist({
            name,
            content,
            url,
            id
        }) : await addlist({
            name,
            content,
            url
        });
        if (res.data.code == 0) {
            this.setState({
                visible: false,
                name: '',
                content: '',
                url: '',
                id: null
            },
                () => {
                    this.getinit();
                });
        }

    };

    handleCancel = e => {
        console.log(e);
        this.setState({
            visible: false,
        });
    };
    handleSubmit = e => {
        e.preventDefault();

    };
    handleInput = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        })
    }
    onShowSizeChange = (current, pageSize) => { //页码发生变化的时候或者pageSize发生变化的时候触发的事件
        console.log(current, pageSize, '********');
        this.setState({
            page: current,
            pageSize
        }, () => { //更新数据
            this.getinit();
        })
    }
    handleSearch = () => {
        // let {search} = this.state;
        this.setState({
            page: 1
        }, () => {
            this.getinit(); //更新数据
        })
    }
    handleEdit = (item) => {
        //点击编辑
        console.log(item);
        //1.显示弹框 2,回显数据
        this.setState({
            visible: true,
            name: item.name,
            content: item.content,
            url: item.url,
            id: item.id
        })
    }
    handleDel = async (id) => {

    }
    confirm = async (id) => { //点击确定删除
        console.log(id);
        let res = await dellist({ id });
        if (res.data.code == 0) {
            this.getinit(); //更新数据
            message.success('Click on Yes');
        }
    }

    cancel = (e) => {
        console.log(e);
        message.error('Click on No');
    }
    render() {
        const { name, content, url, list, total, page, search } = this.state;
        const formItemLayout = {
            labelCol: { span: 6 },
            wrapperCol: { span: 14 },
        };
        const props = {
            name: 'file',
            action: '/upload',
            headers: {
                token: localStorage.userInfo ? JSON.parse(localStorage.userInfo).token : '',
            },
            onChange: (info) => {
                if (info.file.status !== 'uploading') {
                    console.log(info.file, info.fileList);
                }
                if (info.file.status === 'done') { //上传成功
                    console.log(info, 'info')
                    this.setState({
                        url: info.file.response.url
                    })
                    message.success(`${info.file.name} file uploaded successfully`);
                } else if (info.file.status === 'error') {
                    message.error(`${info.file.name} file upload failed.`);
                }
            },
        };
        return (
            <div>
                <Input placeholder="请输入搜索的名字" value={search} name="search" onChange={this.handleInput} />
                <Button onClick={this.handleSearch}>搜索</Button>
                <Button onClick={this.showModal}>新增</Button>
                <Modal
                    title="Basic Modal"
                    visible={this.state.visible}
                    onOk={this.handleOk}
                    onCancel={this.handleCancel}
                >
                    <Form {...formItemLayout} onSubmit={this.handleSubmit}>
                        <Form.Item label="名字">
                            <Input placeholder="请输入名字" value={name} name="name" onChange={this.handleInput}></Input>
                        </Form.Item>
                        <Form.Item label="描述">
                            <Input placeholder="请输入描述" value={content} name="content" onChange={this.handleInput}></Input>
                        </Form.Item>
                        <Form.Item label="图片">
                            {
                                url ? <img src={url} alt="" className="imgs" /> : null
                            }
                            <Upload {...props}>
                                <Button>
                                    <Icon type="upload" /> 上传
                                </Button>
                            </Upload>
                        </Form.Item>
                    </Form>
                </Modal>
                <div style={{ background: '#ECECEC', padding: '30px' }}>
                    <Row gutter={16}>
                        {
                            list && list.map(item => <Col key={item.id} span={8}>
                                <Card
                                    style={{ width: 300 }}
                                    cover={
                                        <img
                                            alt="example"
                                            src={item.url}
                                        />
                                    }
                                    actions={[
                                        <Icon type="setting" key="setting" />,
                                        <Icon type="edit" key="edit" onClick={() => this.handleEdit(item)} />,
                                        <Popconfirm
                                            title="确认要删除吗?"
                                            onConfirm={() => this.confirm(item.id)}
                                            onCancel={this.cancel}
                                            okText="Yes"
                                            cancelText="No"
                                        >
                                            <Icon type="delete" key="delete" />,
                                        </Popconfirm>
                                    ]}
                                >
                                    <Meta
                                        title={item.name}
                                        description={item.content}
                                    />
                                </Card>
                            </Col>)
                        }

                    </Row>
                </div>
                <Pagination
                    showSizeChanger
                    onChange={this.onShowSizeChange}
                    defaultCurrent={page}
                    total={total}
                />
            </div>
        )
    }
}


const WrappedDemo = Form.create({ name: 'validate_other' })(My);

export default WrappedDemo;

if process.env.NODE_DEBUG_OPTION is provided (WebStorm etc), will use it as debug options.

dev Start dev cluster on local env, it will start a master, an agent and a worker.

Command

All the commands support these specific v8 options

$ views//home

   import React, { Component } from 'react'
import { Upload, message, Button, Icon } from 'antd';
import { Document, Page, pdfjs } from 'react-pdf';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;
export default class Home extends Component {
    state = {
        url: '',
        pageNumber: 1,
        numPages: 1,
        flag: false
    }
    onDocumentLoadSuccess = ({ numPages }) => {
        this.setState({
            numPages
        })
    }
    handleClickPreview = () => {
        this.setState({
            flag: true
        })
    }
    handleChangePage =(num)=>{
        this.setState({
            pageNumber:num
        })
    }
    render() {
        const { numPages, pageNumber, url, flag } = this.state;
        const props = {
            name: 'file',
            action: '/upload',
            headers: {
                token: localStorage.userInfo ? JSON.parse(localStorage.userInfo).token : '',
            },
            onChange: (info) => {
                if (info.file.status !== 'uploading') {
                    console.log(info.file, info.fileList);
                }
                if (info.file.status === 'done') {
                    this.setState({
                        url: info.file.response.url
                    })
                    message.success(`${info.file.name} file uploaded successfully`);
                } else if (info.file.status === 'error') {
                    message.error(`${info.file.name} file upload failed.`);
                }
            },
        };

        return (
            <div>
                <Upload {...props}>
                    <Button>
                        <Icon type="upload" /> pdf上传
    </Button>
                </Upload>
                <button disabled={!this.state.url} onClick={this.handleClickPreview}>预览</button>
                {
                    flag ? <div>
                        <Document
                            file={url}
                            onLoadSuccess={this.onDocumentLoadSuccess}
                        >
                            <Page pageNumber={pageNumber} />
                        </Document>
                        <p>Page {pageNumber} of {numPages}</p>
                        <button onClick={()=> this.handleChangePage(pageNumber-1)}>上一页</button>
                        <button onClick={()=> this.handleChangePage(pageNumber+1)}>下一页</button>
                    </div> : null
                }
            </div>
        )
    }
}