【B2C-爱购物商城】 开发文档 day07

版权声明:★Study hard and make progress every day.☺★ https://blog.csdn.net/qq_38225558/article/details/86545817

一、sku相关概念

1、什么是sku??

SKU=Stock Keeping Unit(库存量单位).一个sku能够确定有多少库存及什么价格 --> 相当于是条形码.

什么又是spu??

SPU = Standard Product Unit (标准产品单位)SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。

例如:iPhone X 可以确定一个产品即为一个SPU。

2、sku属性及选项

一个sku能够确定库存及价格.而且知道价格会被多个sku属性影响.而且每个sku属性都是有多个sku属性选项.

一个商品有多个sku,sku的组成是由多个sku属性及它们的sku属性选项进行组合产生 注:我们买卖的sku,不是商品!

xxx旗袍 -sku

sku属性:颜色,和尺寸

sku属性选项: 颜色(红色,白色),尺寸(xl,xxl)

skus:红色xl,红色xxl.白色xl,白色xxl

3、什么是商品SKU?

商品SKU实际上就是SKU,为了避免误解和SKU属性混淆,我用商品SKU来命名,表示从属于商品的、实际销售和存储的子实体。

一个商品SKU,表示该商品关联的若干SKU属性的的属性值的某个组合所形成的子实体。

如对应上面的例子,其中的一种组合 XL + 红色 就会形成一个商品SKU。然后,我们可以在该实体上管理价格、库存、专门的图片等信息。

4、商品变种(变异)

商品变异其实就是商品SKU,只不过在某些技术文章中这样定义了。即以“变异”来表达商品SKU的生成

5、属性集

用于管理各类扩展属性的集合,其中SKU属性也是在管理范畴之内。

商品通过关联属性集而获得该属性集设置好的SKU属性,然后才可以根据这些SKU属性生成商品SKU。

属性集也成为产品类型。

常见的属性集有:服装、PC、家具、图书等。

二、前后端代码实现

1、后端根据数据库生成如下表:
在这里插入图片描述

注:这里t_product中新增了一个medias字段,因此重新生成覆盖了之前的!
在这里插入图片描述

修改生成的代码:
在这里插入图片描述

对于前端显示时间问题解决:
在这里插入图片描述

2、zuul网关配置超时时间 解决前端fallback问题
在这里插入图片描述

3、完成后端代码

