springboot+vue+redis 番茄鲜生电商平台实例五:商品品类管理

 一.概述

将之前的一个项目用spring boot+vue+redis进行了改进.从2023年7月10日开始逐步在CSND博客上逐步分享.如果你查阅的时间距离现在比较久远请注意技术的更迭,

如果想了解前面的内容,你可以点击下面这个链接:

springboot+vue+redis 番茄鲜生电商平台实例一:登录功能

springboot+vue+redis 番茄鲜生电商平台实例二:后台菜单

springboot+vue+redis 番茄鲜生电商平台实例三:路由

springboot+vue+redis 番茄鲜生电商平台实例四:数据显示和搜索

springboot+vue+redis 番茄鲜生电商平台实例五:商品分类的增删改查

二.功能介绍

1.使用computed计算属性实现一级分类和二级分类下拉列表的联动.

2.实现图片上传到服务端.

3.在表格中显示图片

4.完成商品品类新增功能.

三.效果展示

四.代码

goods.vue代码

<template>
    <div  style="margin-bottom: 15px;" >
    <!-- 显示页头 -->
    <a-page-header :style="{ background: 'var(--color-bg-2)' }" title="商品品种" :show-back="false">
      <template #breadcrumb>
        <a-breadcrumb>
          <a-breadcrumb-item>商品管理</a-breadcrumb-item>
          <a-breadcrumb-item>商品品种</a-breadcrumb-item>
        </a-breadcrumb>
      </template>
    </a-page-header>
  </div>

  <a-grid cols="2" col-gap="20">
    <a-grid-item class="item_box">
        <a-form size="medium" :model="goods" @submit="addGoods">
            <a-space :size="18" direction="vertical">
            <a-form-item label="一级分类">
                <a-select v-model="parentId" 
                :rules="[{min:'1',message:'必须选择一级分类'}]"
                :validate-trigger="['change']">
                    <a-option :value="-1">请选择分类</a-option>
                    <a-option v-for="item in parentCategory" :key="item.id" :value="item.id">
                        {{ item.categoryName }}
                    </a-option>
                </a-select>
            </a-form-item>

            <a-form-item label="二级分类" field="goodsCategoryId">
                <a-select v-model="goods.goodsCategoryId" 
                :rules="[{min:'1',message:'必须选择二级分类'}]"
                :validate-trigger="['change']">
                    <a-option :value="0">请选择分类</a-option>
                    <a-option v-for="item in childCategory" :key="item.id" :value="item.id">
                        {{ item.categoryName }}
                    </a-option>
                </a-select>
            </a-form-item>

            <a-form-item field="goodsName" label="商品名"
                 :rules="[{required:true,message:'商品名不能为空'}]"
                 :validate-trigger="['change','input']">
                <a-input v-model="goods.goodsName" placeholder="请输入商品名" />
            </a-form-item>

            <a-form-item label="排序" field="rank">
                <a-input-number v-model="goods.rank" placeholder="排序">
                </a-input-number>
            </a-form-item>

            <a-form-item>
                <a-upload action="http://localhost:8011/upload/uploadGoodsImg" 
             :fileList="file ? [file] : []" @success="onSuccess" @error ="onError"
             :show-file-list="false"  @change="onChange" @progress="onProgress">
                
                <template #upload-button>
                    <div :class="`arco-upload-list-item${file && file.status === 'error' ? ' arco-upload-list-item-error' : ''}`">
                        <div class="arco-upload-list-picture custom-upload-avatar" v-if="file && file.url">
                            <img :src="file.url" />

                            <div class="arco-upload-list-picture-mask"><IconEdit /></div>
                        
                            <a-progress
                            v-if="file.status === 'uploading' && file.percent < 100"
                            :percent="file.percent" type="circle" size="mini"
                            :style="{position: 'absolute',left: '50%',top: '50%',
                                transform: 'translateX(-50%) translateY(-50%)',}"/>
                        </div>
                        <div class="arco-upload-picture-card" v-else>
                            <div class="arco-upload-picture-card-text">
                                <IconPlus />
                                <div style="margin-top: 10px; font-weight: 600">Upload</div>
                            </div>
                        </div>
                    </div>
                </template>
                </a-upload>
            </a-form-item>

            <a-form-item>
                <a-space>
                    <a-button type="primary" html-type="submit">
                        <template #icon><icon-plus /></template>新增
                    </a-button>
                    <a-alert v-if="isShow" :type="msgType">{{msg}}</a-alert>
                </a-space>
            </a-form-item>
            </a-space>
        </a-form>
        
    </a-grid-item>
    <a-grid-item>
        <a-table :columns="columns" :data="goodsList.data">
            <template #img="{ record }">
                <a-image width="50" :src="'http://localhost:8011/goodsImg/'+record.img"></a-image>
            </template>
            <template #goodsCategoryId="{ record }">
                {{ getCategory(record.goodsCategoryId) }}
            </template>
            <template #optional="{ record }">
                <a-button @click="updateGoods(record)">更新</a-button>
            </template>
        </a-table>
    </a-grid-item>
  </a-grid>
</template>
<script src="../../../js/goods.js"></script>
<style scoped>
.item_box{
    background-color: #fff;
    padding:20px;
}
</style>

goods.js代码

import axios from 'axios';
import { forEach } from 'lodash-es';
import { computed, onMounted, reactive,ref } from 'vue';

