2020年6月4号


1.在使用mock接口的时候突然出现了跨域问题,解决方式

image-20200604203809753

首先下载这三个插件,然后进行配置

image-20200604203916905

第一步:新建一个setupProxy文件,使用http-proxy-middleware第三方插件进行配置,图中的/td的意思就是你接口的最后一个/后面的内容,这里配置完了之后,第二步:在你封装api的地方做一点点的改动,每个url前面都加上一个/td

image-20200604204132327

第三步:包括之前二次封装的axios也做了变动,这里的baseUrl就不需要在写了,第四步:另外package.json里面的启动方式也要改变

image-20200604204229967

image-20200604204453894

这样接ok了,参考网址https://www.jianshu.com/p/a2a5163fefac

2,使用react-cropper完成图片的上传剪切,缩放等功能,写这个原因主要是闲不住

案例展示

image-20200604205113577

代码实现方式

image-20200604205236296

点击上传文件的按钮,把avatarModalVisible设置为true,在react的jsx里面不要写if,显示弹框,可以看出上图代码,传给了modal两个参数,分别是弹框是否显示,还有一个就是选择的文件,下面就是点击按钮的方法

const handlFileChange = e => {
  e.persist();
  const file = e.target.files[0]
  if (file) {
    if (file.size <= MAX_FILE_SIZE) {
      setAvatarModalVisible(true)
      setModalFile(file)
    } else {
      message.error('文件过大')
    }
  }
}

在modal里,读取对应的文件,readAsDataURL 方法会读取指定的 BlobFile 对象。读取操作完成的时候,readyState 会变成已完成DONE,并触发 loadend 事件,同时 result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。

image-20200604210018839

在点击保存按钮的时候转为blob格式,我用的是redux存储起来,在父级页面使用

image-20200604210118479

AvatarModal.js

import React, { useState, useEffect, useCallback, useRef } from 'react'
import { Button } from 'antd';
import { getAvatarInfo } from './store/actionCreators'
import Cropper from 'react-cropper' // 引入Cropper
import 'cropperjs/dist/cropper.css' // 引入Cropper对应的css
import store from '../../store'
import './AvatarModal.scss'

const AvatarModal = ({ uploadedImageFile, setAvatarModalVisible }) => {
  // console.log(uploadedImageFile)
  const [src, setSrc] = useState(null)
  const cropperRef = useRef(null)

  useEffect(() => {
    const fileReader = new FileReader()
    fileReader.onload = e => {
      const dataURL = e.target.result
      setSrc(dataURL)
    }
    fileReader.readAsDataURL(uploadedImageFile)
  }, [uploadedImageFile])

  const handleSubmit = useCallback(() => {
    // TODO: 这里可以尝试修改上传图片的尺寸
    cropperRef.current.getCroppedCanvas().toBlob(async blob => {
      //把选中裁切好的的图片传出去
      // 关闭弹窗
      store.dispatch(getAvatarInfo(blob))
      setAvatarModalVisible(false)
    })
  }, [setAvatarModalVisible])

  return (
    <div className="hooks-cropper-modal">
      <div className="modal-panel">
        <div className="cropper-container-container">
          <div className="cropper-container">
            <Cropper
              src={src}
              className="cropper"
              ref={cropperRef}
              viewMode={1}
              zoomable={true} //true为可缩放,反之补能
              aspectRatio={1} // 固定为1:1  可以自己设置比例, 默认情况为自由比例
              guides={false}
              preview=".cropper-preview"
            />
          </div>
          <div className="preview-container">
            <div className="cropper-preview" />
          </div>
        </div>
        <div className="button-row">
          <Button type="primary" onClick={handleSubmit}>点击保存</Button>
        </div>
      </div>
    </div>
  )
}

export default AvatarModal

这样在父组件中获取,并且赋值给img的src就可以

image-20200604210347613

userInfo.js(父组件)

