2020年6月21日(egg react)


案例展示的图片就没有了,因为需要连数据库才能看到页面的整体效果,由于用的是公司的电脑,所以自己的电脑没有数据库的数据,看不到效果图片,之前也在笔记里面说过,做的一个项目是后台管理的类型主要是对菜单和案例进行,增删改查,然后把这些数据相互关联,比如菜单树形结构的生成,菜单和案例之间的关联

1.egg连接数据库

在config文件夹下面的config.default.js进行配置,并且在plugin.js下面注册下载的egg-mysql插件

image-20200621231927440

service文件夹主要是对数据库的操作,controller主要是讲数据库的内容返回给前端,前端第一个接触的就是controller里面的方法,controller在调用service里面操作数据库的方法,就可以实现基本的流程,因为我现在写的也没啥复杂的东西,就是对数据的增删改查

2.controller里面的操作

image-20200621232619378

简单的一个例子,查看菜单列表并且进行分页,使用slice方法,因为这个是菜单列表,就会有,一级,二级,三级,所以最好在后台的时候就做好这些处理,生成树形结构一,前端不用做太多的操作

3.service(menuInfo)

image-20200621232938281

对于数据库的设计,我刚开始是有误的,对于每一个菜单我都建了一个表,但是只用一个表就可以,写parent字段,并且也可以写一个deepth知道它在第几级,treeData方法就是生成树形结构的方法,getAllList返回数据库的数据

当然菜单和案例是有联系的所以会单独写一个方法,因为之前我用的是假数据,页面渲染的时候已经有固定的格式,需要从数据库获取数据并且生成对应的结构

image-20200621233714469

个人是觉得写得有点复杂,有时间调整调整,这里之前的for循环,之前是使用的map,但是node中的map()、forEach()、for()循环有一个特性:当其函数里面里面有回调它就变成异步,之前使用map得到的是一个promise pending

解决方法:可以用async的map来处理这种情况,async是nodejs的一个组件,用来解决node的异步问题

列如

image-20200621234105667

findOne是异步函数,可以使用async.map解决,参考网址https://blog.csdn.net/sam976/article/details/52330424

4.上传图片

image-20200621234259422

const { Controller } = require("egg");
const path = require('path');
const fs = require('fs');
// 异步二进制 写入流
const awaitWriteStream = require('await-stream-ready').write;
//管道读入一个虫洞。
const sendToWormhole = require('stream-wormhole');
class uploadController extends Controller {
  async doUploadImage () {
    const ctx = this.ctx;
    //egg-multipart 已经帮我们处理文件二进制对象
    const stream = await ctx.getFileStream();
    //新建一个文件名
    const filename = Math.random().toString(36).substr(2) + path.extname(stream.filename).toLocaleLowerCase();
    const target = path.join(this.config.baseDir, 'app/public/image', filename);
    //生成一个文件写入 文件流
    const writeStream = fs.createWriteStream(target);
    try {
      //异步把文件流 写入writeStream中
      await awaitWriteStream(stream.pipe(writeStream));
    } catch (err) {
      //如果出现错误,关闭管道
      await sendToWormhole(stream);
      throw err;
    }
    //文件响应
    ctx.body = {
      url: '/public/image/' + filename,
      msg: '上传成功',
      code: 200
    };
  }
}
module.exports = uploadController

// 1.获取传过来的文件二进制转为流
// 2.创建文件
// 3.将创建的文件转为流
// 4.两个流对接

//直接存储base64
 const filename = Date.now() + '.png';
    var target = path.join(this.config.baseDir, 'app/public/image', filename);
    var base64 = imgData.replace(/^data:image\/\w+;base64,/, "");
    var dataBuffer = new Buffer(base64, 'base64'); //把base64码转成buffer对象,
    fs.writeFile(target, dataBuffer, function (err) {//用fs写入文件
      if (err) {
        console.log(err);
      } else {
        console.log('写入成功!');
      }
    })

前端代码没有使用antd.deisgn提供的upload组件,自己封装一个

import React, { useState, createRef, useImperativeHandle, useEffect } from "react"
import './index.css'
import { PlusCircleOutlined, RestOutlined } from '@ant-design/icons';
import { upload } from '../../api/demoInfo';
import { message } from "antd";
const Upload = React.forwardRef((props, ref) => {
  const [imgUrl, setImgUrl] = useState(undefined)
  const [src, setSrc] = useState('')
  useEffect(() => {
    setSrc(props.src)
  }, [props.src])
  const files = createRef();
  useImperativeHandle(ref, () => ({
    getFile: () => {
      return { file: imgUrl, src: src }
    }
  }));
  const hanldeDelImage = () => {
    setImgUrl(undefined)
    setSrc(undefined)
  }
  const handleUpdate = () => {
    // createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用。
    let fileImg = files.current.files[0];
    let formData = new FormData()
    formData.append("file", fileImg)//第一个参数为后端规定字段,第二个参数时需要上传的图片
    upload(formData).then(res => {
      if (res.code === 200) {
        setImgUrl('http://127.0.0.1' + res.url)
        message.success(res.msg)
      } else {
        message.error(res.msg)
      }
    })
  }

//base64
const handleUpdate = (e) => {
    // 利用fileReader对象获取file    
    var file = e.target.files[0];
    var filesize = file.size;
    if (filesize > 2101440) {
      // 图片大于2MB 
      message.info('图片不能大于2M')
    }
    var reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = function (e) {
      // 读取到的图片base64 数据编码 将此编码字符串传给后台即可        
      var imgcode = e.target.result;
      setImgUrl(imgcode)
      upload({ imgData: imgcode }).then(res => {
        if (res.code === 200) {
          message.success(res.msg)
        } else {
          message.error(res.msg)
        }
      })
    }
  }

  return (
    <div>
      <input className="uploadInput" type="file" onChange={handleUpdate} ref={files} />
      <div className="imgContent">
        <div style={{ position: 'relative' }} onClick={hanldeDelImage}>
          {(src || imgUrl) ? <img src={src || imgUrl} alt="图片" /> : <><PlusCircleOutlined />上传图片</>}
          <div className="mask" ><RestOutlined style={{ fontSize: '24px', color: '#ffffff' }} /></div>
        </div>
      </div>
    </div>
  )
})
export default Upload

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