案例展示的图片就没有了,因为需要连数据库才能看到页面的整体效果,由于用的是公司的电脑,所以自己的电脑没有数据库的数据,看不到效果图片,之前也在笔记里面说过,做的一个项目是后台管理的类型主要是对菜单和案例进行,增删改查,然后把这些数据相互关联,比如菜单树形结构的生成,菜单和案例之间的关联
1.egg连接数据库
在config文件夹下面的config.default.js进行配置,并且在plugin.js下面注册下载的egg-mysql插件
service文件夹主要是对数据库的操作,controller主要是讲数据库的内容返回给前端,前端第一个接触的就是controller里面的方法,controller在调用service里面操作数据库的方法,就可以实现基本的流程,因为我现在写的也没啥复杂的东西,就是对数据的增删改查
2.controller里面的操作
简单的一个例子,查看菜单列表并且进行分页,使用slice方法,因为这个是菜单列表,就会有,一级,二级,三级,所以最好在后台的时候就做好这些处理,生成树形结构一,前端不用做太多的操作
3.service(menuInfo)
对于数据库的设计,我刚开始是有误的,对于每一个菜单我都建了一个表,但是只用一个表就可以,写parent字段,并且也可以写一个deepth知道它在第几级,treeData方法就是生成树形结构的方法,getAllList返回数据库的数据
当然菜单和案例是有联系的所以会单独写一个方法,因为之前我用的是假数据,页面渲染的时候已经有固定的格式,需要从数据库获取数据并且生成对应的结构
个人是觉得写得有点复杂,有时间调整调整,这里之前的for循环,之前是使用的map,但是node中的map()、forEach()、for()循环有一个特性:当其函数里面里面有回调它就变成异步,之前使用map得到的是一个promise pending
解决方法:可以用async的map来处理这种情况,async是nodejs的一个组件,用来解决node的异步问题
列如
findOne是异步函数,可以使用async.map解决,参考网址https://blog.csdn.net/sam976/article/details/52330424
4.上传图片
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