商品列表和添加商品(六)

实现的功能

功能 详述
商品列表 渲染组件和子路由;布局;获取和渲染商品列表;自定义格式化时间全局过滤器;搜索和清空;删除商品
添加商品页 渲染组件和子路由;布局;步骤条纵向Tab页;步骤条和标签页联动
基本信息表单 商品分类使用级联选择器,只允许选中三级阻止标签页切换
动态参数页 复选框tag;根据不同tab页执行不同操作
图片上传 渲染组件;图片上传图片删除图片预览
富文本编辑器
添加商品 深拷贝

使用到的Element-ui组件

组件名称_EN 注册 备注
Steps Vue.use(Steps) 步骤条
Step Vue.use(Step)
CheckboxGroup Vue.use(CheckboxGroup) 多选框
Checkbox Vue.use(Checkbox)
Upload Vue.use(Upload) 上传

使用到的依赖

运行依赖,富文本编辑器vue-quill-editor

// [main.js]
// 导入富文本编辑器
import VueQuillEditor from 'vue-quill-editor'
// 导入富文本编辑器对应样式
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme

// 注册富文本编辑器
Vue.use(VueQuillEditor)

运行依赖,lodash深拷贝lodash

// [Add.vue -> methods]
import _ from 'lodash'

一、商品列表

1.渲染组件和子路由

2.布局

①面包屑导航

②卡片视图

③栅格系统 搜索框 添加按钮

④表格

⑤分页

3.获取渲染商品列表

①获取商品列表

②渲染商品列表

1️⃣自定义格式化时间全局过滤器

// [main.js]
Vue.filter('dataFormat', function (originVal) {
  const dt = new Date(originVal)
  const y = dt.getFullYear()
  const m = (dt.getMonth() + 1 + '').padStart(2, '0')
  const d = (dt.getDate() + '').padStart(2, '0')

  const hh = (dt.getHours() + '').padStart(2, '0')
  const mm = (dt.getMinutes() + '').padStart(2, '0')
  const ss = (dt.getSeconds() + '').padStart(2, '0')

  return `${y}-${m}-${d} ${hh}:${mm}:${ss}`

})
<!-- [GoodsList.vue] -->
<el-table-column label="创建时间" width="140px">
    <template slot-scope="scope">{{scope.row.add_time | dataFormat}}</template>
</el-table-column>

4、搜索和清空

5、根据Id删除商品

6、添加商品页跳转

1️⃣编程式导航

// [GoodsList.vue]
goAddpage(){
    this.$router.push('/goods/add')
}
// [index.js]
const routes = [
    // ......
    {path:'/goods/add',component:Add},
    // ......
]

二、添加商品页

1.渲染组件和子路由

2.布局

①面包屑导航

②卡片视图

③警告

④步骤条Steps

:active="activeIndex-0":激活项

<!-- [Add.vue] -->
<!-- 步骤条 -->
<el-steps :space="200" :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-steps>
// [Add.vue -> data]
activeIndex: '0'
/* [assets/css/global.css] */
.el-steps{
    margin: 15px 0;
}
.el-step__title{
    font-size: 13px;
}

⑤纵向标签页Tabs

<!-- [Add.vue] -->
<el-tabs
          :tab-position="'left'"
          v-model="activeIndex"
          :before-leave="beforeTabLeave"
          @tab-click="tabClicked"
        >
          <el-tab-pane label="基本信息" name="0"></el-tab-pane>
          <el-tab-pane label="商品参数" name="1"></el-tab-pane>
          <el-tab-pane label="商品图片" name="3"></el-tab-pane>
          <el-tab-pane label="商品内容" name="4"></el-tab-pane>
</el-tabs>

⑥步骤条和标签页联动

activeIndex: '0',

步骤条:active

​ 转换activeIndex为数值

标签页:v-model

​ 激活的name会绑定到v-model上

⑦表单

使用form包裹tabs,el-tabs必须和el-tab-pane嵌套

3、基本信息

①渲染基本信息表单

1️⃣商品分类使用级联选择器,只允许选中三级

// [Add.vue -> methods]
// 级联选择器选中项变化
handleChange() {
    // 选中的不是三级分类
    if (this.addForm.goods_cat.length !== 3) {
        this.addForm.goods_cat = []
    }
}

2️⃣阻止标签页切换

:before-leave="beforeTabLeave":标签页属性,离开时触发

oldActiveName:离开的标签页name

activeName:进入的标签页name

// [Add.vue -> methods]
beforeTabLeave(activeName, oldActiveName) {
    if (oldActiveName === '0' && this.addForm.goods_cat.length !== 3) {
        this.$message.error('请先选择商品分类')
        return false
    }
}

②获取商品分类数据

4、动态参数

①获取动态参数

@tab-click="tabClicked":标签页属性,点击时触发,可以获取activeIndex访问的tab面板

// [Add.vue -> methods]
async tabClicked() {
      if (this.activeIndex === '1') {
          // 访问动态参数面板
          // ......
      } else if (this.activeIndex === '2') {
          // 访问静态属性面板
          // ......
      }
    },