import React, { useState, useEffect } from 'react'
import { Card, Form, Input, Button, Row, Col, Avatar, message } from 'antd';
import {
  CloudUploadOutlined
} from '@ant-design/icons';
import AvatarModal from './AvatarModal'
import styles from './userInfo.module.css'
import { connect } from 'react-redux'
const UserInfo = ({ avatarObj }) => {
  const MAX_FILE_SIZE = 10 * 1024 * 1024 // 文件最大限制为10M
  const [avatarModalVisible, setAvatarModalVisible] = useState(false)
  const [modalFile, setModalFile] = useState(null)
  const [resultImgUrl, setResultImgUrl] = useState('')
  useEffect(() => {
    if (JSON.stringify(avatarObj.avatarInfoObj) === "{}") {
      let blob = avatarObj.avatarInfoObj
      console.log(blob)
      if (blob.size) {
        setResultImgUrl(URL.createObjectURL(blob))
      }
    }
    return () => {
    }
  }, [avatarObj])
  const layout = {
    labelCol: { span: 8 },
    wrapperCol: { span: 16 },
  };
  const tailLayout = {
    wrapperCol: { offset: 12, span: 12 },
  };
  const onFinish = values => {
    console.log('Success:', values);
  };
  const handlFileChange = e => {
    e.persist();
    const file = e.target.files[0]
    if (file) {
      if (file.size <= MAX_FILE_SIZE) {
        setAvatarModalVisible(true)
        setModalFile(file)
      } else {
        message.error('文件过大')
      }
    }
  }
  return (
    <>
      <Card title="个人信息">
        <Row>
          <Col span={16}>
            <Form
              {...layout}
              name="basic"
              initialValues={{ remember: true }}
              onFinish={onFinish}
            >
              <Form.Item
                label="昵称"
                name="nickName"
                rules={[{ required: true, message: '请输入你的昵称!' }]}
              >
                <Input />
              </Form.Item>
              <Form.Item
                label="账户密码"
                name="password"
                rules={[{ required: true, message: '请输入你的密码!' }]}
              >
                <Input />
              </Form.Item>
              <Form.Item name="comment" label="comment">
                <Input.TextArea />
              </Form.Item>
              <Form.Item {...tailLayout}>
                <Button type="primary" htmlType="submit">
                  保存
        </Button>
              </Form.Item>
            </Form>
          </Col>
          <Col span={5} offset={3} className={styles.mask}>
            <Avatar size={180} className={styles.avatar} src={resultImgUrl ? resultImgUrl : "https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"} />
            <CloudUploadOutlined className={styles.icon} />
            <label className={styles.maskBox}>
              <input
                type="file"
                accept="image/jpeg,image/jpg,image/png"
                className="base-upload-input"
                onChange={handlFileChange}
              />
            </label>
          </Col>
        </Row>
      </Card>
      {

        avatarModalVisible && (
          <AvatarModal
            setAvatarModalVisible={setAvatarModalVisible}
            uploadedImageFile={modalFile}
          />
        )

      }
    </>
  )
}
// toJS()是因为在reducer里面用了fromJS
const mapStateToProps = state => {
  return {
    avatarObj: state.toJS().avatarReducer
  }
}
export default connect(mapStateToProps)(UserInfo)

app.js

3.实现mock+token+react-redux的登录验证

mock的接口是自己瞎写的只是返回了一个假的token而已:sweat_smile:

image-20200604211008784

在这里提交按钮这里触发loginUser这个方法,这里的路由跳转使用的是useHistory

import { USER_INFO, SET_CURRENT_USER } from './constants'
import { useHistory } from "react-router-dom";
import { login } from '../../../api/demoInfo'
export const getUserInfo = (data) => ({
  type: USER_INFO,
  data
})
export const loginUser = () => dispatch => {
  login().then(res => {
    const { token } = res.data;
    localStorage.setItem('token', token);//存储token
    dispatch(setCurrentUser(token))
  })
}
export const setCurrentUser = decoded => {
  return {
    type: SET_CURRENT_USER,
    data: decoded
  }
}
export const logoutUser = () => dispatch => {
  let history = useHistory();
  localStorage.removeItem('token');
  dispatch(setCurrentUser({}));
  history.push('/login');
}

上面就是我store里面action的方法,当然没有这么顺利:ice_cream:

image-20200604211407639

他给我报错了,没错是因为我在action里面使用了异步的方法,不急不急,上网百度,知道了,需要下载react-thunk中间件,于是我配了,不出意外的好了, thunk的原理,可以在actionCreators里通过返回一个函数,然后就可以在函数里编写某些异步操作了,待异步操作结束,最后通过传入的store.dispatch,发出action通知给Store要进行状态更新

image-20200604211540583

reducer.js

import * as actionTypes from './constants';
import { fromJS } from 'immutable';
const defaultState = fromJS({
  userInfoObj: {},
  isAuthenticated: false,
  user: {}
})

export default (state = defaultState, action) => {
  console.log(action)
  switch (action.type) {
    case actionTypes.USER_INFO:
      return state.set('userInfoObj', action.data)
    case actionTypes.SET_CURRENT_USER:
      return state.set('user', action.data),
        state.set('isAuthenticated', action.data)

    default:
      return state;
  }
}

好了整个的流程差不多就是这个样子,我们在路由这里调用一下

router.js

import React from 'react';
import { connect } from 'react-redux'
import { Route, Redirect } from 'react-router-dom';
import styles from './router.module.css';
import Routers from '../config/routes.config'
const RootContainer = ({ userObj }) => {
  let token = userObj.user
  console.log(token, 'effect')
  return (
    <div className={styles.routerBox}>
      {Routers.map((item, index) => {
        return <Route key={index} path={item.path} render={props =>
          (!item.auth ? (<item.component {...props} />) : (token ? <item.component {...props} /> : <Redirect exact to="/" />)
          )} />
      })}
    </div >
  )
}
const mapStateToProps = state => {
  return {
    userObj: state.toJS().userReducer
  }
}
export default connect(mapStateToProps)(RootContainer);

app.js,在使用redirect的时候不能直接使用对应的组件,路由有执行顺序应该先注册一下
输入图片说明