public interface ProductMapper extends BaseMapper<Product> {
    List<Product> loadPageData(Page<Product> page, ProductQuery query); //加载分页数据
}
public interface IProductService extends IService<Product> {
    PageList<Product> selectPageList(ProductQuery query);//跨表高级分页
}
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private ProductExtMapper productExtMapper;

    @Override
    public PageList<Product> selectPageList(ProductQuery query) {
        Page<Product> page = new Page<>(query.getPage(),query.getRows());
        List<Product> data =  productMapper.loadPageData(page,query);
        long total = page.getTotal();
        return new PageList<>(total,data);
    }

    @Override
    public boolean insert(Product entity) {
        //添加本表信息以外,还要存放关联表
        entity.setCreateTime(new Date().getTime());
        productMapper.insert(entity);
        System.out.println(entity.getId());
        if (entity.getProductExt() != null) {
            entity.getProductExt().setProductId(entity.getId());
            productExtMapper.insert(entity.getProductExt());
        }
        return true;
    }
    @Override
    public boolean updateById(Product entity) {
        //添加本表信息以外,还要存放关联表
        entity.setUpdateTime(new Date().getTime());
        productMapper.updateById(entity);

        //通过productId查询productExt
        Wrapper<ProductExt> wrapper = new EntityWrapper<ProductExt>()
                .eq("productId", entity.getId());
        ProductExt productExt = productExtMapper.selectList(wrapper).get(0);

        //把前台传递进来值设置给数据库查询出来值,并且把它修改进去
        ProductExt tmp = entity.getProductExt();
        if ( tmp!= null) {
            productExt.setDescription(tmp.getDescription());
            productExt.setRichContent(tmp.getRichContent());
            productExtMapper.updateById(productExt);
        }
        return true;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhengqing.aigou.mapper.ProductMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.zhengqing.aigou.domain.Product">
        <id column="id" property="id" />
        <result column="createTime" property="createTime" />
        <result column="updateTime" property="updateTime" />
        <result column="name" property="name" />
        <result column="subName" property="subName" />
        <result column="code" property="code" />
        <result column="product_type_id" property="productTypeId" />
        <result column="onSaleTime" property="onSaleTime" />
        <result column="offSaleTime" property="offSaleTime" />
        <result column="brand_id" property="brandId" />
        <result column="state" property="state" />
        <result column="maxPrice" property="maxPrice" />
        <result column="minPrice" property="minPrice" />
        <result column="saleCount" property="saleCount" />
        <result column="viewCount" property="viewCount" />
        <result column="commentCount" property="commentCount" />
        <result column="commentScore" property="commentScore" />
        <result column="viewProperties" property="viewProperties" />
        <result column="goodCommentCount" property="goodCommentCount" />
        <result column="commonCommentCount" property="commonCommentCount" />
        <result column="medias" property="medias" />
        <result column="badCommentCount" property="badCommentCount" />
    </resultMap>

    <!--List<Product> loadPageData(Page<Product> page, ProductQuery query);-->
    <select id="loadPageData" parameterType="ProductQuery" resultMap="productMap">
        SELECT
            p.*, pt.id ptid,
            pt.`name` ptname,
            b.id bid,
            b. NAME bname,
            pe.id peid,
            pe.description,
            pe.richContent
        FROM
            t_product p
        LEFT JOIN t_product_type pt ON p.product_type_id = pt.id
        LEFT JOIN t_brand b ON p.brand_id = b.id
        LEFT JOIN t_product_ext pe on p.id = pe.productId
        order by id ASC
    </select>
    <resultMap id="productMap" type="Product">
        <!--基本信息管理-->
        <id column="id" property="id" />
        <result column="createTime" property="createTime" />
        <result column="updateTime" property="updateTime" />
        <result column="name" property="name" />
        <result column="subName" property="subName" />
        <result column="code" property="code" />
        <result column="product_type_id" property="productTypeId" />
        <result column="onSaleTime" property="onSaleTime" />
        <result column="offSaleTime" property="offSaleTime" />
        <result column="brand_id" property="brandId" />
        <result column="state" property="state" />
        <result column="maxPrice" property="maxPrice" />
        <result column="minPrice" property="minPrice" />
        <result column="saleCount" property="saleCount" />
        <result column="viewCount" property="viewCount" />
        <result column="commentCount" property="commentCount" />
        <result column="commentScore" property="commentScore" />
        <result column="viewProperties" property="viewProperties" />
        <result column="goodCommentCount" property="goodCommentCount" />
        <result column="commonCommentCount" property="commonCommentCount" />
        <result column="medias" property="medias" />
        <result column="badCommentCount" property="badCommentCount" />
        <!--品牌和类型-->
        <association property="brand" javaType="Brand">
            <id column="bid" property="id" />
            <result column="bname" property="name" />
        </association>
        <association property="productType" javaType="ProductType">
            <id column="ptid" property="id" />
            <result column="ptname" property="name" />
        </association>
        <association property="productExt" javaType="ProductExt">
            <id column="peid" property="id" />
            <result column="description" property="description" />
            <result column="richContent" property="richContent" />
        </association>
    </resultMap>

</mapper>
@RestController
@RequestMapping("/product")
public class ProductController {
    @Autowired
    public IProductService productService;

    /**
     * 保存和修改公用的
     * @param product  传递的实体
     * @return Ajaxresult转换结果
     */
    @RequestMapping(value="/save",method= RequestMethod.POST)
    public AjaxResult save(@RequestBody Product product){
        try {
            if(product.getId()!=null){
                productService.updateById(product);
            }else{
                productService.insert(product);
            }
            return AjaxResult.me();
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setMessage("保存对象失败!"+e.getMessage());
        }
    }

    /**
     * 删除对象信息
     */
    @RequestMapping(value="/{id}",method=RequestMethod.DELETE)
    public AjaxResult delete(@PathVariable("id") Long id){
        try {
            productService.deleteById(id);
            return AjaxResult.me();
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setMessage("删除对象失败!"+e.getMessage());
        }
    }

    //获取用户
    @RequestMapping(value = "/{id}",method = RequestMethod.GET)
    public Product get(@PathVariable("id")Long id){
        return productService.selectById(id);
    }

    /**
     * 查看所有的员工信息
     */
    @RequestMapping(value = "/list",method = RequestMethod.GET)
    public List<Product> list(){
        return productService.selectList(null);
    }

    /**
     * 分页查询数据
     * @param query 查询对象
     * @return PageList 分页对象
     */
    @RequestMapping(value = "/json",method = RequestMethod.POST)
    public PageList<Product> json(@RequestBody ProductQuery query){
        return productService.selectPageList(query);
    }
}

4、完成前端代码

①商品类型
在这里插入图片描述
拷贝代码到如下位置
在这里插入图片描述

<!-- 树状选择器 -->
<template>
    <el-popover
            ref="popover"
            placement="bottom-start"
            trigger="click"
            @show="onShowPopover"
            @hide="onHidePopover">
        <el-tree
                ref="tree"
                class="select-tree"
                highlight-current
                :style="`min-width: ${treeWidth}`"
                :data="data"
                :props="props"
                :expand-on-click-node="false"
                :filter-node-method="filterNode"
                :default-expand-all="false"
                @node-click="onClickNode">
        </el-tree>
        <el-input
                slot="reference"
                ref="input"
                v-model="labelModel"
                clearable
                :style="`width: ${width}px`"
                :class="{ 'rotate': showStatus }"
                suffix-icon="el-icon-arrow-down"
                :placeholder="placeholder">
        </el-input>
    </el-popover>
</template>

<script>
    export default {
        name: 'Pagination',
        props: {
            // 接收绑定参数
            value: String,
            // 输入框宽度
            width: String,
            // 选项数据
            options: {
                type: Array,
                required: true,
            },
            // 输入框占位符
            placeholder: {
                type: String,
                required: false,
                default: '请选择',
            },
            // 树节点配置选项
            props: {
                type: Object,
                required: false,
                default: () => ({
                    parent: 'pid',
                    value: 'id',
                    label: 'name',
                    children: 'children',
                }),
            },
        },
        // 设置绑定参数
        model: {
            prop: 'value',
            event: 'selected',
        },
        computed: {
            // 是否为树状结构数据
            dataType() {
                const jsonStr = JSON.stringify(this.options);
                return jsonStr.indexOf(this.props.children) !== -1;
            },
            // 若非树状结构,则转化为树状结构数据
            data() {
                return this.dataType ? this.options : this.switchTree();
            },
        },
        watch: {
            labelModel(val) {
                if (!val) {
                    this.valueModel = '';
                }
                this.$refs.tree.filter(val);
            },
            value(val) {
                this.labelModel = this.queryTree(this.data, val);
            },
        },
        data() {
            return {
                // 树状菜单显示状态
                showStatus: false,
                // 菜单宽度
                treeWidth: 'auto',
                // 输入框显示值
                labelModel: '',
                // 实际请求传值
                valueModel: '0',
            };
        },
        created() {
            // 检测输入框原有值并显示对应 label
            if (this.value) {
                this.labelModel = this.queryTree(this.data, this.value);
            }
            // 获取输入框宽度同步至树状菜单宽度
            this.$nextTick(() => {
                this.treeWidth = `${(this.width || this.$refs.input.$refs.input.clientWidth) - 24}px`;
            });
        },
        methods: {
            // 单击节点
            onClickNode(node) {
                this.labelModel = node[this.props.label];
                this.valueModel = node[this.props.value];
                this.onCloseTree();
            },
            // 偏平数组转化为树状层级结构
            switchTree() {
                return this.cleanChildren(this.buildTree(this.options, '0'));
            },
            // 隐藏树状菜单
            onCloseTree() {
                this.$refs.popover.showPopper = false;
            },
            // 显示时触发
            onShowPopover() {
                this.showStatus = true;
                this.$refs.tree.filter(false);
            },
            // 隐藏时触发
            onHidePopover() {
                this.showStatus = false;
                this.$emit('selected', this.valueModel);
            },
            // 树节点过滤方法
            filterNode(query, data) {
                if (!query) return true;
                return data[this.props.label].indexOf(query) !== -1;
            },
            // 搜索树状数据中的 ID
            queryTree(tree, id) {
                let stark = [];
                stark = stark.concat(tree);
                while (stark.length) {
                    const temp = stark.shift();
                    if (temp[this.props.children]) {
                        stark = stark.concat(temp[this.props.children]);
                    }
                    if (temp[this.props.value] === id) {
                        return temp[this.props.label];
                    }
                }
                return '';
            },
            // 将一维的扁平数组转换为多层级对象
            buildTree(data, id = '0') {
                const fa = (parentId) => {
                    const temp = [];
                    for (let i = 0; i < data.length; i++) {
                        const n = data[i];
                        if (n[this.props.parent] === parentId) {
                            n.children = fa(n.rowGuid);
                            temp.push(n);
                        }
                    }
                    return temp;
                };
                return fa(id);
            },
            // 清除空 children项
            cleanChildren(data) {
                const fa = (list) => {
                    list.map((e) => {
                        if (e.children.length) {
                            fa(e.children);
                        } else {
                            delete e.children;
                        }
                        return e;
                    });
                    return list;
                };
                return fa(data);
            },
        },
    };
</script>

<style>
    .el-input.el-input--suffix {
        cursor: pointer;
        overflow: hidden;
    }
    .el-input.el-input--suffix.rotate .el-input__suffix {
        transform: rotate(180deg);
    }
    .select-tree {
        max-height: 350px;
        overflow-y: scroll;
    }
    /* 菜单滚动条 */
    .select-tree::-webkit-scrollbar {
        z-index: 11;
        width: 6px;
    }
    .select-tree::-webkit-scrollbar-track,
    .select-tree::-webkit-scrollbar-corner {
        background: #fff;
    }
    .select-tree::-webkit-scrollbar-thumb {
        border-radius: 5px;
        width: 6px;
        background: #b4bccc;
    }
    .select-tree::-webkit-scrollbar-track-piece {
        background: #fff;
        width: 6px;
    }
</style>

注:这坨代码很关键
在这里插入图片描述

然后在product.vue中引入
在这里插入图片描述

使用:
在这里插入图片描述

② vue富文本编辑器
在这里插入图片描述

安装:npm install vue-quill-editor --save
在这里插入图片描述
引入:
在这里插入图片描述
使用:
在这里插入图片描述


源码和文档: https://pan.baidu.com/s/1PT3FDUAvCm64oxG53lQdNw

猜你喜欢

转载自blog.csdn.net/qq_38225558/article/details/86545817