Browse Source

完成商品添加功能

zhuyijun 2 years ago
parent
commit
1605caf333
7 changed files with 355 additions and 26 deletions
  1. 1 0
      package.json
  2. 3 0
      src/assets/css/global.css
  3. 286 16
      src/components/goods/Add.vue
  4. 6 6
      src/components/goods/Params.vue
  5. 9 1
      src/main.js
  6. 5 2
      src/plugins/element.js
  7. 45 1
      yarn.lock

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
     "core-js": "^3.6.5",
     "element-ui": "^2.15.7",
     "vue": "^2.6.11",
+    "vue-quill-editor": "^3.0.6",
     "vue-router": "^3.2.0",
     "vue-table-with-tree-grid": "^0.2.4"
   },

+ 3 - 0
src/assets/css/global.css

@@ -33,4 +33,7 @@ html, body , #app {
 
 .el-step__title {
     font-size: 13px;
+}
+.ql-editor{
+    min-height: 300px;
 }

+ 286 - 16
src/components/goods/Add.vue

@@ -3,7 +3,7 @@
  * @Version: 1.0
  * @Autor: zhuyijun
  * @Date: 2021-11-29 22:13:51
- * @LastEditTime: 2021-11-29 23:03:29
+ * @LastEditTime: 2021-12-04 15:41:28
 -->
 <template>
   <div>
@@ -25,27 +25,118 @@
       </el-alert>
       <!-- 步骤条 -->
       <el-steps :space="200"
-                :active="activeIndex"
+                :active="activeIndex - 0"
                 finish-status="success"
                 align-center>
-        <el-step title="基本信息">
-        </el-step>
+        <el-step title="基本信息"></el-step>
         <el-step title="商品参数"></el-step>
         <el-step title="商品属性"></el-step>
         <el-step title="商品图片"></el-step>
         <el-step title="商品内容"></el-step>
         <el-step title="完成"></el-step>
       </el-steps>
-      <!-- tab栏区域 -->
-      <el-tabs :tab-position="tabPosition"
-               style="height: 200px;">
-        <el-tab-pane label="基本信息">基本信息</el-tab-pane>
-        <el-tab-pane label="商品参数">商品参数</el-tab-pane>
-        <el-tab-pane label="商品属性">商品属性</el-tab-pane>
-        <el-tab-pane label="商品图片">商品图片</el-tab-pane>
-        <el-tab-pane label="商品内容">商品内容</el-tab-pane>
-      </el-tabs>
+
+      <el-form :model="addForm"
+               :rules="addFormRules"
+               ref="addFormRef"
+               label-width="100px"
+               label-position="top">
+        <!-- tab栏区域 -->
+        <el-tabs v-model="activeIndex"
+                 :tab-position="tabPosition"
+                 :before-leave="beforeTabLeave"
+                 @tab-click="tabClicked">
+          <el-tab-pane label="基本信息"
+                       name="0">
+            <el-form-item label="商品名称"
+                          prop="goods_name">
+              <el-input v-model="addForm.goods_name"></el-input>
+            </el-form-item>
+            <el-form-item label="商品价格"
+                          prop="goods_price">
+              <el-input v-model="addForm.goods_price"></el-input>
+            </el-form-item>
+            <el-form-item label="商品重量"
+                          prop="goods_weight">
+              <el-input v-model="addForm.goods_weight"></el-input>
+            </el-form-item>
+            <el-form-item label="商品数量"
+                          prop="goods_number">
+              <el-input v-model="addForm.goods_number"></el-input>
+            </el-form-item>
+            <el-form-item label="商品分类"
+                          prop="goods_cat">
+              <el-cascader v-model="addForm.goods_cat"
+                           :options="cateList"
+                           :props="cateProps"
+                           @change="handleChange"></el-cascader>
+            </el-form-item>
+          </el-tab-pane>
+          <el-tab-pane label="商品参数"
+                       name="1">
+            <!-- 渲染表单的Item项 -->
+            <el-form-item v-for="item in manyTableData"
+                          :key="item.attr_id+''"
+                          :label="item.attr_name">
+              <el-checkbox-group v-model="item.attr_vals">
+                <el-checkbox :label="cb"
+                             v-for="(cb, i) in item.attr_vals"
+                             :key="i+''"></el-checkbox>
+              </el-checkbox-group>
+            </el-form-item>
+          </el-tab-pane>
+          <el-tab-pane label="商品属性"
+                       name="2">
+            <!-- 渲染表单的Item项 -->
+            <el-form-item v-for="item in onlyTableData"
+                          :key="item.attr_id +''"
+                          :label="item.attr_name">
+              <el-input v-model="item.attr_vals"></el-input>
+            </el-form-item>
+          </el-tab-pane>
+          <el-tab-pane label="商品图片"
+                       name="3">
+            <!-- 上传图片
+            action: 表示上传api地址
+            list-type: 指定预览组件呈现方式
+             -->
+            <el-upload :action="uploadURL"
+                       :on-preview="handlePreview"
+                       :on-remove="handleRemove"
+                       list-type="picture"
+                       :headers="headerObj"
+                       :on-success="handleSuccess"
+                       v-model="addForm.pics">
+              <el-button size="small"
+                         type="primary">点击上传</el-button>
+            </el-upload>
+
+          </el-tab-pane>
+          <el-tab-pane label="商品内容"
+                       name="4">
+            <!-- 富文本编辑器 -->
+            <quill-editor v-model="addForm.goods_introduce">
+
+            </quill-editor>
+          </el-tab-pane>
+          <el-tab-pane label="完成"
+                       name="5">
+            <!-- 添加商品按钮 -->
+            <el-button type="primary"
+                       class="btnAdd"
+                       @click="add">添加商品</el-button>
+          </el-tab-pane>
+        </el-tabs>
+      </el-form>
     </el-card>