②渲染动态参数

// [Add.vue -> methods]
async tabClicked() {
    if (this.activeIndex === '1') {
        // ......
        res.data.forEach((item) => {
            item.attr_vals = 
                item.attr_vals.length === 0 ? [] : item.attr_vals.split(',')
        })
    }
    // ......
}
<!-- [Add.vue] -->
<el-tab-pane label="商品参数" name="1">
    <el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id">
        <!-- 复选框组 -->
        <el-checkbox-group v-model="item.attr_vals">
            <el-checkbox :label="cb" v-for="(cb,i) in item.attr_vals" :key="i" border></el-checkbox>
        </el-checkbox-group>
    </el-form-item>
</el-tab-pane>

5、静态属性

①获取静态属性

②渲染静态属性

6、图片上传

action:图片上传的api地址

:on-preview:图片预览

:on-remove:删除图片

:headers:上传的请求头

:on-success:图片上传成功后处理操作

①渲染图片+上传

<!-- [Add.vue] -->
<el-tab-pane label="商品图片" name="3">
    <!-- 上传 -->
    <el-upload
               :action="uploadURl"
               :on-preview="handlePreview"
               :on-remove="handleRemove"
               list-type="picture"
               :headers="headerObj"
               :on-success="handleSuccess"
               >
        <el-button size="small" type="primary">点击上传</el-button>
    </el-upload>
</el-tab-pane>
// [Add.vue -> data]
uploadURl: 'http://127.0.0.1:8888/api/private/v1/upload',
// 图片上传的headers请求头对象
headerObj: {
    Authorization: window.sessionStorage.getItem('token'),
},

// [Add.vue -> methods]    
// 监听图片上传成功
handleSuccess(response) {
    // 1,拼接得到一个图片信息对象
    const picInfo = { pic: response.data.tmp_path }
    //2,将图片信息对象,push 到pics数组中
    this.addForm.pics.push(picInfo)
},

②图片移除

// 处理移除图片
handleRemove(file) {
    // 1,获取将要删除的图片的临时路径
    const filePath = file.response.data.tmp_path
    // 2.从pics数组中,找到这个图片对应的索引值
    const i = this.addForm.pics.findIndex((x) => x.pic === filePath)
    // 3.调用数组的 splice方法,把图片信息对象,从pics数组中移除
    this.addForm.pics.splice(i, 1)
},

③图片预览

<!-- 图片预览 -->
<el-dialog title="图片预览" :visible.sync="previewDialogVisible" width="50%">
    <img :src="previewPath" alt class="previewImg" />
</el-dialog>
// [Add.vue -> data]
previewPath: '',
previewDialogVisible: false

// [Add.vue -> methods]    
// 处理图片预览
handlePreview(file) {
    this.previewPath = file.response.data.url
    this.previewDialogVisible = true
}

7、富文本编辑器

<!-- [Add.vue] -->
<el-tab-pane label="商品内容" name="4">
    <!-- 富文本编辑器 -->
    <quill-editor v-model="addForm.goods_introduce"></quill-editor>
    <el-button type="primary" class="btnAdd" @click="add">添加商品</el-button>
</el-tab-pane>
/* [assets/css/global.css] */
.ql-editor{
    min-height: 300px;
}

8、添加商品

表单预校验,校验通过发起请求

①预校验

// [Add.vue -> methods]  
// 添加商品
async add() {
    // 预校验
    this.$refs.addFormRulesRef.validate((valid) => {
        if (!valid) return this.$message.error('请填写必要的表单项')
    })
    // ......
}

②深拷贝

级联选择器要求数组,请求要求字符串---->深拷贝

// [Add.vue -> methods]  
// 添加商品
async add() {
    // ......
    
    // 执行添加的业务逻辑
    // 深拷贝 lodash  this.addForm双向绑定级联选择器会报错
    const form = _.cloneDeep(this.addForm)
    form.goods_cat = form.goods_cat.join(',')
    // ......
}

③商品参数(动态参数+静态属性)

// [Add.vue -> methods]  
// 添加商品
async add() {
    // ......
    // 处理动态参数
    this.manyTableData.forEach((item) => {
        const newInfo = {
            attr_id: item.attr_id,
            attr_value: item.attr_vals.join(','),
        }
        this.addForm.attrs.push(newInfo)
    })
    // 处理静态属性
    this.onlyTableData.forEach((item) => {
        const newInfo = {
            attr_id: item.attr_id,
            attr_value: item.attr_vals,
        }
        this.addForm.attrs.push(newInfo)
    })
    form.attrs = this.addForm.attrs
    // ......
}

④发起请求

// [Add.vue -> methods]  
// 添加商品
async add() {
    // ......
    // 发起请求
    const { data: res } = await this.$http.post('goods', form)
    if (res.meta.status !== 201) {
        return this.$message.error('添加商品失败')
    }
    this.$message.success('添加商品成功')
    this.$router.push('/goods')
}
posted @ 2020-08-27 21:15  wattmelon  阅读(446)  评论(0编辑  收藏  举报