feature(商品添加): 规格调整连带商品调整

This commit is contained in:
luowenfeng
2022-09-14 11:57:30 +08:00
parent 15798cea10
commit e1e55dc841
33 changed files with 705 additions and 337 deletions

View File

@ -43,6 +43,25 @@ export function getPropertyPage(query) {
})
}
// 获得规格名称列表
export function getPropertyList(query) {
return request({
url: '/product/property/list',
method: 'get',
params: query
})
}
// 获得规格名称列表
export function getPropertyListAndValue(query) {
return request({
url: '/product/property/listAndValue',
method: 'get',
params: query
})
}
// 导出规格名称 Excel
export function exportPropertyExcel(query) {
return request({
@ -52,3 +71,49 @@ export function exportPropertyExcel(query) {
responseType: 'blob'
})
}
// ------------------------ 规格名称值 -------------------
// 获得规格名称值分页
export function getPropertyValuePage(query) {
return request({
url: '/product/property/value/page',
method: 'get',
params: query
})
}
// 获得规格名称值
export function getPropertyValue(id) {
return request({
url: '/product/property/value/get?id=' + id,
method: 'get'
})
}
// 创建规格名称值
export function createPropertyValue(data) {
return request({
url: '/product/property/value/create',
method: 'post',
data: data
})
}
// 更新规格名称值
export function updatePropertyValue(data) {
return request({
url: '/product/property/value/update',
method: 'put',
data: data
})
}
// 删除规格名称
export function deletePropertyValue(id) {
return request({
url: '/product/property/value/delete?id=' + id,
method: 'delete'
})
}

View File

@ -34,6 +34,14 @@ export function getSpu(id) {
})
}
// 获得商品spu详情
export function getSpuDetail(id) {
return request({
url: '/product/spu/get/detail?id=' + id,
method: 'get'
})
}
// 获得商品spu分页
export function getSpuPage(query) {
return request({

View File

@ -96,6 +96,17 @@ export const constantRoutes = [
meta: {title: '字典数据', icon: '', activeMenu: '/system/dict'}
}
]
}, {
path: '/property',
component: Layout,
hidden: true,
children: [{
path: 'value/:propertyId(\\d+)',
component: (resolve) => require(['@/views/mall/product/property/value'], resolve),
name: 'PropertyValue',
meta: {title: '规格数据', icon: '', activeMenu: '/mall/property'}
}
]
}, {
path: '/job',
component: Layout,

View File

@ -6,7 +6,7 @@
<el-form-item label="规格名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入规格名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
@ -33,12 +33,14 @@
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="规格名称" align="center" prop="name" />
<el-table-column label="规格名称" align="center" prop="propertyValueList">
<el-table-column label="规格id" align="center" prop="id" />
<el-table-column label="规格名称" align="center" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{formatList(scope.row.propertyValueList)}}</span>
<router-link :to="'/property/value/' + scope.row.id" class="link-type">
<span>{{ scope.row.name }}</span>
</router-link>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="开启状态" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
@ -49,6 +51,7 @@
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="left" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
@ -65,6 +68,9 @@
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="规格id" prop="id" v-if="form.id != null">
<el-input v-model="form.id" disabled />
</el-form-item>
<el-form-item label="规格名称" prop="name">
<el-input v-model="form.name" placeholder="请输入规格名称" />
</el-form-item>
@ -75,26 +81,9 @@
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="属性值">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="addPropertyValue()">添加</el-button>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="备注" />
</el-form-item>
<el-form-item
v-for="(domain, index) in form.propertyValueList"
:key="domain.key"
:prop="'propertyValueList.' + index + '.name'"
:rules="{
required: true, message: '属性值不能为空', trigger: 'blur'
}"
>
<el-row>
<el-col :span="18">
<el-input v-model="domain.name" size="mini"></el-input>
</el-col>
<el-col :span="6">
<el-button style="margin-left: 20px;" size="mini" @click.prevent="removePropertyValue(domain)">删除</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
@ -139,12 +128,17 @@ export default {
form: {
name:'',
status:'',
propertyValueList: [{
name: ''
}],
remark:"",
id: null,
},
// 表单校验
rules: {
name: [
{ required: true, message: "规格不能为空", trigger: "blur" }
],
status: [
{ required: true, message: "状态不能为空", trigger: "blur" }
]
}
};
},
@ -170,12 +164,10 @@ export default {
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
status: undefined,
propertyValueList: [{
name: ''
}]
name:'',
status:'',
remark:"",
id: null,
};
this.resetForm("form");
},
@ -253,27 +245,6 @@ export default {
this.exportLoading = false;
}).catch(() => {});
},
removePropertyValue(item) {
var index = this.form.propertyValueList.indexOf(item)
if (index !== -1) {
this.form.propertyValueList.splice(index, 1)
}
},
addPropertyValue() {
this.form.propertyValueList.push({
name: ''
});
},
formatList(list) {
let str = ''
for (var i = 0; i < list.length; i++) {
str += list[i].name;
if(i != list.length-1){
str+="/";
}
}
return str
}
}
};
</script>

