feature(uniapp商品): 商品功能完善

pull/2/head
luowenfeng 2022-09-01 15:28:05 +08:00
parent 198b1189e8
commit ab6fd30551
9 changed files with 84 additions and 398 deletions

View File

@ -1,14 +1,20 @@
package cn.iocoder.yudao.module.product.controller.admin.sku.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotEmpty;
@ApiModel("管理后台 - 商品 SKU 创建/更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ProductSkuCreateOrUpdateReqVO extends ProductSkuBaseVO {
@ApiModelProperty(value = "商品 id 更新时须有", example = "1")
private Long id;
}

View File

@ -65,10 +65,21 @@ public class ProductSpuBaseVO {
@NotNull(message = "是否展示库存不能为空")
private Boolean showStock;
@ApiModelProperty(value = "库存", required = true, example = "true")
private Integer totalStock;
@ApiModelProperty(value = " 最小价格,单位使用:分", required = true, example = "1024")
private Integer minPrice;
@ApiModelProperty(value = "最大价格,单位使用:分", required = true, example = "1024")
private Integer maxPrice;
// ========== 统计相关字段 =========
@ApiModelProperty(value = "虚拟销量", required = true, example = "1024")
@NotNull(message = "虚拟销量不能为空")
private Integer virtualSalesCount;
@ApiModelProperty(value = "点击量", example = "1024")
private Integer clickCount;
}

View File

@ -42,7 +42,7 @@ public class SpuPageReqVO extends PageParam {
private Integer quantity;
@ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)")
private Boolean status;
private Integer status;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "创建时间")

View File

@ -19,16 +19,11 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
return selectPage(reqVO, new LambdaQueryWrapperX<ProductSpuDO>()
.likeIfPresent(ProductSpuDO::getName, reqVO.getName())
.eqIfPresent(ProductSpuDO::getSellPoint, reqVO.getSellPoint())
.eqIfPresent(ProductSpuDO::getDescription, reqVO.getDescription())
.eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId())
.eqIfPresent(ProductSpuDO::getPicUrls, reqVO.getPicUrls())
.eqIfPresent(ProductSpuDO::getSort, reqVO.getSort())
// .eqIfPresent(ProductSpuDO::getLikeCount, reqVO.getLikeCount())
// .eqIfPresent(ProductSpuDO::getPrice, reqVO.getPrice())
// .eqIfPresent(ProductSpuDO::getQuantity, reqVO.getQuantity())
.eqIfPresent(ProductSpuDO::getStatus, reqVO.getStatus())
.betweenIfPresent(ProductSpuDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ProductSpuDO::getId));
.orderByDesc(ProductSpuDO::getSort));
}
}

View File

@ -90,17 +90,19 @@ public class ProductSpuServiceImpl implements ProductSpuService {
validateSpuExists(updateReqVO.getId());
// 校验分类
categoryService.validateProductCategory(updateReqVO.getCategoryId());
// TODO @luowenfeng校验品牌
// 校验品牌
brandService.validateProductBrand(updateReqVO.getBrandId());
// 校验SKU
List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = updateReqVO.getSkus();
// 多规格才需校验
productSkuService.validateProductSkus(skuCreateReqList, updateReqVO.getSpecType());
// 更新 SPU
ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO);
// TODO @计算各种字段
updateObj.setMarketPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getMarketPrice));
updateObj.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
updateObj.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
updateObj.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum));
ProductSpuMapper.updateById(updateObj);
// 更新 SKU
productSkuService.updateProductSkus(updateObj.getId(), updateReqVO.getSkus());
}
@ -149,7 +151,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
ProductPropertyViewRespVO.Tuple2 tuple2 = new ProductPropertyViewRespVO.Tuple2(pv.getValueId(), propertyValueMaps.get(pv.getValueId()).getName());
propertyValues.add(tuple2);
});
productPropertyViewRespVO.setPropertyValues(propertyValues);
productPropertyViewRespVO.setPropertyValues(propertyValues.stream().distinct().collect(Collectors.toList()));
productPropertyViews.add(productPropertyViewRespVO);
});
spuVO.setProductPropertyViews(productPropertyViews);

