[litemall-admin, litemall-admin-api]: 管理后台调整

This commit is contained in:
Junling Bu
2018-07-05 22:44:37 +08:00
parent a86ec8b650
commit 32ea87b085
25 changed files with 1191 additions and 1968 deletions

View File

@@ -10,17 +10,17 @@ public class GoodsAllinone {
LitemallGoodsSpecification[] specifications;
LitemallGoodsAttribute[] attributes;
// 这里采用 Product 再转换到 LitemallProduct
Product[] products;
LitemallProduct[] products;
public LitemallGoods getGoods() {
return goods;
}
public Product[] getProducts() {
public LitemallProduct[] getProducts() {
return products;
}
public void setProducts(Product[] products) {
public void setProducts(LitemallProduct[] products) {
this.products = products;
}

View File

@@ -93,42 +93,13 @@ public class AdminCategoryController {
// 所有一级分类目录
List<LitemallCategory> l1CatList = categoryService.queryL1();
HashMap<Integer, String> data = new HashMap<>(l1CatList.size());
List<Map<String, Object>> data = new ArrayList<>(l1CatList.size());
for(LitemallCategory category : l1CatList){
data.put(category.getId(), category.getName());
Map<String, Object> d = new HashMap<>(2);
d.put("value", category.getId());
d.put("label", category.getName());
data.add(d);
}
return ResponseUtil.ok(data);
}
@GetMapping("/list2")
public Object list2(@LoginAdmin Integer adminId) {
if (adminId == null) {
return ResponseUtil.unlogin();
}
List<LitemallCategory> l1CatList = categoryService.queryL1();
List<CatVo> list = new ArrayList<>(l1CatList.size());
for(LitemallCategory l1 : l1CatList){
CatVo l1CatVo = new CatVo();
l1CatVo.setValue(l1.getId());
l1CatVo.setLabel(l1.getName());
List<LitemallCategory> l2CatList = categoryService.queryByPid(l1.getId());
List<CatVo> children = new ArrayList<>(l2CatList.size());
for(LitemallCategory l2 : l2CatList) {
CatVo l2CatVo = new CatVo();
l2CatVo.setValue(l2.getId());
l2CatVo.setLabel(l2.getName());
children.add(l2CatVo);
}
l1CatVo.setChildren(children);
list.add(l1CatVo);
}
return ResponseUtil.ok(list);
}
}

View File

@@ -1,84 +0,0 @@
package org.linlinjava.litemall.admin.web;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.admin.annotation.LoginAdmin;
import org.linlinjava.litemall.db.domain.LitemallGoodsAttribute;
import org.linlinjava.litemall.db.service.LitemallGoodsAttributeService;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/goods-attribute")
public class AdminGoodsAttributeController {
private final Log logger = LogFactory.getLog(AdminGoodsAttributeController.class);
@Autowired
private LitemallGoodsAttributeService goodsAttributeService;
@GetMapping("/list")
public Object list(@LoginAdmin Integer adminId,
Integer goodsId,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
String sort, String order){
if(adminId == null){
return ResponseUtil.unlogin();
}
List<LitemallGoodsAttribute> goodsAttributeList = goodsAttributeService.querySelective(goodsId, page, limit, sort, order);
int total = goodsAttributeService.countSelective(goodsId, page, limit, sort, order);
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", goodsAttributeList);
return ResponseUtil.ok(data);
}
@PostMapping("/create")
public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsAttribute goodsAttribute){
if(adminId == null){
return ResponseUtil.unlogin();
}
goodsAttributeService.add(goodsAttribute);
return ResponseUtil.ok(goodsAttribute);
}
@GetMapping("/read")
public Object read(@LoginAdmin Integer adminId, Integer id){
if(adminId == null){
return ResponseUtil.unlogin();
}
if(id == null){
return ResponseUtil.badArgument();
}
LitemallGoodsAttribute goodsAttribute = goodsAttributeService.findById(id);
return ResponseUtil.ok(goodsAttribute);
}
@PostMapping("/update")
public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsAttribute goodsAttribute){
if(adminId == null){
return ResponseUtil.unlogin();
}
goodsAttributeService.updateById(goodsAttribute);
return ResponseUtil.ok(goodsAttribute);
}
@PostMapping("/delete")
public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsAttribute goodsAttribute){
if(adminId == null){
return ResponseUtil.unlogin();
}
goodsAttributeService.deleteById(goodsAttribute.getId());
return ResponseUtil.ok();
}
}

View File

@@ -6,15 +6,10 @@ import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.admin.annotation.LoginAdmin;
import org.linlinjava.litemall.admin.dao.GoodsAllinone;
import org.linlinjava.litemall.admin.dao.Product;
import org.linlinjava.litemall.db.domain.LitemallGoods;
import org.linlinjava.litemall.db.domain.LitemallGoodsAttribute;
import org.linlinjava.litemall.db.domain.LitemallGoodsSpecification;
import org.linlinjava.litemall.db.domain.LitemallProduct;
import org.linlinjava.litemall.db.service.LitemallGoodsAttributeService;
import org.linlinjava.litemall.db.service.LitemallGoodsService;
import org.linlinjava.litemall.admin.util.CatVo;
import org.linlinjava.litemall.db.domain.*;
import org.linlinjava.litemall.db.service.*;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.db.service.LitemallGoodsSpecificationService;
import org.linlinjava.litemall.db.service.LitemallProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
@@ -24,10 +19,7 @@ import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
@RestController
@RequestMapping("/admin/goods")
@@ -45,6 +37,10 @@ public class AdminGoodsController {
private LitemallGoodsAttributeService attributeService;
@Autowired
private LitemallProductService productService;
@Autowired
private LitemallCategoryService categoryService;
@Autowired
private LitemallBrandService brandService;
@GetMapping("/list")
public Object list(@LoginAdmin Integer adminId,
@@ -65,49 +61,19 @@ public class AdminGoodsController {
return ResponseUtil.ok(data);
}
@PostMapping("/create")
public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallGoods goods){
if(adminId == null){
return ResponseUtil.unlogin();
}
goodsService.add(goods);
return ResponseUtil.ok(goods);
}
@GetMapping("/read")
public Object read(@LoginAdmin Integer adminId, Integer id){
if(adminId == null){
return ResponseUtil.unlogin();
}
if(id == null){
return ResponseUtil.badArgument();
}
LitemallGoods goods = goodsService.findById(id);
return ResponseUtil.ok(goods);
}
/*
* TODO
* 目前商品修改的逻辑是
* 1. 更新litemall_goods表
* 2. 逻辑删除litemall_goods_specification、litemall_goods_attribute、litemall_product
* 3. 添加litemall_goods_specification、litemall_goods_attribute、litemall_product
*
* 这里商品三个表的数据采用删除再跟新的策略是因为
* 商品编辑页面,管理员可以添加删除商品规格、添加删除商品属性,因此这里仅仅更新表是不可能的,
* 因此这里只能删除所有旧的数据,然后添加新的数据
*/
@PostMapping("/update")
public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallGoods goods){
if(adminId == null){
return ResponseUtil.unlogin();
}
goodsService.updateById(goods);
return ResponseUtil.ok(goods);
}
@PostMapping("/delete")
public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallGoods goods){
if(adminId == null){
return ResponseUtil.unlogin();
}
goodsService.deleteById(goods.getId());
return ResponseUtil.ok();
}
@PostMapping("/publish")
public Object publish(@LoginAdmin Integer adminId, @RequestBody GoodsAllinone goodsAllinone){
public Object update(@LoginAdmin Integer adminId, @RequestBody GoodsAllinone goodsAllinone){
if(adminId == null){
return ResponseUtil.unlogin();
}
@@ -115,7 +81,88 @@ public class AdminGoodsController {
LitemallGoods goods = goodsAllinone.getGoods();
LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes();
LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications();
Product[] products = goodsAllinone.getProducts();
LitemallProduct[] products = goodsAllinone.getProducts();
// 开启事务管理
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// 商品基本信息表litemall_goods
goodsService.updateById(goods);
Integer gid = goods.getId();
specificationService.deleteByGid(gid);
attributeService.deleteByGid(gid);
productService.deleteByGid(gid);
// 商品规格表litemall_goods_specification
Map<String, Integer> specIds = new HashMap<>();
for(LitemallGoodsSpecification specification : specifications){
specification.setGoodsId(goods.getId());
specification.setAddTime(LocalDateTime.now());
specificationService.add(specification);
specIds.put(specification.getValue(), specification.getId());
}
// 商品参数表litemall_goods_attribute
for(LitemallGoodsAttribute attribute : attributes){
attribute.setGoodsId(goods.getId());
attribute.setAddTime(LocalDateTime.now());
attributeService.add(attribute);
}
// 商品货品表litemall_product
for(LitemallProduct product : products){
product.setGoodsId(goods.getId());
product.setAddTime(LocalDateTime.now());
productService.add(product);
}
} catch (Exception ex) {
txManager.rollback(status);
logger.error("系统内部错误", ex);
}
txManager.commit(status);
return ResponseUtil.ok();
}
@PostMapping("/delete")
public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallGoods goods){
if(adminId == null){
return ResponseUtil.unlogin();
}
// 开启事务管理
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
Integer gid = goods.getId();
goodsService.deleteById(gid);
specificationService.deleteByGid(gid);
attributeService.deleteByGid(gid);
productService.deleteByGid(gid);
} catch (Exception ex) {
txManager.rollback(status);
logger.error("系统内部错误", ex);
}
txManager.commit(status);
return ResponseUtil.ok();
}
@PostMapping("/create")
public Object create(@LoginAdmin Integer adminId, @RequestBody GoodsAllinone goodsAllinone){
if(adminId == null){
return ResponseUtil.unlogin();
}
LitemallGoods goods = goodsAllinone.getGoods();
LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes();
LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications();
LitemallProduct[] products = goodsAllinone.getProducts();
String name = goods.getName();
if(goodsService.checkExistByName(name)){
@@ -149,23 +196,10 @@ public class AdminGoodsController {
}
// 商品货品表litemall_product
for(Product product : products){
LitemallProduct litemallProduct = new LitemallProduct();
litemallProduct.setRetailPrice(product.getPrice());
litemallProduct.setGoodsNumber(product.getNumber());
litemallProduct.setUrl(product.getUrl());
litemallProduct.setGoodsId(goods.getId());
litemallProduct.setAddTime(LocalDateTime.now());
String[] values = product.getSpecifications();
Integer[] ids = new Integer[values.length];
for(int i = 0; i < values.length; i++){
ids[i] = specIds.get(values[i]);
}
Arrays.sort(ids);
litemallProduct.setGoodsSpecificationIds(ids);
productService.add(litemallProduct);
for(LitemallProduct product : products){
product.setGoodsId(goods.getId());
product.setAddTime(LocalDateTime.now());
productService.add(product);
}
} catch (Exception ex) {
txManager.rollback(status);
@@ -175,4 +209,86 @@ public class AdminGoodsController {
return ResponseUtil.ok();
}
@GetMapping("/catAndBrand")
public Object list2(@LoginAdmin Integer adminId) {
if (adminId == null) {
return ResponseUtil.unlogin();
}
// http://element-cn.eleme.io/#/zh-CN/component/cascader
// 管理员设置“所属分类”
List<LitemallCategory> l1CatList = categoryService.queryL1();
List<CatVo> categoryList = new ArrayList<>(l1CatList.size());
for(LitemallCategory l1 : l1CatList){
CatVo l1CatVo = new CatVo();
l1CatVo.setValue(l1.getId());
l1CatVo.setLabel(l1.getName());
List<LitemallCategory> l2CatList = categoryService.queryByPid(l1.getId());
List<CatVo> children = new ArrayList<>(l2CatList.size());
for(LitemallCategory l2 : l2CatList) {
CatVo l2CatVo = new CatVo();
l2CatVo.setValue(l2.getId());
l2CatVo.setLabel(l2.getName());
children.add(l2CatVo);
}
l1CatVo.setChildren(children);
categoryList.add(l1CatVo);
}
// http://element-cn.eleme.io/#/zh-CN/component/select
// 管理员设置“所属品牌商”
List<LitemallBrand> list = brandService.all();
List<Map<String, Object>> brandList = new ArrayList<>(l1CatList.size());
for(LitemallBrand brand : list){
Map<String, Object> b = new HashMap<>(2);
b.put("value", brand.getId());
b.put("label", brand.getName());
brandList.add(b);
}
Map<String, Object> data = new HashMap<>();
data.put("categoryList" ,categoryList);
data.put("brandList", brandList);
return ResponseUtil.ok(data);
}
@GetMapping("/detail")
public Object detail(@LoginAdmin Integer adminId, Integer id) {
if (adminId == null) {
return ResponseUtil.unlogin();
}
if (id == null) {
return ResponseUtil.badArgument();
}
LitemallGoods goods = goodsService.findById(id);
List<LitemallProduct> products = productService.queryByGid(id);
List<LitemallGoodsSpecification> specifications = specificationService.queryByGid(id);
List<LitemallGoodsAttribute> attributes = attributeService.queryByGid(id);
Integer categoryId = goods.getCategoryId();
LitemallCategory category = categoryService.findById(categoryId);
Integer[] categoryIds = new Integer[]{};
if (category != null) {
Integer parentCategoryId = category.getPid();
categoryIds = new Integer[] {parentCategoryId, categoryId};
}
Map<String, Object> data = new HashMap<>();
data.put("goods" ,goods);
data.put("specifications", specifications);
data.put("products", products);
data.put("attributes", attributes);
data.put("categoryIds", categoryIds);
return ResponseUtil.ok(data);
}
}

View File

@@ -1,100 +0,0 @@
package org.linlinjava.litemall.admin.web;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.admin.annotation.LoginAdmin;
import org.linlinjava.litemall.db.domain.LitemallGoodsSpecification;
import org.linlinjava.litemall.db.service.LitemallGoodsSpecificationService;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/goods-specification")
public class AdminGoodsSpecificationController {
private final Log logger = LogFactory.getLog(AdminGoodsSpecificationController.class);
@Autowired
private LitemallGoodsSpecificationService goodsSpecificationService;
@GetMapping("/list")
public Object list(@LoginAdmin Integer adminId,
Integer goodsId,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
String sort, String order){
if(adminId == null){
return ResponseUtil.unlogin();
}
List<LitemallGoodsSpecification> goodsSpecificationList = goodsSpecificationService.querySelective(goodsId, page, limit, sort, order);
int total = goodsSpecificationService.countSelective(goodsId, page, limit, sort, order);
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", goodsSpecificationList);
return ResponseUtil.ok(data);
}
@PostMapping("/create")
public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsSpecification goodsSpecification){
if(adminId == null){
return ResponseUtil.unlogin();
}
goodsSpecification.setAddTime(LocalDateTime.now());
goodsSpecificationService.add(goodsSpecification);
return ResponseUtil.ok(goodsSpecification);
}
@GetMapping("/read")
public Object read(@LoginAdmin Integer adminId, Integer id){
if(adminId == null){
return ResponseUtil.unlogin();
}
if(id == null){
return ResponseUtil.badArgument();
}
LitemallGoodsSpecification goodsSpecification = goodsSpecificationService.findById(id);
return ResponseUtil.ok(goodsSpecification);
}
@PostMapping("/update")
public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsSpecification goodsSpecification){
if(adminId == null){
return ResponseUtil.unlogin();
}
goodsSpecificationService.updateById(goodsSpecification);
return ResponseUtil.ok(goodsSpecification);
}
@PostMapping("/delete")
public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsSpecification goodsSpecification){
if(adminId == null){
return ResponseUtil.unlogin();
}
goodsSpecificationService.deleteById(goodsSpecification.getId());
return ResponseUtil.ok();
}
@GetMapping("/volist")
public Object volist(@LoginAdmin Integer adminId, Integer id){
if(adminId == null){
return ResponseUtil.unlogin();
}
if(id == null){
return ResponseUtil.badArgument();
}
Object goodsSpecificationVoList = goodsSpecificationService.getSpecificationVoList(id);
return ResponseUtil.ok(goodsSpecificationVoList);
}
}

