樂優商城之後臺管理系統

商品分類管理

商城的核心自然是商品,而商品多了以後,肯定要進行分類,並且不同的商品會有不同的品牌信息,我們需要依次去完成:商品分類、品牌、商品的開發

  • 導入數據(sql)

實現功能

商品分類使用了樹狀結構,而這種結構的組件vuetify並沒有爲我們提供,這裏自定義了一個樹狀組件。不要求實現或者查詢組件的實現,只要求可以參照文檔使用該組件即可:
在這裏插入圖片描述
在瀏覽器頁面點擊“分類管理”菜單:
在這裏插入圖片描述
根據這個路由路徑到路由文件(src/route/index.js),可以定位到分類管理頁面:
在這裏插入圖片描述
由路由文件知,頁面是src/pages/item/Category.vue

實體類

leyou-item-interface中添加Category實體類:

@Table(name="tb_category")
public class Category {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Long id;
	private String name;
	private Long parentId;
	private Boolean isParent; // 注意isParent生成的getter和setter方法需要手動加上Is
	private Integer sort;
	// getter和setter略
}

在這裏插入圖片描述

controller

編寫一個controller一般需要知道四個內容:

- 請求方式:決定我們用GetMapping還是PostMapping
- 請求路徑:決定映射路徑
- 請求參數:決定方法的參數
- 返回值結果:決定方法的返回值

在剛纔頁面發起的請求中,我們就能得到絕大多數信息:
在這裏插入圖片描述

  • 請求方式:Get,插敘肯定是get請求
  • 請求路徑:/api/item/category/list。其中/api是網關前綴,/item是網關的路由映射,真實的路徑應該是/category/list
  • 請求參數:pid=0,根據tree組件的說明,應該是父節點的id,第一次查詢爲0,那就是查詢一級類目
  • 返回結果:??
    根據前面tree組件的用法我們知道,返回的應該是json數組:對應的java類型可以是List集合,裏面的元素就是類目對象了。也就是List
    ResponseEntity
@Controller
@RequestMapping("category")
public class CategoryController {

    @Autowired
    private CategoryService categoryService;

    /**
     * 根據父id查詢子節點
     * @param pid
     * @return
     */
    @GetMapping("list")
    public ResponseEntity<List<Category>> queryCategoriesByPid(@RequestParam("pid") Long pid) {

        if (pid == null || pid.longValue() < 0) {
            // 響應400,相當於ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
            return ResponseEntity.badRequest().build();
        }
        List<Category> categories = this.categoryService.queryCategoriesByPid(pid);
        if (CollectionUtils.isEmpty(categories)) {
            // 響應404
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(categories);
    }
}

service

一般service層我們會定義接口和實現類,不過這裏我們就偷懶一下,直接寫實現類了:

@Service
public class CategoryService {

    @Autowired
    private CategoryMapper categoryMapper;

    /**
     * 根據parentId查詢子類目
     * @param pid
     * @return
     */
    public List<Category> queryCategoriesByPid(Long pid) {
        Category record = new Category();
        record.setParentId(pid);
        return this.categoryMapper.select(record);
    }
}

mapper

我們使用通用mapper來簡化開發:

public interface CategoryMapper extends Mapper<Category> {
}

要注意,我們並沒有在mapper接口上聲明@Mapper註解,那麼mybatis如何才能找到接口呢?

我們在啓動類上添加一個掃描包功能:

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.leyou.item.mapper") // mapper接口的包掃描
public class LeyouItemServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(LeyouItemServiceApplication.class, args);
    }
}

跨域問題(cors)