+
+    <el-dialog title="图片预览"
+               :visible.sync="previewVisible"
+               width="75%"
+               @close="previewClosed">
+      <img :src="previewPath"
+           class="previewImg">
+    </el-dialog>
   </div>
 </template>
 
@@ -54,19 +145,198 @@ export default {
   data () {
     return {
       activeIndex: 0,
-      tabPosition: 'left'
+      tabPosition: 'left',
+      addForm: {
+        goods_name: '',
+        goods_price: 0,
+        goods_weight: 0,
+        goods_number: 0,
+        //商品所属分类数组
+        goods_cat: [],
+        //   图片数组
+        pics: [],
+        //商品详情描述
+        goods_introduce: '',
+        attrs: []
+      },
+      addFormRules: {
+        goods_name: [
+          { required: true, message: '请输入商品名称', trigger: 'blur' }
+        ],
+        goods_price: [
+          { required: true, message: '请输入商品价格', trigger: 'blur' }
+        ],
+        goods_weight: [
+          { required: true, message: '请输入商品重量', trigger: 'blur' }
+        ],
+        goods_number: [
+          { required: true, message: '请输入商品数量', trigger: 'blur' }
+        ],
+        goods_cat: [
+          { required: true, message: '请选择商品分类', trigger: 'blur' }
+        ]
+      },
+      cateList: [],
+      cateProps: {
+        expandTrigger: 'hover',
+        label: 'cat_name',
+        value: 'cat_id',
+        children: 'children'
+      },
+      manyTableData: [],
+      onlyTableData: [],
+      //   上传图片的url地址
+      uploadURL: 'http://localhost:8888/api/private/v1/upload',
+      headerObj: {
+        Authorization: window.localStorage.getItem('token')
+      },
+      previewPath: '',
+      previewVisible: false
     }
   },
   created () {
-
+    this.getCateList()
   },
   methods: {
+    async getCateList () {
+      const { data: res } = await this.$http.get('categories')
+      if (res.meta.status !== 200) {
+        this.$message.error(res.meta.msg)
+      }
+      this.cateList = res.data
+    },
+    handleChange () {
+      if (this.addForm.goods_cat.length !== 3) {
+        this.addForm.goods_cat.length = []
+        this.$message.info('请选择三级分类')
+      }
+    },
+    // 监听标签切换
+    beforeTabLeave (activeName, oldActiveName) {
+      if (oldActiveName === '0' && this.addForm.goods_cat.length !== 3) {
+        this.$message.error('请选择商品分类')
+        return false
+      }
+      return true
+    },
+    async getManyAttribute () {
+      const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`, {
+        params: { sel: 'many' }
+      })
+      if (res.meta.status !== 200) {
+        this.$message.error(res.meta.msg)
+      }
+      res.data.forEach(item => {
+        let vals = item.attr_vals
+        item.attr_vals = vals.length === 0 ? [] : vals.split(' ')
+      })
+      this.manyTableData = res.data
+    },
+    async getOnlyAttribute () {
+      const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`, {
+        params: { sel: 'only' }
+      })
+      if (res.meta.status !== 200) {
+        this.$message.error(res.meta.msg)
+      }
+      //   res.data.forEach(item => {
+      //     let vals = item.attr_vals
+      //     item.attr_vals = vals.length === 0 ? [] : vals.split(' ')
+      //   })
+      this.onlyTableData = res.data
+      //   console.log(res.data)
+    },
+    tabClicked () {
+      switch (this.activeIndex) {
+        case '1':
+          this.getManyAttribute()
+          break
+        case '2':
+          this.getOnlyAttribute()
+          break
+        case '3': break
+        case '4': break
+        default:
+      }
+    },
+    // 处理图片预览效果
+    handlePreview (file) {
+      this.previewPath = file.response.data.url
+      this.previewVisible = true
+    },
+    // 处理已出图片操作
+    handleRemove (file) {
+      // 1、获取将要删除的图片的临时路径
+      const filePath = file.response.data.tmp_path
+      // 2、从pics素组中,找到这个图片的索引值
+      const i = this.addForm.pics.findIndex(item => item.pic === filePath)
+      // 3、调用数组的splice方法,把图片信息对象,从pics数组中移除
+      this.addForm.pics.splice(i, 1)
+    },
+    // 监听上传成功的事件
+    handleSuccess (response) {
+      //1、拼接得到一个图片信息对象
+      const picInfo = { pic: response.data.tmp_path }
+      // 2、将图片信息对象 push到pics数组中
+      this.addForm.pics.push(picInfo)
+    },
+    previewClosed () {
+      this.previewPath = ''
+    },
+    // 添加商品函数
+    add () {
+      this.$refs.addFormRef.validate(async (valid) => {
+        if (!valid) return this.$message.error('请填写必要的表单项!')
+        // 深拷贝
+        let form = JSON.parse(JSON.stringify(this.addForm))
+        form.goods_cat = form.goods_cat.join(',')
+        // 处理动态参数
+        this.manyTableData.forEach(item => {
+          const attrInfo = {
+            attr_id: item.attr_id,
+            attr_value: item.attr_vals.join(' ')
+          }
+          form.attrs.push(attrInfo)
+        })
+        // 处理静态属性
+        this.onlyTableData.forEach(item => {
+          const attrInfo = {
+            attr_id: item.attr_id,
+            attr_value: item.attr_vals
+          }
+          form.attrs.push(attrInfo)
+        })
+
+        // 商品名称唯一
 
+        // 执行添加逻辑
+        const { data: res } = await this.$http.post(`goods`, form)
+        if (res.meta.status !== 201) {
+          this.$message.error(res.meta.msg)
+        }
+        this.$message.success(res.meta.msg)
+        this.$router.push('/goods')
+      })
+    }
   },
   computed: {
-
+    cateId () {
+      if (this.addForm.goods_cat.length === 3) {
+        return this.addForm.goods_cat[2]
+      }
+      return null
+    }
   }
 }
 </script>
 <style lang="less" scoped>
