写后台商品页面的思路
1、分析功能需求
管理商品必须要实现的几个功能
- 1、展示所有商品
- 2、添加商品
- 3、修改商品
2、分析数据结构
先看一下数据结构
首先商品分类
类下面的商品详情
3、 实现功能步骤
思路:
1、将添加商品类,添加商品,修改商品分离成单独的组件
2、添加路由
3、 先写出添加商品页面
挂在路由打开mangerprods.vue
<template>
<section class="box">
<div class="head">
<h3>{{this.$route.name}}</h3>
</div>
<!-- 商品管理路由 -->
<div class="prodmenu">\
<el-menu
mode="horizontal"
:default-active="$router.path"
router>
<el-menu-item
v-for="item in $router.options.routes[1].children[1].children"
:key="item.path"
:index="item.path">
{{item.name}}
</el-menu-item>
</el-menu>
</div>
<!-- 渲染路由 -->
<router-view></router-view>
</section>
</template>
<script>
export default {
// ..
}
</script>
<style lang="less" scoped>
@import '../../common/less/index.less';
.box {
.head {
.leftborder
}
.prodmenu {
margin: 0 15px;
}
}
</style>
4、写添加商品类别页面
<template>
<!-- 添加商品类 -->
<div class="addprod">
<h4>添加一个商品类</h4>
<el-input
placeholder="请输入商品类名"
v-model="prodtype"
clearable>
</el-input>
<el-input
placeholder="请输入商品类简介"
v-model="prodsub"
type="textarea"
:rows="2"
clearable>
</el-input>
<el-button type="danger" :disabled="disabled" @click="addtype" round>添加</el-button>
</div>
</template>
<script>
export default {
data () {
return {
prodtype: '',
prodsub: ''
}
},
computed: {
disabled () {
if (this.prodtype === '' || this.prodsub === '') {
return true
} else {
return false
}
}
},
methods: {
addtype () {
console.log('do')
}
}
}
</script>
<style lang="less" scoped>
@import '../../../common/less/index.less';
.addprod {
.learncontent;
.el-input {
margin: 5px 0;
}
.el-button {
margin: 10px 0;
width: 100%;
}
}
</style>
5、写添加商品页面
<template>
<!-- 新增商品 -->
<div class="addprod">
<h4>新增商品</h4>
<el-form ref="addprod" :rules="prodrules" :model="addprod" label-width="80px">
<el-form-item label="商品名" prop="name">
<el-input v-model="addprod.name" placeholder="请输入商品名"></el-input>
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input v-model.number="addprod.price" placeholder="请输入商品价格"></el-input>
</el-form-item>
<el-form-item label="商品主图" prop="image">
<el-upload
class="prod-image"
action="/learn/upload"
:show-file-list="false"
:on-success="handleSuccess"
:before-upload="beforeUpload">
<img v-if="imageUrl" :src="imageUrl" class="cur-image">
<i v-else class="el-icon-plus prod-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="商品类别" prop="type">
<el-select v-model="addprod.type" placeholder="请选择商品类别">
<el-option label="石榴" value="shiliu"></el-option>
<el-option label="火腿" value="ham"></el-option>
</el-select>
</el-form-item>
<el-form-item label="是否上架">
<el-switch v-model="addprod.selling"></el-switch>
</el-form-item>
<el-form-item label="商品简介" prop="desc">
<el-input type="textarea" v-model="addprod.desc" placeholder="请请输入商品简介"></el-input>
</el-form-item>
<el-form-item label="商品详情" prop="info">
<mavon-editor ref="md" @imgAdd="$imgAdd" @imgDel="$imgDel" v-model="addprod.info"></mavon-editor>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="newprod">立即添加</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {UploadFile} from '../../../api/api'
export default {
data () {
return {
imageUrl: '',
addprod: {
name: '',
price: '',
type: '',
selling: '',
desc: '',
info: ''
},
prodrules: {
name: [
{
required: true,
message: '请输入商品名',
trigger: 'blur'
},
{
min: 3,
max: 15,
message: '长度在 3 到 15 个字',
trigger: 'blur'
}
],
price: [
{
required: true,
message: '请输入商品价格',
trigger: 'blur'
},
{
type: 'number',
message: '价格必须是数字',
trigger: 'blur'
}
],
type: [
{
required: true,
message: '商品必须选择一个类别',
trigger: 'change'
}
],
desc: [
{
required: true,
message: '请输入商品简介',
trigger: 'blur'
}
]
}
}
},
methods: {
newprod () {
this.$refs.addprod.validate(valid => {
if (valid) {
// console.log('add prod!')
// const prodFd = new FormData()
// prodFd.append('name', this.addprod.name)
} else {
console.log('请先完成验证')
return false
}
})
},
// mavoneditor图片上传并替换地址
// 绑定@imgAdd event
$imgAdd (pos, $file) {
// 第一步.将图片上传到服务器.
let formdata = new FormData()
formdata.append('file', $file)
UploadFile(formdata)
.then(url => {
// console.log(url)
console.log(this.addprod.info)
// 第二步.将返回的url替换到文本原位置![...](./0) -> ![...](url)
this.$refs.md.$img2Url(pos, url.data)
})
},
$imgDel (pos) {
delete this.img_file[pos]
},
// 获取商品主图上传成功后返回的图片
handleSuccess (res, file) {
this.imageUrl = URL.createObjectURL(file.raw)
},
// 商品主图再上传前对文件进行判断
beforeUpload (file) {
const isPIC = file.type === 'image/jpeg' || 'image/png'
const isLt5M = file.size / 1024 / 1024 < 5
if (!isPIC) {
this.$message.error('上传图片只能是 JPG或PNG 格式!')
}
if (!isLt5M) {
this.$message.error('上传图片大小不能超过 5MB!')
}
return isPIC && isLt5M
}
}
}
</script>
<style lang="less" scoped>
@import '../../../common/less/index.less';
.addprod {
.learncontent;
.el-form {
text-align: left;
.el-select {
width: 100%;
}
.el-switch {
margin: 10px 0 0 0;
}
.prod-image {
width: 200px;
height: 200px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
.cur-image {
width: 100%;
}
.prod-uploader-icon {
font-size: 45px;
color: #8c939d;
width: 200px;
height: 200px;
line-height: 200px;
text-align: center;
}
}
}
}
</style>
6、引入markdown编辑器
cnpm i mavon-editor --save
引用
main.js
使用
再addprod.vue中直接使用
引用mavoneditor
mavonedior上传图片操作
与服务端进行交互的API
因为markdown编辑器是具有实时预览功能,如果我们将本地图片插入,执行步骤是这样的
1、他会立即执行上传图片操作,并获取服务端返回的图片地址
2、获取到图片地址,mavon会立刻向服务端请求这个地址来获取这张图片,并渲染出来
- 7 写服务端代码
4、测试效果
效果是合适的
同时后端也记录了3次上传和3次获取图片
5、遇见的坑
我当时写服务端代码的时候,再上传图片的地方,我将
const form = new formidable.IncomingForm()
写在了页面的开头,并没有将formidable的实例化卸载每一次上传的过程中,这导致了一个问题,上传第一张图片可以成功,但上传第二张开始就发生错误
Can't set headers after they are sent
这是因为我所有的req解析都在同一个实例化的form里面,第一次执行upload成功时,form会调用一次form.on('end'),第二次upload成功时,form也会调用一次form.on('end'),这样就产生了Can't set headers after they are sent这个错误
6、感谢segmentfault的 @程序猿小卡_casper
再此非常感谢segmentfault的 @程序猿小卡_casper,无私的帮助我解决了问题并细心的讲解错误原因,谢谢!!,同时也感谢其他真心帮助我解决问题的朋友!
又兴趣的朋友可以看看这个问题的原题
用nodejs的formidable上传图片,第一张上传成功,再上传发生错误Can't set headers after they are sent
https://segmentfault.com/q/10...
7 、github地址:
learn:https://github.com/lyttonlee/...
server:https://github.com/lyttonlee/...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。