跨域問題(cors)](https://blog.csdn.net/chenhangx/article/details/105413599)
在這裏插入圖片描述

品牌查詢

路由路徑:/item/brand

根據路由文件知,對應的頁面是:src/pages/item/Brand.vue

`tb_brand`
 ENGINE=InnoDB AUTO_INCREMENT=325400 DEFAULT CHARSET=utf8 COMMENT='品牌表,一個品牌下有多個商品(spu),一對多關係';

這裏需要注意的是,品牌和商品分類之間是多對多關係。因此我們有一張中間表,來維護兩者間關係:

CREATE TABLE `tb_category_brand` (
  `category_id` bigint(20) NOT NULL COMMENT '商品類目id',
  `brand_id` bigint(20) NOT NULL COMMENT '品牌id',
  PRIMARY KEY (`category_id`,`brand_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品分類和品牌的中間表,兩者是多對多關係';

但是,你可能會發現,這張表中並沒有設置外鍵約束,似乎與數據庫的設計範式不符。爲什麼這麼做?

  • 外鍵會嚴重影響數據庫讀寫的效率
  • 數據刪除時會比較麻煩

在電商行業,性能是非常重要的。我們寧可在代碼中通過邏輯來維護表關係,也不設置外鍵。

controller

編寫controller先思考四個問題,參照前端頁面的控制檯

  • 請求方式:查詢,肯定是Get
  • 請求路徑:分頁查詢,/brand/page
  • 請求參數:根據我們剛纔編寫的頁面,有分頁功能,有排序功能,有搜索過濾功能,因此至少要有5個參數:
    • page:當前頁,int
    • rows:每頁大小,int
    • sortBy:排序字段,String
    • desc:是否爲降序,boolean
    • key:搜索關鍵詞,String
  • 響應結果:分頁結果一般至少需要兩個數據
    • total:總條數
    • items:當前頁數據
    • totalPage:有些還需要總頁數

PageResult

這裏我們封裝一個類,來表示分頁結果:

public class PageResult<T> {
    private Long total;// 總條數
    private Integer totalPage;// 總頁數
    private List<T> items;// 當前頁數據

    public PageResult() {
    }

    public PageResult(Long total, List<T> items) {
        this.total = total;
        this.items = items;
    }

    public PageResult(Long total, Long totalPage, List<T> items) {
        this.total = total;
        this.totalPage = totalPage;
        this.items = items;
    }

    public Long getTotal() {
        return total;
    }

    public void setTotal(Long total) {
        this.total = total;
    }

    public List<T> getItems() {
        return items;
    }

    public void setItems(List<T> items) {
        this.items = items;
    }

    public Long getTotalPage() {
        return totalPage;
    }

    public void setTotalPage(Long totalPage) {
        this.totalPage = totalPage;
    }
}

另外,這個PageResult以後可能在其它項目中也有需求,因此我們將其抽取到leyou-common中,提高複用性:
不要忘記在leyou-item-service工程的pom.xml中引入leyou-common的依賴

    <groupId>com.leyou.common</groupId>
    <artifactId>leyou-common</artifactId>

Service

在這裏插入圖片描述

@Service
public class BrandService {

    @Autowired
    private BrandMapper brandMapper;

    /**
     * 根據查詢條件分頁並排序查詢品牌信息
     *
     * @param key
     * @param page
     * @param rows
     * @param sortBy
     * @param desc
     * @return
     */
    public PageResult<Brand> queryBrandsByPage(String key, Integer page, Integer rows, String sortBy, Boolean desc) {

        // 初始化example對象
        Example example = new Example(Brand.class);
        Example.Criteria criteria = example.createCriteria();

        // 根據name模糊查詢,或者根據首字母查詢
        if (StringUtils.isNotBlank(key)) {
            criteria.andLike("name", "%" + key + "%").orEqualTo("letter", key);
        }

        // 添加分頁條件
        PageHelper.startPage(page, rows);

        // 添加排序條件
        if (StringUtils.isNotBlank(sortBy)) {
            example.setOrderByClause(sortBy + " " + (desc ? "desc" : "asc"));
        }

        List<Brand> brands = this.brandMapper.selectByExample(example);
        // 包裝成pageInfo
        PageInfo<Brand> pageInfo = new PageInfo<>(brands);
        // 包裝成分頁結果集返回
        return new PageResult<>(pageInfo.getTotal(), pageInfo.getList());
    }
}

異步查詢工具axios

異步查詢數據,自然是通過ajax查詢,大家首先想起的肯定是jQuery。但jQuery與MVVM的思想不吻合,而且ajax只是jQuery的一小部分。因此不可能爲了發起ajax請求而去引用這麼大的一個庫。
Vue官方推薦的ajax請求框架叫做:axios
axios的Get請求語法:

axios.get("/item/category/list?pid=0") // 請求路徑和請求參數拼接
    .then(function(resp){
    	// 成功回調函數
	})
    .catch(function(){
    	// 失敗回調函數
	})
// 參數較多時,可以通過params來傳遞參數
axios.get("/item/category/list", {
        params:{
            pid:0
        }
	})
    .then(function(resp){})// 成功時的回調
    .catch(function(error){})// 失敗時的回調

axios的POST請求語法:

比如新增一個用戶

axios.post("/user",{
    	name:"Jack",
    	age:21
	})
    .then(function(resp){})
    .catch(function(error){})

注意,POST請求傳參,不需要像GET請求那樣定義一個對象,在對象的params參數中傳參。post()方法的第二個參數對象,就是將來要傳遞的參數

PUT和DELETE請求與POST請求類似

axios的全局配置

而在我們的項目中,已經引入了axios,並且進行了簡單的封裝,在src下的http.js中:
http.js中對axios進行了一些默認配置:

import Vue from 'vue'
import axios from 'axios'
import config from './config'
// config中定義的基礎路徑是:http://api.leyou.com/api
axios.defaults.baseURL = config.api; // 設置axios的基礎請求路徑
axios.defaults.timeout = 2000; // 設置axios的請求時間

Vue.prototype.$http = axios;// 將axios賦值給Vue原型的$http屬性,這樣所有vue實例都可使用該對象

在這裏插入圖片描述

項目中使用

我們在組件Brand.vue的getDataFromServer方法,通過$http發起get請求,測試查詢品牌的接口,看是否能獲取到數據:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
可以看到,在請求成功的返回結果response中,有一個data屬性,裏面就是真正的響應數據。

響應結果中與我們設計的一致,包含3個內容:

  • total:總條數,目前是165
  • items:當前頁數據
  • totalPage:總頁數,我們沒有返回

完成分頁和過濾

在這裏插入圖片描述

在這裏插入圖片描述

品牌管理

商品規格管理

商品管理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章