+.el-checkbox {
+  margin: 0 10px 0 0 !important;
+}
+.previewImg {
+  width: 100%;
+}
+.btnAdd {
+  margin-top: 15px;
+}
 </style>

+ 6 - 6
src/components/goods/Params.vue

@@ -3,7 +3,7 @@
  * @Version: 1.0
  * @Autor: zhuyijun
  * @Date: 2021-11-25 22:52:20
- * @LastEditTime: 2021-11-27 23:14:52
+ * @LastEditTime: 2021-12-04 13:18:17
 -->
 <template>
   <div>
@@ -174,9 +174,9 @@
                       prop="attr_name">
           <el-input v-model="addForm.attr_name"></el-input>
         </el-form-item>
-        <el-form-item :label="titleText+'的值'">
+        <!-- <el-form-item :label="titleText+'的值'">
           <el-input v-model="addForm.attr_vals"></el-input>
-        </el-form-item>
+        </el-form-item> -->
       </el-form>
       <span slot="footer"
             class="dialog-footer">
@@ -206,9 +206,9 @@
                       prop="attr_name">
           <el-input v-model="editForm.attr_name"></el-input>
         </el-form-item>
-        <el-form-item :label="titleText+'的值'">
+        <!-- <el-form-item :label="titleText+'的值'">
           <el-input v-model="editForm.attr_vals"></el-input>
