乐优商城(十)商品管理

目录

3.4 商品描述信息

3.4.1 富文本编辑器

3.4.2 Vue-Quill-Editor

3.4.3 使用方法

3.4.4 自定义富文本编辑器

3.4.5 效果

3.5 规格参数

3.5.1 查询商品规格

3.5.2 页面展示规格属性

3.6 SKU特有属性

3.6.1 筛选特有属性

3.6.2 页面渲染特有属性

3.6.3 自由添加和删除文本框

3.7 展示SKU列表

3.7.1 效果预览

3.7.2 求笛卡儿积

3.7.3 页面实现

3.7.4 图片上传列表


3.4 商品描述信息

商品描述信息比较复杂,而且图文并茂,甚至包括视频。

这样的内容,一般都会使用富文本编辑器。

3.4.1 富文本编辑器

通俗来说:富文本,就是比较丰富的文本编辑器。普通的框只能输入文字,而富文本还能给文字加颜色样式等。

富文本编辑器有很多,例如:KindEditor、Ueditor。但并不原生支持vue,所以要使用vue-quill-editor,它是一款支持Vue的富文本编辑器。

3.4.2 Vue-Quill-Editor

GitHub的主页:https://github.com/surmon-china/vue-quill-editor

Vue-Quill-Editor是一个基于Quill的富文本编辑器:Quill的官网

3.4.3 使用方法

使用非常简单:

第一步:安装,使用npm命令:

npm install vue-quill-editor --save

第二步:加载,在js中引入:

全局使用:

import Vue from 'vue'
import VueQuillEditor from 'vue-quill-editor'
​
const options = {}; /* { default global options } */
​
Vue.use(VueQuillEditor, options); // options可选

局部使用:

import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
​
import {quillEditor} from 'vue-quill-editor'
​
var vm = new Vue({
    components:{
        quillEditor
    }
})

第三步:页面引用:

<quill-editor v-model="goods.spuDetail.description" :options="editorOption"/>

3.4.4 自定义富文本编辑器

vue-quill-editor默认的处理方式是直接将图片转成base64编码,这样的结果是整个富文本的html片段十分冗余,通常来讲,每个服务器端接收的post的数据大小都是有限制的,这样的话有可能导致提交失败,或者是用户体验很差,数据要传递很久才全部传送到服务器。因此,在富文本编辑的过程中,对于图片的处理,更合理的做法是将图片上传到服务器,再将图片链接插入到富文本中,以达到最优的体验。所以要进行小小的改造,使其支持上传图片到服务器的功能,请参考《改造vue-quill-editor:实现图片上传到服务器再插入富文本》

使用也非常简单:

<v-stepper-content step="2">
    <v-editor v-model="goods.spuDetail.description" upload-url="/upload/image"/>
</v-stepper-content>
  • upload-url:是图片上传的路径

  • v-model:双向绑定,将富文本编辑器的内容绑定到goods.spuDetail.description

3.4.5 效果

3.5 规格参数

商品规格参数与商品分类绑定,因此我们需要在用户选择商品分类后,去后台查询对应的规格参数模板。

3.5.1 查询商品规格

首先,在data中定义变量,用来分别记录特有和普通规格参数模板:

然后,通过watch监控goods.categories的变化,然后去查询规格:

 查询得到数据后,将数据传入到dataProcess函数中进行处理,将普通属性和特有属性分开。特有属性保存在specialSpecs中,普通属性保存在specifications中。

        //新增商品时数据处理
        dataProces(temp){
          let commonTemplate = [];
          let specialTemplate = [];
          let count = 0;
          temp.forEach(({params}) => {
            params.forEach(({k, options, global}) => {
              if (!global) {
                specialTemplate.push({
                  k, options, selected: []
                })
              }
            })
          });

          for (let i=0;i<temp.length;i++){
            if (temp[i].params.length > 0){
              let temp2 = temp[i].params;
              let param = [];
              for (let j = 0;j < temp2.length; j++){
                if (temp2[j].global){
                  param.push(temp2[j]);
                }
              }
              if (count !== temp2.length && param.length !== 0) {
                commonTemplate.push({
                  group: temp[i].group,
                  params: param,
                });
              }
            }
          }

          this.specialSpecs = specialTemplate;
          this.specifications = commonTemplate;
        }

 查看是否查询到:

3.5.2 页面展示规格属性

获取到了规格参数,还需要把它展示到页面中。

现在查询到的规格参数只有key,并没有值。值需要用户来根据SPU信息填写,因此规格参数最终需要处理为表单。

整体结构

整体来看,规格参数是数组,每个元素是一组规格的集合。我们需要分组来展示。比如每组放到一个card中。

注意事项:

规格参数中的属性有一些需要我们特殊处理:

  • global:是否是全局属性,规格参数中一部分是SPU共享,属于全局属性,另一部是SKU特有,需要根据SKU来填写。因此,在当前版面中,只展示global为true的,即全局属性。sku特有属性放到最后一个面板

  • numerical:是否是数值类型,如果是,把单位补充在页面表单,不允许用户填写,并且要验证用户输入的数据格式

  • options:是否有可选项,如果有,则使用下拉选框来渲染。

页面代码:

      <v-stepper-content step="3">
        <v-flex class="xs10 mx-auto px-3">
            <v-card v-for="spec in specifications" :key="spec.group" class="my-2">
              <v-card-title class="subheading"><h4>{{spec.group}}</h4></v-card-title>
                <v-divider></v-divider>
                <v-card-text v-for="param in spec.params" :key="param.key" v-if="param.global" class="px-5">
                  <!--判断是否有可选项,如果没有,则显示文本框。还要判断是否是数值类型,如果是则把unit显示到后缀-->
                  <v-text-field v-if="param.options.length <= 0" :label="param.k" v-model="param.v" :suffix="param.unit || ''"></v-text-field>
                  <!--否则,显示下拉选项-->
                  <v-select v-else :label="param.k" v-model="param.v" :items="param.options"/>
                </v-card-text>
            </v-card>
        </v-flex>
      </v-stepper-content>

效果:

3.6 SKU特有属性

3.6.1 筛选特有属性

特有属性已经在dataProces函数中处理过了,保存到了specialSpecs中,而且专门添加了一个selected属性,用来保存用户填写的信息。

3.6.2 页面渲染特有属性

接下来,需要把筛选出的特有规格参数,渲染到SKU页面:

预期目标效果是这样的:

可以看到,

  • 每一个特有属性自成一组,都包含标题和选项。我们可以使用card达到这个效果。

  • 无options选项的特有属性,展示一个文本框,有options选项的,展示多个checkbox,让用户选择

页面代码实现:

<!--4、SKU属性-->
<v-stepper-content step="4">
    <v-flex class="mx-auto">
        <!--遍历特有规格参数-->
        <v-card flat v-for="spec in specialSpecs" :key="spec.k">
            <!--特有参数的标题-->
            <v-card-title class="subheading">{{spec.k}}:</v-card-title>
            <!--特有参数的待选项,需要判断是否有options,如果没有,展示文本框,让用户自己输入-->
            <v-card-text v-if="spec.options.length <= 0" class="px-5">
                <v-text-field :label="'输入新的' + spec.k" v-model="spec.selected"/>
            </v-card-text>
            <!--如果有options,需要展示成多个checkbox-->
            <v-card-text v-else class="container fluid grid-list-xs">
                <v-layout row wrap class="px-5">
                    <v-checkbox color="primary" v-for="o in spec.options" :key="o" class="flex xs3"
                                :label="o" v-model="spec.selected" :value="o"/>
                </v-layout>
            </v-card-text>
        </v-card>
    </v-flex>
</v-stepper-content>

3.6.3 自由添加和删除文本框

刚才的实现中,普通文本项只有一个,如果用户想添加更多值就不行。我们需要让用户能够自由添加新的文本框,而且还能删除。这里有个取巧的方法:

1.借助selected属性,因为它是一个数组,所以可以把文本框的个数与数组的长度绑定

2.获取selected的长度,点击增加按钮,则把selected.length++复制给count

3.然后用v-for遍历count,显示出对应个数的文本框,然后文本框绑定对应的selected数组下标,因为v-for遍历是从1开始的, 所以count+1,然后文本框与数组下标绑定的时候下标为i-1

4.删除,是把文本框对应的数组下标中所包含的内容从数组中移除,使用splice函数,从i-1开始