View File

@ -16,7 +16,7 @@ import './permission' // permission control
import './tongji' // 百度统计
import { getDicts } from "@/api/system/dict/data";
import { getConfigKey } from "@/api/infra/config";
import { parseTime, resetForm, handleTree, addBeginAndEndTime} from "@/utils/ruoyi";
import { parseTime, resetForm, handleTree, addBeginAndEndTime, divide} from "@/utils/ruoyi";
import Pagination from "@/components/Pagination";
// 自定义表格工具扩展
import RightToolbar from "@/components/RightToolbar"
@ -36,6 +36,7 @@ Vue.prototype.getDictDataLabel = getDictDataLabel
Vue.prototype.DICT_TYPE = DICT_TYPE
Vue.prototype.handleTree = handleTree
Vue.prototype.addBeginAndEndTime = addBeginAndEndTime
Vue.prototype.divide = divide
// 全局组件挂载
Vue.component('DictTag', DictTag)

View File

@ -222,3 +222,17 @@ export function getPath(path) {
}
return basePath + path;
}
/**
* 除法保留两位小数
*
* @param {*} divisor 除数
* @param {*} dividend 被除数
* @returns
*/
export function divide(divisor, dividend) {
if(divisor == null || dividend == null || dividend == 0){
return null;
}
return Math.floor(divisor/dividend*100)/100;
}

View File

