vue vue-router vuex element-ui axios的學習筆記(十九)寫後臺商品管理頁面

寫後臺商品頁面的思路

一、分析功能需求

管理商品必需要實現的幾個功能
  • 一、展現全部商品
  • 二、添加商品
  • 三、修改商品

二、分析數據結構

先看一下數據結構

首先商品分類vue

選區_014.png

類下面的商品詳情node

選區_016.png

三、 實現功能步驟

思路:

一、將添加商品類,添加商品,修改商品分離成單獨的組件git

選區_017.png

二、添加路由github

選區_018.png

選區_019.png

三、 先寫出添加商品頁面express

掛在路由打開mangerprods.vuenpm

<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>

四、寫添加商品類別頁面segmentfault

<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>

五、寫添加商品頁面後端

<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>

六、引入markdown編輯器api

cnpm i mavon-editor --save

引用
main.js服務器

選區_020.png

使用

再addprod.vue中直接使用

引用mavoneditor

選區_025.png

mavonedior上傳圖片操做

選區_026.png

與服務端進行交互的API

選區_027.png

由於markdown編輯器是具備實時預覽功能,若是咱們將本地圖片插入,執行步驟是這樣的
一、他會當即執行上傳圖片操做,並獲取服務端返回的圖片地址
二、獲取到圖片地址,mavon會馬上向服務端請求這個地址來獲取這張圖片,並渲染出來
  • 7 寫服務端代碼

選區_028.png

選區_029.png

四、測試效果

up.gif

效果是合適的

選區_030.png

同時後端也記錄了3次上傳和3次獲取圖片

五、碰見的坑

我當時寫服務端代碼的時候,再上傳圖片的地方,我將
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這個錯誤

六、感謝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/...

相關文章
相關標籤/搜索