具体代码如下:

        <v-card v-for="spec in specialSpecs" :key="spec.k">
          <v-card-title v-if="spec.options.length <= 0" class="subheading">
            <h4 class="xs3">{{spec.k}}</h4>
            <v-spacer></v-spacer>
            <v-btn flat icon color="primary" @click="count=spec.selected.length++">
              <v-icon>add</v-icon>
            </v-btn>
          </v-card-title>
          <v-card-title v-else class="subheading">
            <h4>{{spec.k}}</h4>
          </v-card-title>
          <v-card-text v-if="spec.options.length <= 0" class="px-5">
            <div v-for="i in count+1" :key="i">
              <v-layout row>
                <v-text-field :label="'输入新的'+spec.k" v-model="spec.selected[i-1]"></v-text-field>
                <v-btn :disabled="spec.selected.length === 1 || spec.selected.length === 0" flat icon color="error" @click="spec.selected.splice(i-1,1),count=spec.selected.length-1">
                  <v-icon>delete</v-icon>
                </v-btn>
              </v-layout>
            </div>
          </v-card-text>
          <v-card-text v-else class="container fluid grid-list-xs">
              <v-layout row wrap class="px-5">
                <v-checkbox color="primary" v-for="p in spec.options" :key="p" class="flex xs3" :label="p" v-model="spec.selected" :value="p">
                </v-checkbox>
              </v-layout>
          </v-card-text>
        </v-card>

效果展示:

3.7 展示SKU列表

3.7.1 效果预览

当我们选定SKU的特有属性时,就会对应出不同排列组合的SKU。

当你选择了上图中的这些选项时:

  • 颜色共2种:土豪金,绚丽红

  • 内存共2种:2GB,4GB

  • 机身存储1种:64GB

此时会产生多少种SKU呢? 应该是 2 * 2 * 1 = 4种。

因此,接下来应该由用户来对这4种sku的信息进行详细填写,比如库存和价格等。而多种sku的最佳展示方式,是表格(淘宝、京东都是这么做的),如图:

而且这个表格应该随着用户选择的不同而动态变化。

3.7.2 求笛卡儿积

N个数组的笛卡尔积

思路:

  • 先拿其中两个数组求笛卡尔积

  • 然后把前面运算的结果作为新数组,与第三个数组求笛卡尔积

方法:

reduce函数:

reduce(callback,initvalue)

callback:是一个回调函数。这个callback可以接收2个参数:arg1,arg2

  • arg1代表的上次运算得到的结果

  • arg2是数组中正要处理的元素

initvalue,初始化值。第一次调用callback时把initvalue作为第一个参数,把数组的第一个元素作为第二个参数运算。如果未指定,则第一次运算会把数组的前两个元素作为参数。

reduce会把数组中的元素逐个用这个函数处理,然后把结果作为下一次回调函数的第一个参数,数组下个元素作为第二个参数,以此类推。

因此,可以把想要求笛卡尔积的多个数组先放到一个大数组中。形成二维数组,然后再来运算。

业务需求

首先,假设已经有了一个特有参数的规格模板:

[
  {
    "k": "机身颜色",
    "selected": ["红色","黑色"]
  },
  {
    "k": "内存",
    "selected": ["8GB","6GB"]
  },
  {
    "k": "机身存储",
    "selected": ["64GB","256GB"]
  }
]

可以看做是一个二维数组。

一维是参数对象。

二维是参数中的selected选项。

最终想要的结果:

[
    {"机身颜色":"红色","内存":"6GB","机身存储":"64GB"},
    {"机身颜色":"红色","内存":"6GB","机身存储":"256GB"},
    {"机身颜色":"红色","内存":"8GB","机身存储":"64GB"},
    {"机身颜色":"红色","内存":"8GB","机身存储":"256GB"},
    {"机身颜色":"黑色","内存":"6GB","机身存储":"64GB"},
    {"机身颜色":"黑色","内存":"6GB","机身存储":"256GB"},
    {"机身颜色":"黑色","内存":"8GB","机身存储":"64GB"},
    {"机身颜色":"黑色","内存":"8GB","机身存储":"256GB"},
]