View File

@@ -112,8 +112,8 @@ public class AdminOrderController {
for (LitemallOrderGoods orderGoods : orderGoodsList) {
Integer productId = orderGoods.getProductId();
LitemallProduct product = productService.findById(productId);
Integer number = product.getGoodsNumber() + orderGoods.getNumber();
product.setGoodsNumber(number);
Integer number = product.getNumber() + orderGoods.getNumber();
product.setNumber(number);
productService.updateById(product);
}
} catch (Exception ex) {
@@ -162,7 +162,7 @@ public class AdminOrderController {
order.setOrderStatus(OrderUtil.STATUS_SHIP);
order.setShipSn(shipSn);
order.setShipChannel(shipChannel);
order.setShipStartTime(LocalDateTime.now());
order.setShipTime(LocalDateTime.now());
orderService.update(order);
return ResponseUtil.ok();
@@ -207,8 +207,8 @@ public class AdminOrderController {
for (LitemallOrderGoods orderGoods : orderGoodsList) {
Integer productId = orderGoods.getProductId();
LitemallProduct product = productService.findById(productId);
Integer number = product.getGoodsNumber() + orderGoods.getNumber();
product.setGoodsNumber(number);
Integer number = product.getNumber() + orderGoods.getNumber();
product.setNumber(number);
productService.updateById(product);
}
} catch (Exception ex) {
@@ -242,9 +242,9 @@ public class AdminOrderController {
List<LitemallOrder> orderList = orderService.queryUnconfirm();
for(LitemallOrder order : orderList){
LocalDateTime shipEnd = order.getShipEndTime();
LocalDateTime ship = order.getShipTime();
LocalDateTime now = LocalDateTime.now();
LocalDateTime expired = shipEnd.plusDays(7);
LocalDateTime expired = ship.plusDays(7);
if(expired.isAfter(now)){
continue;
}

View File

@@ -1,127 +0,0 @@
package org.linlinjava.litemall.admin.web;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.admin.annotation.LoginAdmin;
import org.linlinjava.litemall.db.domain.LitemallGoods;
import org.linlinjava.litemall.db.domain.LitemallProduct;
import org.linlinjava.litemall.db.service.LitemallGoodsService;
import org.linlinjava.litemall.db.service.LitemallGoodsSpecificationService;
import org.linlinjava.litemall.db.service.LitemallProductService;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/product")
public class AdminProductController {
private final Log logger = LogFactory.getLog(AdminProductController.class);
@Autowired
private LitemallProductService productService;
@Autowired
private LitemallGoodsService goodsService;
@Autowired
private LitemallGoodsSpecificationService goodsSpecificationService;
@GetMapping("/list")
public Object list(@LoginAdmin Integer adminId,
Integer goodsId,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
String sort, String order){
if(adminId == null){
return ResponseUtil.unlogin();
}
List<LitemallProduct> productList = productService.querySelective(goodsId, page, limit, sort, order);
int total = productService.countSelective(goodsId, page, limit, sort, order);
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", productList);
return ResponseUtil.ok(data);
}
/**
*
* @param adminId
* @param litemallProduct
* @return
*/
@PostMapping("/create")
public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallProduct litemallProduct){
if(adminId == null){
return ResponseUtil.unlogin();
}
Integer goodsId = litemallProduct.getGoodsId();
if(goodsId == null){
return ResponseUtil.badArgument();
}
LitemallGoods goods = goodsService.findById(goodsId);
if(goods == null){
return ResponseUtil.badArgumentValue();
}
List<LitemallProduct> productList = productService.queryByGid(goodsId);
if(productList.size() != 0){
return ResponseUtil.badArgumentValue();
}
Integer[] goodsSpecificationIds = goodsSpecificationService.queryIdsByGid(goodsId);
if(goodsSpecificationIds.length == 0) {
return ResponseUtil.serious();
}
LitemallProduct product = new LitemallProduct();
product.setGoodsId(goodsId);
product.setGoodsNumber(0);
product.setRetailPrice(new BigDecimal(0.00));
product.setGoodsSpecificationIds(goodsSpecificationIds);
product.setAddTime(LocalDateTime.now());
productService.add(product);
return ResponseUtil.ok();
}
@GetMapping("/read")
public Object read(@LoginAdmin Integer adminId, Integer id){
if(adminId == null){
return ResponseUtil.unlogin();
}
if(id == null){
return ResponseUtil.badArgument();
}
LitemallProduct product = productService.findById(id);
return ResponseUtil.ok(product);
}
@PostMapping("/update")
public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallProduct product){
if(adminId == null){
return ResponseUtil.unlogin();
}
productService.updateById(product);
return ResponseUtil.ok(product);
}
@PostMapping("/delete")
public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallProduct product){
if(adminId == null){
return ResponseUtil.unlogin();
}
productService.deleteById(product.getId());
return ResponseUtil.ok();
}
}

View File

@@ -46,11 +46,3 @@ export function deleteCategory(data) {
data
})
}
export function listCategory2(query) {
return request({
url: '/category/list2',
method: 'get',
params: query
})
}

View File

@@ -1,41 +0,0 @@
import request from '@/utils/request'
export function listGoodsAttribute(query) {
return request({
url: '/goods-attribute/list',
method: 'get',
params: query
})
}
export function createGoodsAttribute(data) {
return request({
url: '/goods-attribute/create',
method: 'post',
data
})
}
export function readGoodsAttribute(data) {
return request({
url: '/goods-attribute/read',
method: 'get',
data
})
}
export function updateGoodsAttribute(data) {
return request({
url: '/goods-attribute/update',
method: 'post',
data
})
}
export function deleteGoodsAttribute(data) {
return request({
url: '/goods-attribute/delete',
method: 'post',
data
})
}

View File

@@ -1,49 +0,0 @@
import request from '@/utils/request'
export function listGoodsSpecification(query) {
return request({
url: '/goods-specification/list',
method: 'get',
params: query
})
}
export function createGoodsSpecification(data) {
return request({
url: '/goods-specification/create',
method: 'post',
data
})
}
export function readGoodsSpecification(data) {
return request({
url: '/goods-specification/read',
method: 'get',
data
})
}
export function updateGoodsSpecification(data) {
return request({
url: '/goods-specification/update',
method: 'post',
data
})
}
export function deleteGoodsSpecification(data) {
return request({
url: '/goods-specification/delete',
method: 'post',
data
})
}
export function listGoodsSpecificationVo(query) {
return request({
url: '/goods-specification/volist',
method: 'get',
params: query
})
}

View File