@ -17,14 +17,6 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="卖点" prop="sellPoint">
<el-input
v-model="queryParams.sellPoint"
placeholder="请输入卖点"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="分类id" prop="categoryId">
<el-input
v-model="queryParams.categoryId"
@ -33,22 +25,6 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="价格(分)" prop="price">
<el-input
v-model="queryParams.price"
placeholder="请输入价格 单位使用:分"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="库存数量" prop="quantity">
<el-input
v-model="queryParams.quantity"
placeholder="请输入库存数量"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
@ -115,8 +91,6 @@
<el-table v-loading="loading" :data="list">
<el-table-column label="主键" align="center" prop="id" />
<el-table-column label="商品名称" align="center" prop="name" />
<!-- <el-table-column label="卖点" align="center" prop="sellPoint"/>-->
<!-- <el-table-column label="描述" align="center" prop="description"/>-->
<el-table-column label="分类id" align="center" prop="categoryId" />
<el-table-column label="商品主图地址" align="center" prop="picUrls">
<template slot-scope="scope">
@ -129,10 +103,14 @@
</template>
</el-table-column>
<el-table-column label="排序字段" align="center" prop="sort" />
<el-table-column label="点赞初始人数" align="center" prop="likeCount" />
<el-table-column label="价格 (分)" align="center" prop="price" />
<el-table-column label="库存数量" align="center" prop="quantity" />
<!-- <el-table-column label="状态" align="center" prop="status"/>-->
<el-table-column label="点击量" align="center" prop="clickCount" />
<el-table-column label="价格区间" align="center" prop="price" />
<el-table-column label="总库存" align="center" prop="totalStock" />
<el-table-column label="状态" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
@ -186,29 +164,10 @@
<script>
import {
createSpu,
updateSpu,
deleteSpu,
getSpu,
getSpuPage,
exportSpuExcel,
} from "@/api/mall/product/spu";
import {
createProductCategory,
deleteProductCategory,
exportCategoryExcel,
getProductCategory,
getProductCategoryList,
updateProductCategory,
} from "@/api/mall/product/category";
import {
createProperty,
updateProperty,
deleteProperty,
getProperty,
getPropertyPage,
exportPropertyExcel,
} from "@/api/mall/product/property";
import Editor from "@/components/Editor";
import ImageUpload from "@/components/ImageUpload";
@ -237,11 +196,6 @@ export default {
selectObect: [],
},
skuTags: [],
propName: {
checkStrictly: true,
label: "name",
value: "id",
},
categoryList: [],
//
loading: true,
@ -277,251 +231,13 @@ export default {
quantity: null,
status: null,
},
//
form: {
id: undefined,
name: undefined,
sellPoint: undefined,
description: undefined,
categoryId: undefined,
categoryIds: [],
picUrls: undefined,
sort: undefined,
likeCount: undefined,
price: undefined,
quantity: undefined,
status: undefined,
isShowTagInput: undefined,
skus: [],
},
//
rules: {
sellPoint: [
{ required: true, message: "卖点不能为空", trigger: "blur" },
],
description: [
{ required: true, message: "描述不能为空", trigger: "blur" },
],
categoryIds: [
{ required: true, message: "分类id不能为空", trigger: "blur" },
],
picUrls: [{ required: true, message: "商品主图地址", trigger: "blur" }],
sort: [
{ required: true, message: "排序字段不能为空", trigger: "blur" },
],
},
tagIndex: 0,
};
},
created() {
this.getList();
this.getPropertyPageList();
this.getListCategory();
},
methods: {
getTableSpecData() {
return this.value;
},
tableSpanMethod({ row, column, rowIndex, columnIndex }) {},
changeSkuStatus(tagIndex) {
if (this.form.skus[tagIndex].status == 0) {
this.form.skus[tagIndex].status = 1;
} else {
this.form.skus[tagIndex].status = 0;
}
},
skuAddProdName() {
if (this.initing) {
return;
}
let skuList = [];
for (let i = 0; i < this.value.length; i++) {
const sku = Object.assign({}, this.value[i]);
if (!sku.properties) {
return;
}
sku.skuName = "";
let properties = sku.properties.split(";");
for (const propertiesKey in properties) {
sku.skuName += properties[propertiesKey].split(":")[1] + " ";
}
sku.prodName = this.prodName + " " + sku.skuName;
skuList.push(sku);
}
this.$emit("input", skuList);
},
handleTagClose(tagIndex, tagItemIndex) {},
//sku
addTag() {
let skus = this.unUseTags.map(function (item, index) {
return item.name;
});
let index = skus.indexOf(this.addTagInput.name);
this.addTagInput.propertyId = this.unUseTags[index].id;
for (let i = 0; i < this.addTagInput.selectValues.length; i++) {
for (
let j = 0;
j < this.unUseTags[index].propertyValueList.length;
j++
) {
if (
this.addTagInput.selectValues[i] ===
this.unUseTags[index].propertyValueList[j].name
) {
this.addTagInput.selectValueIds.push(
this.unUseTags[index].propertyValueList[j].id
);
this.addTagInput.selectObect.push({
id: this.unUseTags[index].propertyValueList[j].id,
name: this.unUseTags[index].propertyValueList[j].name,
});
}
}
}
let addTagInput = JSON.parse(JSON.stringify(this.addTagInput));
this.skuTags.push(addTagInput);
// if (this.skuTags.length > 1) {
this.skuTags = this.skuTags.sort((a, b) => a.propertyId - b.propertyId);
this.skuTags.forEach(function (item, index) {
item.selectObect = item.selectObect.sort((a, b) => a.id - b.id);
});
for (let i = 0; i < this.skuTags.length; i++) {
let selectValueIds = [];
let selectValues = [];
for (let j = 0; j < this.skuTags[i].selectObect.length; j++) {
selectValueIds.push(this.skuTags[i].selectObect[j].id);
selectValues.push(this.skuTags[i].selectObect[j].name);
}
this.skuTags[i].selectValues = selectValues;
this.skuTags[i].selectValueIds = selectValueIds;
}
this.unUseTags.splice(index, 1);
this.isShowTagInput = false;
this.getTable();
},
getTable() {
this.form.skus = [];
let skuTags = JSON.parse(JSON.stringify(this.skuTags));
let sku1s = [];
let skuIds = [];
let propertyIds = [];
let propertyNames = [];
for (let i = 0; i < skuTags.length; i++) {
sku1s.push(skuTags[i].selectValues);
skuIds.push(skuTags[i].selectValueIds);
propertyIds.push(skuTags[i].propertyId);
propertyNames.push(skuTags[i].name);
}
let skuAll = sku1s.reduce(
(x, y) => {
let arr = [];
x.forEach((m) => y.forEach((y) => arr.push(m.concat([y]))));
return arr;
},
[[]]
);
let skuIdAll = skuIds.reduce(
(x, y) => {
let arr = [];
x.forEach((m) => y.forEach((y) => arr.push(m.concat([y]))));
return arr;
},
[[]]
);
for (let i = 0; i < skuAll.length; i++) {
let han = {
propertyNames: propertyNames,
propertyIds: propertyIds,
propertyChildNames: skuAll[i],
propertyChildIds: skuIdAll[i],
properties: [],
picUrl: "",
costPrice: "",
originalPrice: "",
spuId: "",
prodName: "",
price: "",
barCode: "",
status: "0",
};
this.form.skus.push(han);
}
this.form.skus.forEach((x) => {
x.properties = [];
for (let i = 0; i < x.propertyIds.length; i++) {
x.properties.push({
propertyId: x.propertyIds[i],
valueId: x.propertyChildIds[i],
});
}
});
},
hideTagInput() {
this.isShowTagInput = false;
this.addTagInput = {
name: "",
propertyId: "",
selectValues: [],
selectValueIds: [],
selectObect: [],
};
},
shopTagInput() {
if (this.unUseTags.length <= 0) {
return this.$message.error("规格已经添加完毕");
}
this.isShowTagInput = true;
this.addTagInput = {
name: "",
propertyId: "",
selectValues: [],
selectValueIds: [],
selectObect: [],
};
},
//
removeTag(row) {
let skus = this.allhistoryTags.map(function (item, index) {
return item.name;
});
let index = skus.indexOf(this.skuTags[row].name);
this.unUseTags.push(this.allhistoryTags[index]);
this.skuTags.splice(row, 1);
this.getTable();
},
handleTagClick(row) {
for (let i = 0; i < this.propertyPageList.length; i++) {
if (row == this.propertyPageList[i].name) {
this.dbTagValues = this.propertyPageList[i].propertyValueList;
}
}
},
/** 查询规格 */
getPropertyPageList() {
//
getPropertyPage().then((response) => {
this.propertyPageList = response.data.list;
this.unUseTags = this.propertyPageList.map(function (item, index) {
return item;
});
this.allhistoryTags = JSON.parse(JSON.stringify(this.unUseTags));
});
},
/** 查询分类 */
getListCategory() {
//
getProductCategoryList().then((response) => {
this.categoryList = this.handleTree(response.data, "id", "parentId");
});
},
/** 查询列表 */
getList() {
this.loading = true;
@ -530,37 +246,20 @@ export default {
this.addBeginAndEndTime(params, this.dateRangeCreateTime, "createTime");
//
getSpuPage(params).then((response) => {
response.data.list.forEach(element => {
element.price = this.divide(element.minPrice, 100)+"~"+this.divide(element.maxPrice, 100)
});
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
sellPoint: undefined,
description: undefined,
categoryId: undefined,
categoryIds: [],
picUrls: undefined,
sort: undefined,
likeCount: undefined,
price: undefined,
quantity: undefined,
status: undefined,
isShowTagInput: undefined,
skus: [],
};
this.skuTags = [];
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
@ -594,65 +293,6 @@ export default {
this.open = false;
this.getList()
},
getHandleTable() {
this.form.skus = [];
let skuTags = JSON.parse(JSON.stringify(this.skuTags));
let sku1s = [];
let skuIds = [];
let propertyIds = [];
let propertyNames = [];
for (let i = 0; i < skuTags.length; i++) {
sku1s.push(skuTags[i].selectValues);
skuIds.push(skuTags[i].selectValueIds);
propertyIds.push(skuTags[i].propertyId);
propertyNames.push(skuTags[i].name);
}
let skuAll = sku1s.reduce(
(x, y) => {
let arr = [];
x.forEach((m) => y.forEach((y) => arr.push(m.concat([y]))));
return arr;
},
[[]]
);
let skuIdAll = skuIds.reduce(
(x, y) => {
let arr = [];
x.forEach((m) => y.forEach((y) => arr.push(m.concat([y]))));
return arr;
},
[[]]
);
for (let i = 0; i < skuAll.length; i++) {
let han = {
propertyNames: propertyNames,
propertyIds: propertyIds,
propertyChildNames: skuAll[i],
propertyChildIds: skuIdAll[i],
properties: this.form.skusList[i].properties,
picUrl: this.form.skusList[i].picUrl,
costPrice: this.form.skusList[i].costPrice,
originalPrice: this.form.skusList[i].originalPrice,
spuId: this.form.skusList[i].spuId,
prodName: this.form.skusList[i].prodName,
price: this.form.skusList[i].price,
barCode: this.form.skusList[i].barCode,
status: this.form.skusList[i].status,
};
this.form.skus.push(han);
}
this.form.skus.forEach((x) => {
x.properties = [];
for (let i = 0; i < x.propertyIds.length; i++) {
x.properties.push({
propertyId: x.propertyIds[i],
valueId: x.propertyChildIds[i],
});
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;

View File

@ -234,7 +234,7 @@
import {getBrandList} from "@/api/mall/product/brand";
import {getProductCategoryList} from "@/api/mall/product/category";
import {createSpu, getSpu} from "@/api/mall/product/spu";
import {createSpu, updateSpu, getSpu} from "@/api/mall/product/spu";
import {getPropertyPage,} from "@/api/mall/product/property";
import Editor from "@/components/Editor";
import ImageUpload from "@/components/ImageUpload";
@ -261,6 +261,7 @@ export default {
},
//
baseForm: {
id: null,
name: null,
sellPoint: null,
categoryIds: null,
@ -391,6 +392,14 @@ export default {
return;
}
let rates = this.ratesForm.rates;
//
rates.forEach(r=>{
r.marketPrice = r.marketPrice*100;
r.price = r.price*100;
r.costPrice = r.costPrice*100;
})
//
if (this.ratesForm.spec == 2) {
rates.forEach(r => {
@ -403,9 +412,7 @@ export default {
obj = Array.of(s);
}
obj.forEach((v, i) => {
console.log(this.dynamicSpec, r, s, v, i)
let specValue = this.dynamicSpec[i].specValue.find(o => o.name == v);
console.log(specValue)
let propertie = {};
propertie.propertyId = this.dynamicSpec[i].specId;
propertie.valueId = specValue.id;
@ -419,6 +426,7 @@ export default {
rates[0].status = this.baseForm.status;
}
let form = this.baseForm
if(form.picUrls instanceof Array){
form.picUrls = form.picUrls.flatMap(m=>m.split(','))
}else if(form.picUrls.split(',') instanceof Array){
@ -426,18 +434,23 @@ export default {
}else{
form.picUrls = Array.of(form.picUrls)
}
console.log(rates)
form.skus = rates;
form.specType = this.ratesForm.spec;
form.categoryId = form.categoryIds[this.baseForm.categoryIds.length - 1];
if(form.id == null){
createSpu(form).then((response) => {
console.log(response)
this.$modal.msgSuccess("新增成功");
})
}else{
updateSpu(form).then((response) => {
this.$modal.msgSuccess("修改成功");
})
}
});
this.$emit("closeDialog");
}).catch((err) => {
console.log(this.baseForm.picUrls)
});
});
},
/** 查询规格 */
getPropertyPageList() {
@ -458,8 +471,8 @@ export default {
},
updateType(id){
getSpu(id).then((response) =>{
console.log(response)
let data = response.data;
this.baseForm.id=data.id;
this.baseForm.name=data.name;
this.baseForm.sellPoint=data.sellPoint;
this.baseForm.categoryIds=data.categoryIds;
@ -471,6 +484,11 @@ export default {
this.baseForm.showStock=data.showStock;
this.baseForm.brandId=data.brandId;
this.ratesForm.spec=data.specType;
data.skus.forEach(r=>{
r.marketPrice = this.divide(r.marketPrice, 100)
r.price = this.divide(r.price, 100)
r.costPrice = this.divide(r.costPrice, 100)
})
if(this.ratesForm.spec == 2){
data.productPropertyViews.forEach(p=>{
let obj = {};
@ -487,7 +505,6 @@ export default {
})
})
}
console.log(data.skus);
this.ratesForm.rates=data.skus
})
}