1.在使用mock接口的时候突然出现了跨域问题,解决方式
首先下载这三个插件,然后进行配置
第一步:新建一个setupProxy文件,使用http-proxy-middleware第三方插件进行配置,图中的/td的意思就是你接口的最后一个/后面的内容,这里配置完了之后,第二步:在你封装api的地方做一点点的改动,每个url前面都加上一个/td
第三步:包括之前二次封装的axios也做了变动,这里的baseUrl就不需要在写了,第四步:另外package.json里面的启动方式也要改变
这样接ok了,参考网址https://www.jianshu.com/p/a2a5163fefac
2,使用react-cropper
完成图片的上传剪切,缩放等功能,写这个原因主要是闲不住
案例展示
代码实现方式
点击上传文件的按钮,把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
方法会读取指定的 Blob
或 File
对象。读取操作完成的时候,readyState
会变成已完成DONE
,并触发 loadend
事件,同时 result
属性将包含一个data:
URL格式的字符串(base64编码)以表示所读取文件的内容。
在点击保存按钮的时候转为blob格式,我用的是redux存储起来,在父级页面使用
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就可以
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:
在这里提交按钮这里触发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:
他给我报错了,没错是因为我在action里面使用了异步的方法,不急不急,上网百度,知道了,需要下载react-thunk中间件,于是我配了,不出意外的好了, thunk的原理,可以在actionCreators里通过返回一个函数,然后就可以在函数里编写某些异步操作了,待异步操作结束,最后通过传入的store.dispatch,发出action通知给Store要进行状态更新 。
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,个人建议不要用,因为太重
内容展示
,因为都有现成的所以花了一天时间就写完了,其中也没有遇到什么问题,只能说是对antd.design框架的一个简单应用,说一说印象比较深的地方
下面的taskForm就是子组件,简单来说就是父子组件的传值。通过父组件让子组件显示和隐藏,这里用个sync,把v-show写在子组件里面,但是这里就不要用v-show了不起作用,用他组件封装好的visible
把控制权交给子组件
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:
生活的一部分是工作,工作的一部分是解决问题取悦生活,所以好好生活,好好工作,好好热爱(●ˇ∀ˇ●)