上面代码的主要意思是获取token,判断有没有token,如果没有跳转到登录页,如果有展示页面

以上内容是我星期一到星期三的一些工作内容,下面的是星期四到星期五的工作内容

用的是vue的antd.design.pro,因为这个项目是之前别人把架子都已经搭好了,直接用的是pro,个人建议不要用,因为太重

内容展示

image-20200604212742666

image-20200604212823982

,因为都有现成的所以花了一天时间就写完了,其中也没有遇到什么问题,只能说是对antd.design框架的一个简单应用,说一说印象比较深的地方

image-20200604213045022

下面的taskForm就是子组件,简单来说就是父子组件的传值。通过父组件让子组件显示和隐藏,这里用个sync,把v-show写在子组件里面,但是这里就不要用v-show了不起作用,用他组件封装好的visible

image-20200604213203482

把控制权交给子组件

image-20200604213255087

taskForm,因为用的是一个表单,添加和编辑做个判断就行

<template>
  <!-- modal就不要用v-show直接用他提供的visible -->
  <a-modal :title="isEdit?'修改':'新增'" :visible="panelShow" @ok="handleSubmit" @cancel="handleCancel">
    <a-form @submit="handleSubmit" :form="form">
      <a-form-item label="用户名" :labelCol="labelCol" :wrapperCol="wrapperCol">
        <a-input
          autocomplete="false"
          v-decorator="['note', { rules: [{ required: true, message: '用户名不能为空' }],initialValue:isEdit?(dataRow&&dataRow.owner):''}]"
        />
      </a-form-item>
      <a-form-item label="密码" :labelCol="labelCol" :wrapperCol="wrapperCol">
        <a-input
          autocomplete="false"
          type="password"
          v-decorator="['pwd', { rules: [{ required: true, message: '密码不能为空' }],initialValue: '' }]"
        />
      </a-form-item>
  <a-form-item label="秘钥" :labelCol="labelCol" :wrapperCol="wrapperCol">
        <a-input
          onkeyup="value=value.replace(/[^a-zA-Z]/g,'')"
          :min="4"
          autocomplete="false"
          placeholder="请输入英文秘钥"
          v-decorator="['secret', { rules: [{ required: true, message: '秘钥不能为空' }, {min:4,message: '密码不能少于4个字 
        符'}],initialValue: '' }]"
        />
      </a-form-item>
      <a-form-item label="有效时间" :labelCol="labelCol" :wrapperCol="wrapperCol">
        <a-select v-decorator="['time', {rules:[{required: true, message: '请选择开始时间'}]}]">
          <a-select-option :value="0">1天</a-select-option>
          <a-select-option :value="1">3天</a-select-option>
          <a-select-option :value="2">7天</a-select-option>
          <a-select-option :value="3">长期</a-select-option>
        </a-select>
      </a-form-item>
      <a-form-item label="可见应用" :labelCol="labelCol" :wrapperCol="wrapperCol">
        <a-select
          v-decorator="[
            'use',
            {
              rules: [
                { required: true, message: '请选择至少一项', type: 'array' },
              ],
            }
          ]"
          mode="multiple"
        >
          <a-select-option :value="0">智慧乡村</a-select-option>
          <a-select-option :value="1">智慧园区</a-select-option>
          <a-select-option :value="2">智慧农林</a-select-option>
          <a-select-option :value="3">其他</a-select-option>
        </a-select>
      </a-form-item>
    </a-form>
  </a-modal>
</template>

<script>
export default {
  name: 'TaskForm',
  props: {
    record: {
      type: Object,
      default: null
    },
    panelShow: {
      type: Boolean
    },
    dataRow: {
      type: Object,
      default: null
    },
    isEdit: {
      type: Boolean
    }
  },
  data () {
    return {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 5 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 18 }
      },
      form: this.$form.createForm(this)
    }
  },
  mounted () {

  },
  methods: {
    onOk () {
      // console.log('监听了 modal ok 事件')
      return new Promise(resolve => {
        resolve(true)
      })
    },
    onCancel () {
      // console.log('监听了 modal cancel 事件')
      return new Promise(resolve => {
        resolve(true)
      })
    },
    handleSubmit () {
      this.form.validateFields(err => {
        console.log(err)
        if (!err) {
          // 不用再绑定ref,直接使用form
          this.form.resetFields()

          this.$emit('update:panelShow', false)
        }
      })
    },
    handleCancel () {
      this.form.resetFields()
      this.$emit('update:panelShow', false)
    }
  },
  watch: {
    isVisible (val) {
      console.log(val)
      this.IsShowPage = val // 新增isVisible的watch,监听变更并同步到IsShowPage上
    }
  }
}
</script>

目前为止,个人认为两个库,各有有缺点,目前react用的比较顺手,虽然有很多地方也没弄懂:sweat_smile:

生活的一部分是工作,工作的一部分是解决问题取悦生活,所以好好生活,好好工作,好好热爱(●ˇ∀ˇ●)


Author: wxy
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source wxy !
  TOC