@@ -8,30 +8,6 @@ export function listGoods(query) {
})
}
export function createGoods(data) {
return request({
url: '/goods/create',
method: 'post',
data
})
}
export function readGoods(data) {
return request({
url: '/goods/read',
method: 'get',
data
})
}
export function updateGoods(data) {
return request({
url: '/goods/update',
method: 'post',
data
})
}
export function deleteGoods(data) {
return request({
url: '/goods/delete',
@@ -42,8 +18,31 @@ export function deleteGoods(data) {
export function publishGoods(data) {
return request({
url: '/goods/publish',
url: '/goods/create',
method: 'post',
data
})
}
export function detailGoods(id) {
return request({
url: '/goods/detail',
method: 'get',
params: { id }
})
}
export function editGoods(data) {
return request({
url: '/goods/update',
method: 'post',
data
})
}
export function listCatAndBrand() {
return request({
url: '/goods/catAndBrand',
method: 'get'
})
}

View File

@@ -1,41 +0,0 @@
import request from '@/utils/request'
export function listProduct(query) {
return request({
url: '/product/list',
method: 'get',
params: query
})
}
export function createProduct(data) {
return request({
url: '/product/create',
method: 'post',
data
})
}
export function readProduct(data) {
return request({
url: '/product/read',
method: 'get',
data
})
}
export function updateProduct(data) {
return request({
url: '/product/update',
method: 'post',
data
})
}
export function deleteProduct(data) {
return request({
url: '/product/delete',
method: 'post',
data
})
}

View File

@@ -98,12 +98,10 @@ export const asyncRouterMap = [
icon: 'chart'
},
children: [
{ path: 'publish', component: _import('goods/publish'), name: 'publish', meta: { title: '商品上架', noCache: true }},
{ path: 'goods', component: _import('goods/goods'), name: 'goods', meta: { title: '商品管理', noCache: true }},
{ path: 'attribute', component: _import('goods/attribute'), name: 'attribute', meta: { title: '商品参数', noCache: true }},
{ path: 'specification', component: _import('goods/specification'), name: 'specification', meta: { title: '商品规格', noCache: true }},
{ path: 'product', component: _import('goods/product'), name: 'product', meta: { title: '货品管理', noCache: true }},
{ path: 'comment', component: _import('goods/comment'), name: 'comment', meta: { title: '用户评论', noCache: true }}
{ path: 'list', component: _import('goods/list'), name: 'goodsList', meta: { title: '商品列表', noCache: true }},
{ path: 'create', component: _import('goods/create'), name: 'goodsCreate', meta: { title: '商品上架', noCache: true }},
{ path: 'edit', component: _import('goods/edit'), name: 'goodsEdit', meta: { title: '商品编辑', noCache: true }, hidden: true },
{ path: 'comment', component: _import('goods/comment'), name: 'goodsComment', meta: { title: '商品评论', noCache: true }}
]
},

View File

@@ -1,214 +0,0 @@
<template>
<div class="app-container calendar-list-container">
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID" v-model="listQuery.goodsId">
</el-input>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button class="filter-item" type="primary" @click="handleCreate" icon="el-icon-edit">添加</el-button>
<el-button class="filter-item" type="primary" :loading="downloadLoading" icon="el-icon-download" @click="handleDownload">导出</el-button>
</div>
<!-- 查询结果 -->
<el-table size="small" :data="list" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" width="150px" label="商品参数ID" prop="id" sortable>
</el-table-column>
<el-table-column align="center" min-width="100px" label="商品ID" prop="goodsId">
</el-table-column>
<el-table-column align="center" min-width="100px" label="商品参数名称" prop="attribute">
</el-table-column>
<el-table-column align="center" min-width="200px" label="商品参数值" prop="value">
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listQuery.page"
:page-sizes="[10,20,30,50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
<!-- 添加或修改对话框 -->
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form :rules="rules" ref="dataForm" :model="dataForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-form-item label="商品ID" prop="goodsId">
<el-input v-model="dataForm.goodsId"></el-input>
</el-form-item>
<el-form-item label="商品参数名" prop="attribute">
<el-input v-model="dataForm.attribute"></el-input>
</el-form-item>
<el-form-item label="商品参数值" prop="value">
<el-input v-model="dataForm.value"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button v-if="dialogStatus=='create'" type="primary" @click="createData">确定</el-button>
<el-button v-else type="primary" @click="updateData">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listGoodsAttribute, createGoodsAttribute, updateGoodsAttribute, deleteGoodsAttribute } from '@/api/goods-attribute'
export default {
name: 'GoodsAttribute',
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
page: 1,
limit: 10,
goodsId: undefined,
sort: '+id'
},
dataForm: {
id: undefined,
goodsId: undefined,
attribute: undefined,
value: undefined
},
dialogFormVisible: false,
dialogStatus: '',
textMap: {
update: '编辑',
create: '创建'
},
rules: {
goodsId: [{ required: true, message: '商品ID不能为空', trigger: 'blur' }],
attribute: [{ required: true, message: '商品参数名称不能为空', trigger: 'blur' }],
value: [{ required: true, message: '商品参数值不能为空', trigger: 'blur' }]
},
downloadLoading: false
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
listGoodsAttribute(this.listQuery).then(response => {
this.list = response.data.data.items
this.total = response.data.data.total
this.listLoading = false
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
},
resetForm() {
this.dataForm = {
id: undefined,
goodsId: undefined,
attribute: undefined,
value: undefined
}
},
handleCreate() {
this.resetForm()
this.dialogStatus = 'create'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
createData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
createGoodsAttribute(this.dataForm).then(response => {
this.list.unshift(response.data.data)
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleUpdate(row) {
this.dataForm = Object.assign({}, row)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
updateData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
updateGoodsAttribute(this.dataForm).then(() => {
for (const v of this.list) {
if (v.id === this.dataForm.id) {
const index = this.list.indexOf(v)
this.list.splice(index, 1, this.dataForm)
break
}
}
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleDelete(row) {
deleteGoodsAttribute(row).then(response => {
this.$notify({
title: '成功',
message: '删除成功',
type: 'success',
duration: 2000
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['商品参数ID', '商品ID', '商品参数名称', '商品参数值']
const filterVal = ['id', 'goodsId', 'attribute', 'value']
excel.export_json_to_excel2(tHeader, this.list, filterVal, '商品参数信息')
this.downloadLoading = false
})
}
}
}
</script>

View File

@@ -15,25 +15,25 @@
<!-- 查询结果 -->
<el-table size="small" :data="list" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" width="150px" label="评论ID" prop="id" sortable>
<el-table-column align="center" label="评论ID" prop="id">
</el-table-column>
<el-table-column align="center" width="100px" label="用户ID" prop="userId">
<el-table-column align="center" label="用户ID" prop="userId">
</el-table-column>
<el-table-column align="center" width="100px" label="商品ID" prop="valueId">
<el-table-column align="center" label="商品ID" prop="valueId">
</el-table-column>
<el-table-column align="center" min-width="200px" label="评论内容" prop="content">
<el-table-column align="center" min-width="200" label="评论内容" prop="content">
</el-table-column>
<el-table-column align="center" min-width="200px" label="评论图片" prop="picUrls">
<el-table-column align="center" min-width="200" label="评论图片" prop="picUrls">
</el-table-column>
<el-table-column align="center" min-width="100px" label="时间" prop="addTime">
<el-table-column align="center" min-width="100" label="时间" prop="addTime">
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<el-table-column align="center" label="操作" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>

View File

@@ -40,27 +40,20 @@
</el-form-item>
<el-form-item label="首页主图">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadListPicUrl">
<img v-if="goods.listPicUrl" :src="goods.listPicUrl" class="avatar">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadPicUrl">
<img v-if="goods.picUrl" :src="goods.picUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="商品页主图">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadPrimaryPicUrl">
<img v-if="goods.primaryPicUrl" :src="goods.primaryPicUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="宣传画廊">
<el-upload :action='uploadPath' :limit='5' multiple accept=".jpg,.jpeg,.png,.gif" :file-list="galleryFileList" list-type="picture-card" :on-exceed='uploadOverrun' :on-success="handleGalleryUrl" :on-remove="handleRemove">
<el-upload :action='uploadPath' :limit='5' multiple accept=".jpg,.jpeg,.png,.gif" list-type="picture-card" :on-exceed='uploadOverrun' :on-success="handleGalleryUrl" :on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
<el-form-item label="商品单位">
<el-input v-model="goods.goodsUnit" placeholder="件 / 个 / 盒"></el-input>
<el-input v-model="goods.unit" placeholder="件 / 个 / 盒"></el-input>
</el-form-item>
<el-form-item label="关键字">
@@ -77,15 +70,18 @@
</el-form-item>
<el-form-item label="所属品牌商">
<el-input v-model="goods.brandId"></el-input>
<el-select v-model="goods.brandId">
<el-option v-for="item in brandList" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="商品简介">
<el-input v-model="goods.goodsBrief"></el-input>
<el-input v-model="goods.brief"></el-input>
</el-form-item>
<el-form-item label="商品详细介绍">
<editor :init="editorInit" v-model="goods.goodsDesc"></editor>
<editor :init="editorInit" v-model="goods.desc"></editor>
</el-form-item>
</el-form>
</el-card>
@@ -161,7 +157,7 @@
</el-table-column>
<el-table-column property="number" width="100" label="货品数量">
</el-table-column>
<el-table-column property="price" width="100" label="货品图片">
<el-table-column property="url" width="100" label="货品图片">
<template slot-scope="scope">
<img :src="scope.row.url" width="40" v-if="scope.row.url"/>
</template>
@@ -186,7 +182,7 @@
<el-form-item label="货品数量" prop="number">
<el-input v-model="productForm.number"></el-input>
</el-form-item>
<el-form-item label="货品图片" prop="picUrl">
<el-form-item label="货品图片" prop="url">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadProductUrl">
<img v-if="productForm.url" :src="productForm.url" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
@@ -277,14 +273,13 @@
</style>
<script>
import { publishGoods } from '@/api/goods'
import { publishGoods, listCatAndBrand } from '@/api/goods'
import { createStorage, uploadPath } from '@/api/storage'
import { listCategory2 } from '@/api/category'
import Editor from '@tinymce/tinymce-vue'
import { MessageBox } from 'element-ui'
export default {
name: 'GoodsAdd',
name: 'GoodsCreate',
components: { Editor },
data() {
return {
@@ -292,26 +287,9 @@ export default {
newKeywordVisible: false,
newKeyword: '',
keywords: [],
galleryFileList: [],
categoryList: [],
goods: {
id: undefined,
goodsSn: undefined,
name: undefined,
counterPrice: undefined,
retailPrice: undefined,
isHot: false,
isNew: true,
isOnSale: true,
listPicUrl: undefined,
primaryPicUrl: undefined,
goodsBrief: undefined,
goodsDesc: '',
keywords: '',
gallery: [],
categoryId: undefined,
brandId: undefined
},
brandList: [],
goods: { gallery: [] },
specVisiable: false,
specForm: { specification: '', value: '', picUrl: '' },
multipleSpec: false,
@@ -343,12 +321,13 @@ export default {
}
},
created() {
this.getCatList()
this.init()
},
methods: {
getCatList: function() {
listCategory2().then(response => {
this.categoryList = response.data.data
init: function() {
listCatAndBrand().then(response => {
this.categoryList = response.data.data.categoryList
this.brandList = response.data.data.brandList
})
},
handleCategoryChange(value) {
@@ -371,7 +350,7 @@ export default {
type: 'success',
duration: 2000
})
this.$router.push({ path: '/goods/goods' })
this.$router.push({ path: '/goods/list' })
}).catch(response => {
MessageBox.alert('业务错误:' + response.data.errmsg, '警告', {
confirmButtonText: '确定',
@@ -398,11 +377,8 @@ export default {
this.newKeywordVisible = false
this.newKeyword = ''
},
uploadPrimaryPicUrl: function(response) {
this.goods.primaryPicUrl = response.data.url
},
uploadListPicUrl: function(response) {
this.goods.listPicUrl = response.data.url
uploadPicUrl: function(response) {
this.goods.picUrl = response.data.url
},
uploadOverrun: function() {
this.$message({

View File

@@ -0,0 +1,554 @@
<template>
<div class="app-container calendar-list-container">
<el-card class="box-card">
<h3>商品介绍</h3>
<el-form :rules="rules" ref="goods" :model="goods" label-width="150px">
<el-form-item label="商品编号" prop="goodsSn">
<el-input v-model="goods.goodsSn"></el-input>
</el-form-item>
<el-form-item label="商品名称" prop="name">
<el-input v-model="goods.name"></el-input>
</el-form-item>
<el-form-item label="专柜价格" prop="counterPrice">
<el-input v-model="goods.counterPrice" placeholder="0.00">
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item label="当前价格" prop="retailPrice">
<el-input v-model="goods.retailPrice" placeholder="0.00">
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item label="是否新品" prop="isNew">
<el-radio-group v-model="goods.isNew">
<el-radio :label="true">新品</el-radio>
<el-radio :label="false">非新品</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="是否热卖" prop="isHot">
<el-radio-group v-model="goods.isHot">
<el-radio :label="false">普通</el-radio>
<el-radio :label="true">热卖</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="是否在售" prop="isOnSale">
<el-radio-group v-model="goods.isOnSale">
<el-radio :label="true">在售</el-radio>
<el-radio :label="false">未售</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="商品图片">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadPicUrl">
<img v-if="goods.picUrl" :src="goods.picUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="宣传画廊">
<el-upload :action='uploadPath' :limit='5' multiple accept=".jpg,.jpeg,.png,.gif" :file-list="galleryFileList" list-type="picture-card" :on-exceed='uploadOverrun' :on-success="handleGalleryUrl" :on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
<el-form-item label="商品单位">
<el-input v-model="goods.unit" placeholder="件 / 个 / 盒"></el-input>
</el-form-item>
<el-form-item label="关键字">
<el-tag :key="tag" v-for="tag in keywords" closable type="primary" @close="handleClose(tag)">
{{tag}}
</el-tag>
<el-input class="input-new-keyword" v-if="newKeywordVisible" v-model="newKeyword" ref="newKeywordInput" size="small" @keyup.enter.native="handleInputConfirm" @blur="handleInputConfirm">
</el-input>
<el-button v-else class="button-new-keyword" size="small" type="primary" @click="showInput">+ 增加</el-button>
</el-form-item>
<el-form-item label="所属分类">
<el-cascader expand-trigger="hover" :options="categoryList" v-model="categoryIds" @change="handleCategoryChange"></el-cascader>
</el-form-item>
<el-form-item label="所属品牌商">
<el-select v-model="goods.brandId">
<el-option v-for="item in brandList" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="商品简介">
<el-input v-model="goods.brief"></el-input>
</el-form-item>
<el-form-item label="商品详细介绍">
<editor :init="editorInit" v-model="goods.desc"></editor>
</el-form-item>
</el-form>
</el-card>
<el-card class="box-card">
<h3>商品规格</h3>
<el-button :plain="true" @click="handleSpecificationShow" type="primary">添加</el-button>
<el-table :data="specifications">
<el-table-column property="specification" label="规格名" ></el-table-column>
<el-table-column property="value" label="规格值" >
<template slot-scope="scope">
<el-tag type="primary">
{{scope.row.value}}
</el-tag>
</template>
</el-table-column>
<el-table-column property="picUrl" label="规格图片">
<template slot-scope="scope">
<img :src="scope.row.picUrl" width="40" v-if="scope.row.picUrl"/>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="handleSpecificationDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog title="设置规格" :visible.sync="specVisiable">
<el-form :rules="rules" ref="specForm" :model="specForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-form-item label="规格名" prop="specification">
<el-input v-model="specForm.specification"></el-input>
</el-form-item>
<el-form-item label="规格值" prop="value">
<el-input v-model="specForm.value"></el-input>
</el-form-item>
<el-form-item label="规格图片" prop="picUrl">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadSpecPicUrl">
<img v-if="specForm.picUrl" :src="specForm.picUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="specVisiable = false">取消</el-button>
<el-button type="primary" @click="handleSpecificationAdd">确定</el-button>
</div>
</el-dialog>
</el-card>
<el-card class="box-card">
<h3>商品库存</h3>
<el-table :data="products">
<el-table-column property="value" label="货品规格" >
<template slot-scope="scope">
<el-tag :key="tag" v-for="tag in scope.row.specifications">
{{tag}}
</el-tag>
</template>
</el-table-column>
<el-table-column property="price" width="100" label="货品售价">
</el-table-column>
<el-table-column property="number" width="100" label="货品数量">
</el-table-column>
<el-table-column property="url" width="100" label="货品图片">
<template slot-scope="scope">
<img :src="scope.row.url" width="40" v-if="scope.row.url"/>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="100" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleProductShow(scope.row)">设置</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog title="设置货品" :visible.sync="productVisiable">
<el-form ref="productForm" :model="productForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-form-item label="货品规格列" prop="specifications">
<el-tag :key="tag" v-for="tag in productForm.specifications">
{{tag}}
</el-tag>
</el-form-item>
<el-form-item label="货品售价" prop="price">
<el-input v-model="productForm.price"></el-input>
</el-form-item>
<el-form-item label="货品数量" prop="number">
<el-input v-model="productForm.number"></el-input>
</el-form-item>
<el-form-item label="货品图片" prop="url">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadProductUrl">
<img v-if="productForm.url" :src="productForm.url" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="productVisiable = false">取消</el-button>
<el-button type="primary" @click="handleProductEdit">确定</el-button>
</div>
</el-dialog>
</el-card>
<el-card class="box-card">
<h3>商品参数</h3>
<el-button :plain="true" @click="handleAttributeShow" type="primary">添加</el-button>
<el-table :data="attributes">
<el-table-column property="attribute" label="商品参数名称">
</el-table-column>
<el-table-column property="value" label="商品参数值">
</el-table-column>
<el-table-column align="center" label="操作" width="100" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="handleAttributeDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog title="设置商品参数" :visible.sync="attributeVisiable">
<el-form ref="attributeForm" :model="attributeForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-form-item label="商品参数名称" prop="attribute">
<el-input v-model="attributeForm.attribute"></el-input>
</el-form-item>
<el-form-item label="商品参数值" prop="value">
<el-input v-model="attributeForm.value"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="attributeVisiable = false">取消</el-button>
<el-button type="primary" @click="handleAttributeAdd">确定</el-button>
</div>
</el-dialog>
</el-card>
<div class="op-container">
<el-button @click="handleCancel">取消</el-button>
<el-button @click="handleEdit" type="primary">更新商品</el-button>
</div>
</div>
</template>
<style>
.el-card {
margin-bottom: 10px;
}
.el-tag + .el-tag {
margin-left: 10px;
}
.input-new-keyword {
width: 90px;
margin-left: 10px;
vertical-align: bottom;
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #20a0ff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
}
.avatar {
width: 120px;
height: 120px;
display: block;
}
</style>
<script>
import { detailGoods, editGoods, listCatAndBrand } from '@/api/goods'
import { createStorage, uploadPath } from '@/api/storage'
import Editor from '@tinymce/tinymce-vue'
import { MessageBox } from 'element-ui'
export default {
name: 'GoodsEdit',
components: { Editor },
data() {
return {
uploadPath,
newKeywordVisible: false,
newKeyword: '',
keywords: [],
galleryFileList: [],
categoryList: [],
brandList: [],
categoryIds: [],
goods: { gallery: [] },
specVisiable: false,
specForm: { specification: '', value: '', picUrl: '' },
specifications: [{ specification: '规格', value: '标准', picUrl: '' }],
productVisiable: false,
productForm: { id: 0, specifications: [], price: 0.00, number: 0, url: '' },
products: [{ id: 0, specifications: ['标准'], price: 0.00, number: 0, url: '' }],
attributeVisiable: false,
attributeForm: { attribute: '', value: '' },
attributes: [],
rules: {
goodsSn: [{ required: true, message: '商品编号不能为空', trigger: 'blur' }],
name: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }]
},
editorInit: {
language: 'zh_CN',
plugins: ['advlist anchor autolink autoresize autosave emoticons fullscreen hr image imagetools importcss insertdatetime legacyoutput link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace tabfocus table template textcolor textpattern visualblocks visualchars wordcount'],
toolbar: ['bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript ', 'hr bullist numlist link image charmap preview anchor pagebreak fullscreen media table emoticons forecolor backcolor'],
images_upload_handler: function(blobInfo, success, failure) {
const formData = new FormData()
formData.append('file', blobInfo.blob())
createStorage(formData).then(res => {
success(res.data.data.url)
}).catch(() => {
failure('上传失败,请重新上传')
})
}
}
}
},
created() {
this.init()
},
methods: {
init: function() {
if (this.$route.query.id == null) {
return
}
const goodsId = this.$route.query.id
detailGoods(goodsId).then(response => {
this.goods = response.data.data.goods
this.specifications = response.data.data.specifications
this.products = response.data.data.products
this.attributes = response.data.data.attributes
this.categoryIds = response.data.data.categoryIds
this.galleryFileList = []
for (var i = 0; i < this.goods.gallery.length; i++) {
this.galleryFileList.push({
url: this.goods.gallery[i]
})
}
})
listCatAndBrand().then(response => {
this.categoryList = response.data.data.categoryList
this.brandList = response.data.data.brandList
})
},
handleCategoryChange(value) {
this.goods.categoryId = value[value.length - 1]
},
handleCancel: function() {
this.$router.push({ path: '/goods/list' })
},
handleEdit: function() {
const finalGoods = {
goods: this.goods,
specifications: this.specifications,
products: this.products,
attributes: this.attributes
}
editGoods(finalGoods).then(response => {
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
})
this.$router.push({ path: '/goods/list' })
}).catch(response => {
MessageBox.alert('业务错误:' + response.data.errmsg, '警告', {
confirmButtonText: '确定',
type: 'error'
})
})
},
handleClose(tag) {
this.keywords.splice(this.keywords.indexOf(tag), 1)
this.goods.keywords = this.keywords.toString()
},
showInput() {
this.newKeywordVisible = true
this.$nextTick(_ => {
this.$refs.newKeywordInput.$refs.input.focus()
})
},
handleInputConfirm() {
const newKeyword = this.newKeyword
if (newKeyword) {
this.keywords.push(newKeyword)
this.goods.keywords = this.keywords.toString()
}
this.newKeywordVisible = false
this.newKeyword = ''
},
uploadPicUrl: function(response) {
this.goods.picUrl = response.data.url
},
uploadOverrun: function() {
this.$message({
type: 'error',
message: '上传文件个数超出限制!最多上传5张图片!'
})
},
handleGalleryUrl(response, file, fileList) {
if (response.errno === 0) {
this.goods.gallery.push(response.data.url)
}
},
handleRemove: function(file, fileList) {
for (var i = 0; i < this.goods.gallery.length; i++) {
// 这里存在两种情况
// 1. 如果所删除图片是刚刚上传的图片那么图片地址是file.response.data.url
// 此时的file.url虽然存在但是是本机地址而不是远程地址。
// 2. 如果所删除图片是后台返回的已有图片那么图片地址是file.url
var url
if (file.response === undefined) {
url = file.url
} else {
url = file.response.data.url
}
if (this.goods.gallery[i] === url) {
this.goods.gallery.splice(i, 1)
}
}
},
specChanged: function(label) {
if (label === false) {
this.specifications = [{ specification: '规格', value: '标准', picUrl: '' }]
this.products = [{ id: 0, specifications: ['标准'], price: 0.00, number: 0, url: '' }]
} else {
this.specifications = []
this.products = []
}
},
uploadSpecPicUrl: function(response) {
this.specForm.picUrl = response.data.url
},
handleSpecificationShow() {
this.specForm = {}
this.specVisiable = true
},
handleSpecificationAdd() {
var index = this.specifications.length - 1
for (var i = 0; i < this.specifications.length; i++) {
const v = this.specifications[i]
if (v.specification === this.specForm.specification) {
index = i
}
}
this.specifications.splice(index + 1, 0, this.specForm)
this.specVisiable = false
this.specToProduct()
},
handleSpecificationDelete(row) {
const index = this.specifications.indexOf(row)
this.specifications.splice(index, 1)
this.specToProduct()
},
specToProduct() {
if (this.specifications.length === 0) {
return
}
// 根据specifications创建临时规格列表
var specValues = []
var spec = this.specifications[0].specification
var values = []
values.push(0)
for (var i = 1; i < this.specifications.length; i++) {
const aspec = this.specifications[i].specification
if (aspec === spec) {
values.push(i)
} else {
specValues.push(values)
spec = aspec
values = []
values.push(i)
}
}
specValues.push(values)
// 根据临时规格列表生产货品规格
// 算法基于 https://blog.csdn.net/tyhj_sf/article/details/53893125
var productsIndex = 0
var products = []
var combination = []
var n = specValues.length
for (var s = 0; s < n; s++) {
combination[s] = 0
}
var index = 0
var isContinue = false
do {
var specifications = []
for (var x = 0; x < n; x++) {
var z = specValues[x][combination[x]]
specifications.push(this.specifications[z].value)
}
products[productsIndex] = { id: productsIndex, specifications: specifications, price: 0.00, number: 0, url: '' }
productsIndex++
index++
combination[n - 1] = index
for (var j = n - 1; j >= 0; j--) {
if (combination[j] >= specValues[j].length) {
combination[j] = 0
index = 0
if (j - 1 >= 0) {
combination[j - 1] = combination[j - 1] + 1
}
}
}
isContinue = false
for (var p = 0; p < n; p++) {
if (combination[p] !== 0) {
isContinue = true
}
}
} while (isContinue)
this.products = products
},
handleProductShow(row) {
this.productForm = Object.assign({}, row)
this.productVisiable = true
},
uploadProductUrl: function(response) {
this.productForm.url = response.data.url
},
handleProductEdit() {
for (var i = 0; i < this.products.length; i++) {
const v = this.products[i]
if (v.id === this.productForm.id) {
this.products.splice(i, 1, this.productForm)
break
}
}
this.productVisiable = false
},
handleAttributeShow() {
this.attributeForm = {}
this.attributeVisiable = true
},
handleAttributeAdd() {
this.attributes.unshift(this.attributeForm)
this.attributeVisiable = false
},
handleAttributeDelete(row) {
const index = this.attributes.indexOf(row)
this.attributes.splice(index, 1)
}
}
}
</script>

View File

@@ -1,423 +0,0 @@
<template>
<div class="app-container calendar-list-container">
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入商品编号" v-model="listQuery.goodsSn">
</el-input>
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入商品名称" v-model="listQuery.name">
</el-input>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button class="filter-item" type="primary" @click="handleCreate" icon="el-icon-edit">添加</el-button>
<el-button class="filter-item" type="primary" :loading="downloadLoading" icon="el-icon-download" @click="handleDownload">导出</el-button>
</div>
<!-- 查询结果 -->
<el-table size="small" :data="list" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column type="expand">
<template slot-scope="props">
<el-form label-position="left" class="demo-table-expand">
<el-form-item label="首页主图">
<span>{{ props.row.listPicUrl }}</span>
</el-form-item>
<el-form-item label="宣传画廊">
<span>{{ props.row.gallery }}</span>
</el-form-item>
<el-form-item label="商品介绍">
<span>{{ props.row.goodsBrief }}</span>
</el-form-item>
<el-form-item label="商品详细介绍">
<span>{{ props.row.goodsDesc }}</span>
</el-form-item>
<el-form-item label="商品主图">
<span>{{ props.row.primaryPicUrl }}</span>
</el-form-item>
<el-form-item label="商品单位">
<span>{{ props.row.goodsUnit }}</span>
</el-form-item>
<el-form-item label="关键字">
<span>{{ props.row.keyword }}</span>
</el-form-item>
<el-form-item label="类目ID">
<span>{{ props.row.categoryId }}</span>
</el-form-item>
<el-form-item label="品牌商ID">
<span>{{ props.row.brandId }}</span>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column align="center" width="100px" label="商品ID" prop="id" sortable>
</el-table-column>
<el-table-column align="center" min-width="100px" label="商品编号" prop="goodsSn">
</el-table-column>
<el-table-column align="center" min-width="100px" label="名称" prop="name">
</el-table-column>
<el-table-column align="center" min-width="100px" label="专柜价格" prop="counterPrice">
</el-table-column>
<el-table-column align="center" min-width="100px" label="当前价格" prop="retailPrice">
</el-table-column>
<el-table-column align="center" min-width="100px" label="是否新品" prop="isNew">
<template slot-scope="scope">
<el-tag :type="scope.row.isNew ? 'success' : 'error' ">{{scope.row.isNew ? '新品' : '非新品'}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" min-width="100px" label="是否热品" prop="isHot">
<template slot-scope="scope">
<el-tag :type="scope.row.isHot ? 'success' : 'error' ">{{scope.row.isHot ? '热品' : '非热品'}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" min-width="100px" label="是否在售" prop="isOnSale">
<template slot-scope="scope">
<el-tag :type="scope.row.isOnSale ? 'success' : 'error' ">{{scope.row.isOnSale ? '在售' : '未售'}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listQuery.page"
:page-sizes="[10,20,30,50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
<el-tooltip placement="top" content="返回顶部">
<back-to-top :visibilityHeight="100" ></back-to-top>
</el-tooltip>
<!-- 添加或修改对话框 -->
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form :rules="rules" ref="dataForm" :model="dataForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-form-item label="商品编号" prop="goodsSn">
<el-input v-model="dataForm.goodsSn"></el-input>
</el-form-item>
<el-form-item label="商品名称" prop="name">
<el-input v-model="dataForm.name"></el-input>
</el-form-item>
<el-form-item label="专柜价格" prop="counterPrice">
<el-input v-model="dataForm.counterPrice"></el-input>
</el-form-item>
<el-form-item label="当前价格" prop="retailPrice">
<el-input v-model="dataForm.retailPrice"></el-input>
</el-form-item>
<el-form-item label="是否新品" prop="isNew">
<el-select v-model="dataForm.isNew" placeholder="请选择">
<el-option label="新品" :value="true">
</el-option>
<el-option label="新品" :value="false">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="是否热品" prop="isHot">
<el-select v-model="dataForm.isHot" placeholder="请选择">
<el-option label="热品" :value="true">
</el-option>
<el-option label="非热品" :value="false">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="是否在售" prop="isOnSale">
<el-select v-model="dataForm.isOnSale" placeholder="请选择">
<el-option label="在售" :value="true">
</el-option>
<el-option label="未售" :value="false">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="首页主图">
<el-input v-model="dataForm.listPicUrl"></el-input>
</el-form-item>
<el-form-item label="宣传画廊">
<el-upload :action='UPLOAD_API' :limit='5' multiple accept=".jpg,.jpeg,.png,.gif" :file-list="galleryFileList" list-type="picture" :on-exceed='uploadOverrun' :on-success="handleGalleryUrl" :on-remove="handleRemove">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-form-item>
<el-form-item label="商品介绍">
<el-input v-model="dataForm.goodsBrief"></el-input>
</el-form-item>
<el-form-item style="width: 700px;" label="商品详细介绍">
<editor :init="editorInit" v-model="dataForm.goodsDesc"></editor>
</el-form-item>
<el-form-item label="商品主图">
<el-input v-model="dataForm.primaryPicUrl"></el-input>
</el-form-item>
<el-form-item label="商品单位">
<el-input v-model="dataForm.goodsUnit"></el-input>
</el-form-item>
<el-form-item label="关键字">
<el-input v-model="dataForm.keyword"></el-input>
</el-form-item>
<el-form-item label="类目ID">
<el-input v-model="dataForm.categoryId"></el-input>
</el-form-item>
<el-form-item label="品牌商ID">
<el-input v-model="dataForm.brandId"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="updateData">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<style>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 200px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
}
.el-dialog {
width: 800px;
}
</style>
<script>
import { listGoods, updateGoods, deleteGoods } from '@/api/goods'
import { createStorage } from '@/api/storage'
import BackToTop from '@/components/BackToTop'
import Editor from '@tinymce/tinymce-vue'
export default {
name: 'Goods',
components: { BackToTop, Editor },
data() {
return {
list: [],
galleryFileList: [],
total: 0,
listLoading: true,
listQuery: {
page: 1,
limit: 20,
goodsSn: undefined,
name: undefined,
sort: '+id'
},
dataForm: {
id: undefined,
goodsSn: undefined,
name: undefined,
counterPrice: undefined,
retailPrice: undefined,
isHot: false,
isNew: true,
isOnSale: true,
listPicUrl: undefined,
primaryPicUrl: undefined,
goodsBrief: undefined,
goodsDesc: '',
keywords: undefined,
gallery: [],
categoryId: undefined,
brandId: undefined
},
dialogFormVisible: false,
dialogStatus: '',
textMap: {
update: '编辑',
create: '创建'
},
rules: {
goodsSn: [{ required: true, message: '商品编号不能为空', trigger: 'blur' }],
name: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }]
},
downloadLoading: false,
editorInit: {
language: 'zh_CN',
plugins: ['advlist anchor autolink autoresize autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime legacyoutput link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace tabfocus table template textcolor textpattern visualblocks visualchars wordcount'],
toolbar: ['bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript ', 'hr bullist numlist link image charmap preview anchor pagebreak fullscreen media table emoticons forecolor backcolor'],
images_upload_handler: function(blobInfo, success, failure) {
const formData = new FormData()
formData.append('file', blobInfo.blob())
createStorage(formData).then(res => {
success(res.data.data.url)
}).catch(() => {
failure('上传失败,请重新上传')
})
}
}
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
listGoods(this.listQuery).then(response => {
this.list = response.data.data.items
this.total = response.data.data.total
this.listLoading = false
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
},
resetForm() {
this.galleryFileList = []
this.dataForm = {
id: undefined,
goodsSn: undefined,
name: undefined,
counterPrice: undefined,
retailPrice: undefined,
isHot: false,
isNew: true,
isOnSale: true,
listPicUrl: undefined,
primaryPicUrl: undefined,
goodsBrief: undefined,
goodsDesc: '',
keywords: undefined,
gallery: [],
categoryId: undefined,
brandId: undefined
}
},
filterLevel(value, row) {
return row.level === value
},
uploadOverrun: function() {
this.$message({
type: 'error',
message: '上传文件个数超出限制!最多上传5张图片!'
})
},
handleGalleryUrl(response, file, fileList) {
if (response.errno === 0) {
this.dataForm.gallery.push(response.data.url)
}
},
handleRemove: function(file, fileList) {
for (var i = 0; i < this.dataForm.gallery.length; i++) {
// 这里存在两种情况
// 1. 如果所删除图片是刚刚上传的图片那么图片地址是file.response.data.url
// 此时的file.url虽然存在但是是本机地址而不是远程地址。
// 2. 如果所删除图片是后台返回的已有图片那么图片地址是file.url
var url
if (file.response === undefined) {
url = file.url
} else {
url = file.response.data.url
}
if (this.dataForm.gallery[i] === url) {
this.dataForm.gallery.splice(i, 1)
}
}
},
handleCreate() {
this.$router.push({ path: '/goods/publish' })
},
handleUpdate(row) {
this.dataForm = Object.assign({}, row)
this.galleryFileList = []
if (this.dataForm.gallery.length > 0) {
for (var i = 0; i < row.gallery.length; i++) {
this.galleryFileList.push({
name: row.gallery[i].substring(row.gallery[i].lastIndexOf('/') + 1),
url: row.gallery[i]
})
}
}
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
updateData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
updateGoods(this.dataForm).then(() => {
for (const v of this.list) {
if (v.id === this.dataForm.id) {
const index = this.list.indexOf(v)
this.list.splice(index, 1, this.dataForm)
break
}
}
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleDelete(row) {
deleteGoods(row).then(response => {
this.$notify({
title: '成功',
message: '删除成功',
type: 'success',
duration: 2000
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['商品ID', '商品编号', '名称', '专柜价格', '当前价格', '是否新品', '是否热品', '是否在售', '首页主图', '宣传画廊', '商品介绍', '详细介绍', '商品主图', '商品单位', '关键字', '类目ID', '品牌商ID']
const filterVal = ['id', 'goodsSn', 'name', 'counterPrice', 'retailPrice', 'isNew', 'isHot', 'isOnSale', 'listPicUrl', 'gallery', 'goodsBrief', 'goodsDesc', 'primaryPicUrl', 'goodsUnit', 'keywords', 'categoryId', 'brandId']
excel.export_json_to_excel2(tHeader, this.list, filterVal, '商品信息')
this.downloadLoading = false
})
}
}
}
</script>

View File

@@ -0,0 +1,221 @@
<template>
<div class="app-container calendar-list-container">
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入商品编号" v-model="listQuery.goodsSn">
</el-input>
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入商品名称" v-model="listQuery.name">
</el-input>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button class="filter-item" type="primary" @click="handleCreate" icon="el-icon-edit">添加</el-button>
<el-button class="filter-item" type="primary" :loading="downloadLoading" icon="el-icon-download" @click="handleDownload">导出</el-button>
</div>
<!-- 查询结果 -->
<el-table size="small" :data="list" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column type="expand">
<template slot-scope="props">
<el-form label-position="left" class="table-expand">
<el-form-item label="宣传画廊">
<img class="gallery" v-for="pic in props.row.gallery" :key="pic" :src="pic"/>
</el-form-item>
<el-form-item label="商品介绍">
<span>{{ props.row.brief }}</span>
</el-form-item>
<el-form-item label="商品单位">
<span>{{ props.row.unit }}</span>
</el-form-item>
<el-form-item label="关键字">
<span>{{ props.row.keyword }}</span>
</el-form-item>
<el-form-item label="类目ID">
<span>{{ props.row.categoryId }}</span>
</el-form-item>
<el-form-item label="品牌商ID">
<span>{{ props.row.brandId }}</span>
</el-form-item>
<el-form-item label="商品详细介绍">
<div v-html="props.row.desc"></div>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column align="center" label="商品ID" prop="id">
</el-table-column>
<el-table-column align="center" label="商品编号" prop="goodsSn">
</el-table-column>
<el-table-column align="center" min-width="100" label="名称" prop="name">
</el-table-column>
<el-table-column align="center" property="iconUrl" label="图片">
<template slot-scope="scope">
<img :src="scope.row.picUrl" width="40"/>
</template>
</el-table-column>
<el-table-column align="center" label="专柜价格" prop="counterPrice">
</el-table-column>
<el-table-column align="center" label="当前价格" prop="retailPrice">
</el-table-column>
<el-table-column align="center" label="是否新品" prop="isNew">
<template slot-scope="scope">
<el-tag :type="scope.row.isNew ? 'success' : 'error' ">{{scope.row.isNew ? '新品' : '非新品'}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="是否热品" prop="isHot">
<template slot-scope="scope">
<el-tag :type="scope.row.isHot ? 'success' : 'error' ">{{scope.row.isHot ? '热品' : '非热品'}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="是否在售" prop="isOnSale">
<template slot-scope="scope">
<el-tag :type="scope.row.isOnSale ? 'success' : 'error' ">{{scope.row.isOnSale ? '在售' : '未售'}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listQuery.page"
:page-sizes="[10,20,30,50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
<el-tooltip placement="top" content="返回顶部">
<back-to-top :visibilityHeight="100" ></back-to-top>
</el-tooltip>
</div>
</template>
<style>
.table-expand {
font-size: 0;
}
.table-expand label {
width: 100px;
color: #99a9bf;
}
.table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
}
.gallery {
width: 80px;
margin-right: 10px;
}
</style>
<script>
import { listGoods, deleteGoods } from '@/api/goods'
import BackToTop from '@/components/BackToTop'
export default {
name: 'GoodsList',
components: { BackToTop },
data() {
return {
list: [],
total: 0,
listLoading: true,
listQuery: {
page: 1,
limit: 20,
goodsSn: undefined,
name: undefined,
sort: '+id'
},
dataForm: {
id: undefined,
goodsSn: undefined,
name: undefined,
counterPrice: undefined,
retailPrice: undefined,
isHot: false,
isNew: true,
isOnSale: true,
picUrl: undefined,
brief: undefined,
desc: undefined,
keywords: undefined,
gallery: [],
categoryId: undefined,
brandId: undefined
},
downloadLoading: false
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
listGoods(this.listQuery).then(response => {
this.list = response.data.data.items
this.total = response.data.data.total
this.listLoading = false
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
},
handleCreate() {
this.$router.push({ path: '/goods/create' })
},
handleUpdate(row) {
this.$router.push({ path: '/goods/edit', query: { id: row.id }})
},
handleDelete(row) {
deleteGoods(row).then(response => {
this.$notify({
title: '成功',
message: '删除成功',
type: 'success',
duration: 2000
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['商品ID', '商品编号', '名称', '专柜价格', '当前价格', '是否新品', '是否热品', '是否在售', '首页主图', '宣传图片列表', '商品介绍', '详细介绍', '商品图片', '商品单位', '关键字', '类目ID', '品牌商ID']
const filterVal = ['id', 'goodsSn', 'name', 'counterPrice', 'retailPrice', 'isNew', 'isHot', 'isOnSale', 'listPicUrl', 'gallery', 'brief', 'desc', 'picUrl', 'goodsUnit', 'keywords', 'categoryId', 'brandId']
excel.export_json_to_excel2(tHeader, this.list, filterVal, '商品信息')
this.downloadLoading = false
})
}
}
}
</script>

View File

@@ -1,220 +0,0 @@
<template>
<div class="app-container calendar-list-container">
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID" v-model="listQuery.goodsId">
</el-input>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload" :loading="downloadLoading">导出</el-button>
</div>
<!-- 查询结果 -->
<el-table size="small" :data="list" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column type="expand">
<template slot-scope="props">
</template>
</el-table-column>
<el-table-column align="center" width="100px" label="货品ID" prop="id" sortable>
</el-table-column>
<el-table-column align="center" min-width="100px" label="商品ID" prop="goodsId">
</el-table-column>
<el-table-column align="center" min-width="150px" label="商品规格ID列表" prop="goodsSpecificationIds">
<template slot-scope="scope">
{{ scope.row.goodsSpecificationIds.join(',') }}
</template>
</el-table-column>
<el-table-column align="center" min-width="100px" label="货品数量" prop="goodsNumber">
</el-table-column>
<el-table-column align="center" min-width="100px" label="货品价格" prop="retailPrice">
</el-table-column>
<el-table-column align="center" min-width="200px" label="货品图片" prop="url">
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listQuery.page"
:page-sizes="[10,20,30,50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
<!-- 修改对话框 -->
<el-dialog title="编辑商品货品" :visible.sync="editDialogFormVisible">
<el-form :rules="rules" ref="dataForm" :model="dataForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-form-item label="商品ID" prop="goodsId">
<el-input v-model="dataForm.goodsId" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="商品规格ID列表" prop="goodsSpecificationIds">
<el-input v-model="dataForm.goodsSpecificationIds" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="货品数量" prop="goodsNumber">
<el-input v-model="dataForm.goodsNumber"></el-input>
</el-form-item>
<el-form-item label="货品价格" prop="retailPrice">
<el-input v-model="dataForm.retailPrice"></el-input>
</el-form-item>
<el-form-item label="货品图片" prop="url">
<el-input v-model="dataForm.url"></el-input>
<el-upload action="#" list-type="picture" :show-file-list="false" :limit="1" :http-request="uploadUrl">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="editDialogFormVisible = false">取消</el-button>
\ <el-button type="primary" @click="updateData">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<style>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 200px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
}
</style>
<script>
import { listProduct, updateProduct } from '@/api/product'
import { createStorage } from '@/api/storage'
export default {
name: 'Product',
data() {
return {
list: undefined,
total: undefined,
listLoading: true,
listQuery: {
page: 1,
limit: 20,
goodsId: undefined,
sort: '+id'
},
editDialogFormVisible: false,
dataForm: {
id: undefined,
goodsId: undefined,
goodsSpecificationIds: undefined,
goodsNumber: 0,
retailPrice: 0,
url: undefined
},
rules: {
goodsId: [{ required: true, message: '商品ID不能为空', trigger: 'blur' }],
goodsSpecificationIds: [{ required: true, message: '商品规格ID列表不能为空', trigger: 'blur' }]
},
downloadLoading: false
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
listProduct(this.listQuery).then(response => {
this.list = response.data.data.items
this.total = response.data.data.total
this.listLoading = false
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
},
resetForm() {
this.dataForm = {
id: undefined,
goodsId: undefined,
goodsSpecificationIds: undefined,
goodsNumber: 0,
retailPrice: 0,
url: undefined
}
},
uploadUrl(item) {
const formData = new FormData()
formData.append('file', item.file)
createStorage(formData).then(res => {
this.dataForm.url = res.data.data.url
}).catch(() => {
this.$message.error('上传失败,请重新上传')
})
},
handleUpdate(row) {
this.dataForm = Object.assign({}, row)
this.editDialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
updateData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
updateProduct(this.dataForm).then(() => {
for (const v of this.list) {
if (v.id === this.dataForm.id) {
const index = this.list.indexOf(v)
this.list.splice(index, 1, this.dataForm)
break
}
}
this.editDialogFormVisible = false
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['货品ID', '商品ID', '商品规格ID列表', '货品数量', '货品价格', '货品图片']
const filterVal = ['id', 'goodsId', 'goodsSpecificationIds', 'goodsNumber', 'retailPrice', 'url']
excel.export_json_to_excel2(tHeader, this.list, filterVal, '货品信息')
this.downloadLoading = false
})
}
}
}
</script>

View File

@@ -1,179 +0,0 @@
<template>
<div class="app-container calendar-list-container">
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID" v-model="listQuery.goodsId">
</el-input>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button class="filter-item" type="primary" :loading="downloadLoading" icon="el-icon-download" @click="handleDownload">导出</el-button>
</div>
<!-- 查询结果 -->
<el-table size="small" :data="list" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" width="150px" label="商品参数ID" prop="id" sortable>
</el-table-column>
<el-table-column align="center" min-width="100px" label="商品ID" prop="goodsId">
</el-table-column>
<el-table-column align="center" min-width="100px" label="商品规格名称" prop="specification">
</el-table-column>
<el-table-column align="center" min-width="200px" label="商品规格值" prop="value">
</el-table-column>
<el-table-column align="center" min-width="200px" label="商品规格图片" prop="picUrl">
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listQuery.page"
:page-sizes="[10,20,30,50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
<!-- 修改对话框 -->
<el-dialog title="编辑商品规格" :visible.sync="dialogFormVisible">
<el-form :rules="rules" ref="dataForm" :model="dataForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-form-item label="商品ID" prop="goodsId">
<el-input v-model="dataForm.goodsId"></el-input>
</el-form-item>
<el-form-item label="商品规格名" prop="specification">
<el-input v-model="dataForm.specification"></el-input>
</el-form-item>
<el-form-item label="商品规格值" prop="value">
<el-input v-model="dataForm.value"></el-input>
</el-form-item>
<el-form-item label="商品规格图片" prop="picUrl">
<el-input v-model="dataForm.picUrl"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="updateData">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listGoodsSpecification, updateGoodsSpecification } from '@/api/goods-specification'
export default {
name: 'GoodsSpecification',
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
page: 1,
limit: 10,
goodsId: undefined,
sort: '+id'
},
dataForm: {
id: undefined,
goodsId: undefined,
specification: undefined,
value: undefined,
picUrl: undefined
},
dialogFormVisible: false,
dialogStatus: '',
rules: {
goodsId: [{ required: true, message: '商品ID不能为空', trigger: 'blur' }],
specification: [{ required: true, message: '商品规格名称不能为空', trigger: 'blur' }],
value: [{ required: true, message: '商品规格值不能为空', trigger: 'blur' }]
},
downloadLoading: false
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
listGoodsSpecification(this.listQuery).then(response => {
this.list = response.data.data.items
this.total = response.data.data.total
this.listLoading = false
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
},
resetForm() {
this.dataForm = {
id: undefined,
goodsId: undefined,
specification: undefined,
value: undefined,
picUrl: undefined
}
},
handleUpdate(row) {
this.dataForm = Object.assign({}, row)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
updateData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
updateGoodsSpecification(this.dataForm).then(() => {
for (const v of this.list) {
if (v.id === this.dataForm.id) {
const index = this.list.indexOf(v)
this.list.splice(index, 1, this.dataForm)
break
}
}
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['商品规格ID', '商品ID', '商品规格名称', '商品规格值', '商品规格图片']
const filterVal = ['id', 'goodsId', 'specification', 'value', 'picUrl']
excel.export_json_to_excel2(tHeader, this.list, filterVal, '商品规格信息')
this.downloadLoading = false
})
}
}
}
</script>

View File

@@ -15,36 +15,25 @@
<!-- 查询结果 -->
<el-table size="small" :data="list" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column type="expand">
<template slot-scope="props">
</template>
<el-table-column align="center" label="品牌商ID" prop="id">
</el-table-column>
<el-table-column align="center" width="150px" label="品牌商ID" prop="id" sortable>
<el-table-column align="center" label="品牌商名称" prop="name">
</el-table-column>
<el-table-column align="center" min-width="100px" label="品牌商名称" prop="name">
</el-table-column>
<el-table-column align="center" min-width="300px" label="介绍" prop="simpleDesc">
</el-table-column>
<el-table-column align="center" min-width="50px" label="底价" prop="floorPrice">
</el-table-column>
<el-table-column align="center" min-width="100px" label="是否显示" prop="isShow">
<el-table-column align="center" property="picUrl" label="品牌商图片">
<template slot-scope="scope">
<el-tag :type="scope.row.isShow ? 'success' : 'error' ">{{scope.row.isShow ? '可显示' : '不显示'}}</el-tag>
<img :src="scope.row.picUrl" width="80" v-if="scope.row.picUrl"/>
</template>
</el-table-column>
<el-table-column align="center" min-width="100px" label="是否新上" prop="isNew">
<template slot-scope="scope">
<el-tag :type="scope.row.isNew ? 'success' : 'error' ">{{scope.row.isShow ? '是' : '否'}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" min-width="400px" label="介绍" prop="desc">
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<el-table-column align="center" label="底价" prop="floorPrice">
</el-table-column>
<el-table-column align="center" label="操作" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
@@ -66,51 +55,17 @@
<el-input v-model="dataForm.name"></el-input>
</el-form-item>
<el-form-item label="介绍" prop="simpleDesc">
<el-input v-model="dataForm.simpleDesc"></el-input>
<el-input v-model="dataForm.desc"></el-input>
</el-form-item>
<el-form-item label="品牌商图片" prop="picUrl">
<el-input v-model="dataForm.picUrl"></el-input>
<el-upload action="#" list-type="picture" :show-file-list="false" :limit="1" :http-request="uploadPicUrl">
<el-button size="small" type="primary">点击上传</el-button>
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadPicUrl">
<img v-if="dataForm.picUrl" :src="dataForm.picUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="宣传图片" prop="listPicUrl">
<el-input v-model="dataForm.listPicUrl"></el-input>
<el-upload action="#" list-type="picture" :show-file-list="false" :limit="1" :http-request="uploadListPicUrl">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-form-item>
<el-form-item label="APP宣传图片" prop="appListPicUrl">
<el-input v-model="dataForm.appListPicUrl"></el-input>
<el-upload action="#" list-type="picture" :show-file-list="false" :limit="1" :http-request="uploadAppListPicUrl">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-form-item>
</el-form-item>
<el-form-item label="底价" prop="floorPrice">
<el-input v-model="dataForm.floorPrice"></el-input>
</el-form-item>
<el-form-item label="是否显示" prop="isShow">
<el-select v-model="dataForm.isShow" placeholder="请选择">
<el-option label="显示" :value="true">
</el-option>
<el-option label="不显示" :value="false">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="是否新上" prop="isNew">
<el-select v-model="dataForm.isNew" placeholder="请选择">
<el-option label="新上" :value="true">
</el-option>
<el-option label="不是新上" :value="false">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="新上宣传图片" prop="newPicUrl" v-if="dataForm.isNew === 'true'">
<el-input v-model="dataForm.newPicUrl"></el-input>
<el-upload action="#" list-type="picture" :show-file-list="false" :limit="1" :http-request="uploadNewPicUrl">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
@@ -123,27 +78,40 @@
</template>
<style>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 200px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #20a0ff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
}
.avatar {
width: 120px;
height: 120px;
display: block;
}
</style>
<script>
import { listBrand, createBrand, updateBrand, deleteBrand } from '@/api/brand'
import { createStorage } from '@/api/storage'
import { uploadPath } from '@/api/storage'
export default {
name: 'Brand',
data() {
return {
uploadPath,
list: undefined,
total: undefined,
listLoading: true,
@@ -157,14 +125,9 @@ export default {
dataForm: {
id: undefined,
name: '',
simpleDesc: '',
desc: '',
floorPrice: undefined,
picUrl: undefined,
listPicUrl: undefined,
appListPicUrl: undefined,
isShow: undefined,
isNew: undefined,
newPicUrl: undefined
picUrl: undefined
},
dialogFormVisible: false,
dialogStatus: '',
@@ -173,7 +136,7 @@ export default {
create: '创建'
},
rules: {
name: [{ required: true, message: '类目名称不能为空', trigger: 'blur' }]
name: [{ required: true, message: '品牌商名称不能为空', trigger: 'blur' }]
},
downloadLoading: false
}
@@ -210,14 +173,9 @@ export default {
this.dataForm = {
id: undefined,
name: '',
simpleDesc: '',
desc: '',
floorPrice: undefined,
picUrl: undefined,
listPicUrl: undefined,
appListPicUrl: undefined,
isShow: undefined,
isNew: undefined,
newPicUrl: undefined
picUrl: undefined
}
},
handleCreate() {
@@ -228,41 +186,8 @@ export default {
this.$refs['dataForm'].clearValidate()
})
},
uploadPicUrl(item) {
const formData = new FormData()
formData.append('file', item.file)
createStorage(formData).then(res => {
this.dataForm.picUrl = res.data.data.url
}).catch(() => {
this.$message.error('上传失败,请重新上传')
})
},
uploadListPicUrl(item) {
const formData = new FormData()
formData.append('file', item.file)
createStorage(formData).then(res => {
this.dataForm.listPicUrl = res.data.data.url
}).catch(() => {
this.$message.error('上传失败,请重新上传')
})
},
uploadAppListPicUrl(item) {
const formData = new FormData()
formData.append('file', item.file)
createStorage(formData).then(res => {
this.dataForm.appListPicUrl = res.data.data.url
}).catch(() => {
this.$message.error('上传失败,请重新上传')
})
},
uploadNewPicUrl(item) {
const formData = new FormData()
formData.append('file', item.file)
createStorage(formData).then(res => {
this.dataForm.newPicUrl = res.data.data.url
}).catch(() => {
this.$message.error('上传失败,请重新上传')
})
uploadPicUrl: function(response) {
this.dataForm.picUrl = response.data.data.url
},
createData() {
this.$refs['dataForm'].validate((valid) => {
@@ -325,8 +250,8 @@ export default {
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['品牌商ID', '品牌商名称', '介绍', '低价', '是否显示', '品牌商图片', '宣传图片', 'APP宣传图片', '是否新上', '新上宣传图片']
const filterVal = ['id', 'name', 'simpleDesc', 'floorPrice', 'isShow', 'picUrl', 'listPicUrl', 'appListPicUrl', 'isNew', 'newPicUrl']
const tHeader = ['品牌商ID', '品牌商名称', '介绍', '低价', '品牌商图片']
const filterVal = ['id', 'name', 'desc', 'floorPrice', 'picUrl']
excel.export_json_to_excel2(tHeader, this.list, filterVal, '品牌商信息')
this.downloadLoading = false
})

View File

@@ -15,54 +15,41 @@
<!-- 查询结果 -->
<el-table size="small" :data="list" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column type="expand">
<template slot-scope="props">
<el-form label-position="left" class="demo-table-expand">
<el-form-item label="首页页面类目图标">
<img :src="props.row.iconUrl">
</el-form-item>
<el-form-item label="首页页面类目横幅图片">
<img :src="props.row.bannerUrl">
</el-form-item>
<el-form-item label="类目页标题">
<span>{{ props.row.frontName }}</span>
</el-form-item>
<el-form-item label="类目页介绍">
<span>{{ props.row.frontDesc }}</span>
</el-form-item>
<el-form-item label="类目页横幅">
<img :src="props.row.wapBannerUrl">
</el-form-item>
</el-form>
<el-table-column align="center" label="类目ID" prop="id">
</el-table-column>
<el-table-column align="center" label="类目名" prop="name">
</el-table-column>
<el-table-column align="center" property="iconUrl" label="类目图标">
<template slot-scope="scope">
<img :src="scope.row.iconUrl" width="40" v-if="scope.row.iconUrl"/>
</template>
</el-table-column>
<el-table-column align="center" width="100px" label="类目ID" prop="id" sortable>
<el-table-column align="center" property="picUrl" label="类目图片">
<template slot-scope="scope">
<img :src="scope.row.picUrl" width="80" v-if="scope.row.picUrl"/>
</template>
</el-table-column>
<el-table-column align="center" min-width="100px" label="名称" prop="name">
</el-table-column>
<el-table-column align="center" min-width="100px" label="关键字" prop="keyword">
<el-table-column align="center" label="关键字" prop="keyword">
</el-table-column>
<el-table-column align="center" min-width="100px" label="级别" prop="level"
<el-table-column align="center" min-width="100" label="简介" prop="desc">
</el-table-column>
<el-table-column align="center" label="级别" prop="level"
:filters="[{ text: '一级类目', value: 'L1' }, { text: '二级类目', value: 'L2' }]" :filter-method="filterLevel">
<template slot-scope="scope">
<el-tag :type="scope.row.level === 'L1' ? 'primary' : 'info' ">{{scope.row.level === 'L1' ? '一级类目' : '二级类目'}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" min-width="100px" label="父类目ID" prop="parentId">
<el-table-column align="center" label="父类目ID" prop="pid">
</el-table-column>
<el-table-column align="center" min-width="100px" label="是否显示" prop="isShow">
<template slot-scope="scope">
<el-tag :type="scope.row.isShow ? 'success' : 'error' ">{{scope.row.isShow ? '可显示' : '不显示'}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<el-table-column align="center" label="操作" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
@@ -83,10 +70,10 @@
<el-form-item label="类目名称" prop="name">
<el-input v-model="dataForm.name"></el-input>
</el-form-item>
<el-form-item label="类目关键字" prop="keyword">
<el-form-item label="关键字" prop="keyword">
<el-input v-model="dataForm.keyword"></el-input>
</el-form-item>
<el-form-item label="类目级别" prop="level">
<el-form-item label="级别" prop="level">
<el-select v-model="dataForm.level" placeholder="请选择">
<el-option label="一级类目" value="L1">
</el-option>
@@ -94,36 +81,27 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="父类目" prop="parentId" v-if="dataForm.level === 'L2'">
<el-select v-model="dataForm.parentId" placeholder="请选择">
<el-option v-for="(key, val) in catL1" :key="key" :label="key" :value="val">
<el-form-item label="父类目" prop="pid" v-if="dataForm.level === 'L2'">
<el-select v-model="dataForm.pid">
<el-option v-for="item in catL1" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="图标" prop="iconUrl">
<el-input v-model="dataForm.iconUrl"></el-input>
<el-upload action="#" list-type="picture" :show-file-list="false" :limit="1" :http-request="handleIconUrl">
<el-button size="small" type="primary">点击上传</el-button>
<el-form-item label="类目图标" prop="iconUrl">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadIconUrl">
<img v-if="dataForm.iconUrl" :src="dataForm.iconUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="首页横幅" prop="bannerUrl">
<el-input v-model="dataForm.bannerUrl"></el-input>
<el-upload action="#" list-type="picture" :show-file-list="false" :limit="1" :http-request="handleBannerUrl">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-form-item>
<el-form-item label="类目页标题" prop="frontName">
<el-input v-model="dataForm.frontName"></el-input>
</el-form-item>
<el-form-item label="类目页介绍" prop="frontDesc">
<el-input v-model="dataForm.frontDesc"></el-input>
</el-form-item>
<el-form-item label="类目页横幅" prop="wapBannerUrl">
<el-input v-model="dataForm.wapBannerUrl"></el-input>
<el-upload action="#" list-type="picture" :show-file-list="false" :limit="1" :http-request="handleWapBannerUrl">
<el-button size="small" type="primary">点击上传</el-button>
<el-form-item label="类目图片" prop="picUrl">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadPicUrl">
<img v-if="dataForm.picUrl" :src="dataForm.picUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="类目简介" prop="desc">
<el-input v-model="dataForm.desc"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
@@ -136,27 +114,40 @@
</template>
<style>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 200px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #20a0ff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
}
.avatar {
width: 120px;
height: 120px;
display: block;
}
</style>
<script>
import { listCategory, listCatL1, createCategory, updateCategory, deleteCategory } from '@/api/category'
import { createStorage } from '@/api/storage'
import { uploadPath } from '@/api/storage'
export default {
name: 'Category',
data() {
return {
uploadPath,
list: undefined,
total: undefined,
listLoading: true,
@@ -172,14 +163,11 @@ export default {
id: undefined,
name: '',
keyword: '',
level: 'L1',
parentId: '',
isShow: 'true',
frontName: '',
frontDesc: '',
level: 'L2',
pid: 1005000,
desc: '',
iconUrl: undefined,
bannerUrl: undefined,
wapBannerUrl: undefined
picUrl: undefined
},
dialogFormVisible: false,
dialogStatus: '',
@@ -188,8 +176,7 @@ export default {
create: '创建'
},
rules: {
name: [{ required: true, message: '类目名不能为空', trigger: 'blur' }],
keyword: [{ required: true, message: '类目关键字不能为空', trigger: 'blur' }]
name: [{ required: true, message: '类目名不能为空', trigger: 'blur' }]
},
downloadLoading: false
}
@@ -233,14 +220,11 @@ export default {
id: undefined,
name: '',
keyword: '',
level: 'L1',
parentId: '',
isShow: 'true',
frontName: '',
frontDesc: '',
level: 'L2',
pid: 1005000,
desc: '',
iconUrl: undefined,
bannerUrl: undefined,
wapBannerUrl: undefined
picUrl: undefined
}
},
filterLevel(value, row) {
@@ -254,32 +238,11 @@ export default {
this.$refs['dataForm'].clearValidate()
})
},
handleIconUrl(item) {
const formData = new FormData()
formData.append('file', item.file)
createStorage(formData).then(res => {
this.dataForm.iconUrl = res.data.data.url
}).catch(() => {
this.$message.error('上传失败,请重新上传')
})
uploadIconUrl: function(response) {
this.dataForm.iconUrl = response.data.data.url
},
handleBannerUrl(item) {
const formData = new FormData()
formData.append('file', item.file)
createStorage(formData).then(res => {
this.dataForm.bannerUrl = res.data.data.url
}).catch(() => {
this.$message.error('上传失败,请重新上传')
})
},
handleWapBannerUrl(item) {
const formData = new FormData()
formData.append('file', item.file)
createStorage(formData).then(res => {
this.dataForm.wapBannerUrl = res.data.data.url
}).catch(() => {
this.$message.error('上传失败,请重新上传')
})
uploadPicUrl: function(response) {
this.dataForm.picUrl = response.data.data.url
},
createData() {
this.$refs['dataForm'].validate((valid) => {
@@ -342,8 +305,8 @@ export default {
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['类目ID', '名称', '关键字', '级别', '父类目ID', '是否显示', '图标', '首页横幅', '类目页标题', '类目页介绍', '类目页横幅']
const filterVal = ['id', 'name', 'keyword', 'level', 'parentId', 'isShow', 'iconUrl', 'bannerUrl', 'frontName', 'frontDesc', 'wapBannerUrl']
const tHeader = ['类目ID', '名称', '关键字', '级别', '父类目ID', '类目图标', '类目图片', '简介']
const filterVal = ['id', 'name', 'keyword', 'level', 'pid', 'iconUrl', 'picUrl', 'desc']
excel.export_json_to_excel2(tHeader, this.list, filterVal, '商品类目信息')
this.downloadLoading = false
})

View File

@@ -24,7 +24,7 @@
<el-table-column align="center" min-width="300px" label="跳转链接" prop="url">
</el-table-column>
<el-table-column align="center" min-width="100px" label="是否热门" prop="isHot">
<el-table-column align="center" min-width="100px" label="是否推荐" prop="isHot">
<template slot-scope="scope">
<el-tag :type="scope.row.isHot ? 'success' : 'error' ">{{scope.row.isHot ? '是' : '否'}}</el-tag>
</template>
@@ -36,12 +36,6 @@
</template>
</el-table-column>
<el-table-column align="center" min-width="100px" label="是否显示" prop="isShow">
<template slot-scope="scope">
<el-tag :type="scope.row.isShow ? 'success' : 'error' ">{{scope.row.isShow ? '可显示' : '不显示'}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
@@ -66,11 +60,11 @@
<el-form-item label="跳转链接" prop="url">
<el-input v-model="dataForm.url"></el-input>
</el-form-item>
<el-form-item label="是否热门" prop="isHot">
<el-form-item label="是否推荐" prop="isHot">
<el-select v-model="dataForm.isHot" placeholder="请选择">
<el-option label="热门" :value="true">
<el-option label="推荐" :value="true">
</el-option>
<el-option label="非热门" :value="false">
<el-option label="普通" :value="false">
</el-option>
</el-select>
</el-form-item>
@@ -82,14 +76,6 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="是否显示" prop="isShow">
<el-select v-model="dataForm.isShow" placeholder="请选择">
<el-option label="显示" :value="true">
</el-option>
<el-option label="不显示" :value="false">
</el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
@@ -101,20 +87,6 @@
</div>
</template>
<style>
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 200px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
}
</style>
<script>
import { listKeyword, createKeyword, updateKeyword, deleteKeyword } from '@/api/keyword'
@@ -136,9 +108,8 @@ export default {
id: undefined,
keyword: undefined,
url: undefined,
isNew: undefined,
isDefault: undefined,
isShow: undefined
isHot: undefined,
isDefault: undefined
},
dialogFormVisible: false,
dialogStatus: '',
@@ -147,8 +118,7 @@ export default {
create: '创建'
},
rules: {
keyword: [{ required: true, message: '关键词不能为空', trigger: 'blur' }],
url: [{ required: true, message: '跳转链接称不能为空', trigger: 'blur' }]
keyword: [{ required: true, message: '关键词不能为空', trigger: 'blur' }]
},
downloadLoading: false
}
@@ -186,9 +156,8 @@ export default {
id: undefined,
keyword: undefined,
url: undefined,
isNew: undefined,
isDefault: undefined,
isShow: undefined
isHot: undefined,
isDefault: undefined
}
},
handleCreate() {
@@ -260,9 +229,9 @@ export default {
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['关键词ID', '关键词', '跳转链接', '是否新上', '是否默认', '是否显示']
const filterVal = ['id', 'keyword', 'url', 'isNew', 'isDefault', 'isShow']
excel.export_json_to_excel2(tHeader, this.list, filterVal, '关键词信息')
const tHeader = ['关键词ID', '关键词', '跳转链接', '是否推荐', '是否默认']
const filterVal = ['id', 'keyword', 'url', 'isHot', 'isDefault']
excel.export_json_to_excel2(tHeader, this.list, filterVal, '关键词设置')
this.downloadLoading = false
})
}

View File

@@ -85,6 +85,22 @@ public class JacksonUtil {
return null;
}
public static Byte parseByte(String body, String field) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
try {
node = mapper.readTree(body);
JsonNode leaf = node.get(field);
if(leaf != null) {
Integer value = leaf.asInt();
return value.byteValue();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static <T> T parseObject(String body, String field, Class<T> clazz) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
@@ -112,4 +128,5 @@ public class JacksonUtil {
return null;
}
}