export default{
    setup(){
        //添加的商品品类对象
        const goods = reactive({
            id:0,
            goodsName:'',
            img:'',
            goodsCategoryId:0,
            rank:0,
        });
        const goodsList = reactive({data:[]});//商品品类列表
        const goodsCategory = reactive({data:[]});//商品分类
        const parentCategory = computed(()=>{//计算商品分类中父id为0的一级分类
            return goodsCategory.data.filter((category)=>{
                return category.parentId==0;
            });
        });
        const parentId = ref(-1);//选择的一级分类id
        const childCategory = computed(()=>{//根据一级分类计算二级分类
            goods.goodsCategoryId=0;
            return goodsCategory.data.filter((category)=>{
                return category.parentId==parentId.value;
            })
        });
        onMounted(()=>{//页面加载时,获取必要的数据
            //获取分类的数组
            axios.get("http://localhost:8011/fresh_mall/goodsCategory/getAll")
            .then((res)=>{
                goodsCategory.data = res.data.data;
            })
            //显示所有商品品类
            axios.get("http://localhost:8011/fresh_mall/goods/getAll")
            .then((res)=>{
                goodsList.data =res.data.data;
            })
        })
        //上传商品图片
        const file = ref();
        const onChange = (_, currentFile) => {
            file.value = {
            //列举currentFile中的每一项
            ...currentFile,
            url: URL.createObjectURL(currentFile.file),
            };
        };
        const onProgress = (currentFile) => {
            file.value = currentFile;
        };
        const onSuccess = (res)=>{//图片上传成功后
            // console.log(res);
            goods.img = res.response.data;//记录上传的图片路径
        }
        //将对象进行格式化,这样才能将对象传递给服务器.
        const getformData=(obj)=>{
            let formData = new FormData();
            for(var key in obj){
                formData.append(key,obj[key]);
            }
            return formData;
        }
        const msg = ref('');//添加商品品类后显示的信息
        const isShow = ref(false);//是否显示提示信息.
        const msgType = ref('success');//信息提示类型
        //将商品品类数据保存到数据库
        const addGoods = ()=>{
            if(goods.img==''){
                msg.value='必须选择商品图片';
                isShow.value=true;
                msgType.value='warning';
                return;
            }
            let formData = getformData(goods);
            axios.post("http://localhost:8011/fresh_mall/goods/add",formData)
            .then((res)=>{
                console.log(res.data);
                if(res.data.resultCode==6000){//添加成功
                    //清空表单
                    goods.id=0;
                    goods.goodsCategoryId=0;
                    parentId.value = -1;
                    goods.goodsName='';
                    goods.img='';
                    file.url='';
                    goods.rank=0;
                    msg.value="数据添加成功";
                    isShow.value=true;
                    msgType.value='success';
                }
            })
        }
        //显示右侧商品品类列表
        const columns = [{title: '图片',slotName: 'img'}
        ,{title: '品类名称',slotName: 'goodsName',dataIndex:'goodsName'}
        ,{title: '分类',slotName: 'goodsCategoryId',dataIndex:'goodsCategoryId'}
        ,{title: '排序',slotName: 'rank',dataIndex:'rank'}
        ,{title: '操作',slotName: 'optional'}];
        //计算获取分类的名称
        const getCategory = (id)=>{
            for(var i=0;i<goodsCategory.data.length;i++){
                if(goodsCategory.data[i].id==id){
                    return goodsCategory.data[i].categoryName;
                }
            }
        };
        //更新
        const updateGoods = (record)=>{
            goods.id = record.id;
            goods.goodsName = ref(record.goodsName);
            goods.img = record.img;
            goods.rank = record.rank;
            goods.goodsCategoryId = record.goodsCategoryId;
        }
        
        return{
            goods,
            file,
            addGoods,
            parentCategory,
            parentId,
            childCategory,
            goodsList,
            msg,
            isShow,
            onChange,
            onProgress,
            onSuccess,
            columns,
            getCategory,
            updateGoods,
        }
    }
}

服务端代码:略

五.关键代码解释

1. 在上面代码中,使用onMounted(()=>{}) 使用axios从服务器端获取数据.相当于是页面初始化的时候获取数据.

2.两个下拉列表的数据要经过计算之后,再绑定到列表中,所以和使用的是computed计算属性,这里需要注意的是:

        1)使用计算属性时,不要有附加操作,仅仅制作计算并返回计算后的结果,包括console.log也算是附加操作.

        2)在使用ref定义的常量时,需要使用.value属性才能正确的获取值或者复制.

        3)computed计算属性其标答的含义是,在计算属性中的关联值发生变化时,计算属性将重新进行计算,如果没有变化,那么就读取第一次计算的缓存值.但是这里所谓的关联值必须还是常量(由const定义),而不能是由let定义的变量.

3.如果将对象传递到服务器,建议使用axios.post方法,但是在传递对象时需要先格式化.也就是以下面这段代码:

//将对象进行格式化,这样才能将对象传递给服务器.
        const getformData=(obj)=>{
            let formData = new FormData();
            for(var key in obj){
                formData.append(key,obj[key]);
            }
            return formData;
        }

4.在table组件显示数据时,需要制定列也就是colums属性,在指定colums时,需要配置相关的属性,例如:

const columns = [{title: '图片',slotName: 'img'}
        ,{title: '品类名称',slotName: 'goodsName',dataIndex:'goodsName'}
        ,{title: '分类',slotName: 'goodsCategoryId',dataIndex:'goodsCategoryId'}
        ,{title: '排序',slotName: 'rank',dataIndex:'rank'}
        ,{title: '操作',slotName: 'optional'}];

其中title:列头显示的标题,slotName:相当于列的名称,用于在组件中,进行匹配,dataIndex:这个列需要显示的数组中对象的属性值.

5.在vue中

<template #optional="{ record }">
    <a-button @click="updateGoods(record)">更新</a-button>
</template>

可以看到有#optional="{record}",其中optional就是在colums指定的slotName,

record就是绑定数据的每一行记录.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值