思路是这样:

  • 我们的启点是一个空的对象数组:[{}]

  • 然后先与第一个规格求笛卡尔积

  • 然后再把结果与下一个规格求笛卡尔积,依次类推

代码:

在Vue中新增一个计算属性,按照上面所讲的逻辑,计算所有规格参数的笛卡尔积,同时要对数据进行优化,因为SKU数组只包含规格参数是不够的,还需要以下字段:

  • price:价格

  • stock:库存

  • enable:是否启用。虽然笛卡尔积对应了9个SKU,但用户不一定会需要所有的组合,用这个字段进行标记。

  • images:商品的图片

  • indexes:特有属性的索引拼接得到的字符串

需要在已经生成好的笛卡尔积的基础上添加上述字段,最终代码如下:

computed:{
    skus(){
        // 过滤掉用户没有填写数据的规格参数
        const arr = this.specialSpecs.filter(s => s.selected.length > 0);
        // 通过reduce进行累加笛卡尔积
        return  arr.reduce((last, spec, index) => {
            const result = [];
            last.forEach(o => {
                for(let i = 0; i < spec.selected.length; i++){
                    const option = spec.selected[i];
                    const obj = {};
                    Object.assign(obj, o);
                    obj[spec.k] = option;
                    // 拼接当前这个特有属性的索引
                    obj.indexes = (o.indexes||'') + '_'+ i
                    if(index === arr.length - 1){
                        // 如果发现是最后一组,则添加价格、库存等字段
                        Object.assign(obj, { price:0, stock:0,enable:false, images:[]})
                        // 去掉索引字符串开头的下划线
                        obj.indexes = obj.indexes.substring(1);
                    }
                    result.push(obj);
                }
            })
            return result
        },[{}])
    }
}

查看生成的数据:

3.7.3 页面实现

页面展现是一个表格。之前已经用过。表格需要以下信息:

  • items:表格内的数据

  • headers:表头信息

刚才的计算属性skus得到的就是表格数据了。还差头:headers

头部信息也是动态的,用户选择了一个属性,就会多出一个表头。与skus是关联的。

既然如此,需要再次编写一个计算属性,来计算得出header数组:

headers(){
    if(this.skus.length <= 0){
        return []
    }
    const headers = [];
    // 获取skus中的任意一个,获取key,然后遍历其属性
    Object.keys(this.skus[0]).forEach(k => {
        let value = k;
        if(k === 'price'){
            // enable,表头要翻译成“价格”
            k = '价格'
        }else if(k === 'stock'){
            // enable,表头要翻译成“库存”
            k = '库存';
        }else if(k === 'enable'){
            // enable,表头要翻译成“是否启用”
            k = '是否启用'
        } else if(k === 'indexes' || k === 'images'){
            // 图片和索引不在表格中展示
            return;
        }
        headers.push({
            text: k,
            align: 'center',
            sortable: false,
            value
        })
    })
    return headers;
}

接下来编写页面,实现table。

需要注意的是,price、stock字段需要用户填写数值,不能直接展示。enable要展示为checkbox,让用户选择,如图:

代码:

<v-card>
    <!--标题-->
    <v-card-title class="subheading">SKU列表</v-card-title>
    <!--SKU表格,hide-actions因此分页等工具条-->
    <v-data-table :items="skus" :headers="headers" hide-actions item-key="indexes">
        <template slot="items" slot-scope="props">
            <!--价格和库存展示为文本框-->
            <td v-for="(v,k) in props.item" :key="k" v-if="['price', 'stock'].includes(k)"
                class="text-xs-center">
                <v-text-field single-line v-model.number="props.item[k]"/>
            </td>
            <!--enable展示为checkbox-->
            <td class="text-xs-center" v-else-if="k === 'enable'">
                <v-checkbox v-model="props.item[k]"/>
            </td>
            <!--indexes和images不展示,其它展示为普通文本-->
            <td class="text-xs-center" v-else-if="!['indexes','images'].includes(k)">{{v}}</td>
        </template>
    </v-data-table>
</v-card>

效果:

3.7.4 图片上传列表

这个表格中只展示了基本信息,当用户需要上传图片时,该怎么做呢?

Vuetify的table有一个展开功能,可以提供额外的展示空间:

用法也非常简单,添加一个template,把其slot属性指定为expand即可:

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值