View File

@ -9,20 +9,22 @@
<el-form-item label="商品名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入商品名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="分类id" prop="categoryId">
<el-input v-model="queryParams.categoryId" placeholder="请输入分类id" clearable @keyup.enter.native="handleQuery"/>
<el-form-item label="商品编码" prop="code">
<el-input v-model="queryParams.code" placeholder="请输入商品名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择上下架状态" clearable size="small">
<el-option label="请选择字典生成" value="" />
<el-option label="上架" value="0" />
<el-option label="下架" value="1" />
<el-form-item label="商品分类" prop="categoryId">
<el-cascader v-model="queryParams.categoryIds" placeholder="请输入商品分类"
:options="categoryList" :props="propName" clearable ref="category" />
</el-form-item>
<el-form-item label="商品品牌" prop="brandId">
<el-select v-model="queryParams.brandId" placeholder="请输入商品品牌" clearable @keyup.enter.native="handleQuery">
<el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRangeCreateTime" style="width: 240px" value-format="yyyy-MM-dd" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
@ -46,33 +48,35 @@
1. 字段商品信息价格库存销量排序创建时间状态操作
2. tab 分成全部销售中仓库中预警中
-->
<el-table-column label="主键" align="center" prop="id" />
<el-table-column label="商品名称" align="center" prop="name" />
<el-table-column label="分类id" align="center" prop="categoryId" />
<el-table-column label="商品主图地址" align="center" prop="picUrls">
<template slot-scope="scope">
<img
v-if="scope.row.picUrls"
:src="scope.row.picUrls[0]"
alt="分类图片"
class="img-height"
/>
</template>
</el-table-column>
<el-table-column label="排序字段" align="center" prop="sort" />
<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"/>
<el-table-column label="商品信息" align="center" width="260">
<template slot-scope="scope" >
<div class="product-info">
<img
v-if="scope.row.picUrls"
:src="scope.row.picUrls[0]"
alt="分类图片"
class="img-height"
/>
<div class="message">{{scope.row.name}}</div>
</div>
</template>
</el-table-column>
<el-table-column label="价格" align="center" prop="marketPrice" :formatter="unitConversion"/>
<el-table-column label="库存" align="center" prop="totalStock" />
<el-table-column label="销量" align="center" prop="salesCount" />
<el-table-column label="排序" align="center" prop="sort" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<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" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
@ -87,7 +91,7 @@
@pagination="getList"/>
<el-dialog :title="title" :visible.sync="open" width="900px" append-to-body destroy-on-close :close-on-click-modal="false" >
<save @closeDialog="closeDialog" :type="dialogType" :obj="dialogObj" v-if="open" />
<save @closeDialog="closeDialog" :obj="dialogObj" v-if="open" />
</el-dialog>
</div>
</template>
@ -97,7 +101,8 @@ import {
deleteSpu,
getSpuPage,
} from "@/api/mall/product/spu";
import {getProductCategoryList} from "@/api/mall/product/category";
import {getBrandList} from "@/api/mall/product/brand";
import Editor from "@/components/Editor";
import ImageUpload from "@/components/ImageUpload";
import save from "./save";
@ -114,20 +119,12 @@ export default {
},
data() {
return {
tableLeftTitles: [],
dbTagValues: [],
allhistoryTags: [],
unUseTags: [],
propertyPageList: [],
isShowTagInput: false,
addTagInput: {
name: "",
propertyId: "",
selectValues: [],
selectValueIds: [],
selectObect: [],
propName: {
checkStrictly: true,
label: "name",
value: "id",
},
skuTags: [],
brandList: [],
categoryList: [],
// 遮罩层
loading: true,
@ -143,8 +140,6 @@ export default {
title: "",
// 是否显示弹出层
open: false,
// 弹出层类型
dialogType: "add",
// 弹出层参数
dialogObj:{},
dateRangeCreateTime: [],
@ -153,14 +148,10 @@ export default {
pageNo: 1,
pageSize: 10,
name: null,
sellPoint: null,
description: null,
code: null,
categoryIds: [],
categoryId: null,
picUrls: null,
sort: null,
likeCount: null,
price: null,
quantity: null,
brandId: null,
status: null,
},
tagIndex: 0,
@ -168,13 +159,30 @@ export default {
},
created() {
this.getList();
this.getListCategory()
this.getListBrand()
},
methods: {
/** 查询分类列表 */
getListCategory() {
// 执行查询
getProductCategoryList().then((response) => {
this.categoryList = this.handleTree(response.data, "id", "parentId");
});
},
/** 查询品牌列表 */
getListBrand() {
// 执行查询
getBrandList().then((response) => {
this.brandList = response.data;
});
},
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = { ...this.queryParams };
params.categoryId = this.queryParams.categoryIds[ this.queryParams.categoryIds.length - 1];
this.addBeginAndEndTime(params, this.dateRangeCreateTime, "createTime");
// 执行查询
getSpuPage(params).then((response) => {
@ -200,27 +208,26 @@ export default {
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.$refs.category.$refs.panel.checkedValue = [];//也可以是指定的值,默认返回值是数组,也可以返回单个值
  this.$refs.category.$refs.panel.activePath = [];
  this.$refs.category.$refs.panel.syncActivePath();
this.queryParams.categoryIds = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.dialogType = "add";
this.dialogObj={};
this.open = true;
this.title = "添加商品spu";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.dialogType = "upd";
this.dialogObj.id = row.id;
this.open = true;
console.log("修改")
this.title = "修改商品spu";
},
closeDialog(){
console.log("关闭")
this.dialogType = "add";
this.dialogObj={};
this.open = false;
this.getList()
@ -238,6 +245,9 @@ export default {
this.$modal.msgSuccess("删除成功");
})
.catch(() => {});
},
unitConversion(row, column, cellValue){
return this.divide(cellValue, 100);
}
},
};
@ -262,8 +272,25 @@ export default {
vertical-align: bottom;
}
.img-height {
height: 65px;
.product-info{
display: flex;
.img-height {
height: 50px;
width: 50px;
}
.message{
margin-left: 10px;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
word-break: break-all;
-webkit-box-orient: vertical;
white-space: normal;
overflow: hidden;
height: 50px;
line-height: 25px;
}
}
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div class="container">
<el-tabs v-model="activeName" class="tabs" :before-leave="confirmLeave">
<el-tabs v-model="activeName" class="tabs" >
<!-- 基础设置 -->
<el-tab-pane label="基础设置" name="base">
<el-form ref="base" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
@ -12,7 +12,7 @@
</el-form-item>
<!-- TODO @Luowenfeng商品主图80 x 80 即可 -->
<el-form-item label="商品主图" prop="picUrls">
<ImageUpload v-model="baseForm.picUrls" :value="baseForm.picUrls" :limit="10"/>
<ImageUpload v-model="baseForm.picUrls" :value="baseForm.picUrls" :limit="10" />
</el-form-item>
<!-- TODO @Luowenfeng商品视频 -->
<el-form-item label="商品品牌" prop="brandId">
@ -38,10 +38,7 @@
<el-form ref="rates" :model="ratesForm" :rules="rules">
<el-form-item label="启用多规格">
<!-- TODO @Luowenfeng改成开关的按钮关闭单规格开启多规格 -->
<el-radio-group v-model="ratesForm.spec" @change="changeRadio">
<el-radio :label="1">单规格</el-radio>
<el-radio :label="2">多规格</el-radio>
</el-radio-group>
<el-switch v-model="activeSwitch" @change="changeRadio"></el-switch>
</el-form-item>
<!-- 动态添加规格属性 -->
@ -67,7 +64,7 @@
<!-- 规格明细 -->
<el-form-item label="规格明细">
<el-table :data="ratesForm.rates" border style="width: 100%" ref="ratesTable">
<template v-if="ratesForm.spec == 2">
<template v-if="this.activeSwitch">
<el-table-column :key="index" v-for="(item, index) in dynamicSpec.filter(v => v.specName !== undefined)"
:label="item.specName">
<template slot-scope="scope">
@ -81,7 +78,7 @@
style="width: 100px; height: 50px"/>
</template>
</el-table-column>
<template v-if="ratesForm.spec === 2">
<template v-if="this.activeSwitch">
<el-table-column label="sku名称" :render-header="addRedStar" key="91">
<template slot-scope="scope">
<el-form-item :prop="'rates.'+ scope.$index + '.name'" :rules="[{required: true, trigger: 'change'}]">
@ -142,7 +139,7 @@
<el-input v-model="scope.row.barCode"></el-input>
</template>
</el-table-column>
<template v-if="ratesForm.spec === 2">
<template v-if="this.activeSwitch">
<el-table-column fixed="right" label="操作" width="50" key="100">
<template slot-scope="scope">
<el-button @click="scope.row.status = 1" type="text" size="small" v-show="scope.row.status == undefined || scope.row.status == 0 ">禁用</el-button>
@ -153,7 +150,6 @@
</el-table>
</el-form-item>
<el-form-item label="虚拟销量" prop="virtualSalesCount">
<!-- TODO @Luowenfeng使用 input 类型即可 -->
<el-input v-model="baseForm.virtualSalesCount" placeholder="请输入虚拟销量" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/>
</el-form-item>
</el-form>
@ -196,8 +192,8 @@
import {getBrandList} from "@/api/mall/product/brand";
import {getProductCategoryList} from "@/api/mall/product/category";
import {createSpu, updateSpu, getSpu} from "@/api/mall/product/spu";
import {getPropertyPage,} from "@/api/mall/product/property";
import {createSpu, updateSpu, getSpuDetail} from "@/api/mall/product/spu";
import {getPropertyListAndValue,} from "@/api/mall/product/property";
import Editor from "@/components/Editor";
import ImageUpload from "@/components/ImageUpload";
@ -206,16 +202,13 @@ export default {
Editor,
ImageUpload
},
props:{//props列表
type:{
type:String,
default:"add" //定义参数默认值
},
props:{
obj: Object
},
data() {
return {
activeName: "base", // TODO @Luowenfeng切换时不需要校验通过
activeSwitch: false,
activeName: "base",
propName: {
checkStrictly: true,
label: "name",
@ -271,7 +264,7 @@ export default {
this.getListBrand();
this.getListCategory();
this.getPropertyPageList();
if(this.type == 'upd'){
if(this.obj.id != null){
this.updateType(this.obj.id)
}
},
@ -280,14 +273,6 @@ export default {
this.dynamicSpec.splice(index, 1);
this.changeRadio()
},
async confirmLeave(active, old){
await this.$refs[old].validate((valid) => {
console.log(valid)
if (!valid) {
return reject();
}
});
},
// 必选标识
addRedStar(h, { column }) {
return [
@ -296,13 +281,13 @@ export default {
];
},
changeRadio() {
this.activeSwitch ? this.ratesForm.spec = 2: this.ratesForm.spec = 1;
this.$refs.ratesTable.doLayout();
if (this.ratesForm.spec == 1) {
this.ratesForm.rates = [{}]
} else {
this.ratesForm.rates = []
if (this.dynamicSpec.length > 0) {
console.log( this.dynamicSpec)
this.buildRatesFormRates()
}
}
@ -316,6 +301,7 @@ export default {
last.forEach(par1 => {
current.forEach(par2 => {
let v
// 当两个对象合并时,需使用[1,2]方式生成数组而当数组和对象合并时需使用concat
if (par1 instanceof Array) {
v = par1.concat(par2)
} else {
@ -327,7 +313,12 @@ export default {
return array;
})
.forEach(v => {
rates.push({spec: v, status: 0, name: Array.of(v).join()})
let spec = v;
// 当v为单个规格项时会变成字符串。造成表格只截取第一个字符串而不是数组的第一个元素
if (typeof v == 'string') {
spec = Array.of(v)
}
rates.push({spec: spec, status: 0, name: Array.of(v).join()})
});
this.ratesForm.rates = rates
},
@ -363,7 +354,7 @@ export default {
})
// 动态规格调整字段
if (this.ratesForm.spec == 2) {
if (this.activeSwitch) {
rates.forEach(r => {
let properties = []
Array.of(r.spec).forEach(s => {
@ -396,43 +387,42 @@ 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) => {
this.$modal.msgSuccess("新增成功");
this.$emit("closeDialog");
})
}else{
updateSpu(form).then((response) => {
this.$modal.msgSuccess("修改成功");
this.$emit("closeDialog");
})
}
});
this.$emit("closeDialog");
},
/** 查询规格 */
getPropertyPageList() {
// 执行查询
getPropertyPage().then((response) => {
this.propertyPageList = response.data.list;
getPropertyListAndValue().then((response) => {
this.propertyPageList = response.data;
});
},
// 添加规格项目
changeSpec(val) {
let obj = this.propertyPageList.find(o => o.id == val);
let dynamicSpec = this.dynamicSpec;
let spec = dynamicSpec.find(o => o.specId == val)
let spec = this.dynamicSpec.find(o => o.specId == val)
spec.specId = obj.id;
spec.specName = obj.name;
spec.specValue = obj.propertyValueList;
this.dynamicSpec = dynamicSpec;
spec.specValue = obj.values;
this.buildRatesFormRates();
},
updateType(id){
getSpu(id).then((response) =>{
getSpuDetail(id).then((response) =>{
let data = response.data;
this.baseForm.id=data.id;
this.baseForm.name=data.name;
@ -452,6 +442,7 @@ export default {
r.costPrice = this.divide(r.costPrice, 100)
})
if(this.ratesForm.spec == 2){
this.activeSwitch = true;
data.productPropertyViews.forEach(p=>{
let obj = {};
obj.specId = p.propertyId;