-        </el-form-item>
+        </el-form-item> -->
       </el-form>
       <span slot="footer"
             class="dialog-footer">
@@ -300,7 +300,7 @@ export default {
         return this.$message.error(res.meta.msg)
       }
       res.data.forEach(item => {
-        item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []
+        item.attr_vals = item.attr_vals.length ? item.attr_vals.split(' ') : []
         // 文本框的显示与隐藏
         item.inputVisible = false
         // 文本框输入的值

+ 9 - 1
src/main.js

@@ -3,7 +3,7 @@
  * @Version: 1.0
  * @Autor: zhuyijun
  * @Date: 2021-11-21 23:56:19
- * @LastEditTime: 2021-11-28 16:22:39
+ * @LastEditTime: 2021-12-04 14:38:28
  */
 import Vue from 'vue'
 import App from './App.vue'
@@ -15,6 +15,12 @@ import './assets/css/global.css'
 // import './assets/fonts/iconfont.css'
 // 插件
 import TreeTable from 'vue-table-with-tree-grid'
+// 导入富文本编辑器
+import VueQuillEditor from 'vue-quill-editor'
+// 导入富文本编辑器对应的样式
+import 'quill/dist/quill.core.css'
+import 'quill/dist/quill.snow.css'
+import 'quill/dist/quill.bubble.css'
 
 import axios from 'axios'
 // 拦截器 设置请求头中的token
@@ -42,6 +48,8 @@ Vue.filter('dateFormat', function (originVal) {
     const ss = (dt.getSeconds() + '').padStart(2, '0')
     return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
 })
+// 将富文本编辑器注册为全局可用的编辑器
+Vue.use(VueQuillEditor)
 new Vue({
     router,
     render: h => h(App)

+ 5 - 2
src/plugins/element.js

@@ -3,14 +3,14 @@
  * @Version: 1.0
  * @Autor: zhuyijun
  * @Date: 2021-11-21 23:56:19
- * @LastEditTime: 2021-11-29 22:49:05
+ * @LastEditTime: 2021-12-04 13:44:37
  */
 import Vue from 'vue'
 import {
     Button, Form, FormItem, Input, Message, Container, Header, Aside, Main, Footer,
     Menu, MenuItem, Submenu, Breadcrumb, BreadcrumbItem, Card, Row, Col, Table,
     TableColumn, Switch, Tooltip, Pagination, Dialog, MessageBox, Tag, Tree, Select,
-    Option, Cascader, Alert, Tabs, TabPane, Step, Steps
+    Option, Cascader, Alert, Tabs, TabPane, Step, Steps, CheckboxGroup, Checkbox, Upload
 } from 'element-ui'
 Vue.use(Button)
 Vue.use(Form)
@@ -45,5 +45,8 @@ Vue.use(Tabs)
 Vue.use(TabPane)
 Vue.use(Steps)
 Vue.use(Step)
+Vue.use(CheckboxGroup)
+Vue.use(Checkbox)
+Vue.use(Upload)
 Vue.prototype.$message = Message
 Vue.prototype.$confirm = MessageBox.confirm

+ 45 - 1
yarn.lock

@@ -3791,6 +3791,11 @@ event-pubsub@4.3.0:
   resolved "https://registry.npm.taobao.org/event-pubsub/download/event-pubsub-4.3.0.tgz"
   integrity sha1-9o2Ba8KfHsAsU53FjI3UDOcss24=
 
+eventemitter3@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.npm.taobao.org/eventemitter3/download/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
+  integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=
+
 eventemitter3@^4.0.0:
   version "4.0.7"
   resolved "https://registry.npm.taobao.org/eventemitter3/download/eventemitter3-4.0.7.tgz"
@@ -3922,7 +3927,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
     assign-symbols "^1.0.0"
     is-extendable "^1.0.1"
 
-extend@~3.0.2:
+extend@^3.0.2, extend@~3.0.2:
   version "3.0.2"
   resolved "https://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz"
   integrity sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=
@@ -3960,6 +3965,11 @@ fast-deep-equal@^3.1.1:
   resolved "https://registry.nlark.com/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz"
   integrity sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=
 
+fast-diff@1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npm.taobao.org/fast-diff/download/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154"
+  integrity sha1-S2LEK44D3j+EhGC2OQeZIGldAVQ=
+
 fast-glob@^2.2.6:
   version "2.2.7"
   resolved "https://registry.nlark.com/fast-glob/download/fast-glob-2.2.7.tgz"
@@ -6295,6 +6305,11 @@ param-case@2.1.x:
   dependencies:
     no-case "^2.2.0"
 
+parchment@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.npm.taobao.org/parchment/download/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5"
+  integrity sha1-rt7Xq5OP6SHUw0vDOc4RaLwv/eU=
+
 parent-module@^1.0.0:
   version "1.0.1"
   resolved "https://registry.npmmirror.com/parent-module/download/parent-module-1.0.1.tgz"
@@ -7024,6 +7039,27 @@ querystringify@^2.1.1:
   resolved "https://registry.nlark.com/querystringify/download/querystringify-2.2.0.tgz"
   integrity sha1-M0WUG0FTy50ILY7uTNogFqmu9/Y=
 
+quill-delta@^3.6.2:
+  version "3.6.3"
+  resolved "https://registry.npm.taobao.org/quill-delta/download/quill-delta-3.6.3.tgz#b19fd2b89412301c60e1ff213d8d860eac0f1032"
+  integrity sha1-sZ/SuJQSMBxg4f8hPY2GDqwPEDI=
+  dependencies:
+    deep-equal "^1.0.1"
+    extend "^3.0.2"
+    fast-diff "1.1.2"
+
+quill@^1.3.4:
+  version "1.3.7"
+  resolved "https://registry.nlark.com/quill/download/quill-1.3.7.tgz#da5b2f3a2c470e932340cdbf3668c9f21f9286e8"
+  integrity sha1-2lsvOixHDpMjQM2/NmjJ8h+Shug=
+  dependencies:
+    clone "^2.1.1"
+    deep-equal "^1.0.1"
+    eventemitter3 "^2.0.3"
+    extend "^3.0.2"
+    parchment "^1.1.4"
+    quill-delta "^3.6.2"
+
 randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
   version "2.1.0"
   resolved "https://registry.npm.taobao.org/randombytes/download/randombytes-2.1.0.tgz"
@@ -8516,6 +8552,14 @@ vue-loader@^15.9.2:
     vue-hot-reload-api "^2.3.0"
     vue-style-loader "^4.1.0"
 
+vue-quill-editor@^3.0.6:
+  version "3.0.6"
+  resolved "https://registry.nlark.com/vue-quill-editor/download/vue-quill-editor-3.0.6.tgz#1f85646211d68a31a80a72cb7f45bb2f119bc8fb"
+  integrity sha1-H4VkYhHWijGoCnLLf0W7LxGbyPs=
+  dependencies:
+    object-assign "^4.1.1"
+    quill "^1.3.4"
+
 vue-router@^3.2.0:
   version "3.5.3"
   resolved "https://registry.npmmirror.com/vue-router/download/vue-router-3.5.3.tgz"