文章目錄
商品分類管理
商城的核心自然是商品,而商品多了以後,肯定要進行分類,並且不同的商品會有不同的品牌信息,我們需要依次去完成:商品分類、品牌、商品的開發。
- 導入數據(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:總頁數,我們沒有返回